@exxatdesignux/ui 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/template/.agents/skills/shadcn/SKILL.md +242 -0
- package/template/.agents/skills/shadcn/agents/openai.yml +5 -0
- package/template/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/template/.agents/skills/shadcn/assets/shadcn.png +0 -0
- package/template/.agents/skills/shadcn/cli.md +257 -0
- package/template/.agents/skills/shadcn/customization.md +202 -0
- package/template/.agents/skills/shadcn/evals/evals.json +47 -0
- package/template/.agents/skills/shadcn/mcp.md +94 -0
- package/template/.agents/skills/shadcn/rules/base-vs-radix.md +306 -0
- package/template/.agents/skills/shadcn/rules/composition.md +195 -0
- package/template/.agents/skills/shadcn/rules/forms.md +192 -0
- package/template/.agents/skills/shadcn/rules/icons.md +101 -0
- package/template/.agents/skills/shadcn/rules/styling.md +162 -0
- package/template/.claude/skills/exxat-ds-skill/SKILL.md +712 -0
- package/template/.cursor/rules/exxat-accessibility.mdc +33 -0
- package/template/.cursor/rules/exxat-command-menu.mdc +23 -0
- package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +53 -0
- package/template/.cursor/rules/exxat-data-tables.mdc +31 -0
- package/template/.cursor/rules/exxat-ds-agents.mdc +26 -0
- package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +100 -0
- package/template/.cursor/rules/exxat-list-page-connected-views.mdc +16 -0
- package/template/.cursor/rules/exxat-no-toast.mdc +26 -0
- package/template/.cursor/rules/exxat-page-vs-drawer.mdc +22 -0
- package/template/.cursor/rules/exxat-table-properties-drawer.mdc +40 -0
- package/template/AGENTS.md +52 -11
- package/template/app/(app)/dashboard/page.tsx +1 -1
- package/template/app/(app)/data-list/[id]/page.tsx +24 -8
- package/template/app/(app)/data-list/new/page.tsx +7 -4
- package/template/app/(app)/data-list/page.tsx +1 -1
- package/template/app/(app)/examples/page.tsx +41 -0
- package/template/app/(app)/question-bank/page.tsx +3 -3
- package/template/app/globals.css +1 -1
- package/template/components/app-sidebar.tsx +52 -35
- package/template/components/compliance-table.tsx +79 -0
- package/template/components/data-list-client.tsx +36 -25
- package/template/components/data-list-table.tsx +797 -10
- package/template/components/data-views/finder-panel-view.tsx +405 -0
- package/template/components/data-views/folder-grid-view.tsx +86 -0
- package/template/components/data-views/index.ts +59 -0
- package/template/components/data-views/list-page-split-details-placeholder.tsx +39 -0
- package/template/components/data-views/list-page-split-hub-chrome.tsx +60 -0
- package/template/components/data-views/list-page-split-hub-tokens.ts +16 -0
- package/template/components/data-views/list-page-tree-column-header.tsx +31 -0
- package/template/components/data-views/list-page-tree-panel-shell.tsx +91 -0
- package/template/components/data-views/list-page-view-frame.tsx +53 -0
- package/template/components/data-views/os-folder-glyph.tsx +121 -0
- package/template/components/folder-details-shell.tsx +230 -0
- package/template/components/hub-tree-panel-view.tsx +672 -0
- package/template/components/list-hub-status-badge.tsx +17 -3
- package/template/components/placements-page-header.tsx +14 -8
- package/template/components/placements-table-columns.tsx +8 -8
- package/template/components/question-bank-client.tsx +157 -40
- package/template/components/question-bank-new-folder-sheet.tsx +248 -0
- package/template/components/question-bank-os-folder-view.tsx +648 -0
- package/template/components/question-bank-page-header.tsx +3 -3
- package/template/components/question-bank-panel-activator.tsx +9 -0
- package/template/components/question-bank-secondary-nav.tsx +226 -0
- package/template/components/question-bank-table.tsx +707 -22
- package/template/components/secondary-panel.tsx +41 -107
- package/template/components/sites-table.tsx +66 -0
- package/template/components/team-client.tsx +7 -0
- package/template/components/team-table.tsx +156 -1
- package/template/components/templates/list-page.tsx +2 -2
- package/template/components/ui/avatar.tsx +1 -1
- package/template/components/ui/badge.tsx +1 -1
- package/template/components/ui/banner.tsx +1 -1
- package/template/components/ui/breadcrumb.tsx +1 -1
- package/template/components/ui/button.tsx +1 -1
- package/template/components/ui/calendar.tsx +1 -1
- package/template/components/ui/card.tsx +1 -1
- package/template/components/ui/chart.tsx +1 -1
- package/template/components/ui/checkbox.tsx +1 -1
- package/template/components/ui/coach-mark.tsx +1 -1
- package/template/components/ui/collapsible.tsx +1 -1
- package/template/components/ui/command.tsx +1 -1
- package/template/components/ui/date-picker-field.tsx +1 -1
- package/template/components/ui/dialog.tsx +1 -1
- package/template/components/ui/drag-handle-grip.tsx +1 -1
- package/template/components/ui/drawer.tsx +1 -1
- package/template/components/ui/dropdown-menu.tsx +1 -1
- package/template/components/ui/field.tsx +1 -1
- package/template/components/ui/form.tsx +1 -1
- package/template/components/ui/input-group.tsx +1 -1
- package/template/components/ui/input-mask.tsx +1 -1
- package/template/components/ui/input.tsx +1 -1
- package/template/components/ui/kbd.tsx +1 -1
- package/template/components/ui/label.tsx +1 -1
- package/template/components/ui/payment-card-fields.tsx +1 -1
- package/template/components/ui/popover.tsx +1 -1
- package/template/components/ui/radio-group.tsx +1 -1
- package/template/components/ui/resizable.tsx +68 -0
- package/template/components/ui/select.tsx +1 -1
- package/template/components/ui/selection-tile-grid.tsx +1 -1
- package/template/components/ui/separator.tsx +1 -1
- package/template/components/ui/sheet.tsx +1 -1
- package/template/components/ui/sidebar.tsx +1 -1
- package/template/components/ui/skeleton.tsx +1 -1
- package/template/components/ui/sonner.tsx +1 -1
- package/template/components/ui/status-badge.tsx +1 -1
- package/template/components/ui/table.tsx +1 -1
- package/template/components/ui/tabs.tsx +1 -1
- package/template/components/ui/textarea.tsx +1 -1
- package/template/components/ui/tip.tsx +1 -1
- package/template/components/ui/toggle-group.tsx +1 -1
- package/template/components/ui/toggle-switch.tsx +1 -1
- package/template/components/ui/toggle.tsx +1 -1
- package/template/components/ui/tooltip.tsx +1 -1
- package/template/components/ui/view-segmented-control.tsx +1 -1
- package/template/docs/data-views-pattern.md +7 -0
- package/template/hooks/use-app-theme.ts +1 -1
- package/template/hooks/use-coach-mark.ts +1 -1
- package/template/hooks/use-location-hash.ts +15 -0
- package/template/hooks/use-mobile.ts +1 -1
- package/template/hooks/use-mod-key-label.ts +1 -1
- package/template/hooks/use-sidebar-reflow-zoom.ts +40 -0
- package/template/lib/ask-leo-route-context.ts +25 -57
- package/template/lib/coach-mark-registry.ts +13 -13
- package/template/lib/command-menu-config.ts +28 -23
- package/template/lib/command-menu-search-data.ts +10 -9
- package/template/lib/data-list-view-surface.ts +12 -1
- package/template/lib/data-list-view.ts +6 -3
- package/template/lib/date-filter.ts +1 -1
- package/template/lib/mock/dashboard.ts +11 -11
- package/template/lib/mock/navigation.tsx +22 -63
- package/template/lib/mock/placements-kpi.ts +19 -19
- package/template/lib/mock/question-bank-folders.ts +167 -0
- package/template/lib/mock/question-bank-inspector.ts +109 -0
- package/template/lib/mock/question-bank-kpi.ts +1 -1
- package/template/lib/mock/question-bank.ts +80 -0
- package/template/lib/question-bank-nav.ts +91 -0
- package/template/lib/utils.ts +1 -1
- package/template/next.config.mjs +8 -0
- package/template/package.json +1 -0
- package/template/public/folders/icons8-folder-windows-11.svg +1 -0
- package/template/app/(app)/compliance/page.tsx +0 -10
- package/template/app/(app)/rotations/page.tsx +0 -15
- package/template/app/(app)/sites/all/page.tsx +0 -13
- package/template/app/(app)/team/page.tsx +0 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/popover"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/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 "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/select"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/selection-tile-grid"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/separator"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/sheet"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/sidebar"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/skeleton"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/sonner"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/status-badge"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/table"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/tabs"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/textarea"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/tip"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/toggle-group"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/toggle-switch"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/toggle"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/tooltip"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../../packages/ui/src/components/ui/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`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../packages/ui/src/hooks/use-app-theme"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../packages/ui/src/hooks/use-coach-mark"
|
|
@@ -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
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../packages/ui/src/hooks/use-mobile"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "
|
|
1
|
+
export * from "../../../packages/ui/src/hooks/use-mod-key-label"
|
|
@@ -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
|
-
*
|
|
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
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
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
|
|
25
|
+
description: "Overview of metrics, charts, and layout patterns.",
|
|
26
26
|
suggestions: [
|
|
27
|
-
"What changed in
|
|
28
|
-
"Summarize
|
|
29
|
-
"
|
|
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
|
|
37
|
+
description: "Overview of metrics, charts, and layout patterns.",
|
|
38
38
|
suggestions: [
|
|
39
|
-
"What changed in
|
|
40
|
-
"Summarize
|
|
41
|
-
"Which chart should I
|
|
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: "
|
|
49
|
-
description: "
|
|
48
|
+
title: "List hub",
|
|
49
|
+
description: "Demo list with table, list, board, and dashboard views sharing one table state.",
|
|
50
50
|
suggestions: [
|
|
51
|
-
"
|
|
52
|
-
"Summarize
|
|
53
|
-
"What
|
|
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("/
|
|
58
|
+
if (pathname.startsWith("/examples")) {
|
|
59
59
|
return {
|
|
60
|
-
title: "
|
|
61
|
-
description: "
|
|
60
|
+
title: "Patterns",
|
|
61
|
+
description: "Entry points for reusable shells and demos.",
|
|
62
62
|
suggestions: [
|
|
63
|
-
"
|
|
64
|
-
"
|
|
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: "
|
|
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: "
|
|
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: "
|
|
39
|
+
page: "List hub",
|
|
40
40
|
pageUrl: "/data-list",
|
|
41
41
|
stepCount: 6,
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
|
-
id: "
|
|
45
|
-
name: "Customize dashboard (
|
|
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: "
|
|
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
|
|
56
|
-
page: "
|
|
57
|
-
pageUrl: "/
|
|
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
|
|
64
|
-
page: "
|
|
65
|
-
pageUrl: "/
|
|
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
|
|
48
|
-
inputAriaLabel: "Search
|
|
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-
|
|
61
|
-
label: "
|
|
62
|
-
keywords: "leo ai
|
|
63
|
-
askLeoPrompt: "
|
|
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-
|
|
67
|
-
label: "
|
|
68
|
-
keywords: "leo ai
|
|
69
|
-
askLeoPrompt: "
|
|
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-
|
|
73
|
-
label: "
|
|
74
|
-
keywords: "leo ai
|
|
75
|
-
askLeoPrompt: "
|
|
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-
|
|
79
|
-
label: "
|
|
80
|
-
keywords: "leo ai
|
|
81
|
-
askLeoPrompt: "
|
|
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
|
|
102
|
+
keywords: "folders assessment items tree panel",
|
|
96
103
|
},
|
|
97
|
-
{ id: "nav-
|
|
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
|
|
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: `
|
|
13
|
-
label:
|
|
14
|
-
icon: "fa-light fa-
|
|
15
|
-
href:
|
|
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
|
-
/**
|
|
35
|
+
/** Demo rows for the list hub — search-only so the palette stays lightweight on open. */
|
|
35
36
|
export function getCommandMenuSearchDataGroups(): CommandMenuGroup[] {
|
|
36
37
|
return [
|
|
37
38
|
{
|
|
38
|
-
id: "
|
|
39
|
-
heading: "
|
|
40
|
-
items:
|
|
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",
|
|
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",
|
|
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). */
|