@exxatdesignux/ui 0.2.6 → 0.2.8

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 (134) hide show
  1. package/package.json +2 -1
  2. package/template/.agents/skills/shadcn/SKILL.md +242 -0
  3. package/template/.agents/skills/shadcn/agents/openai.yml +5 -0
  4. package/template/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
  5. package/template/.agents/skills/shadcn/assets/shadcn.png +0 -0
  6. package/template/.agents/skills/shadcn/cli.md +257 -0
  7. package/template/.agents/skills/shadcn/customization.md +202 -0
  8. package/template/.agents/skills/shadcn/evals/evals.json +47 -0
  9. package/template/.agents/skills/shadcn/mcp.md +94 -0
  10. package/template/.agents/skills/shadcn/rules/base-vs-radix.md +306 -0
  11. package/template/.agents/skills/shadcn/rules/composition.md +195 -0
  12. package/template/.agents/skills/shadcn/rules/forms.md +192 -0
  13. package/template/.agents/skills/shadcn/rules/icons.md +101 -0
  14. package/template/.agents/skills/shadcn/rules/styling.md +162 -0
  15. package/template/.claude/skills/exxat-ds-skill/SKILL.md +712 -0
  16. package/template/.cursor/rules/exxat-accessibility.mdc +33 -0
  17. package/template/.cursor/rules/exxat-command-menu.mdc +23 -0
  18. package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +53 -0
  19. package/template/.cursor/rules/exxat-data-tables.mdc +31 -0
  20. package/template/.cursor/rules/exxat-ds-agents.mdc +26 -0
  21. package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +100 -0
  22. package/template/.cursor/rules/exxat-list-page-connected-views.mdc +16 -0
  23. package/template/.cursor/rules/exxat-no-toast.mdc +26 -0
  24. package/template/.cursor/rules/exxat-page-vs-drawer.mdc +22 -0
  25. package/template/.cursor/rules/exxat-table-properties-drawer.mdc +40 -0
  26. package/template/AGENTS.md +52 -11
  27. package/template/app/(app)/dashboard/page.tsx +1 -1
  28. package/template/app/(app)/data-list/[id]/page.tsx +24 -8
  29. package/template/app/(app)/data-list/new/page.tsx +7 -4
  30. package/template/app/(app)/data-list/page.tsx +1 -1
  31. package/template/app/(app)/examples/page.tsx +41 -0
  32. package/template/app/(app)/question-bank/page.tsx +3 -3
  33. package/template/components/app-sidebar.tsx +52 -35
  34. package/template/components/compliance-table.tsx +79 -0
  35. package/template/components/data-list-client.tsx +36 -25
  36. package/template/components/data-list-table.tsx +797 -10
  37. package/template/components/data-views/finder-panel-view.tsx +405 -0
  38. package/template/components/data-views/folder-grid-view.tsx +86 -0
  39. package/template/components/data-views/index.ts +59 -0
  40. package/template/components/data-views/list-page-split-details-placeholder.tsx +39 -0
  41. package/template/components/data-views/list-page-split-hub-chrome.tsx +60 -0
  42. package/template/components/data-views/list-page-split-hub-tokens.ts +16 -0
  43. package/template/components/data-views/list-page-tree-column-header.tsx +31 -0
  44. package/template/components/data-views/list-page-tree-panel-shell.tsx +91 -0
  45. package/template/components/data-views/list-page-view-frame.tsx +53 -0
  46. package/template/components/data-views/os-folder-glyph.tsx +121 -0
  47. package/template/components/folder-details-shell.tsx +230 -0
  48. package/template/components/hub-tree-panel-view.tsx +672 -0
  49. package/template/components/list-hub-status-badge.tsx +17 -3
  50. package/template/components/placements-page-header.tsx +14 -8
  51. package/template/components/placements-table-columns.tsx +8 -8
  52. package/template/components/question-bank-client.tsx +157 -40
  53. package/template/components/question-bank-new-folder-sheet.tsx +248 -0
  54. package/template/components/question-bank-os-folder-view.tsx +648 -0
  55. package/template/components/question-bank-page-header.tsx +3 -3
  56. package/template/components/question-bank-panel-activator.tsx +9 -0
  57. package/template/components/question-bank-secondary-nav.tsx +226 -0
  58. package/template/components/question-bank-table.tsx +707 -22
  59. package/template/components/secondary-panel.tsx +41 -107
  60. package/template/components/sites-table.tsx +66 -0
  61. package/template/components/team-client.tsx +7 -0
  62. package/template/components/team-table.tsx +156 -1
  63. package/template/components/templates/list-page.tsx +2 -2
  64. package/template/components/ui/avatar.tsx +1 -1
  65. package/template/components/ui/badge.tsx +1 -1
  66. package/template/components/ui/banner.tsx +1 -1
  67. package/template/components/ui/breadcrumb.tsx +1 -1
  68. package/template/components/ui/button.tsx +1 -1
  69. package/template/components/ui/calendar.tsx +1 -1
  70. package/template/components/ui/card.tsx +1 -1
  71. package/template/components/ui/chart.tsx +1 -1
  72. package/template/components/ui/checkbox.tsx +1 -1
  73. package/template/components/ui/coach-mark.tsx +1 -1
  74. package/template/components/ui/collapsible.tsx +1 -1
  75. package/template/components/ui/command.tsx +1 -1
  76. package/template/components/ui/date-picker-field.tsx +1 -1
  77. package/template/components/ui/dialog.tsx +1 -1
  78. package/template/components/ui/drag-handle-grip.tsx +1 -1
  79. package/template/components/ui/drawer.tsx +1 -1
  80. package/template/components/ui/dropdown-menu.tsx +1 -1
  81. package/template/components/ui/field.tsx +1 -1
  82. package/template/components/ui/form.tsx +1 -1
  83. package/template/components/ui/input-group.tsx +1 -1
  84. package/template/components/ui/input-mask.tsx +1 -1
  85. package/template/components/ui/input.tsx +1 -1
  86. package/template/components/ui/kbd.tsx +1 -1
  87. package/template/components/ui/label.tsx +1 -1
  88. package/template/components/ui/payment-card-fields.tsx +1 -1
  89. package/template/components/ui/popover.tsx +1 -1
  90. package/template/components/ui/radio-group.tsx +1 -1
  91. package/template/components/ui/resizable.tsx +68 -0
  92. package/template/components/ui/select.tsx +1 -1
  93. package/template/components/ui/selection-tile-grid.tsx +1 -1
  94. package/template/components/ui/separator.tsx +1 -1
  95. package/template/components/ui/sheet.tsx +1 -1
  96. package/template/components/ui/sidebar.tsx +1 -1
  97. package/template/components/ui/skeleton.tsx +1 -1
  98. package/template/components/ui/sonner.tsx +1 -1
  99. package/template/components/ui/status-badge.tsx +1 -1
  100. package/template/components/ui/table.tsx +1 -1
  101. package/template/components/ui/tabs.tsx +1 -1
  102. package/template/components/ui/textarea.tsx +1 -1
  103. package/template/components/ui/tip.tsx +1 -1
  104. package/template/components/ui/toggle-group.tsx +1 -1
  105. package/template/components/ui/toggle-switch.tsx +1 -1
  106. package/template/components/ui/toggle.tsx +1 -1
  107. package/template/components/ui/tooltip.tsx +1 -1
  108. package/template/components/ui/view-segmented-control.tsx +1 -1
  109. package/template/docs/data-views-pattern.md +7 -0
  110. package/template/fontawesome-subset.manifest.json +2 -2
  111. package/template/hooks/use-location-hash.ts +15 -0
  112. package/template/hooks/use-sidebar-reflow-zoom.ts +40 -0
  113. package/template/lib/ask-leo-route-context.ts +25 -57
  114. package/template/lib/coach-mark-registry.ts +13 -13
  115. package/template/lib/command-menu-config.ts +28 -23
  116. package/template/lib/command-menu-search-data.ts +10 -9
  117. package/template/lib/data-list-view-surface.ts +12 -1
  118. package/template/lib/data-list-view.ts +6 -3
  119. package/template/lib/mock/dashboard.ts +11 -11
  120. package/template/lib/mock/navigation.tsx +22 -63
  121. package/template/lib/mock/placements-kpi.ts +19 -19
  122. package/template/lib/mock/question-bank-folders.ts +167 -0
  123. package/template/lib/mock/question-bank-inspector.ts +109 -0
  124. package/template/lib/mock/question-bank-kpi.ts +1 -1
  125. package/template/lib/mock/question-bank.ts +80 -0
  126. package/template/lib/question-bank-nav.ts +91 -0
  127. package/template/next.config.mjs +8 -0
  128. package/template/package.json +1 -0
  129. package/template/public/folders/icons8-folder-windows-11.svg +1 -0
  130. package/template/scripts/fontawesome-subset-audit.mjs +2 -3
  131. package/template/app/(app)/compliance/page.tsx +0 -10
  132. package/template/app/(app)/rotations/page.tsx +0 -15
  133. package/template/app/(app)/sites/all/page.tsx +0 -13
  134. package/template/app/(app)/team/page.tsx +0 -10
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/radio-group.tsx"
1
+ export * from "@exxatdesignux/ui/components/radio-group"
@@ -0,0 +1,68 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { GripVertical } from "lucide-react"
5
+ import { Group, Panel, Separator } from "react-resizable-panels"
6
+ import type { GroupProps, PanelProps, SeparatorProps } from "react-resizable-panels"
7
+
8
+ import { cn } from "@/lib/utils"
9
+
10
+ // ─── ResizablePanelGroup ──────────────────────────────────────────────────────
11
+
12
+ export type ResizablePanelGroupProps = Omit<GroupProps, "orientation"> & {
13
+ direction?: "horizontal" | "vertical"
14
+ }
15
+
16
+ export function ResizablePanelGroup({
17
+ direction = "horizontal",
18
+ className,
19
+ ...props
20
+ }: ResizablePanelGroupProps) {
21
+ return (
22
+ <Group
23
+ orientation={direction}
24
+ className={cn(
25
+ "flex h-full w-full",
26
+ direction === "vertical" && "flex-col",
27
+ className,
28
+ )}
29
+ {...props}
30
+ />
31
+ )
32
+ }
33
+
34
+ // ─── ResizablePanel ───────────────────────────────────────────────────────────
35
+
36
+ export type ResizablePanelProps = PanelProps
37
+
38
+ export const ResizablePanel = Panel
39
+
40
+ // ─── ResizableHandle ──────────────────────────────────────────────────────────
41
+
42
+ export type ResizableHandleProps = Omit<SeparatorProps, "children"> & {
43
+ /** Render a visible grip icon on the handle. */
44
+ withHandle?: boolean
45
+ }
46
+
47
+ export function ResizableHandle({ withHandle, className, ...props }: ResizableHandleProps) {
48
+ return (
49
+ <Separator
50
+ className={cn(
51
+ // Base — horizontal handle (between side-by-side panels)
52
+ "relative flex w-1 shrink-0 cursor-col-resize items-center justify-center bg-border/60",
53
+ "transition-colors hover:bg-border active:bg-primary/30",
54
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
55
+ // Vertical handle (between stacked panels)
56
+ "data-[orientation=vertical]:h-1 data-[orientation=vertical]:w-full data-[orientation=vertical]:cursor-row-resize data-[orientation=vertical]:flex-row",
57
+ className,
58
+ )}
59
+ {...props}
60
+ >
61
+ {withHandle && (
62
+ <div className="z-10 flex h-5 w-3.5 items-center justify-center rounded-sm border border-border bg-background shadow-sm">
63
+ <GripVertical className="h-3 w-3 text-muted-foreground" />
64
+ </div>
65
+ )}
66
+ </Separator>
67
+ )
68
+ }
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/select.tsx"
1
+ export * from "@exxatdesignux/ui/components/select"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/selection-tile-grid.tsx"
1
+ export * from "@exxatdesignux/ui/components/selection-tile-grid"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/separator.tsx"
1
+ export * from "@exxatdesignux/ui/components/separator"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/sheet.tsx"
1
+ export * from "@exxatdesignux/ui/components/sheet"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/sidebar.tsx"
1
+ export * from "@exxatdesignux/ui/components/sidebar"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/skeleton.tsx"
1
+ export * from "@exxatdesignux/ui/components/skeleton"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/sonner.tsx"
1
+ export * from "@exxatdesignux/ui/components/sonner"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/status-badge.tsx"
1
+ export * from "@exxatdesignux/ui/components/status-badge"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/table.tsx"
1
+ export * from "@exxatdesignux/ui/components/table"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/tabs.tsx"
1
+ export * from "@exxatdesignux/ui/components/tabs"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/textarea.tsx"
1
+ export * from "@exxatdesignux/ui/components/textarea"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/tip.tsx"
1
+ export * from "@exxatdesignux/ui/components/tip"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/toggle-group.tsx"
1
+ export * from "@exxatdesignux/ui/components/toggle-group"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/toggle-switch.tsx"
1
+ export * from "@exxatdesignux/ui/components/toggle-switch"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/toggle.tsx"
1
+ export * from "@exxatdesignux/ui/components/toggle"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/tooltip.tsx"
1
+ export * from "@exxatdesignux/ui/components/tooltip"
@@ -1 +1 @@
1
- export * from "@exxatdesignux/ui/components/view-segmented-control.tsx"
1
+ export * from "@exxatdesignux/ui/components/view-segmented-control"
@@ -19,6 +19,7 @@ This document describes how list pages combine **views**, **toolbar** behavior,
19
19
  | **Team roster** | `TeamTable` — `DataTable` + `useTableState` + `TablePropertiesDrawer`; list/board/dashboard read **`tableState.rows`** | `components/team-table.tsx` |
