@exxatdesignux/ui 0.3.0 → 0.4.1

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 (214) hide show
  1. package/CHANGELOG.md +701 -6
  2. package/README.md +138 -0
  3. package/bin/init.mjs +134 -31
  4. package/consumer-extras/cursor-rules/exxat-board-cards.mdc +1 -1
  5. package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +2 -2
  6. package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +1 -1
  7. package/consumer-extras/cursor-rules/exxat-data-tables.mdc +2 -0
  8. package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +1 -1
  9. package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +3 -3
  10. package/consumer-extras/cursor-rules/exxat-library-hub-header.mdc +28 -0
  11. package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +1 -1
  12. package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +1 -1
  13. package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +6 -6
  14. package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +1 -1
  15. package/consumer-extras/cursor-skills/exxat-board-cards/SKILL.md +2 -2
  16. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +1 -1
  17. package/consumer-extras/cursor-skills/exxat-collaboration-access/SKILL.md +3 -3
  18. package/consumer-extras/cursor-skills/exxat-dedicated-search-surfaces/SKILL.md +2 -2
  19. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +7 -7
  20. package/consumer-extras/cursor-skills/exxat-kpi-flat-band/SKILL.md +1 -1
  21. package/consumer-extras/cursor-skills/exxat-list-page-view-shells/SKILL.md +1 -1
  22. package/consumer-extras/cursor-skills/exxat-mono-ids/SKILL.md +4 -4
  23. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +8 -8
  24. package/consumer-extras/cursor-skills/exxat-token-economy/SKILL.md +277 -0
  25. package/consumer-extras/handbook/HANDBOOK.md +2 -0
  26. package/consumer-extras/handbook/glossary.md +2 -1
  27. package/consumer-extras/handbook/reference-implementations.md +31 -4
  28. package/consumer-extras/patterns/collaboration-access-pattern.md +7 -7
  29. package/consumer-extras/patterns/data-views-pattern.md +18 -16
  30. package/consumer-extras/patterns/kpi-flat-band-pattern.md +2 -2
  31. package/dist/components/data-table/index.js +2 -2
  32. package/dist/components/data-table/index.js.map +1 -1
  33. package/dist/components/data-table/pagination.js +3 -3
  34. package/dist/components/data-table/pagination.js.map +1 -1
  35. package/dist/components/data-table/use-table-state.d.ts +1 -1
  36. package/dist/components/data-table/use-table-state.js.map +1 -1
  37. package/dist/components/data-views/data-row-list.js.map +1 -1
  38. package/dist/components/data-views/finder-panel-view.d.ts +1 -1
  39. package/dist/components/data-views/finder-panel-view.js.map +1 -1
  40. package/dist/components/data-views/hub-table.d.ts +9 -3
  41. package/dist/components/data-views/hub-table.js +262 -40
  42. package/dist/components/data-views/hub-table.js.map +1 -1
  43. package/dist/components/data-views/index.js +262 -40
  44. package/dist/components/data-views/index.js.map +1 -1
  45. package/dist/components/data-views/list-page-split-hub-tokens.d.ts +2 -2
  46. package/dist/components/data-views/list-page-split-hub-tokens.js.map +1 -1
  47. package/dist/components/data-views/list-page-tree-column-header.d.ts +1 -1
  48. package/dist/components/data-views/list-page-tree-column-header.js.map +1 -1
  49. package/dist/components/data-views/list-page-tree-panel-shell.js.map +1 -1
  50. package/dist/components/data-views/os-folder-glyph.d.ts +1 -1
  51. package/dist/components/data-views/os-folder-glyph.js.map +1 -1
  52. package/dist/components/ui/avatar.d.ts +1 -1
  53. package/dist/components/ui/key-metrics.js.map +1 -1
  54. package/dist/index.js +136 -39
  55. package/dist/index.js.map +1 -1
  56. package/package.json +3 -2
  57. package/src/components/data-table/index.tsx +2 -2
  58. package/src/components/data-table/pagination.tsx +5 -1
  59. package/src/components/data-table/use-table-state.ts +1 -1
  60. package/src/components/data-views/data-row-list.tsx +1 -1
  61. package/src/components/data-views/finder-panel-view.tsx +2 -2
  62. package/src/components/data-views/hub-table.tsx +149 -41
  63. package/src/components/data-views/list-page-split-hub-tokens.ts +2 -2
  64. package/src/components/data-views/list-page-tree-column-header.tsx +1 -1
  65. package/src/components/data-views/os-folder-glyph.tsx +1 -1
  66. package/src/components/ui/key-metrics.tsx +1 -1
  67. package/template/.claude/skills/exxat-ds-skill/SKILL.md +8 -7
  68. package/template/.cursor/rules/exxat-accessibility.mdc +1 -1
  69. package/template/.cursor/rules/exxat-command-menu.mdc +1 -1
  70. package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +6 -6
  71. package/template/.cursor/rules/exxat-data-tables.mdc +3 -3
  72. package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +5 -5
  73. package/template/.cursor/rules/exxat-mono-ids.mdc +1 -1
  74. package/template/.cursor/rules/exxat-page-vs-drawer.mdc +1 -1
  75. package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
  76. package/template/AGENTS.md +43 -37
  77. package/template/app/(app)/columns/page.tsx +11 -0
  78. package/template/app/(app)/library/all/page.tsx +11 -0
  79. package/template/app/(app)/library/find/page.tsx +12 -0
  80. package/template/app/(app)/{question-bank → library}/layout.tsx +16 -16
  81. package/template/app/(app)/library/list/page.tsx +12 -0
  82. package/template/app/(app)/{question-bank → library}/new/page.tsx +10 -10
  83. package/template/app/(app)/library/page.tsx +11 -0
  84. package/template/app/(app)/tokens-themes/page.tsx +11 -0
  85. package/template/components/ask-leo-composer.tsx +2 -2
  86. package/template/components/columns-client.tsx +158 -0
  87. package/template/components/columns-showcase.tsx +541 -0
  88. package/template/components/data-views/index.ts +32 -6
  89. package/template/components/data-views/{question-bank-folder-tree-branch.tsx → library-folder-tree-branch.tsx} +19 -19
  90. package/template/components/data-views/table-cells.tsx +673 -0
  91. package/template/components/folder-details-shell.tsx +11 -11
  92. package/template/components/hub-tree-panel-view.tsx +24 -24
  93. package/template/components/{question-bank-board-view.tsx → library-board-view.tsx} +44 -44
  94. package/template/components/{question-bank-client.tsx → library-client.tsx} +82 -82
  95. package/template/components/{question-bank-dashboard-charts.tsx → library-dashboard-charts.tsx} +14 -14
  96. package/template/components/{question-bank-favorite-button.tsx → library-favorite-button.tsx} +7 -7
  97. package/template/components/{question-bank-hub-client.tsx → library-hub-client.tsx} +43 -43
  98. package/template/components/{question-bank-new-folder-sheet.tsx → library-new-folder-sheet.tsx} +14 -14
  99. package/template/components/{question-bank-os-folder-view.tsx → library-os-folder-view.tsx} +31 -31
  100. package/template/components/{question-bank-page-header.tsx → library-page-header.tsx} +6 -6
  101. package/template/components/library-panel-activator.tsx +8 -0
  102. package/template/components/{question-bank-secondary-nav.tsx → library-secondary-nav.tsx} +60 -60
  103. package/template/components/{question-bank-table.tsx → library-table.tsx} +97 -97
  104. package/template/components/list-hub-status-badge.tsx +2 -2
  105. package/template/components/{new-question-composer.tsx → new-library-item-form.tsx} +37 -37
  106. package/template/components/sidebar/app-sidebar.tsx +61 -5
  107. package/template/components/sidebar/secondary-panel.tsx +109 -56
  108. package/template/components/sidebar/sidebar-auto-collapse.tsx +2 -2
  109. package/template/components/sidebar/sidebar-auto-open.tsx +2 -1
  110. package/template/components/table-properties/types.ts +1 -1
  111. package/template/components/templates/discovery-hub-template.tsx +1 -1
  112. package/template/components/templates/new-focus-template.tsx +2 -2
  113. package/template/components/templates/secondary-panel-hub-template.tsx +1 -1
  114. package/template/components/tokens-secondary-nav.tsx +192 -0
  115. package/template/components/tokens-themes-client.tsx +476 -0
  116. package/template/components/tokens-themes-section.tsx +386 -0
  117. package/template/docs/HANDBOOK.md +187 -0
  118. package/template/docs/blueprints/README.md +1 -1
  119. package/template/docs/blueprints/board-card.md +1 -1
  120. package/template/docs/blueprints/data-table.md +2 -2
  121. package/template/docs/blueprints/list-page-template.md +3 -3
  122. package/template/docs/blueprints/page-header.md +4 -4
  123. package/template/docs/collaboration-access-pattern.md +7 -7
  124. package/template/docs/component-selection-guide.md +1 -1
  125. package/template/docs/data-views-pattern.md +18 -16
  126. package/template/docs/glossary.md +58 -0
  127. package/template/docs/kpi-flat-band-pattern.md +3 -3
  128. package/template/docs/kpi-trend-pattern.md +18 -3
  129. package/template/docs/large-dataset-strategy.md +155 -0
  130. package/template/docs/library-hub-header-pattern.md +25 -0
  131. package/template/docs/migrations/_template.md +1 -1
  132. package/template/docs/reference-implementations.md +151 -0
  133. package/template/docs/token-taxonomy.md +1 -1
  134. package/template/docs/voice-and-tone.md +262 -0
  135. package/template/eslint.config.mjs +9 -39
  136. package/template/hooks/use-secondary-panel-hub-nav.ts +10 -10
  137. package/template/lib/ask-leo-route-context.ts +6 -18
  138. package/template/lib/coach-mark-registry.ts +0 -16
  139. package/template/lib/command-menu-config.ts +5 -12
  140. package/template/lib/command-menu-search-data.ts +8 -39
  141. package/template/lib/{question-bank-authoring.ts → library-authoring.ts} +89 -88
  142. package/template/lib/library-dedicated-search.ts +19 -0
  143. package/template/lib/library-hub-search.ts +90 -0
  144. package/template/lib/library-nav.ts +477 -0
  145. package/template/lib/library-recent-searches.ts +22 -0
  146. package/template/lib/{placements-supported-views.ts → library-supported-views.ts} +2 -2
  147. package/template/lib/list-status-badges.ts +16 -104
  148. package/template/lib/mock/dashboard.ts +1 -1
  149. package/template/lib/mock/{question-bank-folders.ts → library-folders.ts} +30 -30
  150. package/template/lib/mock/library-header-collaborators.ts +54 -0
  151. package/template/lib/mock/{question-bank-inspector.ts → library-inspector.ts} +29 -29
  152. package/template/lib/mock/{question-bank-kpi.ts → library-kpi.ts} +20 -20
  153. package/template/lib/mock/library.ts +249 -0
  154. package/template/lib/mock/navigation.tsx +32 -26
  155. package/template/lib/table-state-lifecycle.ts +1 -1
  156. package/template/next.config.mjs +7 -4
  157. package/template/package.json +0 -1
  158. package/tokens/hooks-index.json +2874 -0
  159. package/consumer-extras/cursor-rules/exxat-question-bank-hub-header.mdc +0 -28
  160. package/template/app/(app)/examples/page.tsx +0 -41
  161. package/template/app/(app)/question-bank/find/page.tsx +0 -12
  162. package/template/app/(app)/question-bank/library/page.tsx +0 -11
  163. package/template/app/(app)/question-bank/list/page.tsx +0 -12
  164. package/template/app/(app)/question-bank/page.tsx +0 -11
  165. package/template/components/compliance-board-view.tsx +0 -142
  166. package/template/components/compliance-client.tsx +0 -92
  167. package/template/components/compliance-page-header.tsx +0 -89
  168. package/template/components/compliance-table.tsx +0 -468
  169. package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
  170. package/template/components/data-view-dashboard-charts-team.tsx +0 -971
  171. package/template/components/data-view-dashboard-charts.tsx +0 -1503
  172. package/template/components/new-placement-back-btn.tsx +0 -28
  173. package/template/components/new-placement-form.tsx +0 -942
  174. package/template/components/placement-board-card.tsx +0 -250
  175. package/template/components/placement-detail.tsx +0 -438
  176. package/template/components/placements-board-view.tsx +0 -397
  177. package/template/components/placements-client.tsx +0 -220
  178. package/template/components/placements-list-view.tsx +0 -124
  179. package/template/components/placements-page-header.tsx +0 -166
  180. package/template/components/placements-table-cells.test.tsx +0 -22
  181. package/template/components/placements-table-cells.tsx +0 -173
  182. package/template/components/placements-table-columns.tsx +0 -210
  183. package/template/components/placements-table.tsx +0 -934
  184. package/template/components/question-bank-panel-activator.tsx +0 -8
  185. package/template/components/rotations-empty-state.tsx +0 -50
  186. package/template/components/rotations-panel-activator.tsx +0 -8
  187. package/template/components/sites-board-view.tsx +0 -67
  188. package/template/components/sites-client.tsx +0 -154
  189. package/template/components/sites-table.tsx +0 -249
  190. package/template/components/team-board-view.tsx +0 -122
  191. package/template/components/team-client.tsx +0 -100
  192. package/template/components/team-page-header.tsx +0 -92
  193. package/template/components/team-table.tsx +0 -553
  194. package/template/docs/question-bank-hub-header-pattern.md +0 -25
  195. package/template/lib/compliance-supported-views.ts +0 -10
  196. package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
  197. package/template/lib/mock/compliance-kpi.ts +0 -61
  198. package/template/lib/mock/compliance.ts +0 -146
  199. package/template/lib/mock/placements-kpi.ts +0 -134
  200. package/template/lib/mock/placements.ts +0 -176
  201. package/template/lib/mock/question-bank-header-collaborators.ts +0 -54
  202. package/template/lib/mock/question-bank.ts +0 -249
  203. package/template/lib/mock/sites-directory.ts +0 -16
  204. package/template/lib/mock/sites-kpi.ts +0 -25
  205. package/template/lib/mock/team-kpi.ts +0 -60
  206. package/template/lib/mock/team.ts +0 -118
  207. package/template/lib/placement-board-card-layout.ts +0 -79
  208. package/template/lib/question-bank-dedicated-search.ts +0 -19
  209. package/template/lib/question-bank-hub-search.ts +0 -90
  210. package/template/lib/question-bank-nav.ts +0 -477
  211. package/template/lib/question-bank-recent-searches.ts +0 -22
  212. package/template/lib/question-bank-supported-views.ts +0 -12
  213. package/template/lib/sites-supported-views.ts +0 -10
  214. package/template/lib/team-supported-views.ts +0 -10
