@exxatdesignux/ui 0.2.18 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/consumer-extras/AGENTS.md +76 -0
  3. package/consumer-extras/README.md +5 -1
  4. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +14 -3
  5. package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +37 -0
  6. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +21 -6
  7. package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +57 -0
  8. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +4 -2
  9. package/consumer-extras/patterns/consumer-app-pattern.md +39 -0
  10. package/consumer-extras/patterns/consumer-upgrade-checklist.md +20 -0
  11. package/consumer-extras/patterns/data-views-pattern.md +40 -3
  12. package/consumer-extras/patterns/focused-workflow-page-pattern.md +84 -0
  13. package/consumer-extras/patterns/shell-surface-elevation-pattern.md +5 -3
  14. package/package.json +2 -1
  15. package/src/components/ui/button-group.tsx +81 -0
  16. package/src/components/ui/button.tsx +4 -4
  17. package/src/globals.css +7 -1858
  18. package/src/theme.css +10 -1126
  19. package/src/tokens/README.md +15 -0
  20. package/src/tokens/base.css +337 -0
  21. package/src/tokens/high-contrast.css +1195 -0
  22. package/src/tokens/layers.css +224 -0
  23. package/src/tokens/tailwind-bridge.css +118 -0
  24. package/src/tokens/themes.css +201 -0
  25. package/template/AGENTS.md +60 -22
  26. package/template/app/(app)/dashboard/loading.tsx +3 -15
  27. package/template/app/(app)/dashboard/page.tsx +2 -14
  28. package/template/app/(app)/data-list/layout.tsx +43 -0
  29. package/template/app/(app)/data-list/page.tsx +2 -2
  30. package/template/app/(app)/examples/focused-workflow/page.tsx +5 -0
  31. package/template/app/(app)/examples/page.tsx +1 -0
  32. package/template/app/(app)/loading.tsx +1 -18
  33. package/template/app/(app)/question-bank/find/page.tsx +2 -1
  34. package/template/app/(app)/question-bank/library/page.tsx +2 -1
  35. package/template/app/(app)/question-bank/list/page.tsx +2 -1
  36. package/template/app/(app)/question-bank/new/page.tsx +15 -23
  37. package/template/app/(app)/question-bank/page.tsx +2 -1
  38. package/template/app/(app)/settings/page.tsx +4 -5
  39. package/template/app/globals.css +7 -1964
  40. package/template/components/app-route-loading.tsx +14 -0
  41. package/template/components/app-sidebar.tsx +70 -55
  42. package/template/components/data-views/index.ts +37 -9
  43. package/template/components/data-views/list-page-calendar-view.tsx +593 -0
  44. package/template/components/data-views/list-page-connected-view-body.tsx +66 -0
  45. package/template/components/data-views/list-page-folder-columns-panel.tsx +345 -0
  46. package/template/components/data-views/list-page-split-hub-chrome.tsx +8 -0
  47. package/template/components/examples/focused-workflow-showcase.tsx +183 -0
  48. package/template/components/list-hub-board-view.tsx +68 -0
  49. package/template/components/list-hub-client.tsx +186 -0
  50. package/template/components/list-hub-list-view.tsx +36 -0
  51. package/template/components/list-hub-panel-activator.tsx +8 -0
  52. package/template/components/list-hub-secondary-nav.tsx +121 -0
  53. package/template/components/list-hub-table.tsx +336 -0
  54. package/template/components/new-question-composer.tsx +6 -24
  55. package/template/components/product-switcher.tsx +3 -2
  56. package/template/components/question-bank-client.tsx +4 -1
  57. package/template/components/question-bank-folder-columns-panel.tsx +104 -0
  58. package/template/components/question-bank-table.tsx +143 -485
  59. package/template/components/secondary-panel/nav-link-rows.tsx +83 -0
  60. package/template/components/secondary-panel.tsx +4 -44
  61. package/template/components/secondary-panels/list-hub-panel.tsx +39 -0
  62. package/template/components/secondary-panels/question-bank-panel.tsx +39 -0
  63. package/template/components/secondary-panels/registry.tsx +15 -0
  64. package/template/components/settings-appearance-card.tsx +3 -2
  65. package/template/components/settings-client.tsx +59 -15
  66. package/template/components/settings-form-row.tsx +9 -4
  67. package/template/components/table-properties/drawer-button.tsx +13 -0
  68. package/template/components/table-properties/drawer.tsx +65 -4
  69. package/template/components/templates/focused-workflow-layouts.tsx +448 -0
  70. package/template/components/templates/focused-workflow-page-template.tsx +69 -0
  71. package/template/components/templates/list-page.tsx +29 -5
  72. package/template/components/templates/nested-secondary-panel-shell.tsx +2 -1
  73. package/template/components/templates/page-loading-shell.tsx +262 -0
  74. package/template/components/ui/button-group.tsx +1 -0
  75. package/template/docs/consumer-app-pattern.md +39 -0
  76. package/template/docs/data-views-pattern.md +40 -3
  77. package/template/docs/drawer-vs-dialog-pattern.md +3 -1
  78. package/template/docs/focused-workflow-page-pattern.md +84 -0
  79. package/template/docs/shell-surface-elevation-pattern.md +5 -3
  80. package/template/lib/command-menu-search-data.ts +11 -27
  81. package/template/lib/data-list-display-options.ts +16 -2
  82. package/template/lib/data-list-view-registry.ts +104 -0
  83. package/template/lib/data-list-view-surface.ts +15 -1
  84. package/template/lib/data-list-view.ts +10 -1
  85. package/template/lib/data-view-dashboard-storage.ts +38 -35
  86. package/template/lib/hub-connected-view-renderers.ts +58 -0
  87. package/template/lib/list-hub-nav.ts +121 -0
  88. package/template/lib/list-hub-supported-views.ts +10 -0
  89. package/template/lib/list-page-table-properties.ts +3 -7
  90. package/template/lib/list-status-badges.ts +4 -97
  91. package/template/lib/mock/list-hub-directory.ts +27 -0
  92. package/template/lib/mock/list-hub-kpi.ts +27 -0
  93. package/template/lib/mock/navigation.tsx +1 -0
  94. package/template/lib/page-loading-variant.ts +40 -0
  95. package/template/lib/question-bank-supported-views.ts +13 -0
  96. package/template/lib/table-state-lifecycle.ts +2 -2
  97. package/template/app/(app)/data-list/[id]/page.tsx +0 -44
  98. package/template/app/(app)/data-list/new/page.tsx +0 -34
  99. package/template/components/compliance-board-view.tsx +0 -142
  100. package/template/components/compliance-client.tsx +0 -92
  101. package/template/components/compliance-list-view.tsx +0 -54
  102. package/template/components/compliance-page-header.tsx +0 -89
  103. package/template/components/compliance-table.tsx +0 -612
  104. package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
  105. package/template/components/data-view-dashboard-charts-team.tsx +0 -971
  106. package/template/components/data-view-dashboard-charts.tsx +0 -1503
  107. package/template/components/new-placement-back-btn.tsx +0 -28
  108. package/template/components/new-placement-form.tsx +0 -1068
  109. package/template/components/placement-board-card.tsx +0 -262
  110. package/template/components/placement-detail.tsx +0 -438
  111. package/template/components/placements-board-view.tsx +0 -404
  112. package/template/components/placements-client.tsx +0 -252
  113. package/template/components/placements-list-view.tsx +0 -171
  114. package/template/components/placements-page-header.tsx +0 -166
  115. package/template/components/placements-table-cells.test.tsx +0 -22
  116. package/template/components/placements-table-cells.tsx +0 -173
  117. package/template/components/placements-table-columns.tsx +0 -640
  118. package/template/components/placements-table.tsx +0 -1642
  119. package/template/components/rotations-empty-state.tsx +0 -50
  120. package/template/components/rotations-panel-activator.tsx +0 -8
  121. package/template/components/sites-all-client.tsx +0 -154
  122. package/template/components/sites-board-view.tsx +0 -67
  123. package/template/components/sites-list-view.tsx +0 -42
  124. package/template/components/sites-table.tsx +0 -382
  125. package/template/components/team-board-view.tsx +0 -122
  126. package/template/components/team-client.tsx +0 -100
  127. package/template/components/team-list-view.tsx +0 -59
  128. package/template/components/team-page-header.tsx +0 -92
  129. package/template/components/team-table.tsx +0 -693
  130. package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
  131. package/template/lib/mock/compliance-kpi.ts +0 -61
  132. package/template/lib/mock/compliance.ts +0 -146
  133. package/template/lib/mock/placements-kpi.ts +0 -134
  134. package/template/lib/mock/placements.ts +0 -183
  135. package/template/lib/mock/sites-directory.ts +0 -16
  136. package/template/lib/mock/sites-kpi.ts +0 -25
  137. package/template/lib/mock/team-kpi.ts +0 -60
  138. package/template/lib/mock/team.ts +0 -118
  139. package/template/lib/placement-board-card-layout.ts +0 -79
  140. package/template/lib/placement-lifecycle.ts +0 -5
