Commit 2d85c99
authored
feat(ui): files-uploaded stat card with sparkline and smoother Activity load-in (#157)
## Feature A: files-uploaded stat card with sparkline
A new **Files uploaded** tile sits immediately after the throughput tile
in the
Activity header, in the same Grafana stat-panel style: a headline count
with the
last 5 minutes of per-bucket file counts drawn behind it.
The chart itself was extracted rather than duplicated.
`SparklineStatTile.vue`
now owns the geometry, the marks, the hover crosshair/readout and the
empty
state; `ThroughputStatTile.vue` became a thin wrapper that turns bytes
into a
rate, and `FilesUploadedStatTile.vue` is its sibling that keeps a count
a count
(a bucket reads as "6 files", never "0.6 files/s"). Counts are
pluralized and
grouped through `Intl.NumberFormat`, all strings via vue-i18n.
Both tiles read one window: the headline is the summary's new
`throughputWindowFiles`, the same window `throughputWindowBytes` already
covered, and both sparklines come from one query over one bucketisation.
### Wire-shape change (breaking, in-tree only)
`activity_throughput_series` now returns `{ bytes: u64[], files: u64[]
}`
instead of a bare `u64[]`. Both arrays are dense, oldest-first and the
same
length, so bucket `i` means the same slice of time in each. The frontend
is the
only caller and is updated in this PR; the store treats a bare-array
response
(a skewed backend) as "no data" rather than plotting indices.
### Bug fixed along the way
`activity_summary` and the series both filtered `event_type =
'upload_done'`,
which silently **dropped every `bundle_upload` row** - the V2-bundling
rows that
pack N small files into one object. A source whose files get bundled
therefore
under-reported "Uploaded today / this week" and read as idle on the
throughput
tile. Both queries now match `event_type IN ('upload_done',
'bundle_upload')`,
and file counts use `COALESCE(file_count, 1)` so a plain upload counts
as one
file and a bundle counts as all of its members. Covered by new tests.
## Feature B: smoother Activity load-in
Switching to the Activity tab painted empty tiles, an empty filter bar
and
"Showing 0 of 0", then swapped each one out as its query landed. The
view now
holds a skeleton of its own shape (tiles, filter bar, table) until the
first
load settles - in a `finally`, so a failed load falls through to its
error state
rather than pulsing forever - then fades the real content in with a
one-shot CSS
animation. `prefers-reduced-motion` disables both the fade and the
pulse.
A plain keyframe rather than `<Transition>`: the content enters a
fragment that
was never in the DOM, so there is nothing to transition from, and a
keyframe
cannot get stuck mid-flight.
## Also
`loadEventTypeOptions` now coerces a non-array response to `[]`. An
unregistered/skewed command *resolves* `undefined` instead of rejecting,
which
crashed the view's `eventTypeOptions.length` read on the next render.
## Tests
- `cargo test -p driven-core state::` - 83 passed, 0 failed
- `vitest run` - 365 passed, 0 failed (38 files)
- `cargo clippy --workspace --all-targets` - no warnings
- `cargo fmt --all`, `eslint .` (0 errors), `vue-tsc --noEmit` clean
- `.sqlx/` offline cache regenerated via `just sqlx-prepare`
New: sqlite tests for per-bucket file counts, bundle rows counting all
members,
and the summary's window file count; mount tests for `SparklineStatTile`
and
`FilesUploadedStatTile`; an `Activity.vue` test covering the skeleton,
the
failed-load path and the new tile's position and headline.
🤖 Generated with [Claude Code](https://claude.com/claude-code)1 parent eed80a6 commit 2d85c99
20 files changed
Lines changed: 1444 additions & 451 deletions
File tree
- .sqlx
- crates/driven-core/src/state
- src-tauri/src/commands
- ui/src
- __tests__
- components
- ipc
- locales
- stores
- views
Lines changed: 0 additions & 28 deletions
This file was deleted.
Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
495 | 495 | | |
496 | 496 | | |
497 | 497 | | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
498 | 506 | | |
499 | 507 | | |
500 | 508 | | |
501 | 509 | | |
502 | 510 | | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
503 | 528 | | |
504 | 529 | | |
505 | 530 | | |
| |||
1508 | 1533 | | |
1509 | 1534 | | |
1510 | 1535 | | |
1511 | | - | |
1512 | | - | |
| 1536 | + | |
| 1537 | + | |
1513 | 1538 | | |
1514 | 1539 | | |
1515 | 1540 | | |
| |||
1529 | 1554 | | |
1530 | 1555 | | |
1531 | 1556 | | |
1532 | | - | |
1533 | | - | |
1534 | | - | |
1535 | | - | |
| 1557 | + | |
| 1558 | + | |
| 1559 | + | |
| 1560 | + | |
1536 | 1561 | | |
1537 | 1562 | | |
1538 | | - | |
1539 | | - | |
1540 | | - | |
1541 | | - | |
| 1563 | + | |
| 1564 | + | |
| 1565 | + | |
| 1566 | + | |
| 1567 | + | |
| 1568 | + | |
| 1569 | + | |
1542 | 1570 | | |
1543 | 1571 | | |
1544 | 1572 | | |
| |||
1548 | 1576 | | |
1549 | 1577 | | |
1550 | 1578 | | |
1551 | | - | |
| 1579 | + | |
1552 | 1580 | | |
1553 | | - | |
| 1581 | + | |
1554 | 1582 | | |
1555 | 1583 | | |
1556 | 1584 | | |
| |||
0 commit comments