@@ -1,8 +1,8 @@
1
1
  "use client"
2
2
 
3
3
  /**
4
- * Question bank hub — ListPageTemplate + KeyMetrics + QuestionBankTable (Team / Compliance pattern).
5
- * URL hash syncs the active view tab; `?scope=` + `folderId=` sync with the secondary nav (`lib/question-bank-nav.ts`).
4
+ * Library hub — ListPageTemplate + KeyMetrics + LibraryTable (Team / Compliance pattern).
5
+ * URL hash syncs the active view tab; `?scope=` + `folderId=` sync with the secondary nav (`lib/library-nav.ts`).
6
6
  * (Primary sidebar “Library” must not treat that hash as “off-route” — `app-sidebar` `isNavActive` ignores hash for `href` without `#…`.)
7
7
  */
8
8
 
@@ -14,40 +14,40 @@ import {
14
14
  dataListViewIcon,
15
15
  type DataListViewType,
16
16
  } from "@/components/data-views"
17
- import { QUESTION_BANK_SUPPORTED_VIEWS } from "@/lib/question-bank-supported-views"
18
- import { QuestionBankPageHeader } from "@/components/question-bank-page-header"
19
- import { QuestionBankNewFolderSheet } from "@/components/question-bank-new-folder-sheet"
17
+ import { LIBRARY_SUPPORTED_VIEWS } from "@/lib/library-supported-views"
18
+ import { LibraryPageHeader } from "@/components/library-page-header"
19
+ import { LibraryNewFolderSheet } from "@/components/library-new-folder-sheet"
20
20
  import { CollaborationAccessFlow } from "@/components/collaboration-access-flow"