20
20
  | **Dashboard view (list tab)** | **`KeyMetrics`** (`variant="flat"` or `"card"`) — same KPI system as the template metrics strip; **do not** add ad-hoc `Card` grids for entity summaries | `TeamTable` dashboard branch, `lib/mock/team-kpi.ts` |
21
21
  | **Export** | `ExportDrawer` | `ListPageTemplate` export props; `DataListClient` |
22
+ | **View body layout** (gutter + centered max-width for folder / icon / panel-style content) | **`ListPageViewFrame`** (`components/data-views/list-page-view-frame.tsx`, re-exported from `components/data-views`) | **`FolderGridView`** (uses the frame); **`QuestionBankOsFolderView`** — see **`AGENTS.md` §4.5** |
22
23
 
23
24
  **Rules:** (1) Import and compose these components; pass **props** and **column defs** for your entity. (2) If something is missing, **extend the shared component** under `components/` (e.g. a new optional slot on `DataTableToolbar`) rather than copying markup into a single page. (3) Card-only or lightweight pages may use a smaller **Properties** sheet only when there is **no** table—otherwise use `TablePropertiesDrawer` with `DataTable` (see Team).
24
25
 
@@ -29,6 +30,12 @@ If the page uses a **`DataTable`** (or equivalent data grid) as the main surface
29
30
  - **Reference:** `DataListClient` (Placements) and `TeamClient` (Team).
