feat(panel): customisable dashboard with registrable widgets (spec 0058)#2579
Merged
Conversation
Add a per-staff customisable dashboard to the Inertia panel: a widget extension point, nine first-party widgets over real order/product data, a brand-anchored chart palette, and two new public chart components. - Widget extension point: `Widget` abstract, `WidgetRegistry`, `DashboardRange`/`WidgetSpan`, `Section::widgets()` + `Panel::widget()`, ordered through the shared `OrderResolver` and permission-filtered. Each visible widget's `data()` ships as a deferred Inertia prop in its own group, so widgets load in parallel behind skeletons and hidden widgets cost nothing. - Per-staff layout persisted server-side in a new `staff_preferences` table (merge-on-read reconciles stored layout with the live registry); PUT/DELETE preference endpoints; customise mode with drag reorder, hide, add dialog and reset. - Nine widgets: KPIs (deltas + sparklines), revenue-over-time, recent orders, top products, sales by channel, new-vs-returning, customer-group split, low stock, tasks. Revenue valued in the default currency via the exchange rate captured at placement. - Brand chart palette (`--color-chart-*`, anchored on the icon blue), dataviz-validated in both themes; `Sparkline` and `DonutChart` added to the public surface; `TimeSeriesChart` recoloured off sage. - Large summary totals abbreviate (£1.4M) in tight readouts via `OrderMetrics::formatCompact()`, exact value preserved in tooltips and donut legends. - Example add-on gains a dashboard widget; extension docs updated. Translations across all 16 locales. Spec: specs/0058-panel-dashboard-widgets.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The cross-db job loads every suite's files in one process; two bare top-level helper functions collided there — the dashboard OrderMetrics test's `placedOrder()` with core's `DerivedOrderStatusTest`, and the generically named `metrics()`. Rename to `placedDashboardOrder()` / `dashboardMetrics()`. Per-suite runs never hit this because each suite loads only its own files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Implements the panel dashboard from spec 0058 — a per-staff customisable dashboard for the Inertia panel, with a widget extension point add-ons can plug into, nine first-party widgets over real data, and a brand-anchored chart palette.
What's included
Widget extension point
Widgetabstract +WidgetRegistry, registered viaSection::widgets()orPanel::widget(), ordered through the sharedOrderResolverand permission-filtered.data(DashboardRange)ships as a deferred Inertia prop in its own group, so widgets load in parallel behind skeletons; a slow add-on widget never blocks the rest, and hidden widgets compute nothing.Per-staff layout
staff_preferencestable (generic per-staff key/value). Merge-on-read reconciles the stored layout with the live registry (uninstalled keys drop, new widgets append with defaults).PUT/DELETE panel/dashboard/preferences; customise mode with drag-reorder, hide, an "Add a widget" dialog, and reset-to-defaults. Range + layout persist on every change.Nine first-party widgets — KPIs (deltas + sparklines), revenue-over-time, recent orders, top products, sales by channel, new-vs-returning, customer-group split, low stock, tasks. Revenue is valued in the default currency via the exchange rate captured at placement (the spec-0050 basis); figures aggregate in SQL.
Charts & brand colours
--color-chart-*theme tokens anchored on the Lunar icon blue, dataviz-validated in light and dark.TimeSeriesChartrecoloured off sage.SparklineandDonutChartcomponents (dependency-free SVG), exported viaui.ts/@lunarphp/panel.£1.4M) in tight readouts viaOrderMetrics::formatCompact(), with the exact value preserved in tooltips and donut legends.Add-on surface — the example add-on gains a
CustomerCountWidget; extension docs (TMP-DOCS.md, example README) updated. Translations across all 16 locales.Design decisions
k/M/Bsuffixes are en-centric via lang keys (ICU has no compact-currency mode); locale-perfect compaction is a documented follow-on.Testing
npm run build+ add-on build.🤖 Generated with Claude Code