21
- import { QuestionBankTable, type QuestionBankTableHandle } from "@/components/question-bank-table"
21
+ import { LibraryTable, type LibraryTableHandle } from "@/components/library-table"
22
22
  import { SecondaryPanelHubTemplate } from "@/components/templates/secondary-panel-hub-template"
23
- import { QuestionBankAccessBridge, QuestionBankFolderBridge } from "@/components/sidebar"
23
+ import { LibraryAccessBridge, LibraryFolderBridge } from "@/components/sidebar"
24
24
  import { KeyMetrics } from "@/components/key-metrics"
25
25
  import { useSidebar } from "@/components/ui/sidebar"
26
26
  import { useSecondaryPanelHubNav } from "@/hooks/use-secondary-panel-hub-nav"
27
- import { QUESTION_BANK_ITEMS } from "@/lib/mock/question-bank"
28
- import { QUESTION_BANK_HEADER_COLLABORATORS } from "@/lib/mock/question-bank-header-collaborators"
29
- import { DEFAULT_QUESTION_BANK_FOLDERS, type QuestionBankFolder } from "@/lib/mock/question-bank-folders"
30
- import { questionBankKpiInsight, questionBankKpiMetrics } from "@/lib/mock/question-bank-kpi"
27
+ import { LIBRARY_ITEMS } from "@/lib/mock/library"
28
+ import { LIBRARY_HEADER_COLLABORATORS } from "@/lib/mock/library-header-collaborators"
29
+ import { DEFAULT_LIBRARY_FOLDERS, type LibraryFolder } from "@/lib/mock/library-folders"
30
+ import { libraryKpiInsight, libraryKpiMetrics } from "@/lib/mock/library-kpi"
31
31
  import {
32
- applyQuestionBankHubDisplayFilters,
33
- isQuestionBankDefaultNav,
34
- isQuestionBankDedicatedSearchPathname,
35
- parseQuestionBankNav,
36
- QUESTION_BANK_HUB_BREADCRUMB,
37
- QUESTION_BANK_HUB_FIND_PATH,
38
- QUESTION_BANK_LIBRARY_PATH,
39
- QUESTION_BANK_LIBRARY_HUB_PATHS,
40
- QUESTION_BANK_LIST_PATH,
41
- questionBankCanonicalNavHref,
42
- questionBankHubHeaderModel,
43
- questionBankHubTextMatchesNothing,
44
- type QuestionBankLandingFilterState,
45
- } from "@/lib/question-bank-nav"
32
+ applyLibraryHubDisplayFilters,
33
+ isLibraryDefaultNav,
34
+ isLibraryDedicatedSearchPathname,
35
+ parseLibraryNav,
36
+ LIBRARY_HUB_BREADCRUMB,
37
+ LIBRARY_HUB_FIND_PATH,
38
+ LIBRARY_ALL_PATH,
39
+ LIBRARY_LIBRARY_HUB_PATHS,
40
+ LIBRARY_LIST_PATH,
41
+ libraryCanonicalNavHref,
42
+ libraryHubHeaderModel,
43
+ libraryHubTextMatchesNothing,
44
+ type LibraryLandingFilterState,
45
+ } from "@/lib/library-nav"
46
46
  import {
47
- patchQuestionBankDedicatedSearchParams,
48
- QUESTION_BANK_DEDICATED_SEARCH_PLACEHOLDERS,
49
- } from "@/lib/question-bank-dedicated-search"
50
- import { recordQuestionBankRecentSearch, questionBankDedicatedSearchRecents } from "@/lib/question-bank-recent-searches"
47
+ patchLibraryDedicatedSearchParams,
48
+ LIBRARY_DEDICATED_SEARCH_PLACEHOLDERS,
49
+ } from "@/lib/library-dedicated-search"
50
+ import { recordLibraryRecentSearch, libraryDedicatedSearchRecents } from "@/lib/library-recent-searches"
51
51
  import { DedicatedSearchRecents } from "@/components/dedicated-search-recents"
