From cea442d125e05ba0275cd9f0acc730533bedfc38 Mon Sep 17 00:00:00 2001 From: stroblme Date: Thu, 15 Oct 2026 15:54:47 +0200 Subject: [PATCH] Draw a round dashboard in the mosaic as its circle with a dot per face DashboardSummary carries the dashboard's shape, so the list no longer lays a round dashboard's faces out at grid places they never use. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01WgBfkm4rCmtd1T4vHWjEPx --- backend/fluksio/flow/dashboards.py | 4 +++ backend/tests/flow/test_dashboards.py | 7 +++++ frontend/src/client/schemas.gen.ts | 6 ++++ frontend/src/client/types.gen.ts | 1 + .../src/components/Common/DashboardMosaic.tsx | 28 +++++++++++++++++++ 5 files changed, 46 insertions(+) diff --git a/backend/fluksio/flow/dashboards.py b/backend/fluksio/flow/dashboards.py index 01319a6..a1c34ba 100644 --- a/backend/fluksio/flow/dashboards.py +++ b/backend/fluksio/flow/dashboards.py @@ -598,6 +598,9 @@ class DashboardSummary(BaseModel): version: int = 1 #: The grid `footprint` is placed in — see ``DashboardDef.columns``. columns: int = 12 + #: A round dashboard's widgets are faces rather than tiles, so a list + #: draws them as a circle instead of at the grid places they carry. + shape: Literal["rect", "round"] = "rect" #: Every widget's shape, so a list can draw the dashboard's outline #: without a document read per tile. footprint: list[WidgetFootprint] = Field(default_factory=list) @@ -666,6 +669,7 @@ class DashboardStore: has_draft=defn.has_draft, version=defn.version, columns=defn.columns, + shape=defn.shape, footprint=defn.footprint, updated_at=self.updated_at(name), ) diff --git a/backend/tests/flow/test_dashboards.py b/backend/tests/flow/test_dashboards.py index 858b0da..f528dcc 100644 --- a/backend/tests/flow/test_dashboards.py +++ b/backend/tests/flow/test_dashboards.py @@ -415,6 +415,7 @@ def test_a_summary_carries_the_footprint_of_the_document(store: DashboardStore): (summary,) = store.list() assert summary.columns == saved.columns + assert summary.shape == "rect" assert [(f.type, f.x, f.y, f.w, f.h) for f in summary.footprint] == [ ("chart", 2, 1, 6, 4), ("gauge", 0, 5, 2, 2), @@ -422,6 +423,12 @@ def test_a_summary_carries_the_footprint_of_the_document(store: DashboardStore): ] +def test_a_summary_says_a_dashboard_is_round(store: DashboardStore): + store.write(DashboardDef(name="knob", shape="round")) + + assert store.list()[0].shape == "round" + + def test_a_summary_carries_when_the_working_copy_was_written(store: DashboardStore): published = store.write(default_dashboard("house")) document = store.root / "house" / "dashboard.json" diff --git a/frontend/src/client/schemas.gen.ts b/frontend/src/client/schemas.gen.ts index badc07c..e780ce1 100644 --- a/frontend/src/client/schemas.gen.ts +++ b/frontend/src/client/schemas.gen.ts @@ -828,6 +828,12 @@ export const DashboardSummarySchema = { title: 'Columns', default: 12 }, + shape: { + type: 'string', + enum: ['rect', 'round'], + title: 'Shape', + default: 'rect' + }, footprint: { items: { '$ref': '#/components/schemas/WidgetFootprint' diff --git a/frontend/src/client/types.gen.ts b/frontend/src/client/types.gen.ts index 3f1300e..7d42afa 100644 --- a/frontend/src/client/types.gen.ts +++ b/frontend/src/client/types.gen.ts @@ -215,6 +215,7 @@ export type DashboardSummary = { has_draft?: boolean; version?: number; columns?: number; + shape?: 'rect' | 'round'; footprint?: Array; updated_at?: (string | null); }; diff --git a/frontend/src/components/Common/DashboardMosaic.tsx b/frontend/src/components/Common/DashboardMosaic.tsx index a3e5cb7..cd623eb 100644 --- a/frontend/src/components/Common/DashboardMosaic.tsx +++ b/frontend/src/components/Common/DashboardMosaic.tsx @@ -65,6 +65,9 @@ function blocksOf(footprint: WidgetFootprint[]): { * dashboard. The summary carries the shapes, so it costs no request either. */ function Footprint({ dashboard }: { dashboard: DashboardSummary }) { + if (dashboard.shape === "round") { + return + } const { blocks, rows } = blocksOf(dashboard.footprint ?? []) const columns = dashboard.columns || DEFAULT_COLUMNS // Before the editor could place things, every widget was written at 0,0; @@ -112,6 +115,31 @@ function Footprint({ dashboard }: { dashboard: DashboardSummary }) { ) } +/** + * A round dashboard shows one face at a time, so its grid places mean + * nothing: it is drawn as the circle, with a dot per face where a panel puts + * its page dots. + */ +function RoundFootprint({ faces }: { faces: WidgetFootprint[] }) { + return ( +
+
+
+ {faces.map((face, index) => ( + + ))} +
+
+
+ ) +} + /** One dashboard in the mosaic. */ function Tile({ dashboard,