@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
@@ -0,0 +1,226 @@
1
+ "use client"
2
+
3
+ /**
4
+ * Question bank secondary sidebar — All / My / folder tree (Font Awesome only).
5
+ * Scope syncs to the main hub via `?scope=` + optional `folderId=` (`lib/question-bank-nav.ts`).
6
+ */
7
+
8
+ import * as React from "react"
9
+ import Link from "next/link"
10
+ import { usePathname, useSearchParams } from "next/navigation"
11
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
12
+ import { Tip } from "@/components/ui/tip"
13
+ import { cn } from "@/lib/utils"
14
+ import type { QuestionBankFolder } from "@/lib/mock/question-bank-folders"
15
+ import { QUESTION_BANK_FOLDER_ICON_COLORS } from "@/lib/mock/question-bank-folders"
16
+ import { DEFAULT_QUESTION_BANK_FOLDERS } from "@/lib/mock/question-bank-folders"
17
+ import { useSecondaryPanel } from "@/components/secondary-panel"
18
+ import {
19
+ parseQuestionBankNav,
20
+ questionBankNavHref,
21
+ type QuestionBankNavScope,
22
+ } from "@/lib/question-bank-nav"
23
+
24
+ function matchesQuery(q: string, ...parts: (string | undefined)[]) {
25
+ if (!q) return true
26
+ return parts.some(p => p && p.toLowerCase().includes(q))
27
+ }
28
+
29
+ function isNavActive(
30
+ pathname: string,
31
+ nav: ReturnType<typeof parseQuestionBankNav>,
32
+ scope: QuestionBankNavScope,
33
+ folderId?: string | null,
34
+ ) {
35
+ if (pathname !== "/question-bank") return false
36
+ if (scope === "all") return nav.scope === "all"
37
+ if (scope === "my") return nav.scope === "my"
38
+ if (scope === "folder" && folderId) {
39
+ return nav.scope === "folder" && nav.folderId === folderId
40
+ }
41
+ return false
42
+ }
43
+
44
+ function NavRow({
45
+ href,
46
+ active,
47
+ iconClass,
48
+ label,
49
+ onClick,
50
+ }: {
51
+ href: string
52
+ active: boolean
53
+ iconClass: string
54
+ label: string
55
+ /** e.g. reopen secondary panel on same-route “All questions” */
56
+ onClick?: () => void
57
+ }) {
58
+ return (
59
+ <li className="min-w-0">
60
+ <Tip label={label} side="right">
61
+ <Link
62
+ href={href}
63
+ onClick={() => onClick?.()}
64
+ aria-current={active ? "page" : undefined}
65
+ className={cn(
66
+ "flex w-full min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors",
67
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
68
+ active
69
+ ? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
70
+ : "text-sidebar-foreground hover:bg-sidebar-accent/50",
71
+ )}
72
+ >
73
+ <span className="size-4 shrink-0 text-center text-[13px] leading-none" aria-hidden>
74
+ <i className={cn(active ? "fa-solid" : "fa-light", iconClass)} aria-hidden />
75
+ </span>
76
+ <span className="min-w-0 flex-1 truncate">{label}</span>
77
+ </Link>
78
+ </Tip>
79
+ </li>
80
+ )
81
+ }
82
+
83
+ function PanelFolderBranch({
84
+ folder,
85
+ folders,
86
+ query,
87
+ depth,
88
+ pathname,
89
+ nav,
90
+ }: {
91
+ folder: QuestionBankFolder
92
+ folders: QuestionBankFolder[]
93
+ query: string
94
+ depth: number
95
+ pathname: string
96
+ nav: ReturnType<typeof parseQuestionBankNav>
97
+ }) {
98
+ const childFolders = folders
99
+ .filter(f => f.parentId === folder.id)
100
+ .sort((a, b) => a.name.localeCompare(b.name))
101
+
102
+ const visibleFolders = React.useMemo(
103
+ () => childFolders.filter(f => matchesQuery(query, f.name)),
104
+ [childFolders, query],
105
+ )
106
+
107
+ const hasSubfolders = childFolders.length > 0
108
+ const indent = depth * 10
109
+
110
+ if (query && !matchesQuery(query, folder.name) && visibleFolders.length === 0) {
111
+ return null
112
+ }
113
+
114
+ const folderHref = questionBankNavHref({ scope: "folder", folderId: folder.id })
115
+ const folderActive = isNavActive(pathname, nav, "folder", folder.id)
116
+
117
+ return (
118
+ <Collapsible defaultOpen={depth < 1} className="group">
119
+ <div className="flex min-w-0 items-center rounded-md hover:bg-sidebar-accent/40">
120
+ <div style={{ width: indent }} className="shrink-0" aria-hidden />
121
+ {hasSubfolders ? (
122
+ <CollapsibleTrigger asChild>
123
+ <button
124
+ type="button"
125
+ className="flex size-8 shrink-0 items-center justify-center text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
126
+ aria-label={`${folder.name} — expand or collapse`}
127
+ >
128
+ <i
129
+ className="fa-light fa-chevron-right text-xs transition-transform duration-150 group-data-[state=open]:rotate-90"
130
+ aria-hidden
131
+ />
132
+ </button>
133
+ </CollapsibleTrigger>
134
+ ) : (
135
+ <div className="size-8 shrink-0" aria-hidden />
136
+ )}
137
+ <Tip label={folder.name} side="right">
138
+ <Link
139
+ href={folderHref}
140
+ aria-current={folderActive ? "page" : undefined}
141
+ className={cn(
142
+ "flex min-w-0 flex-1 items-center gap-2 py-1.5 pr-2 text-left text-sm transition-colors",
143
+ "rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
144
+ folderActive
145
+ ? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
146
+ : "text-sidebar-foreground hover:bg-sidebar-accent/50",
147
+ )}
148
+ >
149
+ <i
150
+ className={cn("fa-light shrink-0 text-sm", folder.icon, QUESTION_BANK_FOLDER_ICON_COLORS[folder.colorKey])}
151
+ aria-hidden
152
+ />
153
+ <span className="min-w-0 flex-1 truncate leading-tight">{folder.name}</span>
154
+ {hasSubfolders ? (
155
+ <span className="shrink-0 text-xs tabular-nums text-muted-foreground">{childFolders.length}</span>
156
+ ) : null}
157
+ </Link>
158
+ </Tip>
159
+ </div>
160
+ {hasSubfolders ? (
161
+ <CollapsibleContent>
162
+ {visibleFolders.map(child => (
163
+ <PanelFolderBranch
164
+ key={child.id}
165
+ folder={child}
166
+ folders={folders}
167
+ query={query}
168
+ depth={depth + 1}
169
+ pathname={pathname}
170
+ nav={nav}
171
+ />
172
+ ))}
173
+ </CollapsibleContent>
174
+ ) : null}
175
+ </Collapsible>
176
+ )
177
+ }
178
+
179
+ export function QuestionBankSecondaryNav({ query }: { query: string }) {
180
+ const pathname = usePathname()
181
+ const searchParams = useSearchParams()
182
+ const searchParamsKey = searchParams.toString()
183
+ const { openPanel } = useSecondaryPanel()
184
+ const nav = React.useMemo(
185
+ () => parseQuestionBankNav(new URLSearchParams(searchParamsKey)),
186
+ [searchParamsKey],
187
+ )
188
+
189
+ const folders = DEFAULT_QUESTION_BANK_FOLDERS
190
+ const q = query.trim().toLowerCase()
191
+
192
+ const roots = React.useMemo(
193
+ () => folders.filter(f => f.parentId === null).sort((a, b) => a.name.localeCompare(b.name)),
194
+ [folders],
195
+ )
196
+
197
+ return (
198
+ <div className="min-h-0 flex-1 overflow-y-auto px-3 pb-4" role="navigation" aria-label="Question bank">
199
+ <ul className="space-y-0.5" role="list">
200
+ <NavRow
201
+ href={questionBankNavHref({ scope: "all" })}
202
+ active={isNavActive(pathname, nav, "all")}
203
+ iconClass="fa-table-list"
204
+ label="All questions"
205
+ onClick={() => openPanel("question-bank")}
206
+ />
207
+ <NavRow
208
+ href={questionBankNavHref({ scope: "my" })}
209
+ active={isNavActive(pathname, nav, "my")}
210
+ iconClass="fa-user"
211
+ label="My questions"
212
+ />
213
+ <li role="presentation" className="select-none">
214
+ <span className="block px-2 pt-3 pb-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground/70">
215
+ Folders
216
+ </span>
217
+ </li>
218
+ {roots.map(folder => (
219
+ <li key={folder.id} className="min-w-0">
220
+ <PanelFolderBranch folder={folder} folders={folders} query={q} depth={0} pathname={pathname} nav={nav} />
221
+ </li>
222
+ ))}
223
+ </ul>
224
+ </div>
225
+ )
226
+ }