Add the collection dashboard
One GET /api/stats call, aggregated in a single pass over the library.
Completion funnel, breakdowns by system, genre, decade, condition and
rating, a backlog that links into the filtered library, and a value card.
Form was picked before colour, and most of the page is not a chart: single
numbers are stat tiles, the breakdowns are bar lists with the value printed
per row, which is also the table view.
The colour work, in order:
* one hue for the breakdown bars — identity is on the axis labels, so
colour has nothing to encode, and a darker-where-bigger ramp would just
double-encode bar length
* an ordinal ramp for the funnel, since owned/played/finished are ordered
stages rather than peers
* validated with the dataviz validator against this app's real card
surfaces rather than a reference one, which caught that the documented
ordinal light-end measures 1.91:1 here and fails the 2:1 floor; the ramp
starts a step darker
* status colour used once, on the stale-valuation notice, with an icon and
text so it never carries meaning alone
Two bugs found by rendering it and looking, which the validator cannot see:
* the ratings card was showing condition data under a ratings heading — a
chart whose title did not describe its contents. Fixed by adding a real
rating distribution rather than relabelling the card.
* dark mode rendered light cards on a dark page. Copying the reference
pattern's `color-scheme` onto the container overrode how every
descendant resolved light-dark(), and the `:root`-prefixed media
override never matched at all, because Angular's emulated encapsulation
scopes selectors in component styles. Both replaced by light-dark()
values that inherit the app's own scheme.
The value card reports coverage, age and source beside the total, and flags
valuations older than 90 days, because a bare figure mixes fresh with stale
and silently omits everything unpriced.
148 backend tests.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -291,6 +291,40 @@ parser follows the widely-used convention — integer pennies under hyphenated
|
||||
keys — and is tolerant enough that a naming mismatch reads as "no price" rather
|
||||
than throwing. `PriceChartingProvider.Parse` is the one place to adjust.
|
||||
|
||||
### Dashboard
|
||||
|
||||
`/dashboard` — one `GET /api/stats` call, aggregated server-side in a single pass
|
||||
over the library rather than a dozen grouped queries that could disagree.
|
||||
|
||||
Form was chosen before colour, and most of the page is deliberately not a chart:
|
||||
headline numbers are stat tiles, the backlog is a link into a filtered library
|
||||
view, and the breakdowns are horizontal bar lists with the value printed on each
|
||||
row — which doubles as the table view.
|
||||
|
||||
Colour decisions worth keeping:
|
||||
|
||||
- **One hue for the breakdown bars.** Identity is carried by the axis labels, so
|
||||
colour has nothing to encode; twelve systems in twelve hues would be twelve
|
||||
ways to be wrong, and a darker-where-bigger ramp would double-encode length.
|
||||
- **An ordinal ramp for the funnel**, because owned → played → finished are
|
||||
ordered stages, not peer categories.
|
||||
- **The palette was validated with the dataviz validator against this app's own
|
||||
card surfaces** (`#f8f2f6` light, `#1d1b1e` dark), not against a reference
|
||||
surface. That mattered: the documented ordinal light-end measured 1.91:1 here
|
||||
and failed the 2:1 floor, so the ramp starts a step darker.
|
||||
- **Status colour appears once**, on the stale-valuation notice, always with an
|
||||
icon and text so it never carries meaning alone.
|
||||
|
||||
The value card is deliberately wordy. A bare total silently mixes fresh and old
|
||||
valuations and excludes everything unpriced, so coverage, age and source travel
|
||||
with the figure, and a valuation older than 90 days is called out.
|
||||
|
||||
Theming note: the chart variables use `light-dark()` rather than a
|
||||
`prefers-color-scheme` block. Angular's emulated encapsulation scopes selectors
|
||||
in component styles, so a `:root`-prefixed media override never matches from
|
||||
there — and setting `color-scheme` on the container overrides how every
|
||||
descendant resolves `light-dark()`, which rendered light cards on a dark page.
|
||||
|
||||
### Database changes
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user