Skip to content

feat(dashboard): Week 3 AC 4 (bonus) - music analytics page with Chart.js#17

Merged
BODMAT merged 7 commits into
developmentfrom
feature/music-analytics
May 28, 2026
Merged

feat(dashboard): Week 3 AC 4 (bonus) - music analytics page with Chart.js#17
BODMAT merged 7 commits into
developmentfrom
feature/music-analytics

Conversation

@BODMAT

@BODMAT BODMAT commented May 28, 2026

Copy link
Copy Markdown
Owner

What

Implements the bonus AC 4 for Week 3: a /dashboard/music page that
visualises the correlation between music listening and coding activity.
Data is sourced from existing Track + Event + Session tables —
no new migrations required.

Changes

Code

  • types/music-stats.tsMusicStats response type and sub-types (TopArtist, TopTrack, ProductivityRow, HourBucket)
  • server/music.ts — five query functions (getTopArtists, getTopTracks, getMusicProductivity, getHourlyPattern, getMusicTotals); all aggregate correctly across sessions
  • app/api/v1/music/stats/route.tsGET /api/v1/music/stats?range=...; cookie-first auth + Bearer fallback; Zod query validation
  • app/dashboard/music/page.tsx — Server Component auth guard
  • app/dashboard/music/music-client.tsx"use client", TanStack Query, range picker, summary stats, empty state, skeleton
  • app/dashboard/music/top-artists-chart.tsx — horizontal Bar (Chart.js), cyan, COUNT(DISTINCT title) per artist
  • app/dashboard/music/productivity-chart.tsx — horizontal Bar (Chart.js), purple, events/min while track was active
  • app/dashboard/music/hourly-chart.tsx — grouped vertical Bar (Chart.js), 0–23 hours, listening min + event count
  • server/range.ts — extracted resolveRange from server/reports.ts (shared by both reports and music routes)
  • header-nav.tsx / mobile-nav.tsx — added MUSIC nav item

Tooling / config

  • n/a

Docs / plan

  • plans/week3/AC4-music-analytics.md — full design rationale, architecture, commit map, and verified decisions (updated post-review with logic fix notes)

Design decisions

  • Chart.js over D3 — Chart.js is already installed and used on the feed; using one charting library across the dashboard reduces cognitive overhead.
  • resolveRange extracted to server/range.ts — both the reports and music routes need it; moving it prevents duplication and avoids a circular import.
  • getTopArtists via $queryRaw — Prisma groupBy only supports COUNT(field) (all rows); COUNT(DISTINCT title) requires raw SQL to correctly count unique songs per artist.
  • getMusicProductivity GROUP BY artist, title + SUM — Track is @@unique([sessionId, artist, title]), so the same song across 5 sessions produces 5 rows; without aggregation each session appeared as a separate bar in the chart.
  • endedAt = null fallback — replaced COALESCE(endedAt, NOW()) with COALESCE(endedAt, capturedAt + make_interval(secs => listenedMs/1000)); the old fallback stretched the JOIN window to the present moment and inflated perMin for tracks whose end was not recorded.
  • getMusicTotals.totalTracks — uses groupBy [artist, title] to count distinct songs; _count: { id } was counting capture rows (one per session per track), not unique titles.

Verification

  • npx tsc --noEmit (dashboard) — clean
  • npm run lint (dashboard) — clean
  • npm run typecheck (extension) — n/a (no extension changes)
  • Manual: /dashboard/music loads with correct data for LAST 7 DAYS
  • Manual: range chips (TODAY / YESTERDAY / LAST 7D / LAST 30D / ALL TIME) re-fetch and re-render all charts
  • Manual: MUSIC nav item highlights active on /dashboard/music
  • Manual: MUSIC visible in mobile burger dropdown

Out of scope (future PRs)

  • Custom date range input on the music page
  • Hourly chart timezone correction (currently UTC — requires client-side timezone offset)
  • Drill-down view per track or artist

Closes

  • Week 3 AC 4 bonus (docs/Task.md — music analytics)

BODMAT added 7 commits May 28, 2026 20:21
- getTopArtists: COUNT(DISTINCT title) via $queryRaw (was counting rows, not unique tracks)
- getMusicProductivity: GROUP BY artist+title + SUM(listenedMs); endedAt null fallback uses capturedAt+listenedMs duration instead of NOW()
- getMusicTotals: totalTracks counts distinct (artist,title) pairs via groupBy (was counting all capture rows)
@vercel

vercel Bot commented May 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worktrace Ready Ready Preview, Comment May 28, 2026 5:49pm

@BODMAT BODMAT merged commit 8e181d8 into development May 28, 2026
4 checks passed
@BODMAT BODMAT deleted the feature/music-analytics branch May 28, 2026 17:51
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.

1 participant