@@ -24,20 +24,21 @@ Cross-cutting Cursor rules also live in the repo root `.cursor/rules/` (data tab
24
24
  12. **Before** adding **onboarding tours, feature walkthroughs, or coach marks**, read **§11** and `references/coach-marks.md`.
25
25
  13. **Before** changing the **global command palette (⌘K)** or search/AI entry UX, read **§7.1** and **`docs/command-menu-pattern.md`**.
26
26
  14. **Before** choosing **drawer vs dialog vs new page** for a task flow, read **§6.4**, **`docs/data-views-pattern.md`** (Page vs drawer), and **`docs/drawer-vs-dialog-pattern.md`** (modal vs side panel on the same route).
27
- 15. **Before** adding **success/error/confirmation feedback**, read **§6.5** and **`.cursor/rules/exxat-no-toast.mdc`** (no toast or snackbars).
28
- 16. Prefer **composing existing components** over new one-off UI. If something is missing, **extend** shared components under `components/`, not a single page file.
27
+ 15. **Before** adding or changing a **dedicated form, wizard, or sectioned settings route**, read **`docs/focused-workflow-page-pattern.md`**, **`.cursor/rules/exxat-focused-workflow-page.mdc`**, **`.cursor/skills/exxat-focused-workflow-page/SKILL.md`**, and run **§14** checklist.
28
+ 16. **Before** adding **success/error/confirmation feedback**, read **§6.5** and **`.cursor/rules/exxat-no-toast.mdc`** (no toast or snackbars).
29
+ 17. Prefer **composing existing components** over new one-off UI. If something is missing, **extend** shared components under `components/`, not a single page file.
29
30
  - **MUST** scan `components/` (especially `components/ui/`, `components/data-views/`, `components/templates/`, `components/key-metrics.tsx`, `components/page-header.tsx`, and the charts/banner/dot-pattern surfaces) **before** writing any new UI. If a primitive or composition already exists, **use it** — don't build a parallel one.
30
- - **Examples of existing surfaces to reuse:** card grid → `ListPageBoardCard` + `BoardCardIconRow` / `BoardCardTwoLineBlock`; AI / dot animation → `AiThinkingOverlay` + `DotPattern`; search input → `InputGroup` + `InputGroupAddon` + `InputGroupInput`; page title → `PageHeader` (serif via `font-heading`); list hub shell → `ListPageTemplate` (`metrics`, `defaultTabs`, `renderContent`); metrics strip → `KeyMetrics`; **view body gutter + centered max-width** → **`ListPageViewFrame`** (**§4.5**); **shared access / invite** → **`PageHeader` `variant="collaboration"`** + **`InviteCollaboratorsDrawer`** (**§4.7**).
31
+ - **Examples of existing surfaces to reuse:** card grid → `ListPageBoardCard` + `BoardCardIconRow` / `BoardCardTwoLineBlock`; AI / dot animation → `AiThinkingOverlay` + `DotPattern`; search input → `InputGroup` + `InputGroupAddon` + `InputGroupInput`; page title → `PageHeader` (serif via `font-heading`); list hub shell → `ListPageTemplate` (`metrics`, `defaultTabs`, `renderContent`); **dedicated form / wizard / settings route** → **`FocusedWorkflowPageTemplate`** + **`focused-workflow-layouts.tsx`** (**§14**); metrics strip → `KeyMetrics`; **view body gutter + centered max-width** → **`ListPageViewFrame`** (**§4.5**); **shared access / invite** → **`PageHeader` `variant="collaboration"`** + **`InviteCollaboratorsDrawer`** (**§4.7**).
31
32
  - **If** nothing fits and you would add a **new shared primitive or large bespoke widget**: **ask the user** for direction first — **`.cursor/rules/exxat-reuse-before-custom.mdc`** (unless the task already explicitly approved a greenfield build).
32
- 17. **Match** naming, imports, and patterns of the nearest reference implementation (usually Placements).
33
- 18. **Before** adding entity **mock data**, a **new view tab**, or **detail/inspector** panels on a list hub, read **`.cursor/rules/exxat-centralized-list-dataset.mdc`** and **`.cursor/skills/exxat-centralized-list-dataset/SKILL.md`** (single **`useTableState`** row bag for every view; **no** parallel mock arrays per view).
34
- 19. **Before** choosing **cards vs table rows vs simple lists** for a hub, read **`docs/card-vs-rows-pattern.md`** and **`.cursor/rules/exxat-card-vs-list-rows.mdc`**.
35
- 20. **Before** adding **`KeyMetrics`** strips on list hubs or dashboard key-metrics cards, read **`docs/kpi-strip-max-four-pattern.md`** and **`.cursor/rules/exxat-kpi-max-four.mdc`** (at most **four** tiles).
36
- 21. **Before** styling **`KeyMetrics variant="flat"`** (list hub metrics strip, dashboard mix KPI band), read **`docs/kpi-flat-band-pattern.md`** and **`.cursor/rules/exxat-kpi-flat-band.mdc`** / **`.cursor/skills/exxat-kpi-flat-band/SKILL.md`** (transparent band, OKLCH glow, border hairlines only).
37
- 22. **Before** changing **secondary panel** or **sidebar** brand chrome, read **`docs/shell-surface-elevation-pattern.md`** and **§4.6** ( **`--secondary-panel-bg`**, active product theme).
38
- 23. **Before** adding **new shared UI primitives** or bespoke widgets when nothing in **`components/`** fits after scanning, follow **`.cursor/rules/exxat-reuse-before-custom.mdc`** — **ask the user** for direction unless the task already approved a greenfield build.
33
+ 18. **Match** naming, imports, and patterns of the nearest reference implementation (usually Placements).
34
+ 19. **Before** adding entity **mock data**, a **new view tab**, or **detail/inspector** panels on a list hub, read **`.cursor/rules/exxat-centralized-list-dataset.mdc`** and **`.cursor/skills/exxat-centralized-list-dataset/SKILL.md`** (single **`useTableState`** row bag for every view; **no** parallel mock arrays per view).
35
+ 20. **Before** choosing **cards vs table rows vs simple lists** for a hub, read **`docs/card-vs-rows-pattern.md`** and **`.cursor/rules/exxat-card-vs-list-rows.mdc`**.
36
+ 21. **Before** adding **`KeyMetrics`** strips on list hubs or dashboard key-metrics cards, read **`docs/kpi-strip-max-four-pattern.md`** and **`.cursor/rules/exxat-kpi-max-four.mdc`** (at most **four** tiles).
37
+ 22. **Before** styling **`KeyMetrics variant="flat"`** (list hub metrics strip, dashboard mix KPI band), read **`docs/kpi-flat-band-pattern.md`** and **`.cursor/rules/exxat-kpi-flat-band.mdc`** / **`.cursor/skills/exxat-kpi-flat-band/SKILL.md`** (transparent band, OKLCH glow, border hairlines only).
38
+ 23. **Before** changing **secondary panel** or **sidebar** brand chrome, read **`docs/shell-surface-elevation-pattern.md`** and **§4.6** ( **`--secondary-panel-bg`**, active product theme).
39
+ 24. **Before** adding **new shared UI primitives** or bespoke widgets when nothing in **`components/`** fits after scanning, follow **`.cursor/rules/exxat-reuse-before-custom.mdc`** — **ask the user** for direction unless the task already approved a greenfield build.
39
40
 
40
- **Longer narrative and architecture:** `docs/data-views-pattern.md`, `docs/drawer-vs-dialog-pattern.md`, `docs/card-vs-rows-pattern.md`, `docs/kpi-strip-max-four-pattern.md`, **`docs/kpi-flat-band-pattern.md`**, **`docs/shell-surface-elevation-pattern.md`**, `docs/command-menu-pattern.md`, `docs/collaboration-access-pattern.md` (keep in sync with this handbook for big refactors).
41
+ **Longer narrative and architecture:** `docs/data-views-pattern.md`, `docs/drawer-vs-dialog-pattern.md`, **`docs/focused-workflow-page-pattern.md`**, `docs/card-vs-rows-pattern.md`, `docs/kpi-strip-max-four-pattern.md`, **`docs/kpi-flat-band-pattern.md`**, **`docs/shell-surface-elevation-pattern.md`**, `docs/command-menu-pattern.md`, `docs/collaboration-access-pattern.md` (keep in sync with this handbook for big refactors).
41
42
 
42
43
  ---
43
44
 
@@ -75,7 +76,7 @@ If two documents conflict, prefer the **more specific** rule for the file type,
75
76
 
76
77
  **MUST:** If the main surface is a **`DataTable`** (or equivalent data grid), wrap it in **`ListPageTemplate`** so the **views toolbar** exists (tabs, add view, per-tab settings). Do **not** place `DataTable` only under `PageHeader` without the tab shell.
77
78
 
78
- **Reference implementations:** `components/data-list-client.tsx` (Placements), `components/team-client.tsx` (Team).
79
+ **Reference implementations:** `components/list-hub-client.tsx` (List hub), `components/question-bank-client.tsx` (Question bank). Template mirror: `packages/ui/template/components/list-hub-*.tsx`, `question-bank-*.tsx`.
79
80
 
80
81
  **Rationale:** Consistent navigation, saved views, per-tab view type (table / list / board / dashboard), export at template level.
81
82
 
@@ -93,6 +94,19 @@ If two documents conflict, prefer the **more specific** rule for the file type,
93
94
 
94
95
  **Details:** `docs/data-views-pattern.md` (mock data, connected views, dashboard view).
95
96
 
97
+ ### 4.1.1 View registry + `ListPageConnectedViewBody` (required for new hubs)
98
+
99
+ **MUST** route view bodies through the shared registry — **MUST NOT** use `if (view === "board")` chains with a dashboard fallback.
100
+
101
+ | Step | Action |
102
+ |------|--------|
103
+ | **Register** | Add the tile in **`lib/data-list-view.ts`**; capabilities derive in **`lib/data-list-view-registry.ts`** (`renderKind`, `showsListPageHubMetricsStrip`). |
104
+ | **Declare hub** | **`lib/<hub>-supported-views.ts`** — pass the same array to **`ListPageTemplate`** (`supportedViewTypes`) and **`TablePropertiesDrawerButton`** (`supportedViewTypes`). |
105
+ | **Render** | Hub **`*-table.tsx`** returns **`ListPageConnectedViewBody`** with **`defineHubViewRenderers(supportedViewTypes, { … })`** — one entry per **`DataListViewRenderKind`**; dev warns on gaps; missing kinds show **`ListPageViewNotConfigured`** (never silent dashboard). |
106
+ | **Bodies** | Generic UI in **`components/data-views/`** (`ListPageCalendarView`, `ListPageBoardTemplate`, `ListPageFolderColumnsPanel`, `FolderGridView`, …); entity wiring in thin hub wrappers (e.g. **`QuestionBankFolderColumnsPanel`**). **MUST NOT** export QB-only tree branches from the generic **`data-views`** barrel. |
107
+
108
+ **Golden references (this app):** `components/list-hub-table.tsx`, `components/question-bank-table.tsx`. **Template** (`packages/ui/template/`): same list-hub + question-bank stack — keep in sync when publishing **`@exxatdesignux/ui`**.
109
+
96
110
  ### 4.2 `TablePropertiesDrawer` and the active view
97
111
 
98
112
  **MUST:** Any page that uses **`ListPageTemplate`** with **`tab.viewType`** (table / list / board / dashboard) and renders **`TablePropertiesDrawer`** **MUST** pass:
@@ -101,10 +115,11 @@ If two documents conflict, prefer the **more specific** rule for the file type,
101
115
  |------|--------|
102
116
  | **`currentView`** | The same **`DataListViewType`** as the tab’s active view (e.g. **`view={tab.viewType}`** on the table component). |
103
117
  | **`onViewChange`** | From **`renderContent={(tab, updateTab) => ...}`**: **`(v) => updateTab({ viewType: v, icon: dataListViewIcon(v) })`** — import **`dataListViewIcon`** from **`@/lib/data-list-view`**. |
118
+ | **`supportedViewTypes`** | Same **`lib/<hub>-supported-views.ts`** array as **`ListPageTemplate`** — limits Properties view tiles and **⌘1–9** Add-view shortcuts to views the hub implements. |
104
119
 
105
- Thread **`view`** and **`onViewChange`** from the **client** → **table / toolbar wrapper** → **`TablePropertiesDrawer`**. If **`currentView`** is omitted, the drawer defaults to **table** labels and controls even on **Board**, which is incorrect.
120
+ Thread **`view`**, **`onViewChange`**, and **`supportedViewTypes`** from the **client** → **table / toolbar wrapper** → **`TablePropertiesDrawer`**. If **`currentView`** is omitted, the drawer defaults to **table** labels and controls even on **Board**, which is incorrect.
106
121
 
107
- **Reference:** `components/data-list-table.tsx`, `components/team-table.tsx`, `components/compliance-table.tsx`. Root **`.cursor/rules/exxat-table-properties-drawer.mdc`**.
122
+ **Reference:** `components/list-hub-table.tsx`, `components/question-bank-table.tsx`; template **`components/placements-table.tsx`**, **`team-table.tsx`**. Root **`.cursor/rules/exxat-table-properties-drawer.mdc`**.
108
123
 
109
124
  ### 4.3 Data view dashboard — charts, customisation, and parity with the gallery
110
125
 
@@ -251,7 +266,9 @@ Match **Placements**:
251
266
 
252
267
  **Rationale:** Drawers preserve **spatial context**; dialogs enforce **focus**; full pages avoid cramming complex work into overlays.
253
268
 
254
- **Details:** `docs/data-views-pattern.md` (Page vs drawer), **`docs/drawer-vs-dialog-pattern.md`** (drawer vs modal on the same route). Root **`.cursor/rules/exxat-page-vs-drawer.mdc`**, **`.cursor/rules/exxat-drawer-vs-dialog.mdc`**.
269
+ **Details:** `docs/data-views-pattern.md` (Page vs drawer), **`docs/drawer-vs-dialog-pattern.md`** (drawer vs modal on the same route), **`docs/focused-workflow-page-pattern.md`** (dedicated form/wizard/settings shell — **`FocusedWorkflowPageTemplate`** + body layouts). Root **`.cursor/rules/exxat-page-vs-drawer.mdc`**, **`.cursor/rules/exxat-drawer-vs-dialog.mdc`**, **`.cursor/rules/exxat-focused-workflow-page.mdc`**, **`.cursor/skills/exxat-focused-workflow-page/SKILL.md`**.
270
+
271
+ **Focused workflow routes:** Primary / long / multi-step / sectioned settings on an **own URL** → **`FocusedWorkflowPageTemplate`** (§14 checklist) — **not** **`ListPageTemplate`**, **not** Miller-column explorers.
255
272
 
256
273
  ### 6.5 Messaging — no toast
257
274
 
@@ -425,11 +442,14 @@ Reference: `components/new-placement-form.tsx` (Next/Back buttons); full shortcu
425
442
 
426
443
  | Need | Reuse | Where |
427
444
  |------|--------|--------|
428
- | View tabs + shell | `ListPageTemplate` | `components/templates/list-page.tsx` |
445
+ | View tabs + shell | `ListPageTemplate` (+ **`supportedViewTypes`**) | `components/templates/list-page.tsx` |
446
+ | Focused form / wizard / settings route | `FocusedWorkflowPageTemplate` + layouts in `focused-workflow-layouts.tsx` | `components/templates/focused-workflow-page-template.tsx` — **`docs/focused-workflow-page-pattern.md`** |
447
+ | View router | `ListPageConnectedViewBody`, `defineHubViewRenderers`, `data-list-view-registry` | `components/data-views/list-page-connected-view-body.tsx`, `lib/hub-connected-view-renderers.ts`, `lib/data-list-view-registry.ts` |
429
448
  | Table + toolbar | `DataTable`, `DataTableToolbar`, `useTableState` | `components/data-table/` |
430
- | Properties | `TablePropertiesDrawer` (+ **`currentView`** / **`onViewChange`** when using view tabs — §4.2) | `@/components/table-properties` |
431
- | Placements flow | `DataListClient`, `DataListTable` | `components/data-list-client.tsx`, `components/data-list-table.tsx` |
432
- | Team flow | `TeamClient`, `TeamTable`, `TeamPageHeader` | `components/team-client.tsx`, etc. |
449
+ | Properties | `TablePropertiesDrawer` (+ **`currentView`** / **`onViewChange`** / **`supportedViewTypes`** — §4.2) | `@/components/table-properties` |
450
+ | List hub (golden) | `ListHubClient`, `ListHubTable` | `components/list-hub-client.tsx`, `list-hub-table.tsx` |
451
+ | Question bank | `QuestionBankClient`, `QuestionBankTable` | `components/question-bank-client.tsx`, `question-bank-table.tsx` |
452
+ | Template-only hubs (npm) | Placements, Team, Compliance, Sites | `packages/ui/template/components/*` — diff on upgrade |
433
453
  | Dashboard view tab (KPIs + charts) | **`DashboardReportCharts`**; default **`ChartsOverview`** (placement demo). **Team** passes **`chartsSection`** (`TeamDashboardChartsSection`) so graphs match roster rows. KPIs from **`tableState.rows`** | `components/dashboard-report-charts.tsx`, `data-view-dashboard-charts-team.tsx`, `data-list-table.tsx` |
434
454
  | Data view layout + graph keyboard tokens | **`loadDataViewLayout` / `saveDataViewLayout`**, **`CHART_KBD_ACTIVE_BAR`**, **`CHART_KBD_ACTIVE_PIE_SHAPE`** | `lib/data-view-dashboard-storage.ts`, `lib/chart-keyboard-selection.ts` |
435
455
  | Customize dashboard coach marks | Shared steps in **`lib/dashboard-customize-coach-mark.ts`**; flows **`placements-dashboard-customize`**, **`team-dashboard-customize`**, **`compliance-dashboard-customize`** | `hooks/use-coach-mark.ts` (`enabled`, `dependsOnDismissedFlowId`), `data-list-table.tsx`, `team-table.tsx`, `compliance-table.tsx` |
@@ -512,6 +532,8 @@ New pages **SHOULD** namespace keys and version JSON (`v: 1`) for future migrati
512
532
 
513
533
  - **Deep dive:** `docs/data-views-pattern.md` (includes **Page vs drawer** with **§6.4**)
514
534
  - **Drawer vs dialog (same route):** `docs/drawer-vs-dialog-pattern.md` — **`.cursor/rules/exxat-drawer-vs-dialog.mdc`**
535
+ - **Focused workflow (form / wizard / settings route):** `docs/focused-workflow-page-pattern.md` — **`.cursor/rules/exxat-focused-workflow-page.mdc`**, **`.cursor/skills/exxat-focused-workflow-page/SKILL.md`**
536
+ - **Consumer app (npm):** `docs/consumer-app-pattern.md` — **`packages/ui/consumer-extras/AGENTS.md`**, **`.cursor/rules/exxat-consumer-app.mdc`**, **`.cursor/skills/exxat-consumer-app/SKILL.md`**
515
537
  - **Cards vs table rows:** `docs/card-vs-rows-pattern.md` — **`.cursor/rules/exxat-card-vs-list-rows.mdc`**
516
538
  - **KPI strip (max four tiles):** `docs/kpi-strip-max-four-pattern.md` — **`.cursor/rules/exxat-kpi-max-four.mdc`**
517
539
  - **KPI flat band (list hubs):** `docs/kpi-flat-band-pattern.md` — **`.cursor/rules/exxat-kpi-flat-band.mdc`**
@@ -533,7 +555,7 @@ New pages **SHOULD** namespace keys and version JSON (`v: 1`) for future migrati
533
555
  | Match Placements for export + primary CTA + More menu | Outline button as the single primary CTA on exportable pages |
534
556
  | Pair `Kbd` hints with real shortcuts | Browser-reserved chords for app actions |
535
557
  | Global palette: **§7.1** — search + quick in-menu AI vs **Ask Leo**; **`dataGroups`** + **`searchOnly`** for bulky indexes | Palette as link-only dump; AI that belongs in **Ask Leo** forced into the palette; mounting full **`dataGroups`** on open when **`searchOnly`** should hide them |
536
- | **§6.4** — **drawer** when **page context + quick** view/actions; **dialog** for **blocking** confirm/alert/short choice; **new page** for primary / long / own-URL flows | Forcing **full workflows** into a drawer when a route fits; using a **dialog** when users must **reference** the grid (prefer drawer); **routing** for tasks that are only quick glances over a hub |
558
+ | **§6.4** — **drawer** when **page context + quick** view/actions; **dialog** for **blocking** confirm/alert/short choice; **focused workflow route** (`FocusedWorkflowPageTemplate`) for primary / long / wizard / sectioned settings | Forcing **full workflows** into a drawer when a route fits; using a **dialog** when users must **reference** the grid (prefer drawer); **`ListPageTemplate`** or Miller columns on dedicated create/edit routes |
537
559
  | **KPI strips** — **≤ 4** `MetricItem` per **`KeyMetrics`** on template metrics + Data-tab key-metrics cards (**`KEY_METRICS_KPI_COUNT_MAX`**) | Fifth+ headline tile in the same strip; duplicate tiles to pad count |
538
560
  | **Cards vs rows** — **DataTable** for dense comparable hubs; **`ListPageBoardCard`** / **`ListPageViewFrame`** when visual/kanban/folder — **`docs/card-vs-rows-pattern.md`** | Card walls for **50+** homogeneous records where the product expects **sort/filter/compare** without a deliberate UX exception |
539
561
  | **Reuse before custom** — scan **`components/`** + **§9**; **ask the user** before new shared primitives or large bespoke widgets — **`exxat-reuse-before-custom.mdc`** | Parallel stacks; silent new “table” or metric systems when **`DataTable`** / **`KeyMetrics`** already apply |
@@ -556,6 +578,7 @@ New pages **SHOULD** namespace keys and version JSON (`v: 1`) for future migrati
556
578
  Copy and complete when implementing or reviewing:
557
579
 
558
580
  - [ ] **Centralized dataset:** One **`useTableState`** / **`tableState.rows`** for **all** view tabs and inspectors; **TablePropertiesDrawer** on the **same** `DataTable`; **no** parallel mock arrays per view — **`.cursor/rules/exxat-centralized-list-dataset.mdc`**.
581
+ - [ ] **View registry (§4.1.1):** **`lib/<hub>-supported-views.ts`**; **`ListPageConnectedViewBody`** + **`defineHubViewRenderers`**; same **`supportedViewTypes`** on template + Properties; no silent dashboard fallback — **`docs/data-views-pattern.md`**.
559
582
  - [ ] **Reuse:** `ListPageTemplate`, `DataTable` / `useTableState`, `TablePropertiesDrawer` — no parallel bespoke tabs/filters. **New shared primitives:** **ask the user** after scanning **`components/`** + **§9** — **`.cursor/rules/exxat-reuse-before-custom.mdc`**.
560
583
  - [ ] **Tabs:** Any main `DataTable` sits under `ListPageTemplate` with appropriate view tabs.
561
584
  - [ ] **Inset:** No double horizontal padding around `DataTable`.
@@ -568,7 +591,7 @@ Copy and complete when implementing or reviewing:
568
591
  - [ ] **Data view dashboard (Placements / Team / Compliance):** Charts use **`ChartFigure`** + **`ChartDataTable`**; **Edit layout** on toolbar; **`activeBar` / `activeShape`** keyboard styling from **`lib/chart-keyboard-selection`** — not opacity-only **`Cell`** hacks (§4.3).
569
592
  - [ ] **Dashboard layout persistence:** **`lib/data-view-dashboard-storage`** (or **`saveDashboardLayout`** / **`loadDashboardLayout`** on Placements); **`mergeDashboardLayout`** on load — no new ad-hoc storage keys for the same layout (§4.3).
570
593
  - [ ] **⌘K palette (§7.1):** If adding or changing **`dataGroups`**, map rows in **`lib/command-menu-search-data.ts`** (not `command-menu.tsx`); use **`searchOnly`** on bulky groups; keep **`docs/command-menu-pattern.md`** aligned.
571
- - [ ] **Page vs drawer vs dialog (§6.4):** Quick auxiliary with **parent context** and interactable hub → **drawer/sheet**; **blocking** short confirm → **dialog**; primary or long flows → **new route** — **`docs/data-views-pattern.md`**, **`docs/drawer-vs-dialog-pattern.md`**.
594
+ - [ ] **Page vs drawer vs dialog (§6.4):** Quick auxiliary with **parent context** and interactable hub → **drawer/sheet**; **blocking** short confirm → **dialog**; primary or long flows → **focused workflow route** or other dedicated page — **`docs/data-views-pattern.md`**, **`docs/drawer-vs-dialog-pattern.md`**, **`docs/focused-workflow-page-pattern.md`**.
572
595
  - [ ] **Cards vs rows:** Primary sortable hub with many homogeneous records → **`DataTable`**; kanban / visual tiles → **`ListPageBoardCard`** — **`docs/card-vs-rows-pattern.md`**, **`.cursor/rules/exxat-card-vs-list-rows.mdc`**.
573
596
  - [ ] **KPI count (max four):** **`entityKpiMetrics`** (and any static **`MetricItem[]`** for the same strip) has **≤ 4** tiles for template metrics + Data-tab key-metrics — **`docs/kpi-strip-max-four-pattern.md`**, **`.cursor/rules/exxat-kpi-max-four.mdc`**.
574
597
  - [ ] **No toast (§6.5):** No **`toast()`** / Sonner / snackbars — use banners, inline status, or dialogs.
@@ -590,4 +613,19 @@ Copy and complete when implementing or reviewing:
590
613
 
591
614
  ---
592
615
 
593
- *Last updated: KPI flat band + shell surface elevation pattern docs/rules/skills; §4.6 secondary panel OKLCH; monospace system IDs; question bank folder header; drawer vs dialog / card vs rows / KPI max-four; §4.8 dedicated search; §4.7 collaboration; §4.1 centralized dataset; §4.5 view shells; Font Awesome; §9.1 sidebar; §4.4 board cards; §6.5 no toast; §7.1 command palette; §13 checklist.*
616
+ ## 14. AI execution checklist (focused workflow route)
617
+
618
+ Copy and complete when implementing or reviewing a **dedicated form, wizard, or settings** page (not a list hub):
619
+
620
+ - [ ] **`FocusedWorkflowPageTemplate`** on **`app/(app)/…/page.tsx`** — **`siteHeader`** with **`back`** or **`breadcrumbs`** + **`title`**.
621
+ - [ ] **`maxWidth`**: **`md`** | **`lg`** | **`xl`** — not list-hub **`max-w-[1440px]`**.
622
+ - [ ] **One body layout:** **`FocusedWorkflowSingleColumn`** | **`FocusedWorkflowStepForm`** | **`FocusedWorkflowSidebarSections`** | **`FocusedWorkflowEmptyState`**.
623
+ - [ ] **Wizard / actions:** **`FocusedWorkflowWizardFooter`** or **`FocusedWorkflowActionFooter`** + **`Shortcut`** + **`<Kbd variant="bare">`** (**`exxat-kbd-shortcuts.mdc`**).
624
+ - [ ] **Domain UI** in **`*-composer.tsx`** / **`*-client.tsx`**; **`FocusedWorkflow*`** names stay generic.
625
+ - [ ] **No** **`ListPageTemplate`**, **no** **`ListPageFolderColumnsPanel`**, **no** Miller-column split explorer in this shell.
626
+ - [ ] **No toast (§6.5)** for product feedback on this route.
627
+ - [ ] **Reference:** **`/examples/focused-workflow`**, **`docs/focused-workflow-page-pattern.md`**, **`.cursor/skills/exxat-focused-workflow-page/SKILL.md`**.
628
+
629
+ ---
630
+
631
+ *Last updated: focused workflow page pattern + §14 checklist + rule/skill; KPI flat band + shell surface elevation; §4.6 secondary panel OKLCH; monospace system IDs; question bank folder header; drawer vs dialog / card vs rows / KPI max-four; §4.8 dedicated search; §4.7 collaboration; §4.1 centralized dataset; §4.5 view shells; Font Awesome; §9.1 sidebar; §4.4 board cards; §6.5 no toast; §7.1 command palette; §13 checklist.*
@@ -1,18 +1,6 @@
1
- import { Skeleton } from "@/components/ui/skeleton"
1
+ import { DashboardLoadingFallback } from "@/components/templates/page-loading-shell"
2
2
 
3
- /** Route-level fallback while the dashboard shell + tabs chunk load. */
3
+ /** Dashboard route same shell as `app/(app)/loading` dashboard variant. */
4
4
  export default function DashboardLoading() {
5
- return (
6
- <div className="flex flex-col gap-4 p-4 md:p-6" aria-busy="true" aria-label="Loading dashboard">
7
- <Skeleton className="h-9 w-56 max-w-full" />
8
- <Skeleton className="h-11 w-full max-w-xl" />
9
- <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
10
- <Skeleton className="h-24 rounded-xl" />
11
- <Skeleton className="h-24 rounded-xl" />
12
- <Skeleton className="h-24 rounded-xl" />
13
- <Skeleton className="h-24 rounded-xl" />
14
- </div>
15
- <Skeleton className="min-h-[320px] w-full rounded-xl" />
16
- </div>
17
- )
5
+ return <DashboardLoadingFallback />
18
6
  }
@@ -1,24 +1,12 @@
1
1
  import dynamic from "next/dynamic"
2
2
  import { PrimaryPageTemplate } from "@/components/templates/primary-page-template"
3
- import { Skeleton } from "@/components/ui/skeleton"
3
+ import { DashboardLoadingBody } from "@/components/templates/page-loading-shell"
4
4
  import { DASHBOARD_METRICS, DASHBOARD_INSIGHT } from "@/lib/mock/dashboard"
5
5
 
6
6
  const DashboardTabs = dynamic(
7
7
  () => import("@/components/dashboard-tabs").then(m => ({ default: m.DashboardTabs })),
8
8
  {
9
- loading: () => (
10
- <div className="flex flex-col gap-4 p-4 md:p-6" aria-busy="true" aria-label="Loading dashboard">
11
- <Skeleton className="h-9 w-56 max-w-full" />
12
- <Skeleton className="h-11 w-full max-w-xl" />
13
- <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
14
- <Skeleton className="h-24 rounded-xl" />
15
- <Skeleton className="h-24 rounded-xl" />
16
- <Skeleton className="h-24 rounded-xl" />
17
- <Skeleton className="h-24 rounded-xl" />
18
- </div>
19
- <Skeleton className="min-h-[320px] w-full rounded-xl" />
20
- </div>
21
- ),
9
+ loading: () => <DashboardLoadingBody />,
22
10
  },
23
11
  )
24
12
 
@@ -0,0 +1,43 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { usePathname } from "next/navigation"
5
+
6
+ import { useSecondaryPanel } from "@/components/secondary-panel"
7
+ import { LIST_HUB_PATH } from "@/lib/list-hub-nav"
8
+
9
+ /**
10
+ * Keeps the List hub secondary panel open on `/data-list` while scope links update in place.
11
+ */
12
+ export default function DataListLayout({ children }: { children: React.ReactNode }) {
13
+ const pathname = usePathname()
14
+ const { openPanel, closePanel, activePanel } = useSecondaryPanel()
15
+ const closePanelRef = React.useRef(closePanel)
16
+ const openPanelRef = React.useRef(openPanel)
17
+
18
+ React.useEffect(() => {
19
+ closePanelRef.current = closePanel
20
+ openPanelRef.current = openPanel
21
+ })
22
+
23
+ React.useEffect(() => {
24
+ return () => {
25
+ closePanelRef.current({ mainSidebar: "leave" })
26
+ }
27
+ }, [])
28
+
29
+ React.useEffect(() => {
30
+ const normalized =
31
+ pathname.length > 1 && pathname.endsWith("/") ? pathname.slice(0, -1) : pathname
32
+ if (normalized !== LIST_HUB_PATH) {
33
+ closePanelRef.current({ mainSidebar: "leave" })
34
+ return undefined
35
+ }
36
+ if (activePanel !== "list-hub") {
37
+ openPanelRef.current("list-hub")
38
+ }
39
+ return undefined
40
+ }, [pathname, activePanel])
41
+
42
+ return children
43
+ }
@@ -1,10 +1,10 @@
1
- import { PlacementsClient } from "@/components/placements-client"
1
+ import { ListHubClient } from "@/components/list-hub-client"
2
2
  import { PrimaryPageTemplate } from "@/components/templates/primary-page-template"
3
3
 
4
4
  export default function DataListPage() {
5
5
  return (
6
6
  <PrimaryPageTemplate siteHeader={{ title: "List hub" }}>
7
- <PlacementsClient />
7
+ <ListHubClient />
8
8
  </PrimaryPageTemplate>
9
9
  )
10
10
  }
@@ -0,0 +1,5 @@
1
+ import { FocusedWorkflowShowcase } from "@/components/examples/focused-workflow-showcase"
2
+
3
+ export default function FocusedWorkflowExamplePage() {
4
+ return <FocusedWorkflowShowcase />
5
+ }
@@ -7,6 +7,7 @@ const LINKS = [
7
7
  { href: "/data-list", label: "List hub", description: "Table, list, board, and dashboard views on shared state." },
8
8
  { href: "/question-bank", label: "Question bank", description: "Discovery hub for browsing folders, recents, and AI-assisted create/import flows." },
9
9
  { href: "/question-bank/library", label: "Question library", description: "Folders, OS folder view, panel, and tree demos on mock items." },
10
+ { href: "/examples/focused-workflow", label: "Focused workflow", description: "Empty, step wizard, and sidebar section layouts for large forms." },
10
11
  { href: "/settings", label: "Settings", description: "Appearance, tours, and shell preferences." },
11
12
  { href: "/help", label: "Help", description: "Support and documentation entry points." },
12
13
  ] as const
@@ -1,18 +1 @@
1
- import { Skeleton } from "@/components/ui/skeleton"
2
-
3
- /**
4
- * Default loading UI for app routes (sidebar chrome stays; main column shows this fallback).
5
- */
6
- export default function AppRouteLoading() {
7
- return (
8
- <div
9
- className="flex flex-col gap-4 p-6 md:p-8"
10
- aria-busy="true"
11
- aria-label="Loading page"
12
- >
13
- <Skeleton className="h-8 w-48" />
14
- <Skeleton className="h-32 w-full max-w-3xl" />
15
- <Skeleton className="h-64 w-full" />
16
- </div>
17
- )
18
- }
1
+ export { AppRouteLoading as default } from "@/components/app-route-loading"
@@ -1,11 +1,12 @@
1
1
  import { Suspense } from "react"
2
2
 
3
3
  import { QuestionBankClient } from "@/components/question-bank-client"
4
+ import { DedicatedSearchLoadingFallback } from "@/components/templates/page-loading-shell"
4
5
 
5
6
  /** Discovery hub composer results — same hub chrome as the library, distinct from `/question-bank/list`. */
6
7
  export default function QuestionBankHubFindPage() {
7
8
  return (
8
- <Suspense fallback={null}>
9
+ <Suspense fallback={<DedicatedSearchLoadingFallback />}>
9
10
  <QuestionBankClient />
10
11
  </Suspense>
11
12
  )
@@ -1,10 +1,11 @@
1
1
  import { Suspense } from "react"
2
2
 
3
3
  import { QuestionBankClient } from "@/components/question-bank-client"
4
+ import { PrimaryListHubLoadingFallback } from "@/components/templates/page-loading-shell"
4
5
 
5
6
  export default function QuestionBankLibraryPage() {
6
7
  return (
7
- <Suspense fallback={null}>
8
+ <Suspense fallback={<PrimaryListHubLoadingFallback />}>
8
9
  <QuestionBankClient />
9
10
  </Suspense>
10
11
  )
@@ -1,11 +1,12 @@
1
1
  import { Suspense } from "react"
2
2
 
3
3
  import { QuestionBankClient } from "@/components/question-bank-client"
4
+ import { DedicatedSearchLoadingFallback } from "@/components/templates/page-loading-shell"
4
5
 
5
6
  /** Question bank list surface — same hub as `/question-bank/library`, optimized for `?q=` search landings. */
6
7
  export default function QuestionBankListPage() {
7
8
  return (
8
- <Suspense fallback={null}>
9
+ <Suspense fallback={<DedicatedSearchLoadingFallback />}>
9
10
  <QuestionBankClient />
10
11
  </Suspense>
11
12
  )
@@ -1,22 +1,14 @@
1
1
  /**
2
2
  * `/question-bank/new` — full-page authoring composer.
3
3
  *
4
- * Mirrors focused flows: `SiteHeader` back nav ( Question hub) + `PageHeader` title
5
- * `SidebarAutoCollapse` mounted in `beforeSiteHeader` so the main sidebar
6
- * collapses on enter and restores to its prior state on leave
7
- * • A wider max-width than the placement form (the composer carries a
8
- * two-column document + metadata-rail layout)
9
- *
10
- * The body is `NewQuestionComposer` — see `components/new-question-composer.tsx`.
11
- *
12
- * Folder pre-selection: callers may pass `?folderId=<id>` so users dropped into
13
- * the composer from a folder-scoped library land with that folder pre-selected
14
- * in the metadata rail.
4
+ * Pattern: `FocusedWorkflowPageTemplate` + `NewQuestionComposer` (single column).
5
+ * Folder pre-selection: `?folderId=<id>` from a folder-scoped library link.
15
6
  */
16
7
 
17
8
  import { NewQuestionComposer } from "@/components/new-question-composer"
18
9
  import { SidebarAutoCollapse } from "@/components/sidebar-auto-collapse"
19
- import { PrimaryPageTemplate } from "@/components/templates/primary-page-template"
10
+ import { FocusedWorkflowPageTemplate } from "@/components/templates/focused-workflow-page-template"
11
+ import { FocusedWorkflowSingleColumn } from "@/components/templates/focused-workflow-layouts"
20
12
  import {
21
13
  DEFAULT_QUESTION_BANK_FOLDERS,
22
14
  type QuestionBankFolder,
@@ -40,19 +32,19 @@ export default async function NewQuestionPage({ searchParams }: NewQuestionPageP
40
32
  const draftQuestionId = generateDraftQuestionId()
41
33
 
42
34
  return (
43
- <PrimaryPageTemplate
35
+ <FocusedWorkflowPageTemplate
44
36
  beforeSiteHeader={<SidebarAutoCollapse />}
45
37
  siteHeader={{ back }}
46
- bodyClassName="min-h-0 flex-1 overflow-y-auto overscroll-y-contain"
47
- maxWidthClassName="mx-auto w-full max-w-[1100px]"
48
- contentClassName="px-8 py-4 pb-16"
38
+ maxWidth="lg"
49
39
  >
50
- <NewQuestionComposer
51
- draftQuestionId={draftQuestionId}
52
- defaultFolderId={defaultFolderId}
53
- backHref={back.href}
54
- folders={folders}
55
- />
56
- </PrimaryPageTemplate>
40
+ <FocusedWorkflowSingleColumn>
41
+ <NewQuestionComposer
42
+ draftQuestionId={draftQuestionId}
43
+ defaultFolderId={defaultFolderId}
44
+ backHref={back.href}
45
+ folders={folders}
46
+ />
47
+ </FocusedWorkflowSingleColumn>
48
+ </FocusedWorkflowPageTemplate>
57
49
  )
58
50
  }
@@ -1,10 +1,11 @@
1
1
  import { Suspense } from "react"
2
2
 
3
3
  import { QuestionBankHubClient } from "@/components/question-bank-hub-client"
4
+ import { QuestionBankHubLoadingFallback } from "@/components/templates/page-loading-shell"
4
5
 
5
6
  export default function QuestionBankHubPage() {
6
7
  return (
7
- <Suspense fallback={null}>
8
+ <Suspense fallback={<QuestionBankHubLoadingFallback />}>
8
9
  <QuestionBankHubClient />
9
10
  </Suspense>
10
11
  )
@@ -1,17 +1,16 @@
1
- import { PrimaryPageTemplate } from "@/components/templates/primary-page-template"
2
1
  import { SettingsClient } from "@/components/settings-client"
2
+ import { FocusedWorkflowPageTemplate } from "@/components/templates/focused-workflow-page-template"
3
3
 
4
4
  export default function SettingsPage() {
5
5
  return (
6
- <PrimaryPageTemplate
7
- maxWidthClassName="max-w-3xl"
8
- contentClassName="px-8 pt-10 pb-32"
6
+ <FocusedWorkflowPageTemplate
7
+ maxWidth="lg"
9
8
  siteHeader={{
10
9
  breadcrumbs: [{ label: "Dashboard", href: "/dashboard" }],
11
10
  title: "Settings",
12
11
  }}
13
12
  >
14
13
  <SettingsClient />
15
- </PrimaryPageTemplate>
14
+ </FocusedWorkflowPageTemplate>
16
15
  )
17
16
  }