52
52
  import { DedicatedSearchUrlComposer } from "@/components/dedicated-search-url-composer"
53
53
  import { DedicatedSearchLandingTemplate } from "@/components/templates/dedicated-search-landing-template"
@@ -82,32 +82,32 @@ const DEFAULT_TABS: ViewTab[] = [
82
82
 
83
83
  const SEARCH_LANDING_TABS: ViewTab[] = [DEFAULT_TABS[0]]
84
84
 
85
- function ignoreQuestionBankTabsUpdate(_next: ViewTab[]) {}
86
- function ignoreQuestionBankTabActivation(_id: string) {}
85
+ function ignoreLibraryTabsUpdate(_next: ViewTab[]) {}
86
+ function ignoreLibraryTabActivation(_id: string) {}
87
87
  /** Stable no-op for search-landing branch where manage-access is not available. */
88
88
  function noopManageAccess() {}
89
89
 
90
- function questionBankQueryPrefixFromSearchString(qs: string) {
90
+ function libraryQueryPrefixFromSearchString(qs: string) {
91
91
  return qs ? `?${qs}` : ""
92
92
  }
93
93
 
94
- export function QuestionBankClient() {
94
+ export function LibraryClient() {
95
95
  const router = useRouter()
96
96
  const searchParams = useSearchParams()
97
97
  const { navState, searchParamsKey, pathname, isHubPath, hubBasePath } = useSecondaryPanelHubNav({
98
- hubPathname: QUESTION_BANK_LIBRARY_PATH,
99
- hubPathnames: QUESTION_BANK_LIBRARY_HUB_PATHS,
100
- panelId: "question-bank",
101
- parseNav: parseQuestionBankNav,
102
- canonicalHref: questionBankCanonicalNavHref,
103
- shouldReopenPanel: isQuestionBankDefaultNav,
98
+ hubPathname: LIBRARY_ALL_PATH,
99
+ hubPathnames: LIBRARY_LIBRARY_HUB_PATHS,
100
+ panelId: "library",
101
+ parseNav: parseLibraryNav,
102
+ canonicalHref: libraryCanonicalNavHref,
103
+ shouldReopenPanel: isLibraryDefaultNav,
104
104
  /** Hub/find + list are full-width — layout closes the panel; do not fight it with `openPanel`. */
105
- reopenPanelOnPathnames: [QUESTION_BANK_LIBRARY_PATH],
105
+ reopenPanelOnPathnames: [LIBRARY_ALL_PATH],
106
106
  })
107
- const isDedicatedSearch = isQuestionBankDedicatedSearchPathname(pathname)
108
- const isHubFindSurface = pathname === QUESTION_BANK_HUB_FIND_PATH
107
+ const isDedicatedSearch = isLibraryDedicatedSearchPathname(pathname)
108
+ const isHubFindSurface = pathname === LIBRARY_HUB_FIND_PATH
109
109
  const dedicatedSearchTitle = isHubFindSurface ? "Discovery search" : "Search Questions"
110
- const landingFilters = React.useMemo((): QuestionBankLandingFilterState | null => {
110
+ const landingFilters = React.useMemo((): LibraryLandingFilterState | null => {
111
111
  if (!isDedicatedSearch) return null
112
112
  const sp = new URLSearchParams(searchParamsKey)
113
113
  return {
@@ -135,7 +135,7 @@ export function QuestionBankClient() {
135
135
  if (!isHubPath || isDedicatedSearch) return
136
136
  const apply = () => {
137
137
  const current = navRef.current
138
- if (!QUESTION_BANK_LIBRARY_HUB_PATHS.includes(current.pathname)) return
138
+ if (!LIBRARY_LIBRARY_HUB_PATHS.includes(current.pathname)) return
139
139
  const raw = typeof window !== "undefined" ? window.location.hash.slice(1) : ""
140
140
  let nextId = "questions"
141
141
  if (raw === "panel-view" || raw === "tree-panel") {
@@ -145,7 +145,7 @@ export function QuestionBankClient() {
145
145
  }
146
146
  setActiveTabId(nextId)
147
147
  if (nextId === "questions" && raw && raw !== "questions") {
148
- const prefix = questionBankQueryPrefixFromSearchString(current.searchParamsKey)
148
+ const prefix = libraryQueryPrefixFromSearchString(current.searchParamsKey)
149
149
  router.replace(`${current.hubBasePath}${prefix}`, { scroll: false })
150
150
  }
151
151
  }
@@ -160,7 +160,7 @@ export function QuestionBankClient() {
160
160
  if (isDedicatedSearch) return
161
161
  setActiveTabId(id)
162
162
  if (!isHubPath) return
163
- const prefix = questionBankQueryPrefixFromSearchString(searchParamsKey)
163
+ const prefix = libraryQueryPrefixFromSearchString(searchParamsKey)
164
164
  if (id === "questions") {
165
165
  router.replace(`${hubBasePath}${prefix}`, { scroll: false })
166
166
  } else {
@@ -175,12 +175,12 @@ export function QuestionBankClient() {
175
175
  React.useLayoutEffect(() => {
176
176
  if (hasUrlSearch) setShowMetrics(false)
177
177
  }, [hasUrlSearch])
178
- const tableRef = React.useRef<QuestionBankTableHandle>(null)
179
- const [items, setItems] = React.useState(() => QUESTION_BANK_ITEMS.map(q => ({ ...q })))
180
- const [folders, setFolders] = React.useState(() => DEFAULT_QUESTION_BANK_FOLDERS.map(f => ({ ...f })))
178
+ const tableRef = React.useRef<LibraryTableHandle>(null)
179
+ const [items, setItems] = React.useState(() => LIBRARY_ITEMS.map(q => ({ ...q })))
180
+ const [folders, setFolders] = React.useState(() => DEFAULT_LIBRARY_FOLDERS.map(f => ({ ...f })))
181
181
 
182
182
  const [hubFolderCustomizeSheetOpen, setHubFolderCustomizeSheetOpen] = React.useState(false)
183
- const [hubFolderCustomizeTarget, setHubFolderCustomizeTarget] = React.useState<QuestionBankFolder | null>(null)
183
+ const [hubFolderCustomizeTarget, setHubFolderCustomizeTarget] = React.useState<LibraryFolder | null>(null)
184
184
 
185
185
  const openHubScopedFolderCustomize = React.useCallback(() => {
186
186
  if (navState.scope !== "folder" || !navState.folderId) return
@@ -191,7 +191,7 @@ export function QuestionBankClient() {
191
191
  }, [folders, navState.folderId, navState.scope])
192
192
 
193
193
  /**
194
- * Open the full-page authoring composer (`/question-bank/new`).
194
+ * Open the full-page authoring composer (`/library/new`).
195
195
  * Pre-collapses the main sidebar (Placements pattern) so the user sees one
196
196
  * smooth animation into the focused authoring flow. Folder scope, when
197
197
  * present, is forwarded as `?folderId=` so the destination dropdown lands
@@ -204,21 +204,21 @@ export function QuestionBankClient() {
204
204
  ? `?folderId=${encodeURIComponent(navState.folderId)}`
205
205
  : ""
206
206
  setMainSidebarOpen(false)
207
- window.setTimeout(() => router.push(`/question-bank/new${folderQuery}`), 260)
207
+ window.setTimeout(() => router.push(`/library/new${folderQuery}`), 260)
208
208
  }, [navState.folderId, navState.scope, router, setMainSidebarOpen])
209
209
 
210
210
  const filteredItems = React.useMemo(
211
- () => applyQuestionBankHubDisplayFilters(items, folders, navState, landingFilters),
211
+ () => applyLibraryHubDisplayFilters(items, folders, navState, landingFilters),
212
212
  [items, folders, landingFilters, navState],
213
213
  )
214
214
 
215
215
  const count = filteredItems.length
216
216
 
217
- const metrics = React.useMemo(() => questionBankKpiMetrics(filteredItems), [filteredItems])
218
- const insight = React.useMemo(() => questionBankKpiInsight(filteredItems), [filteredItems])
217
+ const metrics = React.useMemo(() => libraryKpiMetrics(filteredItems), [filteredItems])
218
+ const insight = React.useMemo(() => libraryKpiInsight(filteredItems), [filteredItems])
219
219
 
220
220
  const hubHeader = React.useMemo(
221
- () => questionBankHubHeaderModel(folders, navState),
221
+ () => libraryHubHeaderModel(folders, navState),
222
222
  [folders, navState],
223
223
  )
224
224
 
@@ -226,27 +226,27 @@ export function QuestionBankClient() {
226
226
  () =>
227
227
  isDedicatedSearch &&
228
228
  landingFilters != null &&
229
- questionBankHubTextMatchesNothing(items, folders, navState, landingFilters),
229
+ libraryHubTextMatchesNothing(items, folders, navState, landingFilters),
230
230
  [folders, isDedicatedSearch, items, landingFilters, navState],
231
231
  )
232
232
 
233
233
  if (isDedicatedSearch) {
234
- const dedicatedReplacePath = isHubFindSurface ? QUESTION_BANK_HUB_FIND_PATH : QUESTION_BANK_LIST_PATH
234
+ const dedicatedReplacePath = isHubFindSurface ? LIBRARY_HUB_FIND_PATH : LIBRARY_LIST_PATH
235
235
  const showDedicatedSearchResults = hasUrlSearch
236
236
 
237
237
  return (
238
238
  <>
239
- <QuestionBankFolderBridge
239
+ <LibraryFolderBridge
240
240
  folders={folders}
241
241
  onFoldersChange={setFolders}
242
242
  items={items}
243
243
  onItemsChange={setItems}
244
244
  />
245
- <QuestionBankAccessBridge openManageAccess={noopManageAccess} />
245
+ <LibraryAccessBridge openManageAccess={noopManageAccess} />
246
246
  <SecondaryPanelHubTemplate
247
247
  siteHeader={{
248
248
  title: dedicatedSearchTitle,
249
- breadcrumbs: [{ label: QUESTION_BANK_HUB_BREADCRUMB.label, href: QUESTION_BANK_HUB_BREADCRUMB.href }],
249
+ breadcrumbs: [{ label: LIBRARY_HUB_BREADCRUMB.label, href: LIBRARY_HUB_BREADCRUMB.href }],
250
250
  }}
251
251
  contentClassName={
252
252
  showDedicatedSearchResults ? DEDICATED_SEARCH_RESULTS_OUTER_CONTENT_CLASSNAME : undefined
@@ -256,16 +256,16 @@ export function QuestionBankClient() {
256
256
  <ListPageTemplate
257
257
  defaultTabs={DEFAULT_TABS}
258
258
  tabs={SEARCH_LANDING_TABS}
259
- onTabsChange={ignoreQuestionBankTabsUpdate}
259
+ onTabsChange={ignoreLibraryTabsUpdate}
260
260
  activeTabId={SEARCH_LANDING_TABS[0]!.id}
261
- onActiveTabChange={ignoreQuestionBankTabActivation}
261
+ onActiveTabChange={ignoreLibraryTabActivation}
262
262
  hideViewsToolbar
263
- supportedViewTypes={QUESTION_BANK_SUPPORTED_VIEWS}
263
+ supportedViewTypes={LIBRARY_SUPPORTED_VIEWS}
264
264
  getTabCount={() => count}
265
265
  tablePropertiesRef={tableRef}
266
266
  header={(
267
267
  <DedicatedSearchResultsHeaderChrome>
268
- <QuestionBankPageHeader
268
+ <LibraryPageHeader
269
269
  variant="default"
270
270
  title={dedicatedSearchTitle}
271
271
  questionCount={count}
@@ -276,10 +276,10 @@ export function QuestionBankClient() {
276
276
  <DedicatedSearchUrlComposer
277
277
  searchParamsKey={searchParamsKey}
278
278
  replacePath={dedicatedReplacePath}
279
- patchSearchParams={patchQuestionBankDedicatedSearchParams}
280
- onRecordSubmission={recordQuestionBankRecentSearch}
279
+ patchSearchParams={patchLibraryDedicatedSearchParams}
280
+ onRecordSubmission={recordLibraryRecentSearch}
281
281
  layout="default"
282
- animatedPlaceholders={QUESTION_BANK_DEDICATED_SEARCH_PLACEHOLDERS}
282
+ animatedPlaceholders={LIBRARY_DEDICATED_SEARCH_PLACEHOLDERS}
283
283
  animatedPlaceholderIntervalMs={4800}
284
284
  animatedPlaceholderMaxLines={2}
285
285
  placeholder="Search the bank…"
@@ -304,7 +304,7 @@ export function QuestionBankClient() {
304
304
  onExportOpenChange={setExportOpen}
305
305
  exportTotalRows={count}
306
306
  renderContent={(tab, updateTab) => (
307
- <QuestionBankTable
307
+ <LibraryTable
308
308
  key={tab.id}
309
309
  ref={tableRef}
310
310
  items={items}
@@ -322,15 +322,15 @@ export function QuestionBankClient() {
322
322
  />
323
323
  ) : (
324
324
  <DedicatedSearchLandingTemplate
325
- title={isHubFindSurface ? "Discovery search" : "Search your question bank"}
325
+ title={isHubFindSurface ? "Discovery search" : "Search your library"}
326
326
  composer={(
327
327
  <DedicatedSearchUrlComposer
328
328
  searchParamsKey={searchParamsKey}
329
329
  replacePath={dedicatedReplacePath}
330
- patchSearchParams={patchQuestionBankDedicatedSearchParams}
331
- onRecordSubmission={recordQuestionBankRecentSearch}
330
+ patchSearchParams={patchLibraryDedicatedSearchParams}
331
+ onRecordSubmission={recordLibraryRecentSearch}
332
332
  layout="hero"
333
- animatedPlaceholders={QUESTION_BANK_DEDICATED_SEARCH_PLACEHOLDERS}
333
+ animatedPlaceholders={LIBRARY_DEDICATED_SEARCH_PLACEHOLDERS}
334
334
  animatedPlaceholderIntervalMs={4800}
335
335
  animatedPlaceholderMaxLines={2}
336
336
  placeholder="Search the bank…"
@@ -347,10 +347,10 @@ export function QuestionBankClient() {
347
347
  )}
348
348
  trailing={(
349
349
  <DedicatedSearchRecents
350
- recents={questionBankDedicatedSearchRecents}
350
+ recents={libraryDedicatedSearchRecents}
351
351
  searchParamsKey={searchParamsKey}
352
352
  replacePath={dedicatedReplacePath}
353
- patchSearchParams={patchQuestionBankDedicatedSearchParams}
353
+ patchSearchParams={patchLibraryDedicatedSearchParams}
354
354
  />
355
355
  )}
356
356
  />
@@ -362,7 +362,7 @@ export function QuestionBankClient() {
362
362
 
363
363
  return (
364
364
  <CollaborationAccessFlow
365
- initialCollaborators={QUESTION_BANK_HEADER_COLLABORATORS}
365
+ initialCollaborators={LIBRARY_HEADER_COLLABORATORS}
366
366
  resourceLabel={hubHeader.title}
367
367
  >
368
368
  {({ collaborators, openInvite }) => (
@@ -370,13 +370,13 @@ export function QuestionBankClient() {
370
370
  <SecondaryPanelHubTemplate
371
371
  bridges={(
372
372
  <>
373
- <QuestionBankFolderBridge
373
+ <LibraryFolderBridge
374
374
  folders={folders}
375
375
  onFoldersChange={setFolders}
376
376
  items={items}
377
377
  onItemsChange={setItems}
378
378
  />
379
- <QuestionBankAccessBridge openManageAccess={openInvite} />
379
+ <LibraryAccessBridge openManageAccess={openInvite} />
380
380
  </>
381
381
  )}
382
382
  siteHeader={{
@@ -390,11 +390,11 @@ export function QuestionBankClient() {
390
390
  onTabsChange={setTabs}
391
391
  activeTabId={activeTabId}
392
392
  onActiveTabChange={onActiveTabChange}
393
- supportedViewTypes={QUESTION_BANK_SUPPORTED_VIEWS}
393
+ supportedViewTypes={LIBRARY_SUPPORTED_VIEWS}
394
394
  getTabCount={() => count}
395
395
  tablePropertiesRef={tableRef}
396
396
  header={(
397
- <QuestionBankPageHeader
397
+ <LibraryPageHeader
398
398
  variant="collaboration"
399
399
  title={hubHeader.title}
400
400
  questionCount={count}
@@ -424,7 +424,7 @@ export function QuestionBankClient() {
424
424
  onExportOpenChange={setExportOpen}
425
425
  exportTotalRows={count}
426
426
  renderContent={(tab, updateTab) => (
427
- <QuestionBankTable
427
+ <LibraryTable
428
428
  key={tab.id}
429
429
  ref={tableRef}
430
430
  items={items}
@@ -441,7 +441,7 @@ export function QuestionBankClient() {
441
441
  )}
442
442
  />
443
443
  </SecondaryPanelHubTemplate>
444
- <QuestionBankNewFolderSheet
444
+ <LibraryNewFolderSheet
445
445
  open={hubFolderCustomizeSheetOpen}
446
446
  onOpenChange={open => {
447
447
  setHubFolderCustomizeSheetOpen(open)
@@ -1,8 +1,8 @@
1
1
  "use client"
2
2
 
3
3
  /**
4
- * Question bank **Data** view — KPI strip + Recharts cards. Loaded via `next/dynamic`
5
- * from `question-bank-table` so table/list/board/folder routes do not eagerly bundle Recharts.
4
+ * Library **Data** view — KPI strip + Recharts cards. Loaded via `next/dynamic`
5
+ * from `library-table` so table/list/board/folder routes do not eagerly bundle Recharts.
6
6
  */
7
7
 
8
8
  import * as React from "react"
@@ -17,34 +17,34 @@ import {
17
17
  type ChartConfig,
18
18
  } from "@/components/ui/chart"
19
19
  import { CHART_KBD_ACTIVE_BAR } from "@/lib/chart-keyboard-selection"
20
- import type { QuestionBankItem, QuestionBankType } from "@/lib/mock/question-bank"
21
- import { questionBankKpiInsight, questionBankKpiMetrics } from "@/lib/mock/question-bank-kpi"
20
+ import type { LibraryItem, LibraryItemType } from "@/lib/mock/library"
21
+ import { libraryKpiInsight, libraryKpiMetrics } from "@/lib/mock/library-kpi"
22
22
 
23
23
  const BAR_CFG: ChartConfig = {
24
24
  count: { label: "Questions", color: "var(--color-chart-2)" },
25
25
  }
26
26
 
27
- const TYPE_LABEL: Record<QuestionBankType, string> = {
27
+ const TYPE_LABEL: Record<LibraryItemType, string> = {
28
28
  multiple_choice: "Multiple choice",
29
29
  true_false: "True / false",
30
30
  short_answer: "Short answer",
31
31
  }
32
32
 
33
- function aggregateByType(rows: QuestionBankItem[]) {
34
- const c: Record<QuestionBankType, number> = {
33
+ function aggregateByType(rows: LibraryItem[]) {
34
+ const c: Record<LibraryItemType, number> = {
35
35
  multiple_choice: 0,
36
36
  true_false: 0,
37
37
  short_answer: 0,
38
38
  }
39
39
  for (const r of rows) c[r.type]++
40
- return (Object.keys(c) as QuestionBankType[]).map(key => ({
40
+ return (Object.keys(c) as LibraryItemType[]).map(key => ({
41
41
  name: TYPE_LABEL[key],
42
42
  value: c[key],
43
43
  key,
44
44
  }))
45
45
  }
46
46
 
47
- function aggregateByTopic(rows: QuestionBankItem[]) {
47
+ function aggregateByTopic(rows: LibraryItem[]) {
48
48
  const map = new Map<string, number>()
49
49
  for (const r of rows) map.set(r.topic, (map.get(r.topic) ?? 0) + 1)
50
50
  return [...map.entries()]
@@ -53,7 +53,7 @@ function aggregateByTopic(rows: QuestionBankItem[]) {
53
53
  .slice(0, 8)
54
54
  }
55
55
 
56
- function QuestionsByTypeChart({ rows }: { rows: QuestionBankItem[] }) {
56
+ function QuestionsByTypeChart({ rows }: { rows: LibraryItem[] }) {
57
57
  const data = React.useMemo(() => aggregateByType(rows), [rows])
58
58
  if (rows.length === 0) {
59
59
  return (
@@ -98,7 +98,7 @@ function QuestionsByTypeChart({ rows }: { rows: QuestionBankItem[] }) {
98
98
  )
99
99
  }
100
100
 
101
- function QuestionsByTopicChart({ rows }: { rows: QuestionBankItem[] }) {
101
+ function QuestionsByTopicChart({ rows }: { rows: LibraryItem[] }) {
102
102
  const data = React.useMemo(() => aggregateByTopic(rows), [rows])
103
103
  if (rows.length === 0) {
104
104
  return (
@@ -143,11 +143,11 @@ function QuestionsByTopicChart({ rows }: { rows: QuestionBankItem[] }) {
143
143
  )
144
144
  }
145
145
 
146
- export function QuestionBankDashboardChartsSection({ rows }: { rows: QuestionBankItem[] }) {
146
+ export function LibraryDashboardChartsSection({ rows }: { rows: LibraryItem[] }) {
147
147
  const kpi = React.useMemo(
148
148
  () => ({
149
- metrics: questionBankKpiMetrics(rows),
150
- insight: questionBankKpiInsight(rows),
149
+ metrics: libraryKpiMetrics(rows),
150
+ insight: libraryKpiInsight(rows),
151
151
  }),
152
152
  [rows],
153
153
  )
@@ -1,24 +1,24 @@
1
1
  "use client"
2
2
 
3
- import type { QuestionBankItem } from "@/lib/mock/question-bank"
3
+ import type { LibraryItem } from "@/lib/mock/library"
4
4
  import { Button } from "@/components/ui/button"
5
5
  import { Tip } from "@/components/ui/tip"
6
6
  import { cn } from "@/lib/utils"
7
- import { isQuestionBankItemFavorite } from "@/lib/question-bank-nav"
7
+ import { isLibraryItemFavorite } from "@/lib/library-nav"
8
8
 
9
9
  /** Parent must use this class so non-favorited stars show on row/cell hover (`group-hover/favcell`). */
10
- export const QUESTION_BANK_FAVORITE_HOVER_GROUP = "group/favcell"
10
+ export const LIBRARY_FAVORITE_HOVER_GROUP = "group/favcell"
11
11
 
12
- export function QuestionBankFavoriteButton({
12
+ export function LibraryFavoriteButton({
13
13
  row,
14
14
  onToggleFavorite,
15
15
  stopPropagation = true,
16
16
  }: {
17
- row: QuestionBankItem
18
- onToggleFavorite: (row: QuestionBankItem) => void
17
+ row: LibraryItem
18
+ onToggleFavorite: (row: LibraryItem) => void
19
19
  stopPropagation?: boolean
20
20
  }) {
21
- const fav = isQuestionBankItemFavorite(row)
21
+ const fav = isLibraryItemFavorite(row)
22
22
  const label = fav ? "Remove from favorites" : "Add to favorites"
23
23
  return (
24
24
  <Tip side="top" label={label}>