Repository navigation
fix(dashboard): additive layout controls, compact defaults and stable ordering - #1045
Draft
antoinetoussaint-byte wants to merge 2 commits into
Draft
antoinetoussaint-byte wants to merge 2 commits into
antoinetoussaint-byte wants to merge 2 commits into
Conversation
…refuse re-implementing it (#1041) `@codefly-dev/ui/dashboard` shipped two tiers. The host's own operations pages used the metric one; a solution's dashboard got the older basic one — twice over, because `src/solutions/SolutionDashboard.tsx` carried its own copy of the widget-to-chart dispatch, the responsive grid, the card shell and the empty state beside the kit's `<Dashboard>`, and both copies drew with `charts.tsx`. So the same declaration rendered plainer inside the host than beside it, and there were two renderers of one thing to keep in step. **A fix at the owner.** `<Dashboard>` now paints with the metric tier, contract unchanged (a resolved `DashboardView` in; no fetching, no host context, no app imports). A scalar widget is a `StatTile` — the series total in the widget's format and unit, a sparkline and a delta against the previous bucket when the metric is bucketed over time, and a `MetricStateBadge` from `series.coverage`. A series widget is the kit's metric line/area/bar chart over a `ChartSeries`, which brings the axes, the crosshair readout, the legend and the visually-hidden data table that is the chart's screen-reader representation. A withheld total renders as a dash and never as `0`: `Metric.value` now admits `null` so that is a property of the primitive rather than of a state mapping, and a new `no_total` state says which of partial / empty / no-total withheld it. A series with no points reads as no data, not as a zero, because the aggregate omits a bucket instead of emitting an empty one. A sparkline and a bucket delta are drawn only for a time-bucketed series — a line through counts grouped by event type would assert an order the data does not have. `SolutionDashboard.tsx` now mounts that renderer and draws nothing itself. It keeps exactly what is the host's, composed through four new presentational slots: the + menu as header `actions`, the drag-to-reorder `SortableGrid` as `layout`, the per-widget audit query as `renderWidget`, and its own `empty` line. The queries stay one per widget — a widget whose metric errors still fails alone — which is why the kit also exports `DashboardWidget`, the same renderer for one widget: React fixes the number of hooks a component may call, so a varying set of per-widget queries cannot live in a renderer that takes the whole view. Its 16 existing tests still pass; two assertions and one helper moved onto the markup the metric tier produces, each with the reason in place. **A gate**, in the shape the kit's other consumer already uses: `src/components/__tests__/kit.test.ts`. A declaration taking a name `@codefly-dev/ui` exports must render the kit's component of that name or carry a reviewed entry saying what the two things are; `class-variance-authority` is no longer a dependency of this app (nothing imported it), so a kit-shaped primitive cannot be built here at all; and the two chart surfaces stay re-exports. Module-local declarations count, not only exported ones — that is how both duplicates in this tree arrived, and a gate matching only exports walked past both. Nine fixtures cover must-catch, must-not-catch, and the same duplication reached another way. Docs: `module/AUDIT_METRICS.md` gains "How a resolved series is drawn" — the tier, and a table of what each widget and each coverage state renders, which is what downstream consumers were guessing at; `module/services/frontend/AGENTS.md` says what the solution dashboard owns and what the kit draws; the kit's README names the tier and the slots. Refs #1041 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Oct 8, 2026
Contributor
Author
|
A lazybox agent is working on this. claim detail |
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.
Closes #1044. Includes the existing metric-tier implementation from #1043 / #1041 (commit 71b8749), reused after a coordination comment rather than implemented again. This is one self-contained PR against main; do not merge or deploy as part of this task.
A declaration with four scalar widgets followed by five series now renders compact scalar cards above half-width desktop charts, with single-column mobile layout. Short cards retain their natural height. The sortable host view uses the same kit geometry as a standalone dashboard.
Classification and ownership
runDashboard;fromDashboardDatacarries sections, columns and spans to the kit.Section, scalars useStatTile, and charts use the metric tier reused from Draw every dashboard with the kit's metric tier, and refuse re-implementing it (#1041) #1043.SortableGrid. Widget identity stays stable across band changes, and keyboard focus follows the widget ID.STOPGAPcomment inuseDashboardLayoutnames the missing durable settings field). They do not follow a viewer across devices. No new workaround or issue was introduced.Additive contract and rollout
Dashboard.columns,MetricWidget.spanandDashboard.sectionsare optional. Sections haveid,title, optionaldescriptionandcolumns; widgets refer to them with optionalsection. Column counts and spans are integers 1–4. Stack ignores sizing hints. Ungrouped widgets appear first, then sections in declared section order; widget order is preserved within each. Preferences reorder within a declared section and cannot change membership.All schema additions are additive: existing declarations continue working untouched. This validator ignores future unknown presentation fields on dashboards, widgets and sections, while metric/query validation stays strict. Already-released hosts used exact-key validation and will reject these new fields. Upgrade those hosts before sending the fields, or keep sending the legacy declaration. This PR cannot retroactively make old binaries ignore fields.
#958 already proposes the same
sections/ widgetsectionshape. This PR follows that vocabulary and adds column counts and spans, but does not include its broader section editing, metric-format or time-bucket changes. The overlapping section implementation in #958 will need reconciliation; it must not be merged independently without that review.Reordering diagnosis
Saved layouts were already keyed by widget ID, with a
seenlist, not by position. A reproduced three-to-nine-widget failure was an unchanged saved default: the parser appended new IDs after the three old tiles, interleaving newly declared scalars with old charts. An unchanged saved default now follows the current declaration. Real custom orders retain surviving IDs, append new IDs once and preserve intentional removals. The affected browser's actual saved value was unavailable, so this is a reproduced failure mode, not a claim that its historical storage was inspected.The nine-widget regression fixture is serialized directly from the supplied consumer declaration under a generic filename (
platform-signins.json), with all three platform events, nine metrics, the derived ratio andcount_distinctoveractor_idpreserved. Tests cover untouched order, saved three-to-nine growth, customized order/removal, renaming/reordering of declarations, section geometry, scalar and series spans, keyboard focus, and rejected cross-section drops.Documentation and generated artifacts
Behavior docs updated:
module/docs/plugin-manifest-schema.md,module/AUDIT_METRICS.md,module/services/frontend/AGENTS.md, andpackages/codefly-ui/README.md.Before layout edits,
prepare:frontendplus preview stylesheet regeneration returned a clean tree. The complete generated preview stylesheet is retained, as is Next'snext-env.d.tsemitted by native CI type generation (production paths). No generated output was hand-edited or selectively retained. Published versions move together: UI kits 0.12.7, SDK 0.3.18, manifest 0.1.2; npm regenerated the lockfile.Verification
go test dashboard.go graph_test.gopassed.codefly ci run --changed-file module/services/frontend/code/src/solutions/SolutionDashboard.tsx --temporary-portswas attempted twice. The first run reached 4,107 passing tests and exposed four assertions about UI co-versioning/shared versions and the section primitive; those were fixed, and the subsequent full pure suite passed. The final native rerun was terminated with exit code 137 before emitting a report. Its log does not establish the cause. The complete native gate, dependency audit, SBOM and container build remain unverified. GitHub checks are running on this draft PR.Not verified: a newly registered live consumer composition, production deployment, publication, or every service's full gate. Nothing was merged or deployed.