@exxatdesignux/ui 0.3.0 → 0.4.0

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 (210) hide show
  1. package/CHANGELOG.md +608 -6
  2. package/consumer-extras/cursor-rules/exxat-board-cards.mdc +1 -1
  3. package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +2 -2
  4. package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +1 -1
  5. package/consumer-extras/cursor-rules/exxat-data-tables.mdc +2 -0
  6. package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +1 -1
  7. package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +3 -3
  8. package/consumer-extras/cursor-rules/exxat-library-hub-header.mdc +28 -0
  9. package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +1 -1
  10. package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +1 -1
  11. package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +6 -6
  12. package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +1 -1
  13. package/consumer-extras/cursor-skills/exxat-board-cards/SKILL.md +2 -2
  14. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +1 -1
  15. package/consumer-extras/cursor-skills/exxat-collaboration-access/SKILL.md +3 -3
  16. package/consumer-extras/cursor-skills/exxat-dedicated-search-surfaces/SKILL.md +2 -2
  17. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +7 -7
  18. package/consumer-extras/cursor-skills/exxat-kpi-flat-band/SKILL.md +1 -1
  19. package/consumer-extras/cursor-skills/exxat-list-page-view-shells/SKILL.md +1 -1
  20. package/consumer-extras/cursor-skills/exxat-mono-ids/SKILL.md +4 -4
  21. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +8 -8
  22. package/consumer-extras/cursor-skills/exxat-token-economy/SKILL.md +277 -0
  23. package/consumer-extras/handbook/HANDBOOK.md +2 -0
  24. package/consumer-extras/handbook/glossary.md +2 -1
  25. package/consumer-extras/handbook/reference-implementations.md +31 -4
  26. package/consumer-extras/patterns/collaboration-access-pattern.md +7 -7
  27. package/consumer-extras/patterns/data-views-pattern.md +18 -16
  28. package/consumer-extras/patterns/kpi-flat-band-pattern.md +2 -2
  29. package/dist/components/data-table/index.js +2 -2
  30. package/dist/components/data-table/index.js.map +1 -1
  31. package/dist/components/data-table/pagination.js +3 -3
  32. package/dist/components/data-table/pagination.js.map +1 -1
  33. package/dist/components/data-table/use-table-state.d.ts +1 -1
  34. package/dist/components/data-table/use-table-state.js.map +1 -1
  35. package/dist/components/data-views/data-row-list.js.map +1 -1
  36. package/dist/components/data-views/finder-panel-view.d.ts +1 -1
  37. package/dist/components/data-views/finder-panel-view.js.map +1 -1
  38. package/dist/components/data-views/hub-table.d.ts +9 -3
  39. package/dist/components/data-views/hub-table.js +262 -40
  40. package/dist/components/data-views/hub-table.js.map +1 -1
  41. package/dist/components/data-views/index.js +262 -40
  42. package/dist/components/data-views/index.js.map +1 -1
  43. package/dist/components/data-views/list-page-split-hub-tokens.d.ts +2 -2
  44. package/dist/components/data-views/list-page-split-hub-tokens.js.map +1 -1
  45. package/dist/components/data-views/list-page-tree-column-header.d.ts +1 -1
  46. package/dist/components/data-views/list-page-tree-column-header.js.map +1 -1
  47. package/dist/components/data-views/list-page-tree-panel-shell.js.map +1 -1
  48. package/dist/components/data-views/os-folder-glyph.d.ts +1 -1
  49. package/dist/components/data-views/os-folder-glyph.js.map +1 -1
  50. package/dist/components/ui/avatar.d.ts +1 -1
  51. package/dist/components/ui/banner.d.ts +2 -2
  52. package/dist/components/ui/key-metrics.js.map +1 -1
  53. package/dist/index.js +136 -39
  54. package/dist/index.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/components/data-table/index.tsx +2 -2
  57. package/src/components/data-table/pagination.tsx +5 -1
  58. package/src/components/data-table/use-table-state.ts +1 -1
  59. package/src/components/data-views/data-row-list.tsx +1 -1
  60. package/src/components/data-views/finder-panel-view.tsx +2 -2
  61. package/src/components/data-views/hub-table.tsx +149 -41
  62. package/src/components/data-views/list-page-split-hub-tokens.ts +2 -2
  63. package/src/components/data-views/list-page-tree-column-header.tsx +1 -1
  64. package/src/components/data-views/os-folder-glyph.tsx +1 -1
  65. package/src/components/ui/key-metrics.tsx +1 -1
  66. package/template/.claude/skills/exxat-ds-skill/SKILL.md +8 -7
  67. package/template/.cursor/rules/exxat-accessibility.mdc +1 -1
  68. package/template/.cursor/rules/exxat-command-menu.mdc +1 -1
  69. package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +6 -6
  70. package/template/.cursor/rules/exxat-data-tables.mdc +3 -3
  71. package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +5 -5
  72. package/template/.cursor/rules/exxat-mono-ids.mdc +1 -1
  73. package/template/.cursor/rules/exxat-page-vs-drawer.mdc +1 -1
  74. package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
  75. package/template/AGENTS.md +43 -37
  76. package/template/app/(app)/columns/page.tsx +11 -0
  77. package/template/app/(app)/library/all/page.tsx +11 -0
  78. package/template/app/(app)/library/find/page.tsx +12 -0
  79. package/template/app/(app)/{question-bank → library}/layout.tsx +16 -16
  80. package/template/app/(app)/library/list/page.tsx +12 -0
  81. package/template/app/(app)/{question-bank → library}/new/page.tsx +10 -10
  82. package/template/app/(app)/library/page.tsx +11 -0
  83. package/template/app/(app)/tokens-themes/page.tsx +11 -0
  84. package/template/components/ask-leo-composer.tsx +2 -2
  85. package/template/components/columns-client.tsx +158 -0
  86. package/template/components/columns-showcase.tsx +541 -0
  87. package/template/components/data-views/index.ts +32 -6
  88. package/template/components/data-views/{question-bank-folder-tree-branch.tsx → library-folder-tree-branch.tsx} +19 -19
  89. package/template/components/data-views/table-cells.tsx +673 -0
  90. package/template/components/folder-details-shell.tsx +11 -11
  91. package/template/components/hub-tree-panel-view.tsx +24 -24
  92. package/template/components/{question-bank-board-view.tsx → library-board-view.tsx} +44 -44
  93. package/template/components/{question-bank-client.tsx → library-client.tsx} +82 -82
  94. package/template/components/{question-bank-dashboard-charts.tsx → library-dashboard-charts.tsx} +14 -14
  95. package/template/components/{question-bank-favorite-button.tsx → library-favorite-button.tsx} +7 -7
  96. package/template/components/{question-bank-hub-client.tsx → library-hub-client.tsx} +43 -43
  97. package/template/components/{question-bank-new-folder-sheet.tsx → library-new-folder-sheet.tsx} +14 -14
  98. package/template/components/{question-bank-os-folder-view.tsx → library-os-folder-view.tsx} +31 -31
  99. package/template/components/{question-bank-page-header.tsx → library-page-header.tsx} +6 -6
  100. package/template/components/library-panel-activator.tsx +8 -0
  101. package/template/components/{question-bank-secondary-nav.tsx → library-secondary-nav.tsx} +60 -60
  102. package/template/components/{question-bank-table.tsx → library-table.tsx} +97 -97
  103. package/template/components/list-hub-status-badge.tsx +2 -2
  104. package/template/components/{new-question-composer.tsx → new-library-item-form.tsx} +37 -37
  105. package/template/components/sidebar/app-sidebar.tsx +61 -5
  106. package/template/components/sidebar/secondary-panel.tsx +109 -56
  107. package/template/components/sidebar/sidebar-auto-collapse.tsx +2 -2
  108. package/template/components/sidebar/sidebar-auto-open.tsx +2 -1
  109. package/template/components/table-properties/types.ts +1 -1
  110. package/template/components/templates/discovery-hub-template.tsx +1 -1
  111. package/template/components/templates/new-focus-template.tsx +2 -2
  112. package/template/components/templates/secondary-panel-hub-template.tsx +1 -1
  113. package/template/components/tokens-secondary-nav.tsx +192 -0
  114. package/template/components/tokens-themes-client.tsx +476 -0
  115. package/template/components/tokens-themes-section.tsx +386 -0
  116. package/template/docs/HANDBOOK.md +187 -0
  117. package/template/docs/blueprints/README.md +1 -1
  118. package/template/docs/blueprints/board-card.md +1 -1
  119. package/template/docs/blueprints/data-table.md +2 -2
  120. package/template/docs/blueprints/list-page-template.md +3 -3
  121. package/template/docs/blueprints/page-header.md +4 -4
  122. package/template/docs/collaboration-access-pattern.md +7 -7
  123. package/template/docs/component-selection-guide.md +1 -1
  124. package/template/docs/data-views-pattern.md +18 -16
  125. package/template/docs/glossary.md +58 -0
  126. package/template/docs/kpi-flat-band-pattern.md +3 -3
  127. package/template/docs/kpi-trend-pattern.md +18 -3
  128. package/template/docs/large-dataset-strategy.md +155 -0
  129. package/template/docs/library-hub-header-pattern.md +25 -0
  130. package/template/docs/migrations/_template.md +1 -1
  131. package/template/docs/reference-implementations.md +151 -0
  132. package/template/docs/token-taxonomy.md +1 -1
  133. package/template/docs/voice-and-tone.md +262 -0
  134. package/template/hooks/use-secondary-panel-hub-nav.ts +10 -10
  135. package/template/lib/ask-leo-route-context.ts +6 -18
  136. package/template/lib/coach-mark-registry.ts +0 -16
  137. package/template/lib/command-menu-config.ts +5 -12
  138. package/template/lib/command-menu-search-data.ts +8 -39
  139. package/template/lib/{question-bank-authoring.ts → library-authoring.ts} +89 -88
  140. package/template/lib/library-dedicated-search.ts +19 -0
  141. package/template/lib/library-hub-search.ts +90 -0
  142. package/template/lib/library-nav.ts +477 -0
  143. package/template/lib/library-recent-searches.ts +22 -0
  144. package/template/lib/{placements-supported-views.ts → library-supported-views.ts} +2 -2
  145. package/template/lib/list-status-badges.ts +16 -104
  146. package/template/lib/mock/dashboard.ts +1 -1
  147. package/template/lib/mock/{question-bank-folders.ts → library-folders.ts} +30 -30
  148. package/template/lib/mock/library-header-collaborators.ts +54 -0
  149. package/template/lib/mock/{question-bank-inspector.ts → library-inspector.ts} +29 -29
  150. package/template/lib/mock/{question-bank-kpi.ts → library-kpi.ts} +20 -20
  151. package/template/lib/mock/library.ts +249 -0
  152. package/template/lib/mock/navigation.tsx +32 -26
  153. package/template/lib/table-state-lifecycle.ts +1 -1
  154. package/template/next.config.mjs +7 -4
  155. package/consumer-extras/cursor-rules/exxat-question-bank-hub-header.mdc +0 -28
  156. package/template/app/(app)/examples/page.tsx +0 -41
  157. package/template/app/(app)/question-bank/find/page.tsx +0 -12
  158. package/template/app/(app)/question-bank/library/page.tsx +0 -11
  159. package/template/app/(app)/question-bank/list/page.tsx +0 -12
  160. package/template/app/(app)/question-bank/page.tsx +0 -11
  161. package/template/components/compliance-board-view.tsx +0 -142
  162. package/template/components/compliance-client.tsx +0 -92
  163. package/template/components/compliance-page-header.tsx +0 -89
  164. package/template/components/compliance-table.tsx +0 -468
  165. package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
  166. package/template/components/data-view-dashboard-charts-team.tsx +0 -971
  167. package/template/components/data-view-dashboard-charts.tsx +0 -1503
  168. package/template/components/new-placement-back-btn.tsx +0 -28
  169. package/template/components/new-placement-form.tsx +0 -942
  170. package/template/components/placement-board-card.tsx +0 -250
  171. package/template/components/placement-detail.tsx +0 -438
  172. package/template/components/placements-board-view.tsx +0 -397
  173. package/template/components/placements-client.tsx +0 -220
  174. package/template/components/placements-list-view.tsx +0 -124
  175. package/template/components/placements-page-header.tsx +0 -166
  176. package/template/components/placements-table-cells.test.tsx +0 -22
  177. package/template/components/placements-table-cells.tsx +0 -173
  178. package/template/components/placements-table-columns.tsx +0 -210
  179. package/template/components/placements-table.tsx +0 -934
  180. package/template/components/question-bank-panel-activator.tsx +0 -8
  181. package/template/components/rotations-empty-state.tsx +0 -50
  182. package/template/components/rotations-panel-activator.tsx +0 -8
  183. package/template/components/sites-board-view.tsx +0 -67
  184. package/template/components/sites-client.tsx +0 -154
  185. package/template/components/sites-table.tsx +0 -249
  186. package/template/components/team-board-view.tsx +0 -122
  187. package/template/components/team-client.tsx +0 -100
  188. package/template/components/team-page-header.tsx +0 -92
  189. package/template/components/team-table.tsx +0 -553
  190. package/template/docs/question-bank-hub-header-pattern.md +0 -25
  191. package/template/lib/compliance-supported-views.ts +0 -10
  192. package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
  193. package/template/lib/mock/compliance-kpi.ts +0 -61
  194. package/template/lib/mock/compliance.ts +0 -146
  195. package/template/lib/mock/placements-kpi.ts +0 -134
  196. package/template/lib/mock/placements.ts +0 -176
  197. package/template/lib/mock/question-bank-header-collaborators.ts +0 -54
  198. package/template/lib/mock/question-bank.ts +0 -249
  199. package/template/lib/mock/sites-directory.ts +0 -16
  200. package/template/lib/mock/sites-kpi.ts +0 -25
  201. package/template/lib/mock/team-kpi.ts +0 -60
  202. package/template/lib/mock/team.ts +0 -118
  203. package/template/lib/placement-board-card-layout.ts +0 -79
  204. package/template/lib/question-bank-dedicated-search.ts +0 -19
  205. package/template/lib/question-bank-hub-search.ts +0 -90
  206. package/template/lib/question-bank-nav.ts +0 -477
  207. package/template/lib/question-bank-recent-searches.ts +0 -22
  208. package/template/lib/question-bank-supported-views.ts +0 -12
  209. package/template/lib/sites-supported-views.ts +0 -10
  210. package/template/lib/team-supported-views.ts +0 -10
