Dashboard analytics improvements #83

Closed
opened 2026-03-08 17:27:18 +01:00 by myrmidex · 1 comment
Owner

Summary

Improve the dashboard beyond basic counts to show meaningful trends and breakdowns.

Context

DashboardStatsService currently provides period-based totals (articles_fetched, articles_published, percentage). There are no per-feed or per-channel breakdowns, no trend data, and no visual charts.

Ideas

  • Per-feed article count breakdown
  • Per-channel publish count breakdown
  • Trend chart: articles fetched/published over time (last 7/30 days)
  • Approval rate over time
  • Publishing success/failure rate
  • Most active feeds ranking
  • Recent activity feed (last N actions: fetched, approved, published, errors)
  • Decide on charting approach (Alpine.js + Chart.js, or server-rendered)
## Summary Improve the dashboard beyond basic counts to show meaningful trends and breakdowns. ## Context `DashboardStatsService` currently provides period-based totals (articles_fetched, articles_published, percentage). There are no per-feed or per-channel breakdowns, no trend data, and no visual charts. ## Ideas - [x] Per-feed article count breakdown - [x] Per-channel publish count breakdown - [x] Trend chart: articles fetched/published over time (last 7/30 days) - [x] Approval rate over time - [x] Publishing success/failure rate - [x] Most active feeds ranking - [x] Recent activity feed (last N actions: fetched, approved, published, errors) - [x] Decide on charting approach (Alpine.js + Chart.js, or server-rendered)
myrmidex added this to the v2.0.0 milestone 2026-03-08 17:27:18 +01:00
myrmidex added the
enhancement
label 2026-03-08 17:27:18 +01:00
myrmidex modified the milestone from v2.0.0 to v1.4.0 2026-03-09 00:12:08 +01:00
myrmidex self-assigned this 2026-08-11 23:33:07 +02:00
Author
Owner

Delivered across 16 commits (ef5346e..a8d9655).

Foundation

  • App\Support\DateRange — arbitrary from/to range replaces named periods; the five presets survive only as UI buttons populating the two date fields
  • One class per stat in app/Dashboard/Stats/, with Stat (identity), BreakdownStat, SeriesStat, and a DailySeriesStat template-method base carrying the too-wide-range guard
  • Livewire 4 islands per panel, so a range change re-renders only the affected panels

Panels

  • Articles per feed, publications per channel (breakdowns)
  • Fetched vs published trend, approval rate, publish success rate (charts)
  • Recent activity, surfacing the existing Activity page infrastructure

Charting: Chart.js 4.5.1 bundled through Vite, driven by an Alpine trendChart() component. Livewire is now bundled through Vite too rather than injected.

Notes on two items

Most active feeds ranking — resolved without new code. ArticlesPerFeed already ranks feeds by article count descending, which is a most-active-feeds ranking; a separate published-based panel would have shown near-identical numbers beside it.

Publish success rate — buckets on route_articles.updated_at rather than a dedicated stamp, chosen deliberately to keep it a single commit. A retry re-dates its article into the retry day's bucket, so a past day's rate can shift. Pinned as intended by PublishSuccessRateTest::test_a_retry_moves_the_article_to_the_retry_day.

Breaking change (approved): /api/v1/dashboard/stats now takes ?from=&to= and returns range: {from, to}; available_periods and current_period are gone. Both params are required together — a lone ?from= now returns 422 instead of silently defaulting to today.

Verification: 1143 tests / 2808 assertions green, Pint clean (338 files), PHPStan clean. pr-reviewer ran over the combined diff and found no must-fix issues; its should-fix findings were addressed in a8d9655 (API partial-range validation, a shared ActivityLog::scopeWithSubjectDetails() eager-load, dead-code removal, and a query-count regression test). Charts confirmed rendering in a browser.

Also added 2024_01_01_000021_add_decided_at_to_route_articles — approval decisions are now timestamped, which the approval-rate chart buckets on.

Delivered across 16 commits (`ef5346e`..`a8d9655`). **Foundation** - `App\Support\DateRange` — arbitrary from/to range replaces named periods; the five presets survive only as UI buttons populating the two date fields - One class per stat in `app/Dashboard/Stats/`, with `Stat` (identity), `BreakdownStat`, `SeriesStat`, and a `DailySeriesStat` template-method base carrying the too-wide-range guard - Livewire 4 islands per panel, so a range change re-renders only the affected panels **Panels** - Articles per feed, publications per channel (breakdowns) - Fetched vs published trend, approval rate, publish success rate (charts) - Recent activity, surfacing the existing Activity page infrastructure **Charting:** Chart.js 4.5.1 bundled through Vite, driven by an Alpine `trendChart()` component. Livewire is now bundled through Vite too rather than injected. **Notes on two items** *Most active feeds ranking* — resolved without new code. `ArticlesPerFeed` already ranks feeds by article count descending, which is a most-active-feeds ranking; a separate published-based panel would have shown near-identical numbers beside it. *Publish success rate* — buckets on `route_articles.updated_at` rather than a dedicated stamp, chosen deliberately to keep it a single commit. A retry re-dates its article into the retry day's bucket, so a past day's rate can shift. Pinned as intended by `PublishSuccessRateTest::test_a_retry_moves_the_article_to_the_retry_day`. **Breaking change (approved):** `/api/v1/dashboard/stats` now takes `?from=&to=` and returns `range: {from, to}`; `available_periods` and `current_period` are gone. Both params are required together — a lone `?from=` now returns 422 instead of silently defaulting to today. **Verification:** 1143 tests / 2808 assertions green, Pint clean (338 files), PHPStan clean. `pr-reviewer` ran over the combined diff and found no must-fix issues; its should-fix findings were addressed in `a8d9655` (API partial-range validation, a shared `ActivityLog::scopeWithSubjectDetails()` eager-load, dead-code removal, and a query-count regression test). Charts confirmed rendering in a browser. Also added `2024_01_01_000021_add_decided_at_to_route_articles` — approval decisions are now timestamped, which the approval-rate chart buckets on.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lvl0/fedi-feed-router#83
No description provided.