30
31
  - **Rationale:** Consistent navigation, saved views, per-tab view type (table / list / board), and export at the template level.
31
32
 
33
+ ## View layout frame (centered, reusable)
34
+
35
+ Non-table view branches (e.g. **folder** icon grid, **panel** finder, OS-style folder explorer) **should** use **`ListPageViewFrame`** for the same horizontal gutter and optional **`max-w-*`** centering as other hubs — **not** hand-copied `mx-4 lg:mx-6` + `mx-auto max-w-6xl` in each `*-table.tsx`. New view types belong in **`components/data-views/`** as **generic** components with render props; hub tables only wire **`tableState.rows`** and callbacks.
36
+
37
+ **Handbook:** **`AGENTS.md` §4.5**. **Cursor:** **`.cursor/rules/exxat-list-page-view-shells.mdc`**. **Skill:** **`.cursor/skills/exxat-list-page-view-shells/SKILL.md`**. **Do not** wrap **`DataTable`** in the frame if that stacks padding with the table toolbar (**`AGENTS.md` §5**).
38
+
32
39
  ## Architecture
33
40
 
34
41
  - **Page shell** — `ListPageTemplate` owns the views toolbar (tabs), optional metrics, and export drawer. Content for the active tab is rendered via `renderContent`.
@@ -2,8 +2,8 @@
2
2
  "kitId": "d9bd5774e0",