@@ -8,10 +8,10 @@ import type * as React from "react"
8
8
  import { logoDevUrl } from "@/lib/logo-dev"
9
9
  import { stockPortraitUrl } from "@/lib/stock-portrait"
10
10
  import {
11
- QUESTION_BANK_ENTRY_PATH,
12
- QUESTION_BANK_HUB_FIND_PATH,
13
- QUESTION_BANK_LIBRARY_PATH,
14
- } from "@/lib/question-bank-nav"
11
+ LIBRARY_ENTRY_PATH,
12
+ LIBRARY_HUB_FIND_PATH,
13
+ LIBRARY_ALL_PATH,
14
+ } from "@/lib/library-nav"
15
15
 
16
16
  // ── Types ─────────────────────────────────────────────────────────────────────
17
17
 
@@ -93,39 +93,32 @@ export const NAV_PRIMARY: NavLinkItem[] = [
93
93
  iconActive: <i className="fa-solid fa-grid-2" aria-hidden="true" />,
94
94
  },
95
95
  {
96
- key: "examples",
97
- title: "Patterns",
98
- url: "/examples",
99
- icon: <i className="fa-light fa-layer-group" aria-hidden="true" />,
100
- iconActive: <i className="fa-solid fa-layer-group" aria-hidden="true" />,
101
- },
102
- {
103
- key: "question-bank",
104
- title: "Question bank",
105
- url: QUESTION_BANK_ENTRY_PATH,
96
+ key: "library",
97
+ title: "Library",
98
+ url: LIBRARY_ENTRY_PATH,
106
99
  icon: <i className="fa-light fa-books" aria-hidden="true" />,
107
100
  iconActive: <i className="fa-solid fa-books" aria-hidden="true" />,
108
- secondaryPanel: "question-bank",
109
- primaryHubChildKey: "question-bank-hub",
101
+ secondaryPanel: "library",
102
+ primaryHubChildKey: "library-hub",
110
103
  children: [
111
104
  {
112
- key: "question-bank-hub",
113
- title: "Question hub",
114
- url: QUESTION_BANK_ENTRY_PATH,
105
+ key: "library-hub",
106
+ title: "Library home",
107
+ url: LIBRARY_ENTRY_PATH,
115
108
  icon: <i className="fa-light fa-sparkles" aria-hidden="true" />,
116
109
  iconActive: <i className="fa-solid fa-sparkles" aria-hidden="true" />,
117
110
  },
118
111
  {
119
- key: "question-bank-search",
112
+ key: "library-search",
120
113
  title: "Search",
121
- url: QUESTION_BANK_HUB_FIND_PATH,
114
+ url: LIBRARY_HUB_FIND_PATH,
122
115
  icon: <i className="fa-light fa-magnifying-glass" aria-hidden="true" />,
123
116
  iconActive: <i className="fa-solid fa-magnifying-glass" aria-hidden="true" />,
124
117
  },
125
118
  {
126
- key: "question-bank-library",
127
- title: "Library",
128
- url: QUESTION_BANK_LIBRARY_PATH,
119
+ key: "library-all",
120
+ title: "All items",
121
+ url: LIBRARY_ALL_PATH,
129
122
  icon: <i className="fa-light fa-table-list" aria-hidden="true" />,
130
123
  iconActive: <i className="fa-solid fa-table-list" aria-hidden="true" />,
131
124
  },
@@ -141,10 +134,23 @@ export const NAV_DOCUMENTS: NavLinkItem[] = [
141
134
  {
142
135
  key: "tokens",
143
136
  title: "Tokens & themes",
144
- /** Same page as Settingsdisambiguate active state via `#appearance` (see `isNavActive`). */
145
- url: "/settings#appearance",
137
+ /** Dedicated route (was previously /settings#appearance split out so the nav target
138
+ * is bookmarkable and there's no active-state collision with Settings). */
139
+ url: "/tokens-themes",
146
140
  icon: <i className="fa-light fa-palette" aria-hidden="true" />,
147
141
  iconActive: <i className="fa-solid fa-palette" aria-hidden="true" />,
142
+ /** Opens the `tokens` secondary panel — categories live in the rail, not in view tabs.
143
+ * `useAutoPanel("tokens")` inside the hub also collapses the main sidebar
144
+ * (`secondary-panel.tsx#openPanel`) per the Library library pattern. */
145
+ secondaryPanel: "tokens",
146
+ },
147
+ {
148
+ key: "columns",
149
+ title: "Column types",
150
+ /** DataTable column-pattern showcase — every cell renderer the DS supports. */
151
+ url: "/columns",
152
+ icon: <i className="fa-light fa-table-columns" aria-hidden="true" />,
153
+ iconActive: <i className="fa-solid fa-table-columns" aria-hidden="true" />,
148
154
  },
149
155
  {
150
156
  key: "more",
@@ -388,7 +388,7 @@ export function schedulePageSave(namespace: string, payload: PersistedPageV1): v
388
388
  // ─────────────────────────────────────────────────────────────────────────────
389
389
 
390
390
  export interface UseTableStateLifecycleOptions<TExtras extends Record<string, unknown> | void = void> {
391
- /** Storage namespace, e.g. `"placements"`, `"team"`, `"question-bank"`. */
391
+ /** Storage namespace, e.g. `"placements"`, `"team"`, `"library"`. */
392
392
  namespace: string
393
393
  /**
394
394
  * Sub-key per lifecycle tab. A hub with only one lifecycle should pass a
@@ -168,10 +168,13 @@ const nextConfig = {
168
168
  },
169
169
  async redirects() {
170
170
  return [
171
- { source: "/rotations", destination: "/examples", permanent: false },
172
- { source: "/compliance", destination: "/examples", permanent: false },
173
- { source: "/sites/all", destination: "/examples", permanent: false },
174
- { source: "/sites/all/:path*", destination: "/examples", permanent: false },
171
+ // Legacy demo routes (placements, rotations, compliance, sites) were removed.
172
+ // Redirect anything pointing at them to Dashboard rather than 404.
173
+ { source: "/rotations", destination: "/dashboard", permanent: false },
174
+ { source: "/compliance", destination: "/dashboard", permanent: false },
175
+ { source: "/sites/all", destination: "/dashboard", permanent: false },
176
+ { source: "/sites/all/:path*", destination: "/dashboard", permanent: false },
177
+ { source: "/examples", destination: "/dashboard", permanent: false },
175
178
  ]
176
179
  },
177
180
  }
@@ -1,28 +0,0 @@
1
- ---
2
- description: Question bank library — folder-scoped hub header More menu must expose Customize folder; sheet on hub client
3
- globs: apps/web/components/question-bank-*.tsx, packages/ui/template/components/question-bank-*.tsx
4
- alwaysApply: false
5
- ---
6
-
7
- # Exxat DS — Question bank hub header (folder scope)
8
-
9
- When the question bank library URL is **scoped to a folder** (`parseQuestionBankNav` → **`scope === "folder"`** and **`folderId`** set), users are effectively on a **“folder page”**: the hub title matches that folder, and **global** actions belong in the **`QuestionBankPageHeader`** **⋯ More** menu — not only on per-row or per-tile overflow menus inside a single view tab.
10
-
11
- **Pattern doc:** **`apps/web/docs/question-bank-hub-header-pattern.md`**. **Handbook:** **`apps/web/AGENTS.md` §4.6** (folder-scoped hub chrome).
12
-
13
- ## MUST
14
-
15
- 1. **`QuestionBankPageHeader`** — When **`navState.scope === "folder"`** and **`navState.folderId`** resolves to a row in **`folders`**, pass **`onCustomizeFolder`** so **⋯ More** includes **Customize folder** ( **`fa-wand-magic-sparkles`** + label **Customize folder** ), placed after **Invite people** (collaboration variant) and before **Export**.
16
- 2. **Hub client** — Mount **`QuestionBankNewFolderSheet`** on the **hub client** (e.g. **`QuestionBankClient`**) next to **`ListPageTemplate`**, driven by local **`open` / `customizingFolder`** state opened from **`onCustomizeFolder`**. **MUST NOT** rely on **`QuestionBankTable`** alone to host the sheet when some view branches (**table**, **list**, **board**, **dashboard**) do not render that sheet — users would lose **Customize folder** on those tabs.
17
- 3. **`onCreated`** — On save, **`setFolders`** (or equivalent) **maps** the scoped folder **`id`** to updated **`name`**, **`icon`**, **`colorKey`** — same contract as **`QuestionBankTable`** panel/tree customize handlers.
18
-
19
- ## MUST NOT
20
-
21
- - Omit **Customize folder** from the header **⋯** when the URL is folder-scoped, expecting users to find it only on secondary-nav tree rows or OS-folder tiles.
22
- - Mount **only** one customize sheet inside **`QuestionBankTable`** without a **client-level** sheet when the hub uses **`ListPageTemplate`** view tabs that omit that table subtree.
23
-
24
- ## See also
25
-
26
- - **`.cursor/rules/exxat-primary-nav-secondary-panel.mdc`** — URL scope + secondary panel.
27
- - **`.cursor/rules/exxat-collaboration-access.mdc`** — **`variant="collaboration"`** header + **⋯** invite pattern.
28
- - **`lib/question-bank-nav.ts`** — **`parseQuestionBankNav`**, **`QuestionBankNavState`**.
@@ -1,41 +0,0 @@
1
- import Link from "next/link"
2
- import { PrimaryPageTemplate } from "@/components/templates/primary-page-template"
3
- import { Button } from "@/components/ui/button"
4
-
5
- const LINKS = [
6
- { href: "/dashboard", label: "Dashboard", description: "Metrics, charts, and layout patterns." },
7
- { href: "/question-bank", label: "Question bank", description: "Discovery hub for browsing folders, recents, and AI-assisted create/import flows." },
8
- { href: "/question-bank/library", label: "Question library", description: "Folders, OS folder view, panel, and tree demos on mock items." },
9
- { href: "/settings", label: "Settings", description: "Appearance, tours, and shell preferences." },
10
- { href: "/help", label: "Help", description: "Support and documentation entry points." },
11
- ] as const
12
-
13
- export default function ExamplesPage() {
14
- return (
15
- <PrimaryPageTemplate
16
- siteHeader={{ title: "Patterns" }}
17
- maxWidthClassName="max-w-3xl"
18
- contentClassName="px-4 lg:px-6 py-8"
19
- bodyClassName="overflow-y-auto"
20
- >
21
- <p className="text-sm text-muted-foreground mb-6">
22
- This workspace ships neutral chrome so you can reuse layouts, data views, and tokens as a design system.
23
- </p>
24
- <ul className="flex flex-col gap-3" role="list">
25
- {LINKS.map((item) => (
26
- <li key={item.href}>
27
- <Button variant="outline" className="h-auto w-full justify-start gap-3 py-4 px-4" asChild>
28
- <Link href={item.href}>
29
- <span className="flex min-w-0 flex-1 flex-col items-start gap-0.5 text-left">
30
- <span className="font-medium text-foreground">{item.label}</span>
31
- <span className="text-xs font-normal text-muted-foreground">{item.description}</span>
32
- </span>
33
- <i className="fa-light fa-arrow-right shrink-0 text-muted-foreground" aria-hidden="true" />
34
- </Link>
35
- </Button>
36
- </li>
37
- ))}
38
- </ul>
39
- </PrimaryPageTemplate>
40
- )
41
- }
@@ -1,12 +0,0 @@
1
- import { Suspense } from "react"
2
-
3
- import { QuestionBankClient } from "@/components/question-bank-client"
4
-
5
- /** Discovery hub composer results — same hub chrome as the library, distinct from `/question-bank/list`. */
6
- export default function QuestionBankHubFindPage() {
7
- return (
8
- <Suspense fallback={null}>
9
- <QuestionBankClient />
10
- </Suspense>
11
- )
12
- }
@@ -1,11 +0,0 @@
1
- import { Suspense } from "react"
2
-
3
- import { QuestionBankClient } from "@/components/question-bank-client"
4
-
5
- export default function QuestionBankLibraryPage() {
6
- return (
7
- <Suspense fallback={null}>
8
- <QuestionBankClient />
9
- </Suspense>
10
- )
11
- }
@@ -1,12 +0,0 @@
1
- import { Suspense } from "react"
2
-
3
- import { QuestionBankClient } from "@/components/question-bank-client"
4
-
5
- /** Question bank list surface — same hub as `/question-bank/library`, optimized for `?q=` search landings. */
6
- export default function QuestionBankListPage() {
7
- return (
8
- <Suspense fallback={null}>
9
- <QuestionBankClient />
10
- </Suspense>
11
- )
12
- }
@@ -1,11 +0,0 @@
1
- import { Suspense } from "react"
2
-
3
- import { QuestionBankHubClient } from "@/components/question-bank-hub-client"
4
-
5
- export default function QuestionBankHubPage() {
6
- return (
7
- <Suspense fallback={null}>
8
- <QuestionBankHubClient />
9
- </Suspense>
10
- )
11
- }
@@ -1,142 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import { initialsFromDisplayName } from "@/lib/initials-from-name"
5
- import {
6
- COMPLIANCE_STATUS_BADGE_CLASS,
7
- COMPLIANCE_STATUS_ICON,
8
- COMPLIANCE_STATUS_LABEL,
9
- } from "@/lib/list-status-badges"
10
- import type { ComplianceItem } from "@/lib/mock/compliance"
11
- import { BoardCardTwoLineBlock } from "@/components/data-views/board-card-primitives"
12
- import { ListHubStatusBadge } from "@/components/list-hub-status-badge"
13
- import {
14
- ListPageBoardCard,
15
- ListPageBoardCardAvatar,
16
- ListPageBoardCardBadgeRow,
17
- ListPageBoardCardBody,
18
- ListPageBoardCardHeader,
19
- ListPageBoardCardTitleRow,
20
- } from "@/components/data-views/list-page-board-card"
21
- import {
22
- ListPageBoardTemplate,
23
- type ListPageBoardColumnDef,
24
- } from "@/components/data-views/list-page-board-template"
25
-
26
- const NEUTRAL_COUNT_BADGE = "bg-muted/90 text-foreground"
27
-
28
- const STATUS_BOARD_COLUMNS: ListPageBoardColumnDef<ComplianceItem>[] = [
29
- {
30
- id: "compliant",
31
- label: "Compliant",
32
- description: "On track",
33
- filter: r => r.status === "compliant",
34
- },
35
- {
36
- id: "due_soon",
37
- label: "Due soon",
38
- description: "Within window",
39
- filter: r => r.status === "due_soon",
40
- },
41
- {
42
- id: "overdue",
43
- label: "Overdue",
44
- description: "Action required",
45
- filter: r => r.status === "overdue",
46
- },
47
- {
48
- id: "pending",
49
- label: "Pending",
50
- description: "Not started",
51
- filter: r => r.status === "pending",
52
- },
53
- ]
54
-
55
- function categoryBoardColumns(rows: ComplianceItem[]): {
56
- columns: ListPageBoardColumnDef<ComplianceItem>[]
57
- badgeMap: Record<string, string>
58
- } {
59
- const labels = [...new Set(rows.map(r => r.category))].sort((a, b) => a.localeCompare(b))
60
- const columns: ListPageBoardColumnDef<ComplianceItem>[] = labels.map(label => ({
61
- id: `category:${label}`,
62
- label,
63
- filter: (r: ComplianceItem) => r.category === label,
64
- }))
65
- const badgeMap = Object.fromEntries(labels.map(l => [`category:${l}`, NEUTRAL_COUNT_BADGE]))
66
- return { columns, badgeMap }
67
- }
68
-
69
- function useComplianceBoardModel(rows: ComplianceItem[], groupByColumnKey: string) {
70
- return React.useMemo(() => {
71
- if (groupByColumnKey === "category") {
72
- const { columns, badgeMap } = categoryBoardColumns(rows)
73
- return { columns, badgeMap }
74
- }
75
- return {
76
- columns: STATUS_BOARD_COLUMNS,
77
- badgeMap: COMPLIANCE_STATUS_BADGE_CLASS as Record<string, string>,
78
- }
79
- }, [rows, groupByColumnKey])
80
- }
81
-
82
- function ComplianceBoardCard({
83
- row,
84
- onRowActivate,
85
- }: {
86
- row: ComplianceItem
87
- onRowActivate?: (row: ComplianceItem) => void
88
- }) {
89
- const ownerInitials = initialsFromDisplayName(row.owner)
90
- return (
91
- <ListPageBoardCard className="w-full" onClick={onRowActivate ? () => onRowActivate(row) : undefined}>
92
- <ListPageBoardCardHeader>
93
- <ListPageBoardCardTitleRow
94
- title={row.title}
95
- titleClassName="line-clamp-2"
96
- trailing={<ListPageBoardCardAvatar initials={ownerInitials} />}
97
- />
98
- <ListPageBoardCardBadgeRow>
99
- <ListHubStatusBadge
100
- surface="board"
101
- label={COMPLIANCE_STATUS_LABEL[row.status]}
102
- tintClassName={COMPLIANCE_STATUS_BADGE_CLASS[row.status]}
103
- icon={COMPLIANCE_STATUS_ICON[row.status]}
104
- />
105
- </ListPageBoardCardBadgeRow>
106
- <ListPageBoardCardBody>
107
- <BoardCardTwoLineBlock iconClass="fa-tag" line1={row.category} line2={`Due ${row.dueDate}`} />
108
- <BoardCardTwoLineBlock iconClass="fa-user" line1={row.owner} line2="Owner" />
109
- </ListPageBoardCardBody>
110
- </ListPageBoardCardHeader>
111
- </ListPageBoardCard>
112
- )
113
- }
114
-
115
- export const COMPLIANCE_BOARD_GROUP_OPTIONS = [
116
- { key: "status", label: "Status" },
117
- { key: "category", label: "Category" },
118
- ] as const
119
-
120
- export function ComplianceBoardView({
121
- rows,
122
- groupByColumnKey,
123
- onRowActivate,
124
- }: {
125
- rows: ComplianceItem[]
126
- groupByColumnKey: string
127
- onRowActivate?: (row: ComplianceItem) => void
128
- }) {
129
- const key = groupByColumnKey === "category" ? "category" : "status"
130
- const { columns, badgeMap } = useComplianceBoardModel(rows, key)
131
-
132
- return (
133
- <ListPageBoardTemplate
134
- columns={columns}
135
- rows={rows}
136
- getRowKey={r => r.id}
137
- columnCountBadgeClassName={badgeMap}
138
- emptyColumnLabel="No items"
139
- renderCard={row => <ComplianceBoardCard row={row} onRowActivate={onRowActivate} />}
140
- />
141
- )
142
- }
@@ -1,92 +0,0 @@
1
- "use client"
2
-
3
- /**
4
- * Compliance list page — `ListPageTemplate` + `ComplianceTable`; view types from `@/components/data-views`.
5
- */
6
-
7
- import * as React from "react"
8
- import {
9
- ListPageTemplate,
10
- type ViewTab,
11
- dataListViewIcon,
12
- type DataListViewType,
13
- } from "@/components/data-views"
14
- import { CompliancePageHeader } from "@/components/compliance-page-header"
15
- import { ComplianceTable, type ComplianceTableHandle } from "@/components/compliance-table"
16
- import { KeyMetrics } from "@/components/key-metrics"
17
- import { useAskLeoPageContext } from "@/components/ask-leo-sidebar"
18
- import { COMPLIANCE_ITEMS } from "@/lib/mock/compliance"
19
- import { complianceKpiInsight, complianceKpiMetrics } from "@/lib/mock/compliance-kpi"
20
-
21
- const DEFAULT_TABS: ViewTab[] = [
22
- {
23
- id: "obligations",
24
- label: "Obligations",
25
- viewType: "table",
26
- icon: "fa-table",
27
- filterId: "all",
28
- },
29
- ]
30
-
31
- export function ComplianceClient() {
32
- const [exportOpen, setExportOpen] = React.useState(false)
33
- const [showMetrics, setShowMetrics] = React.useState(true)
34
- const tableRef = React.useRef<ComplianceTableHandle>(null)
35
- const count = COMPLIANCE_ITEMS.length
36
-
37
- const metrics = React.useMemo(() => complianceKpiMetrics(COMPLIANCE_ITEMS), [])
38
- const insight = React.useMemo(() => complianceKpiInsight(COMPLIANCE_ITEMS), [])
39
-
40
- useAskLeoPageContext(
41
- React.useMemo(
42
- () => ({
43
- title: "Compliance",
44
- description: `${count} obligations tracked on this hub.`,
45
- suggestions: [
46
- "What’s due this week?",
47
- "Summarize open items by student",
48
- ],
49
- }),
50
- [count],
51
- ),
52
- )
53
-
54
- return (
55
- <ListPageTemplate
56
- defaultTabs={DEFAULT_TABS}
57
- getTabCount={() => count}
58
- tablePropertiesRef={tableRef}
59
- header={
60
- <CompliancePageHeader
61
- itemCount={count}
62
- onAddReview={() => {}}
63
- onExport={() => setExportOpen(true)}
64
- showMetrics={showMetrics}
65
- onToggleMetrics={() => setShowMetrics(v => !v)}
66
- />
67
- }
68
- metrics={
69
- <KeyMetrics
70
- variant="flat"
71
- metrics={metrics}
72
- insight={insight}
73
- showHeader={false}
74
- metricsSingleRow
75
- />
76
- }
77
- showMetrics={showMetrics}
78
- exportOpen={exportOpen}
79
- onExportOpenChange={setExportOpen}
80
- exportTotalRows={count}
81
- renderContent={(tab, updateTab) => (
82
- <ComplianceTable
83
- key={tab.id}
84
- ref={tableRef}
85
- items={COMPLIANCE_ITEMS}
86
- view={tab.viewType}
87
- onViewChange={(v: DataListViewType) => updateTab({ viewType: v, icon: dataListViewIcon(v) })}
88
- />
89
- )}
90
- />
91
- )
92
- }
@@ -1,89 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import { Button } from "@/components/ui/button"
5
- import { PageHeader } from "@/components/page-header"
6
- import {
7
- DropdownMenu,
8
- DropdownMenuContent,
9
- DropdownMenuItem,
10
- DropdownMenuSeparator,
11
- DropdownMenuTrigger,
12
- } from "@/components/ui/dropdown-menu"
13
- import { Tip } from "@/components/ui/tip"
14
-
15
- export interface CompliancePageHeaderProps {
16
- itemCount: number
17
- onAddReview: () => void
18
- onExport: () => void
19
- showMetrics: boolean
20
- onToggleMetrics: () => void
21
- showTitleBlock?: boolean
22
- }
23
-
24
- export function CompliancePageHeader({
25
- itemCount,
26
- onAddReview,
27
- onExport,
28
- showMetrics,
29
- onToggleMetrics,
30
- showTitleBlock = true,
31
- }: CompliancePageHeaderProps) {
32
- const [moreOpen, setMoreOpen] = React.useState(false)
33
- const countLine = `${itemCount} ${itemCount === 1 ? "item" : "items"} · Last updated now`
34
-
35
- return (
36
- <PageHeader
37
- title="Compliance"
38
- subtitle={countLine}
39
- showTitleBlock={showTitleBlock}
40
- actions={(
41
- <div className="flex items-center gap-2" role="group" aria-label="Compliance actions">
42
- <Tip side="bottom" label="Schedule a review (demo)">
43
- <Button type="button" size="lg" onClick={onAddReview}>
44
- <i className="fa-light fa-calendar-check" aria-hidden="true" />
45
- New review
46
- </Button>
47
- </Tip>
48
- <DropdownMenu open={moreOpen} onOpenChange={setMoreOpen}>
49
- <Tip side="bottom" label="More actions">
50
- <DropdownMenuTrigger asChild>
51
- <Button
52
- type="button"
53
- size="lg"
54
- variant="outline"
55
- className="aspect-square px-0"
56
- aria-label="More actions"
57
- >
58
- <i className="fa-light fa-ellipsis text-base" aria-hidden="true" />
59
- </Button>
60
- </DropdownMenuTrigger>
61
- </Tip>
62
- <DropdownMenuContent align="end">
63
- <DropdownMenuItem
64
- onSelect={() => {
65
- window.setTimeout(() => onExport(), 0)
66
- }}
67
- >
68
- <i className="fa-light fa-arrow-down-to-line" aria-hidden="true" />
69
- Export
70
- </DropdownMenuItem>
71
- <DropdownMenuSeparator />
72
- <DropdownMenuItem
73
- onSelect={() => {
74
- window.setTimeout(() => onToggleMetrics(), 0)
75
- }}
76
- >
77
- <i
78
- className={`fa-light ${showMetrics ? "fa-eye-slash" : "fa-eye"}`}
79
- aria-hidden="true"
80
- />
81
- {showMetrics ? "Hide metric section" : "Show metric section"}
82
- </DropdownMenuItem>
83
- </DropdownMenuContent>
84
- </DropdownMenu>
85
- </div>
86
- )}
87
- />
88
- )
89
- }