Skip to content

feat(ui): dashboard with collapsible sections and history charts - #105

Open
khaosdoctor wants to merge 32 commits into
roziscoding:mainfrom
khaosdoctor:feat/dashboard
Open

khaosdoctor wants to merge 32 commits into
roziscoding:mainfrom
khaosdoctor:feat/dashboard

Conversation

@khaosdoctor

@khaosdoctor khaosdoctor commented Nov 9, 2026 •

Copy link
Copy Markdown

What does this PR do?

Redesigns the Dashboard into collapsible sections with history charts. Builds on #104 (its uploads data and live progress), so its commits show up here until #104 is merged.

There's a range picker in the top bar now (5 min, 30 min, 60 min, 24 h, 7 days, 30 days) and every history chart follows it. Sections, top to bottom, each one folds and starts open:

  • Lifetime: files imported, bytes downloaded, bytes uploaded, upload/download ratio, transfers.
  • Connections: peers and servers, as before.
  • Activity: bytes moved and average speed, both mirrored on the x axis, uploads up and downloads down. Bars for the bytes, areas for the speed.
  • Peer split: which peers pulled the most from you, and which you pulled the most from.
  • Library: title count of your catalog and each peer's over time, and the top 5 files peers pulled from you.
  • Imports and failures: fastest, average and slowest import in the range on top of the import time chart, and the current failed downloads by error code as a stacked column, where the codes past the top 7 go into an "Other" segment that a second column breaks down.
  • Transfers: running downloads and uploads, moved to the bottom.

