@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.
- package/CHANGELOG.md +15 -0
- package/consumer-extras/AGENTS.md +76 -0
- package/consumer-extras/README.md +5 -1
- package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +14 -3
- package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +37 -0
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +21 -6
- package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +57 -0
- package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +4 -2
- package/consumer-extras/patterns/consumer-app-pattern.md +39 -0
- package/consumer-extras/patterns/consumer-upgrade-checklist.md +20 -0
- package/consumer-extras/patterns/data-views-pattern.md +40 -3
- package/consumer-extras/patterns/focused-workflow-page-pattern.md +84 -0
- package/consumer-extras/patterns/shell-surface-elevation-pattern.md +5 -3
- package/package.json +2 -1
- package/src/components/ui/button-group.tsx +81 -0
- package/src/components/ui/button.tsx +4 -4
- package/src/globals.css +7 -1858
- package/src/theme.css +10 -1126
- package/src/tokens/README.md +15 -0
- package/src/tokens/base.css +337 -0
- package/src/tokens/high-contrast.css +1195 -0
- package/src/tokens/layers.css +224 -0
- package/src/tokens/tailwind-bridge.css +118 -0
- package/src/tokens/themes.css +201 -0
- package/template/AGENTS.md +60 -22
- package/template/app/(app)/dashboard/loading.tsx +3 -15
- package/template/app/(app)/dashboard/page.tsx +2 -14
- package/template/app/(app)/data-list/layout.tsx +43 -0
- package/template/app/(app)/data-list/page.tsx +2 -2
- package/template/app/(app)/examples/focused-workflow/page.tsx +5 -0
- package/template/app/(app)/examples/page.tsx +1 -0
- package/template/app/(app)/loading.tsx +1 -18
- package/template/app/(app)/question-bank/find/page.tsx +2 -1
- package/template/app/(app)/question-bank/library/page.tsx +2 -1
- package/template/app/(app)/question-bank/list/page.tsx +2 -1
- package/template/app/(app)/question-bank/new/page.tsx +15 -23
- package/template/app/(app)/question-bank/page.tsx +2 -1
- package/template/app/(app)/settings/page.tsx +4 -5
- package/template/app/globals.css +7 -1964
- package/template/components/app-route-loading.tsx +14 -0
- package/template/components/app-sidebar.tsx +70 -55
- package/template/components/data-views/index.ts +37 -9
- package/template/components/data-views/list-page-calendar-view.tsx +593 -0
- package/template/components/data-views/list-page-connected-view-body.tsx +66 -0
- package/template/components/data-views/list-page-folder-columns-panel.tsx +345 -0
- package/template/components/data-views/list-page-split-hub-chrome.tsx +8 -0
- package/template/components/examples/focused-workflow-showcase.tsx +183 -0
- package/template/components/list-hub-board-view.tsx +68 -0
- package/template/components/list-hub-client.tsx +186 -0
- package/template/components/list-hub-list-view.tsx +36 -0
- package/template/components/list-hub-panel-activator.tsx +8 -0
- package/template/components/list-hub-secondary-nav.tsx +121 -0
- package/template/components/list-hub-table.tsx +336 -0
- package/template/components/new-question-composer.tsx +6 -24
- package/template/components/product-switcher.tsx +3 -2
- package/template/components/question-bank-client.tsx +4 -1
- package/template/components/question-bank-folder-columns-panel.tsx +104 -0
- package/template/components/question-bank-table.tsx +143 -485
- package/template/components/secondary-panel/nav-link-rows.tsx +83 -0
- package/template/components/secondary-panel.tsx +4 -44
- package/template/components/secondary-panels/list-hub-panel.tsx +39 -0
- package/template/components/secondary-panels/question-bank-panel.tsx +39 -0
- package/template/components/secondary-panels/registry.tsx +15 -0
- package/template/components/settings-appearance-card.tsx +3 -2
- package/template/components/settings-client.tsx +59 -15
- package/template/components/settings-form-row.tsx +9 -4
- package/template/components/table-properties/drawer-button.tsx +13 -0
- package/template/components/table-properties/drawer.tsx +65 -4
- package/template/components/templates/focused-workflow-layouts.tsx +448 -0
- package/template/components/templates/focused-workflow-page-template.tsx +69 -0
- package/template/components/templates/list-page.tsx +29 -5
- package/template/components/templates/nested-secondary-panel-shell.tsx +2 -1
- package/template/components/templates/page-loading-shell.tsx +262 -0
- package/template/components/ui/button-group.tsx +1 -0
- package/template/docs/consumer-app-pattern.md +39 -0
- package/template/docs/data-views-pattern.md +40 -3
- package/template/docs/drawer-vs-dialog-pattern.md +3 -1
- package/template/docs/focused-workflow-page-pattern.md +84 -0
- package/template/docs/shell-surface-elevation-pattern.md +5 -3
- package/template/lib/command-menu-search-data.ts +11 -27
- package/template/lib/data-list-display-options.ts +16 -2
- package/template/lib/data-list-view-registry.ts +104 -0
- package/template/lib/data-list-view-surface.ts +15 -1
- package/template/lib/data-list-view.ts +10 -1
- package/template/lib/data-view-dashboard-storage.ts +38 -35
- package/template/lib/hub-connected-view-renderers.ts +58 -0
- package/template/lib/list-hub-nav.ts +121 -0
- package/template/lib/list-hub-supported-views.ts +10 -0
- package/template/lib/list-page-table-properties.ts +3 -7
- package/template/lib/list-status-badges.ts +4 -97
- package/template/lib/mock/list-hub-directory.ts +27 -0
- package/template/lib/mock/list-hub-kpi.ts +27 -0
- package/template/lib/mock/navigation.tsx +1 -0
- package/template/lib/page-loading-variant.ts +40 -0
- package/template/lib/question-bank-supported-views.ts +13 -0
- package/template/lib/table-state-lifecycle.ts +2 -2
- package/template/app/(app)/data-list/[id]/page.tsx +0 -44
- package/template/app/(app)/data-list/new/page.tsx +0 -34
- package/template/components/compliance-board-view.tsx +0 -142
- package/template/components/compliance-client.tsx +0 -92
- package/template/components/compliance-list-view.tsx +0 -54
- package/template/components/compliance-page-header.tsx +0 -89
- package/template/components/compliance-table.tsx +0 -612
- package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
- package/template/components/data-view-dashboard-charts-team.tsx +0 -971
- package/template/components/data-view-dashboard-charts.tsx +0 -1503
- package/template/components/new-placement-back-btn.tsx +0 -28
- package/template/components/new-placement-form.tsx +0 -1068
- package/template/components/placement-board-card.tsx +0 -262
- package/template/components/placement-detail.tsx +0 -438
- package/template/components/placements-board-view.tsx +0 -404
- package/template/components/placements-client.tsx +0 -252
- package/template/components/placements-list-view.tsx +0 -171
- package/template/components/placements-page-header.tsx +0 -166
- package/template/components/placements-table-cells.test.tsx +0 -22
- package/template/components/placements-table-cells.tsx +0 -173
- package/template/components/placements-table-columns.tsx +0 -640
- package/template/components/placements-table.tsx +0 -1642
- package/template/components/rotations-empty-state.tsx +0 -50
- package/template/components/rotations-panel-activator.tsx +0 -8
- package/template/components/sites-all-client.tsx +0 -154
- package/template/components/sites-board-view.tsx +0 -67
- package/template/components/sites-list-view.tsx +0 -42
- package/template/components/sites-table.tsx +0 -382
- package/template/components/team-board-view.tsx +0 -122
- package/template/components/team-client.tsx +0 -100
- package/template/components/team-list-view.tsx +0 -59
- package/template/components/team-page-header.tsx +0 -92
- package/template/components/team-table.tsx +0 -693
- package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
- package/template/lib/mock/compliance-kpi.ts +0 -61
- package/template/lib/mock/compliance.ts +0 -146
- package/template/lib/mock/placements-kpi.ts +0 -134
- package/template/lib/mock/placements.ts +0 -183
- package/template/lib/mock/sites-directory.ts +0 -16
- package/template/lib/mock/sites-kpi.ts +0 -25
- package/template/lib/mock/team-kpi.ts +0 -60
- package/template/lib/mock/team.ts +0 -118
- package/template/lib/placement-board-card-layout.ts +0 -79
- package/template/lib/placement-lifecycle.ts +0 -5
package/template/AGENTS.md
CHANGED
|
@@ -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 **
|
|
28
|
-
16.
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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/
|
|
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
|
|
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/
|
|
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`**
|
|
431
|
-
|
|
|
432
|
-
|
|
|
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; **
|
|
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 → **
|
|
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
|
-
|
|
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 {
|
|
1
|
+
import { DashboardLoadingFallback } from "@/components/templates/page-loading-shell"
|
|
2
2
|
|
|
3
|
-
/**
|
|
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 {
|
|
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 {
|
|
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
|
-
<
|
|
7
|
+
<ListHubClient />
|
|
8
8
|
</PrimaryPageTemplate>
|
|
9
9
|
)
|
|
10
10
|
}
|
|
@@ -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
|
-
|
|
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={
|
|
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={
|
|
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={
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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 {
|
|
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
|
-
<
|
|
35
|
+
<FocusedWorkflowPageTemplate
|
|
44
36
|
beforeSiteHeader={<SidebarAutoCollapse />}
|
|
45
37
|
siteHeader={{ back }}
|
|
46
|
-
|
|
47
|
-
maxWidthClassName="mx-auto w-full max-w-[1100px]"
|
|
48
|
-
contentClassName="px-8 py-4 pb-16"
|
|
38
|
+
maxWidth="lg"
|
|
49
39
|
>
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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={
|
|
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
|
-
<
|
|
7
|
-
|
|
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
|
-
</
|
|
14
|
+
</FocusedWorkflowPageTemplate>
|
|
16
15
|
)
|
|
17
16
|
}
|