feat(ui): files-uploaded stat card with sparkline and smoother Activity load-in - #157
Merged
Merged
Conversation
…ty load-in
Adds a "Files uploaded" stat tile next to the throughput tile, sharing one
generalized sparkline component, and replaces the Activity tab's load-in
flash with a skeleton that fades into the real content.
Backend: activity_throughput_series now returns {bytes, files} per bucket
from one query, and both it and activity_summary count bundle_upload rows
(which they previously dropped entirely).
Contributor
Coverage
Gate: passed - no coverage regression (epsilon 0.1 pp). |
This was referenced Jul 25, 2026
pmaxhogan
added a commit
that referenced
this pull request
Jul 25, 2026
## Summary - `telemetry_events_since` counted only `upload_done` rows via `COUNT(*)`, so `bundle_upload` rows (N member files each) were invisible to anonymous telemetry - the same undercount class #157 fixed for the dashboard summary and throughput series. - Now uses the shared upload-row definition: `event_type IN ('upload_done','bundle_upload')` with `SUM(COALESCE(file_count, 1))` for files; bytes sum both types. - `.sqlx` offline cache regenerated (`just sqlx-prepare` steps). ## Testing - Extended `telemetry_events_since_aggregates_uploads_errors_and_deep_verify` with in-window, pre-window, and future-dated `bundle_upload` rows (in-window bundle of 5 files counts as 5; window bounds still apply). - `cargo test -p driven-core --lib` telemetry test green; `cargo test -p driven-app --lib telemetry` 31/31; clippy clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JLB3E2Jm7knNJd37fVpH8X Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
pmaxhogan
added a commit
that referenced
this pull request
Jul 25, 2026
🤖 I have created a release *beep* *boop* --- ## [2.3.0](v2.2.0...v2.3.0) (2026-07-25) ### Features * **core:** record a backup_done activity row when a run completes ([#160](#160)) ([90cde5c](90cde5c)) * **ui:** files-uploaded stat card with sparkline and smoother Activity load-in ([#157](#157)) ([2d85c99](2d85c99)) * **ui:** live streaming folder-tree preview for the exclusion editor ([#158](#158)) ([47ebe14](47ebe14)) ### Bug Fixes * **telemetry:** count bundled uploads in the anonymous aggregate ([#159](#159)) ([11af7ea](11af7ea)) * **ui:** label bundle_upload and hook activity event types ([#154](#154)) ([2998c76](2998c76)) * **ui:** make the backing-up bar a true determinate progress bar ([#155](#155)) ([eed80a6](eed80a6)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.vuenow owns the geometry, the marks, the hover crosshair/readout and the empty
state;
ThroughputStatTile.vuebecame a thin wrapper that turns bytes into arate, and
FilesUploadedStatTile.vueis 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 windowthroughputWindowBytesalreadycovered, and both sparklines come from one query over one bucketisation.
Wire-shape change (breaking, in-tree only)
activity_throughput_seriesnow returns{ bytes: u64[], files: u64[] }instead of a bare
u64[]. Both arrays are dense, oldest-first and the samelength, so bucket
imeans the same slice of time in each. The frontend is theonly 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_summaryand the series both filteredevent_type = 'upload_done',which silently dropped every
bundle_uploadrow - the V2-bundling rows thatpack 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 onefile 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 staterather than pulsing forever - then fades the real content in with a one-shot CSS
animation.
prefers-reduced-motiondisables both the fade and the pulse.A plain keyframe rather than
<Transition>: the content enters a fragment thatwas never in the DOM, so there is nothing to transition from, and a keyframe
cannot get stuck mid-flight.
Also
loadEventTypeOptionsnow coerces a non-array response to[]. Anunregistered/skewed command resolves
undefinedinstead of rejecting, whichcrashed the view's
eventTypeOptions.lengthread on the next render.Tests
cargo test -p driven-core state::- 83 passed, 0 failedvitest run- 365 passed, 0 failed (38 files)cargo clippy --workspace --all-targets- no warningscargo fmt --all,eslint .(0 errors),vue-tsc --noEmitclean.sqlx/offline cache regenerated viajust sqlx-prepareNew: sqlite tests for per-bucket file counts, bundle rows counting all members,
and the summary's window file count; mount tests for
SparklineStatTileandFilesUploadedStatTile; anActivity.vuetest covering the skeleton, thefailed-load path and the new tile's position and headline.
🤖 Generated with Claude Code