Implements PRP-20 — PRPs/PRP-20-explorer-interactivity.md.
Turns the three primary Explorer pages (Sales, Stores, Products) from flat read-only views into an interactive analysis surface.
Scope
- Click-through detail views — two deep-linkable routes (
/explorer/stores/:storeId, /explorer/products/:productId): entity profile, date-scoped KPIs, revenue-over-time chart, top-contributors drilldown.
- Richer tables — server-side column sorting, CSV export, column-visibility toggles on Stores / Products / Runs.
- Charts on Sales — revenue-by-dimension bar chart + revenue-over-time line chart alongside the ranked list.
- Cross-filtering — filter / date / dimension state persisted in the URL; "View in Sales" link carries
store_id / product_id.
Backend (additive — no migration, no new slice, no app/main.py change, no .env var)
GET /analytics/timeseries — daily/weekly/monthly/quarterly aggregated sales, filterable by store_id / product_id / category.
GET /dimensions/stores and /dimensions/products gain optional allow-listed sort_by / sort_order query params.
- Also closes the
analytics slice test gap (no test files today).
Maintainer-confirmed decisions (2026-05-18)
- All four interactivity directions ship (detail views, richer tables, Sales charts, cross-filtering).
- Time-series data via a dedicated
GET /analytics/timeseries endpoint, not reused /analytics/drilldowns.
- Entity detail views are dedicated React Router route pages (deep-linkable), not Sheet/Dialog.
Task checklist
No Alembic migration. No new slice. No app/main.py change. No new .env var.
Implements PRP-20 —
PRPs/PRP-20-explorer-interactivity.md.Turns the three primary Explorer pages (Sales, Stores, Products) from flat read-only views into an interactive analysis surface.
Scope
/explorer/stores/:storeId,/explorer/products/:productId): entity profile, date-scoped KPIs, revenue-over-time chart, top-contributors drilldown.store_id/product_id.Backend (additive — no migration, no new slice, no
app/main.pychange, no.envvar)GET /analytics/timeseries— daily/weekly/monthly/quarterly aggregated sales, filterable bystore_id/product_id/category.GET /dimensions/storesand/dimensions/productsgain optional allow-listedsort_by/sort_orderquery params.analyticsslice test gap (no test files today).Maintainer-confirmed decisions (2026-05-18)
GET /analytics/timeseriesendpoint, not reused/analytics/drilldowns.Task checklist
GET /analytics/timeseriesendpoint (schemas + service + routes)sort_by/sort_orderon/dimensionsstore + product listingsuse-timeseries,use-lifecycle-curve, sort params)csv-export.test.ts)devNo Alembic migration. No new slice. No
app/main.pychange. No new.envvar.