3
3
  "generatedAt": "2026-04-23T07:50:58.982Z",
4
4
  "sourcePaths": [
5
- "apps/web/{app,components,lib,contexts,hooks}",
6
- "packages/ui/src"
5
+ "{app,components,lib,contexts,hooks}",
6
+ "node_modules/@exxatdesignux/ui/src"
7
7
  ],
8
8
  "stylesDetected": [
9
9
  "duotone",
@@ -0,0 +1,15 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ /** Current `window.location.hash` (including `#`), updated on `hashchange`. */
6
+ export function useLocationHash(): string {
7
+ const [hash, setHash] = React.useState("")
8
+ React.useEffect(() => {
9
+ const read = () => setHash(typeof window !== "undefined" ? window.location.hash : "")
10
+ read()
11
+ window.addEventListener("hashchange", read)
12
+ return () => window.removeEventListener("hashchange", read)
13
+ }, [])
14
+ return hash
15
+ }
@@ -0,0 +1,40 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ /**
6
+ * When true, the sidebar should **not** pin utilities + profile to the bottom — the whole
7
+ * rail becomes one scroll surface (WCAG 1.4.10 reflow at high zoom / very short viewports).
8
+ *
9
+ * Uses `visualViewport.scale` where available (pinch / some browser zoom) and falls back to
10
+ * short viewport height.
11
+ */
12
+ export function useSidebarReflowZoom(): boolean {
13
+ const [reflow, setReflow] = React.useState(false)
14
+
15
+ React.useEffect(() => {
16
+ const vv = window.visualViewport
17
+
18
+ function compute() {
19
+ const scale = vv?.scale ?? 1
20
+ const short = window.matchMedia("(max-height: 640px)").matches
21
+ const veryShort = window.innerHeight <= 420
22
+ setReflow(scale >= 1.99 || short || veryShort)
23
+ }
24
+
25
+ compute()
26
+ vv?.addEventListener("resize", compute)
27
+ vv?.addEventListener("scroll", compute)
28
+ window.addEventListener("resize", compute)
29
+ const mql = window.matchMedia("(max-height: 640px)")
30
+ mql.addEventListener("change", compute)
31
+ return () => {
32
+ vv?.removeEventListener("resize", compute)
33
+ vv?.removeEventListener("scroll", compute)
34
+ window.removeEventListener("resize", compute)
35
+ mql.removeEventListener("change", compute)
36
+ }
37
+ }, [])
38
+
39
+ return reflow
40
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Route-derived defaults for Ask Leo when no page calls `useAskLeoPageContext`.
3
- * Keeps titles and starters aligned with primary nav (not raw URL segments).
3
+ * Copy stays generic for the design-system shell.
4
4
  */
5
5
 
6
6
  export interface AskLeoRouteContextPayload {
@@ -12,21 +12,21 @@ export interface AskLeoRouteContextPayload {
12
12
 
13
13
  /** Fallback starters when a page provides no `suggestions`. */
14
14
  export const ASK_LEO_GENERIC_SUGGESTIONS = [
15
- "Which students are at risk of non-compliance?",
16
- "Show me placements ending this month",
17
- "Summarize placement progress",
18
- "Compare this cycle vs. last cycle",
15
+ "How do I meet WCAG 2.1 AA for tables and dialogs?",
16
+ "Explain how semantic tokens map to components",
17
+ "What belongs on a ListPageTemplate hub?",
18
+ "How should charts expose keyboard exploration?",
19
19
  ]
20
20
 
21
21
  export function getAskLeoRouteContext(pathname: string | null): AskLeoRouteContextPayload {
22
22
  if (!pathname || pathname === "/") {
23
23
  return {
24
24
  title: "Dashboard",
25
- description: "Overview of metrics, charts, and AI insights.",
25
+ description: "Overview of metrics, charts, and layout patterns.",
26
26
  suggestions: [
27
- "What changed in my key metrics this week?",
28
- "Summarize tasks and insights I should act on",
29
- "Explain the trend on the main placement chart",
27
+ "What changed in the headline metrics this week?",
28
+ "Summarize the dashboard layout for a stakeholder",
29
+ "Which chart should I inspect first for anomalies?",
30
30
  ],
31
31
  }
32
32
  }
@@ -34,67 +34,35 @@ export function getAskLeoRouteContext(pathname: string | null): AskLeoRouteConte
34
34
  if (pathname.startsWith("/dashboard")) {
35
35
  return {
36
36
  title: "Dashboard",
37
- description: "Overview of metrics, charts, and AI insights.",
37
+ description: "Overview of metrics, charts, and layout patterns.",
38
38
  suggestions: [
39
- "What changed in my key metrics this week?",
40
- "Summarize tasks and insights I should act on",
41
- "Which chart should I look at first for placement health?",
39
+ "What changed in the headline metrics this week?",
40
+ "Summarize the dashboard layout for a stakeholder",
41
+ "Which chart should I inspect first for anomalies?",
42
42
  ],
43
43
  }
44
44
  }
45
45
 
46
46
  if (pathname.startsWith("/data-list")) {
47
47
  return {
48
- title: "Placements",
49
- description: "List, filter, and export placement records — table, board, and dashboard views.",
48
+ title: "List hub",
49
+ description: "Demo list with table, list, board, and dashboard views sharing one table state.",
50
50
  suggestions: [
51
- "Which placements end in the next 30 days?",
52
- "Summarize placements by site for this lifecycle tab",
53
- "What filters would narrow this list to at-risk students?",
51
+ "How do filters and search interact with board and dashboard tabs?",
52
+ "Summarize what is shown in the active view",
53
+ "What columns are useful for a dense data grid?",
54
54
  ],
55
55
  }
56
56
  }
57
57
 
58
- if (pathname.startsWith("/team")) {
58
+ if (pathname.startsWith("/examples")) {
59
59
  return {
60
- title: "Team",
61
- description: "Directory and workload for coordinators and faculty.",
60
+ title: "Patterns",
61
+ description: "Entry points for reusable shells and demos.",
62
62
  suggestions: [
63
- "Who owns the most active placements?",
64
- "Summarize team workload by program",
65
- ],
66
- }
67
- }
68
-
69
- if (pathname.startsWith("/compliance")) {
70
- return {
71
- title: "Compliance",
72
- description: "Compliance tracking and outstanding requirements.",
73
- suggestions: [
74
- "List items due this week",
75
- "Which students have outstanding compliance tasks?",
76
- ],
77
- }
78
- }
79
-
80
- if (pathname.startsWith("/question-bank")) {
81
- return {
82
- title: "Question bank",
83
- description: "Assessment and question library.",
84
- suggestions: [
85
- "Suggest questions for a clinical reasoning quiz",
86
- "How do I filter by difficulty or topic?",
87
- ],
88
- }
89
- }
90
-
91
- if (pathname.startsWith("/rotations")) {
92
- return {
93
- title: "Rotations",
94
- description: "Rotation schedules and capacity.",
95
- suggestions: [
96
- "Summarize rotation blocks for this term",
97
- "Which sites have open capacity?",
63
+ "Where is the list hub implemented?",
64
+ "How is the command palette wired?",
65
+ "What is the sidebar + content layout pattern?",
98
66
  ],
99
67
  }
100
68
  }
@@ -113,7 +81,7 @@ export function getAskLeoRouteContext(pathname: string | null): AskLeoRouteConte
113
81
  if (pathname.startsWith("/help")) {
114
82
  return {
115
83
  title: "Get help",
116
- description: "Exxat Help Center and support resources.",
84
+ description: "Support and documentation entry points.",
117
85
  suggestions: [
118
86
  "Where do I find training articles?",
119
87
  "How do I contact support?",
@@ -32,37 +32,37 @@ export const COACH_MARK_FLOWS: CoachMarkFlowDef[] = [
32
32
  stepCount: 4,
33
33
  },
34
34
  {
35
- id: "placements-views-tour",
35
+ id: "data-list-views-tour",
36
36
  name: "Views & Properties Tour",
37
37
  description:
38
38
  "Walks through view tabs, customisation, adding views, search, filters, and the Properties panel.",
39
- page: "Placements",
39
+ page: "List hub",
40
40
  pageUrl: "/data-list",
41
41
  stepCount: 6,
42
42
  },
43
43
  {
44
- id: "placements-dashboard-customize",
45
- name: "Customize dashboard (Placements)",
44
+ id: "data-list-dashboard-customize",
45
+ name: "Customize dashboard (List hub)",
46
46
  description:
47
47
  "Highlights Edit layout on the Data view dashboard toolbar — drag widgets, chart types, and width.",
48
- page: "Placements",
48
+ page: "List hub",
49
49
  pageUrl: "/data-list",
50
50
  stepCount: 1,
51
51
  },
52
52
  {
53
53
  id: "team-dashboard-customize",
54
- name: "Customize dashboard (Team)",
55
- description: "Same as Placements — Edit layout for the Team Data dashboard.",
56
- page: "Team",
57
- pageUrl: "/team",
54
+ name: "Customize dashboard (Team pattern)",
55
+ description: "Same toolbar pattern as the list hub — Edit layout for a Data dashboard tab.",
56
+ page: "Examples",
57
+ pageUrl: "/examples",
58
58
  stepCount: 1,
59
59
  },
60
60
  {
61
61
  id: "compliance-dashboard-customize",
62
- name: "Customize dashboard (Compliance)",
63
- description: "Same as Placements — Edit layout for the Compliance Data dashboard.",
64
- page: "Compliance",
65
- pageUrl: "/compliance",
62
+ name: "Customize dashboard (Compliance pattern)",
63
+ description: "Same toolbar pattern as the list hub — Edit layout for a Data dashboard tab.",
64
+ page: "Examples",
65
+ pageUrl: "/examples",
66
66
  stepCount: 1,
67
67
  },
68
68
  ]
@@ -44,8 +44,8 @@ const COMMAND_MENU_SHELL: Omit<CommandMenuConfig, "groups"> = {
44
44
  dialogTitle: "Search",
45
45
  dialogDescription:
46
46
  "Search pages and components, or use AI suggestions. Use arrow keys to move through results, Enter to open, Escape to close.",
47
- inputPlaceholder: "Search or ask Leo anything…",
48
- inputAriaLabel: "Search pages, components, or ask Leo",
47
+ inputPlaceholder: "Search routes, patterns, and demo rows…",
48
+ inputAriaLabel: "Search routes, patterns, or demo data",
49
49
  emptyMessage: "No results found.",
50
50
  closeMenuAriaLabel: "Close command menu",
51
51
  }
@@ -57,28 +57,28 @@ const STATIC_COMMAND_GROUPS: CommandMenuGroup[] = [
57
57
  heading: "AI suggestions",
58
58
  items: [
59
59
  {
60
- id: "ai-compliance",
61
- label: "Which students are at risk of non-compliance?",
62
- keywords: "leo ai assistant compliance students risk",
63
- askLeoPrompt: "Which students are at risk of non-compliance?",
60
+ id: "ai-a11y",
61
+ label: "How do I meet WCAG 2.1 AA for tables and dialogs?",
62
+ keywords: "leo ai accessibility wcag aria keyboard",
63
+ askLeoPrompt: "How do I meet WCAG 2.1 AA for tables and dialogs in this design system?",
64
64
  },
65
65
  {
66
- id: "ai-placements-month",
67
- label: "Show me placements ending this month",
68
- keywords: "leo ai placements calendar month",
69
- askLeoPrompt: "Show me placements ending this month",
66
+ id: "ai-tokens",
67
+ label: "Explain how semantic tokens map to components",
68
+ keywords: "leo ai theme tokens css variables tailwind",
69
+ askLeoPrompt: "Explain how semantic tokens map to components in this app shell.",
70
70
  },
71
71
  {
72
- id: "ai-summarize",
73
- label: "Summarize placement progress",
74
- keywords: "leo ai summary placements progress",
75
- askLeoPrompt: "Summarize placement progress",
72
+ id: "ai-list-page",
73
+ label: "What belongs on a ListPageTemplate hub?",
74
+ keywords: "leo ai list page data views board dashboard",
75
+ askLeoPrompt: "What belongs on a ListPageTemplate hub in Exxat DS?",
76
76
  },
77
77
  {
78
- id: "ai-compare-cycle",
79
- label: "Compare this cycle vs. last cycle",
80
- keywords: "leo ai compare cycle trend",
81
- askLeoPrompt: "Compare this cycle vs. last cycle",
78
+ id: "ai-charts",
79
+ label: "How should charts expose keyboard exploration?",
80
+ keywords: "leo ai charts keyboard chartfigure recharts",
81
+ askLeoPrompt: "How should charts expose keyboard exploration in this design system?",
82
82
  },
83
83
  ],
84
84
  },
@@ -87,18 +87,23 @@ const STATIC_COMMAND_GROUPS: CommandMenuGroup[] = [
87
87
  heading: "Navigation",
88
88
  items: [
89
89
  { id: "nav-dashboard", icon: "fa-light fa-grid-2", label: "Dashboard", href: "/dashboard" },
90
+ {
91
+ id: "nav-examples",
92
+ icon: "fa-light fa-layer-group",
93
+ label: "Patterns",
94
+ href: "/examples",
95
+ keywords: "examples gallery showcase",
96
+ },
90
97
  {
91
98
  id: "nav-question-bank",
92
99
  icon: "fa-light fa-books",
93
100
  label: "Question bank",
94
101
  href: "/question-bank",
95
- keywords: "assessment items exam quiz",
102
+ keywords: "folders assessment items tree panel",
96
103
  },
97
- { id: "nav-placements", icon: "fa-light fa-user-graduate", label: "Placements", href: "/data-list" },
98
- { id: "nav-rotations", icon: "fa-light fa-arrows-rotate", label: "Rotations", href: "/rotations" },
99
- { id: "nav-team", icon: "fa-light fa-users", label: "Team", href: "/team" },
100
- { id: "nav-compliance", icon: "fa-light fa-shield-check", label: "Compliance", href: "/compliance" },
104
+ { id: "nav-data-list", icon: "fa-light fa-table", label: "List hub", href: "/data-list" },
101
105
  { id: "nav-settings", icon: "fa-light fa-gear", label: "Settings", href: "/settings" },
106
+ { id: "nav-help", icon: "fa-light fa-circle-question", label: "Help", href: "/help" },
102
107
  ],
103
108
  },
104
109
  ]
@@ -5,15 +5,16 @@
5
5
  import type { CommandMenuGroup, CommandMenuItem } from "@/lib/command-menu-config"
6
6
  import { ALL_PLACEMENTS } from "@/lib/mock/placements"
7
7
 
8
- function placementSearchItems(): CommandMenuItem[] {
8
+ function sampleRowSearchItems(): CommandMenuItem[] {
9
9
  return ALL_PLACEMENTS.map((p) => {
10
10
  const nameParts = p.student.trim().split(/\s+/)
11
11
  return {
12
- id: `placement-${p.id}`,
13
- label: `${p.student} — ${p.internship}`,
14
- icon: "fa-light fa-user-graduate",
15
- href: "/data-list",
12
+ id: `sample-row-${p.id}`,
13
+ label: `Row ${p.id} — ${p.student}`,
14
+ icon: "fa-light fa-table",
15
+ href: `/data-list/${p.id}`,
16
16
  keywords: [
17
+ `row ${p.id}`,
17
18
  p.student,
18
19
  ...nameParts,
19
20
  p.program,
@@ -31,13 +32,13 @@ function placementSearchItems(): CommandMenuItem[] {
31
32
  })
32
33
  }
33
34
 
34
- /** Placements (student names, sites, programs, …)passed as `dataGroups` in the app layout. */
35
+ /** Demo rows for the list hubsearch-only so the palette stays lightweight on open. */
35
36
  export function getCommandMenuSearchDataGroups(): CommandMenuGroup[] {
36
37
  return [
37
38
  {
38
- id: "placements",
39
- heading: "Placements",
40
- items: placementSearchItems(),
39
+ id: "sample-rows",
40
+ heading: "Demo rows",
41
+ items: sampleRowSearchItems(),
41
42
  searchOnly: true,
42
43
  },
43
44
  ]
@@ -11,6 +11,8 @@
11
11
  * | `list` | `DataTableToolbar` + list layout |
12
12
  * | `board` | `DataTableToolbar` + board / kanban |
13
13
  * | `dashboard`| `DataTableToolbar` + KPI (`KeyMetrics`) + optional charts (`ChartCard`, Recharts, etc.) |
14
+ * | `folder` | `DataTableToolbar` + icon grid (macOS-Finder-style) |
15
+ * | `panel` | `DataTableToolbar` + resizable split (list / tree column + detail inspector) |
14
16
  */
15
17
 
16
18
  import type { DataListViewType } from "@/lib/data-list-view"
@@ -21,6 +23,9 @@ export type DataListViewRenderKind =
21
23
  | "list-with-toolbar"
22
24
  | "board-with-toolbar"
23
25
  | "dashboard-with-toolbar"
26
+ | "folder-with-toolbar"
27
+ | "panel-with-toolbar"
28
+ | "tree-panel-with-toolbar"
24
29
 
25
30
  /**
26
31
  * Stable classification for switch/if chains. **Every** `DataListViewType` maps to exactly one kind.
@@ -36,6 +41,12 @@ export function getDataListViewRenderKind(view: DataListViewType): DataListViewR
36
41
  return "board-with-toolbar"
37
42
  case "dashboard":
38
43
  return "dashboard-with-toolbar"
44
+ case "folder":
45
+ return "folder-with-toolbar"
46
+ case "panel":
47
+ return "panel-with-toolbar"
48
+ case "tree-panel":
49
+ return "tree-panel-with-toolbar"
39
50
  default: {
40
51
  const _x: never = view
41
52
  return _x
@@ -54,5 +65,5 @@ export function usesDashboardSurface(view: DataListViewType): boolean {
54
65
 
55
66
  /** Shared toolbar (search, filters, properties); body differs by view. */
56
67
  export function usesToolbarWithFilteredRows(view: DataListViewType): boolean {
57
- return view === "list" || view === "board" || view === "dashboard"
68
+ return view === "list" || view === "board" || view === "dashboard" || view === "folder" || view === "panel" || view === "tree-panel"
58
69
  }
@@ -5,7 +5,7 @@
5
5
  * `dataListViewLabel` / `dataListViewIcon` on every page so Table / List / Board / Dashboard
6
6
  * stay consistent and stay wired to the same `useTableState` dataset (see `docs/data-views-pattern.md`).
7
7
  */
8
- export type DataListViewType = "table" | "list" | "board" | "dashboard"
8
+ export type DataListViewType = "table" | "list" | "board" | "dashboard" | "folder" | "panel" | "tree-panel"
9
9
 
10
10
  export const DATA_LIST_VIEW_TILES: readonly {
11
11
  value: DataListViewType
@@ -13,9 +13,12 @@ export const DATA_LIST_VIEW_TILES: readonly {
13
13
  icon: string
14
14
  }[] = [
15
15
  { value: "table", icon: "fa-table", label: "Table view" },
16
- { value: "list", icon: "fa-list", label: "List view" },
16
+ { value: "list", icon: "fa-list", label: "List view" },
17
17
  { value: "board", icon: "fa-table-columns", label: "Board view" },
18
- { value: "dashboard", icon: "fa-chart-mixed", label: "Dashboard view" },
18
+ { value: "dashboard", icon: "fa-chart-mixed", label: "Dashboard view" },
19
+ { value: "folder", icon: "fa-grid-2", label: "Folder view" },
20
+ { value: "panel", icon: "fa-sidebar", label: "List & details" },
21
+ { value: "tree-panel", icon: "fa-sitemap", label: "Tree & details" },
19
22
  ]
20
23
 
21
24
  /** User-facing name for tabs, Properties summary rows, and tooltips (not entity-specific). */