Backend:

  • New GET /stats (management API) with a range of 5m, 30m, 1h, 24h, 7d or 30d (the default, also used for anything it doesn't know). Each range comes back as 24 to 30 buckets (10 s, 1 min, 2 min, 1 h, 6 h or a day) plus the fastest/average/slowest single import and lifetime rankings. Computed in SQL from the download and upload rows; nothing is streamed, the page refetches once per bucket, between 10 s and a minute.
  • Migration 0016 adds downloads.imported_at (set when *arr confirms the import) and a catalog_size_history table, one row per source per UTC day, written after every catalog sync. Both start recording after the upgrade, so those charts fill in from then on. Catalog size stays daily, so on the short ranges it shows that day's reading.
  • Download speed is bytes over each transfer's start-to-finish time, so time waiting between retries pulls it down. Upload speed comes from the receipts introduced in feat: downloads UX, failed-download cleanup, live progress and uploads view #104, so only confirmed uploads count.

The charts are plain SVG and CSS (no chart library), with a fixed categorical palette checked for color vision deficiencies in both themes, hover tooltips, and a screen-reader table per chart.

Related issue

Depends on #104.

Screenshots

image image image image image

Checklist

  • Commits follow Conventional Commits
  • Commits are signed (GitHub enforces this — unsigned commits are rejected)
  • mise run lint:fix was run and its output is committed
  • mise run test passes (and mise run test:e2e if transfer/request paths changed)
  • Docs updated if behavior or configuration changed

Copilot AI balanced review requested due to automatic review settings November 9, 2026 00:03
@greptile-apps

greptile-apps Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[High impact] The PR appears safe to merge, with a remaining non-blocking issue where stalled downloads can keep showing an old speed.

Summary

Adds collapsible dashboard sections with selectable history ranges, transfer charts, peer rankings, catalog history, and import times.

  • The dashboard shows history charts for transfers, peers, the library, and imports.
  • The management UI records files that newer peers pull from this instance.
  • An optional setting retries failed downloads before removing them.
  • Operators can select downloads and run retry, cancel, or delete on a group.
  • Running downloads show their recent speed and estimated time left.
  • The UI displays the version supplied by its build or release tag.

Reviews (8) · Last reviewed commit: "fix: date the dashboard history migratio..." · Reviewed by Greptile

Comment thread apps/ui/app/pages/index.vue Resolved

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved findings compromise statistics accuracy and refresh scalability, allow inflated upload totals, and leave dashboard error handling and keyboard/screen-reader accessibility incomplete.

7 open issues
What changed in this PR

Redesigns jack's dashboard around collapsible sections and history charts,
building on #104's upload tracking and live download progress.

Changes:

  • Adds daily activity charts and lifetime rankings backed by /stats.
  • Records catalog-size history and import timestamps for dashboard reporting.
  • Includes #104's transfer controls, uploads view, and build-version display.
File Description
website/​reference/​configuration.md Documents failed-download retry and removal settings.
website/​guide/​management-ui.md Describes dashboard charts and uploads.
apps/​ui/​nuxt.config.ts Resolves the displayed build version.
apps/​ui/​Dockerfile Accepts the version as a build argument.
apps/​ui/​app/​utils/​format.ts Adds transfer-rate, date, and version formatting.
apps/​ui/​app/​utils/​format.test.ts Tests release-version formatting.
apps/​ui/​app/​types/​management.ts Defines upload and dashboard statistics types.
apps/​ui/​app/​pages/​uploads.vue Adds a filterable upload-history page.
apps/​ui/​app/​pages/​settings.vue Updates version display and adds repository link.
apps/​ui/​app/​pages/​index.vue Builds the collapsible dashboard and polls statistics.
apps/​ui/​app/​pages/​downloads.vue Adds bulk actions and live transfer rates.
apps/​ui/​app/​layouts/​default.vue Adds uploads navigation and sidebar version.
apps/​ui/​app/​components/​RankBars.vue Renders ranked statistics as bars.
apps/​ui/​app/​components/​LineChart.vue Adds SVG time-series charts.
apps/​ui/​app/​components/​DownloadsSection.vue Exposes failed-download cleanup settings.
apps/​ui/​app/​components/​DashSection.vue Provides collapsible dashboard sections.
apps/​ui/​app/​components/​ColumnChart.vue Adds stacked daily-volume charts.
apps/​ui/​app/​assets/​css/​main.css Defines chart colors for both themes.
apps/​backend/​src/​modules/​uploads/​uploads.service.ts Handles upload tracking and receipt expiry.
apps/​backend/​src/​modules/​uploads/​uploads.repository.ts Persists upload history and summaries.
apps/​backend/​src/​modules/​status/​status.router.ts Exposes uploads, streams, and statistics routes.
apps/​backend/​src/​modules/​status/​status.controller.ts Supplies dashboard data and coalesces live updates.
apps/​backend/​src/​modules/​stats/​stats.ts Computes daily series and lifetime rankings.
apps/​backend/​src/​modules/​stats/​stats.repository.ts Records daily catalog sizes.
apps/​backend/​src/​modules/​peer/​peer.router.ts Tracks file requests and accepts upload receipts.
apps/​backend/​src/​modules/​peer/​peer.controller.ts Explains zero-copy tracking in comments.
apps/​backend/​src/​modules/​downloads/​live-progress.ts Tracks in-memory progress and recent speed.
apps/​backend/​src/​modules/​downloads/​failed-download-reaper.ts Retries and removes persistent failures.
apps/​backend/​src/​modules/​downloads/​downloads.service.ts Feeds live progress and guards automated deletion.
apps/​backend/​src/​modules/​downloads/​downloads.repository.ts Records import completion timestamps.
apps/​backend/​src/​modules/​config/​config.service.ts Updates comments on live configuration behavior.
apps/​backend/​src/​modules/​config/​config.router.ts Documents immediately applied settings.
apps/​backend/​src/​modules/​catalog/​catalog-sync.service.ts Adds a post-sync history callback.
apps/​backend/​src/​middleware/​require-auth.ts Exposes key IDs for receipt ownership checks.
apps/​backend/​src/​management-app.ts Injects upload and statistics services.
apps/​backend/​src/​lib/​servers/​peer.ts Reports live bytes and sends upload receipts.
apps/​backend/​src/​lib/​openapi.ts Adds upload API documentation grouping.
apps/​backend/​src/​lib/​config.ts Validates failed-download cleanup settings.
apps/​backend/​src/​index.ts Wires history recording and transfer services.
apps/​backend/​src/​database/​schema.ts Defines uploads and dashboard-history fields.
apps/​backend/​src/​app.ts Connects upload tracking to peer routes.
apps/​backend/​src/​__tests__/​uploads-repository.test.ts Tests upload persistence and lifecycle.
apps/​backend/​src/​__tests__/​upload-tracking.test.ts Tests tracking and receipt routes.
apps/​backend/​src/​__tests__/​stream-coalesce.test.ts Tests stream notification coalescing.
apps/​backend/​src/​__tests__/​stats.test.ts Tests statistics and catalog-history recording.
apps/​backend/​src/​__tests__/​qbittorrent-mapper.test.ts Adds import timestamps to fixtures.
apps/​backend/​src/​__tests__/​peer-download.test.ts Tests sending upload receipts.
apps/​backend/​src/​__tests__/​live-progress.test.ts Tests speed calculations and progress overlays.
apps/​backend/​src/​__tests__/​failed-download-reaper.test.ts Tests retry, removal, and cancellation safeguards.
apps/​backend/​src/​__tests__/​downloads-service.test.ts Extends configuration fixtures.
apps/​backend/​drizzle/​meta/​0015_snapshot.json Captures dashboard-history schema state.
apps/​backend/​drizzle/​meta/​0014_snapshot.json Captures upload schema state.
apps/​backend/​drizzle/​meta/​_journal.json Registers the new migrations.
apps/​backend/​drizzle/​0015_dashboard_history.sql Creates catalog history and import timestamps.
apps/​backend/​drizzle/​0014_uploads.sql Creates upload storage and indexes.
.github/​workflows/​publish.yml Passes the image version into UI builds.

Review effort: Balanced

Comment thread apps/backend/src/modules/stats/stats.ts Resolved
Comment thread apps/ui/app/components/DashSection.vue Resolved
Comment thread apps/ui/app/components/LineChart.vue Resolved
Comment thread apps/ui/app/pages/index.vue Resolved
@khaosdoctor

Copy link
Copy Markdown
Author

@greptileai review

Comment thread apps/ui/app/components/LineChart.vue Resolved
@khaosdoctor

Copy link
Copy Markdown
Author

@greptileai review

Comment thread apps/backend/src/lib/servers/peer.ts Resolved
Comment on lines +41 to +44
// Measured between progress writes (every 10 s / 64 MiB); only while running.
const speedBps = d.status === 'downloading'
? d.speedBps
: null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Stopped downloads show old speeds

Removing LiveProgressTracker also removes the check that stopped showing old speeds. After a transfer has reported a speed, a network stall or retry delay leaves its status as downloading. enrichDownload therefore keeps returning the old speedBps and ETA even when no bytes are arriving, making the download look like it is still moving.

Clear the speed when an attempt fails, and expire old samples with a notification so open pages update too.

Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ffca03e without bringing back a second byte count: a failed attempt clears speed_bps (and notifies) before its retry wait, and enrichDownload drops a speed whose row hasn't been written for 30 s, so a stall shows no speed past that. The 10 s / 64 MiB cadence is discussed in the maintainer's thread above.

Comment thread apps/backend/drizzle/meta/_journal.json Resolved

@roziscoding roziscoding left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed the current stacked delta after the follow-up fixes. Existing review findings are addressed, and I found no new actionable issues. Backend tests, UI typecheck, targeted lint, and diff checks pass.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants