@exxatdesignux/ui 0.2.18 → 0.2.19

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 (140) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/consumer-extras/AGENTS.md +76 -0
  3. package/consumer-extras/README.md +5 -1
  4. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +14 -3
  5. package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +37 -0
  6. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +21 -6
  7. package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +57 -0
  8. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +4 -2
  9. package/consumer-extras/patterns/consumer-app-pattern.md +39 -0
  10. package/consumer-extras/patterns/consumer-upgrade-checklist.md +20 -0
  11. package/consumer-extras/patterns/data-views-pattern.md +40 -3
  12. package/consumer-extras/patterns/focused-workflow-page-pattern.md +84 -0
  13. package/consumer-extras/patterns/shell-surface-elevation-pattern.md +5 -3
  14. package/package.json +2 -1
  15. package/src/components/ui/button-group.tsx +81 -0
  16. package/src/components/ui/button.tsx +4 -4
  17. package/src/globals.css +7 -1858
  18. package/src/theme.css +10 -1126
  19. package/src/tokens/README.md +15 -0
  20. package/src/tokens/base.css +337 -0
  21. package/src/tokens/high-contrast.css +1195 -0
  22. package/src/tokens/layers.css +224 -0
  23. package/src/tokens/tailwind-bridge.css +118 -0
  24. package/src/tokens/themes.css +201 -0
  25. package/template/AGENTS.md +60 -22
  26. package/template/app/(app)/dashboard/loading.tsx +3 -15
  27. package/template/app/(app)/dashboard/page.tsx +2 -14
  28. package/template/app/(app)/data-list/layout.tsx +43 -0
  29. package/template/app/(app)/data-list/page.tsx +2 -2
  30. package/template/app/(app)/examples/focused-workflow/page.tsx +5 -0
  31. package/template/app/(app)/examples/page.tsx +1 -0
  32. package/template/app/(app)/loading.tsx +1 -18
  33. package/template/app/(app)/question-bank/find/page.tsx +2 -1
  34. package/template/app/(app)/question-bank/library/page.tsx +2 -1
  35. package/template/app/(app)/question-bank/list/page.tsx +2 -1
  36. package/template/app/(app)/question-bank/new/page.tsx +15 -23
  37. package/template/app/(app)/question-bank/page.tsx +2 -1
  38. package/template/app/(app)/settings/page.tsx +4 -5
  39. package/template/app/globals.css +7 -1964
  40. package/template/components/app-route-loading.tsx +14 -0
  41. package/template/components/app-sidebar.tsx +70 -55
  42. package/template/components/data-views/index.ts +37 -9
  43. package/template/components/data-views/list-page-calendar-view.tsx +593 -0
  44. package/template/components/data-views/list-page-connected-view-body.tsx +66 -0
  45. package/template/components/data-views/list-page-folder-columns-panel.tsx +345 -0
  46. package/template/components/data-views/list-page-split-hub-chrome.tsx +8 -0
  47. package/template/components/examples/focused-workflow-showcase.tsx +183 -0
  48. package/template/components/list-hub-board-view.tsx +68 -0
  49. package/template/components/list-hub-client.tsx +186 -0
  50. package/template/components/list-hub-list-view.tsx +36 -0
  51. package/template/components/list-hub-panel-activator.tsx +8 -0
  52. package/template/components/list-hub-secondary-nav.tsx +121 -0
  53. package/template/components/list-hub-table.tsx +336 -0
  54. package/template/components/new-question-composer.tsx +6 -24
  55. package/template/components/product-switcher.tsx +3 -2
  56. package/template/components/question-bank-client.tsx +4 -1
  57. package/template/components/question-bank-folder-columns-panel.tsx +104 -0
  58. package/template/components/question-bank-table.tsx +143 -485
  59. package/template/components/secondary-panel/nav-link-rows.tsx +83 -0
  60. package/template/components/secondary-panel.tsx +4 -44
  61. package/template/components/secondary-panels/list-hub-panel.tsx +39 -0
  62. package/template/components/secondary-panels/question-bank-panel.tsx +39 -0
  63. package/template/components/secondary-panels/registry.tsx +15 -0
  64. package/template/components/settings-appearance-card.tsx +3 -2
  65. package/template/components/settings-client.tsx +59 -15
  66. package/template/components/settings-form-row.tsx +9 -4
  67. package/template/components/table-properties/drawer-button.tsx +13 -0
  68. package/template/components/table-properties/drawer.tsx +65 -4
  69. package/template/components/templates/focused-workflow-layouts.tsx +448 -0
  70. package/template/components/templates/focused-workflow-page-template.tsx +69 -0
  71. package/template/components/templates/list-page.tsx +29 -5
  72. package/template/components/templates/nested-secondary-panel-shell.tsx +2 -1
  73. package/template/components/templates/page-loading-shell.tsx +262 -0
  74. package/template/components/ui/button-group.tsx +1 -0
  75. package/template/docs/consumer-app-pattern.md +39 -0
  76. package/template/docs/data-views-pattern.md +40 -3
  77. package/template/docs/drawer-vs-dialog-pattern.md +3 -1
  78. package/template/docs/focused-workflow-page-pattern.md +84 -0
  79. package/template/docs/shell-surface-elevation-pattern.md +5 -3
  80. package/template/lib/command-menu-search-data.ts +11 -27
  81. package/template/lib/data-list-display-options.ts +16 -2
  82. package/template/lib/data-list-view-registry.ts +104 -0
  83. package/template/lib/data-list-view-surface.ts +15 -1
  84. package/template/lib/data-list-view.ts +10 -1
  85. package/template/lib/data-view-dashboard-storage.ts +38 -35
  86. package/template/lib/hub-connected-view-renderers.ts +58 -0
  87. package/template/lib/list-hub-nav.ts +121 -0
  88. package/template/lib/list-hub-supported-views.ts +10 -0
  89. package/template/lib/list-page-table-properties.ts +3 -7
  90. package/template/lib/list-status-badges.ts +4 -97
  91. package/template/lib/mock/list-hub-directory.ts +27 -0
  92. package/template/lib/mock/list-hub-kpi.ts +27 -0
  93. package/template/lib/mock/navigation.tsx +1 -0
  94. package/template/lib/page-loading-variant.ts +40 -0
  95. package/template/lib/question-bank-supported-views.ts +13 -0
  96. package/template/lib/table-state-lifecycle.ts +2 -2
  97. package/template/app/(app)/data-list/[id]/page.tsx +0 -44
  98. package/template/app/(app)/data-list/new/page.tsx +0 -34
  99. package/template/components/compliance-board-view.tsx +0 -142
  100. package/template/components/compliance-client.tsx +0 -92
  101. package/template/components/compliance-list-view.tsx +0 -54
  102. package/template/components/compliance-page-header.tsx +0 -89
  103. package/template/components/compliance-table.tsx +0 -612
  104. package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
  105. package/template/components/data-view-dashboard-charts-team.tsx +0 -971
  106. package/template/components/data-view-dashboard-charts.tsx +0 -1503
  107. package/template/components/new-placement-back-btn.tsx +0 -28
  108. package/template/components/new-placement-form.tsx +0 -1068
  109. package/template/components/placement-board-card.tsx +0 -262
  110. package/template/components/placement-detail.tsx +0 -438
  111. package/template/components/placements-board-view.tsx +0 -404
  112. package/template/components/placements-client.tsx +0 -252
  113. package/template/components/placements-list-view.tsx +0 -171
  114. package/template/components/placements-page-header.tsx +0 -166
  115. package/template/components/placements-table-cells.test.tsx +0 -22
  116. package/template/components/placements-table-cells.tsx +0 -173
  117. package/template/components/placements-table-columns.tsx +0 -640
  118. package/template/components/placements-table.tsx +0 -1642
  119. package/template/components/rotations-empty-state.tsx +0 -50
  120. package/template/components/rotations-panel-activator.tsx +0 -8
  121. package/template/components/sites-all-client.tsx +0 -154
  122. package/template/components/sites-board-view.tsx +0 -67
  123. package/template/components/sites-list-view.tsx +0 -42
  124. package/template/components/sites-table.tsx +0 -382
  125. package/template/components/team-board-view.tsx +0 -122
  126. package/template/components/team-client.tsx +0 -100
  127. package/template/components/team-list-view.tsx +0 -59
  128. package/template/components/team-page-header.tsx +0 -92
  129. package/template/components/team-table.tsx +0 -693
  130. package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
  131. package/template/lib/mock/compliance-kpi.ts +0 -61
  132. package/template/lib/mock/compliance.ts +0 -146
  133. package/template/lib/mock/placements-kpi.ts +0 -134
  134. package/template/lib/mock/placements.ts +0 -183
  135. package/template/lib/mock/sites-directory.ts +0 -16
  136. package/template/lib/mock/sites-kpi.ts +0 -25
  137. package/template/lib/mock/team-kpi.ts +0 -60
  138. package/template/lib/mock/team.ts +0 -118
  139. package/template/lib/placement-board-card-layout.ts +0 -79
  140. package/template/lib/placement-lifecycle.ts +0 -5
@@ -1,262 +0,0 @@
1
- "use client"
2
-
3
- /**
4
- * Placement-specific board card — composes shared board primitives with column defs and lifecycle layout rules.
5
- */
6
-
7
- import * as React from "react"
8
- import { cn } from "@/lib/utils"
9
- import type { Placement } from "@/lib/mock/placements"
10
- import { StatusBadge } from "@/components/placements-table-cells"
11
- import { AvatarInitials } from "@/components/ui/avatar"
12
- import { Badge } from "@/components/ui/badge"
13
- import {
14
- ListPageBoardCard,
15
- ListPageBoardCardBadgeRow,
16
- ListPageBoardCardBody,
17
- ListPageBoardCardHeader,
18
- ListPageBoardCardSecondary,
19
- ListPageBoardCardTitleRow,
20
- } from "@/components/data-views/list-page-board-card"
21
- import type { BoardLineCount } from "@/lib/data-list-display-options"
22
- import {
23
- type BoardCardLifecycleTabId,
24
- filterColumnsForBoardCard,
25
- isBoardFieldActive,
26
- remainingBodyColumns,
27
- scheduleKeysForTab,
28
- } from "@/lib/placement-board-card-layout"
29
- import { getConditionalRowBackground } from "@/lib/conditional-rule-match"
30
- import type { ConditionalRule } from "@/components/table-properties/types"
31
- import type { CellContext, ColumnDef } from "@/components/data-table/types"
32
- import {
33
- BoardCardIconRow,
34
- BoardCardTwoLineBlock,
35
- lineClampClass,
36
- } from "@/components/data-views/board-card-primitives"
37
-
38
- const BOARD_CELL_CTX: CellContext<Placement> = {
39
- rowIndex: 0,
40
- selected: false,
41
- onSelect: () => {},
42
- }
43
-
44
- function columnIconClass(col: ColumnDef<Placement>): string {
45
- if (col.filter?.icon) return col.filter.icon
46
- const fallbacks: Record<string, string> = {
47
- specialization: "fa-stethoscope",
48
- site: "fa-hospital",
49
- internship: "fa-briefcase",
50
- supervisor: "fa-user-tie",
51
- start: "fa-calendar-days",
52
- compliance: "fa-shield-check",
53
- daysUntilStart: "fa-calendar-days",
54
- readiness: "fa-flag",
55
- progressWeeksDone: "fa-chart-line",
56
- endDate: "fa-calendar-xmark",
57
- lastCheckin: "fa-calendar-clock",
58
- completionDate: "fa-calendar-check",
59
- finalStatus: "fa-circle-check",
60
- rating: "fa-star",
61
- suggestedToHire: "fa-user-check",
62
- duration: "fa-clock",
63
- program: "fa-graduation-cap",
64
- student: "fa-user",
65
- status: "fa-circle-dot",
66
- }
67
- return fallbacks[col.key] ?? "fa-tag"
68
- }
69
-
70
- function boardCellContent(row: Placement, col: ColumnDef<Placement>): React.ReactNode {
71
- if (col.key === "status") return <StatusBadge status={row.status} surface="board" />
72
- if (col.cell) return col.cell(row, BOARD_CELL_CTX)
73
- return <span className="text-foreground/90">{String(row[col.key as keyof Placement] ?? "")}</span>
74
- }
75
-
76
- function renderScheduleSection(
77
- row: Placement,
78
- tab: BoardCardLifecycleTabId,
79
- hiddenColKeys: Set<string>,
80
- boardColumns: ColumnDef<Placement>[],
81
- ): React.ReactNode {
82
- const sk = scheduleKeysForTab(tab)
83
- const anyActive = sk.some(k => isBoardFieldActive(k, tab, hiddenColKeys, boardColumns))
84
- if (!anyActive) return null
85
-
86
- switch (tab) {
87
- case "all": {
88
- const aStart = isBoardFieldActive("start", tab, hiddenColKeys, boardColumns)
89
- const aDur = isBoardFieldActive("duration", tab, hiddenColKeys, boardColumns)
90
- if (!aStart && !aDur) return null
91
- return (
92
- <BoardCardTwoLineBlock
93
- iconClass="fa-calendar-days"
94
- line1={aStart ? row.start : "—"}
95
- line2={aDur ? row.duration : "—"}
96
- />
97
- )
98
- }
99
- case "upcoming": {
100
- const aStart = isBoardFieldActive("start", tab, hiddenColKeys, boardColumns)
101
- const aDays = isBoardFieldActive("daysUntilStart", tab, hiddenColKeys, boardColumns)
102
- if (!aStart && !aDays) return null
103
- const line2 =
104
- aDays && row.daysUntilStart > 0
105
- ? `Starts in ${row.daysUntilStart} days`
106
- : aDays && row.daysUntilStart === 0
107
- ? "Starts today"
108
- : "—"
109
- return (
110
- <BoardCardTwoLineBlock
111
- iconClass="fa-calendar-days"
112
- line1={aStart ? row.start : "—"}
113
- line2={line2}
114
- />
115
- )
116
- }
117
- case "ongoing": {
118
- const aP = isBoardFieldActive("progressWeeksDone", tab, hiddenColKeys, boardColumns)
119
- const aEnd = isBoardFieldActive("endDate", tab, hiddenColKeys, boardColumns)
120
- if (!aP && !aEnd) return null
121
- return (
122
- <BoardCardTwoLineBlock
123
- iconClass="fa-calendar-days"
124
- line1={aP ? `${row.progressWeeksDone} / ${row.progressWeeksTotal} wks` : "—"}
125
- line2={aEnd ? row.endDate : "—"}
126
- />
127
- )
128
- }
129
- case "completed": {
130
- const aComp = isBoardFieldActive("completionDate", tab, hiddenColKeys, boardColumns)
131
- const aFinal = isBoardFieldActive("finalStatus", tab, hiddenColKeys, boardColumns)
132
- if (!aComp && !aFinal) return null
133
- const finalCol = boardColumns.find(c => c.key === "finalStatus")
134
- return (
135
- <BoardCardTwoLineBlock
136
- iconClass="fa-calendar-check"
137
- line1={aComp ? row.completionDate : "—"}
138
- line2={
139
- aFinal && finalCol ? (
140
- <span className="inline-flex min-w-0 max-w-full [&_span]:text-xs">
141
- {boardCellContent(row, finalCol)}
142
- </span>
143
- ) : aFinal ? (
144
- row.finalStatus
145
- ) : (
146
- "—"
147
- )
148
- }
149
- line2ClassName={aFinal && finalCol ? "text-xs" : undefined}
150
- />
151
- )
152
- }
153
- default:
154
- return null
155
- }
156
- }
157
-
158
- export function BoardPlacementCard({
159
- row,
160
- lifecycleTabId,
161
- hiddenColKeys,
162
- lineCount,
163
- conditionalRules,
164
- boardColumns,
165
- onOpen,
166
- }: {
167
- row: Placement
168
- lifecycleTabId: BoardCardLifecycleTabId
169
- hiddenColKeys: Set<string>
170
- lineCount: BoardLineCount
171
- conditionalRules: ConditionalRule[] | undefined
172
- boardColumns: ColumnDef<Placement>[]
173
- onOpen: (id: number) => void
174
- }) {
175
- const lc = lineClampClass(lineCount)
176
- const ruleBg = getConditionalRowBackground(row, conditionalRules, boardColumns)
177
-
178
- const visibleCols = boardColumns.filter(c => !hiddenColKeys.has(c.key))
179
- const showStudent = visibleCols.some(c => c.key === "student")
180
- const cardCols = filterColumnsForBoardCard(lifecycleTabId, visibleCols)
181
- const remainingCols = remainingBodyColumns(lifecycleTabId, cardCols)
182
-
183
- const showStatus = isBoardFieldActive("status", lifecycleTabId, hiddenColKeys, boardColumns)
184
- const showSite = isBoardFieldActive("site", lifecycleTabId, hiddenColKeys, boardColumns)
185
- const siteCol = boardColumns.find(c => c.key === "site")
186
-
187
- const cardShell = (className: string, children: React.ReactNode) => (
188
- <ListPageBoardCard
189
- className={className}
190
- style={ruleBg ? { background: ruleBg } : undefined}
191
- isNew={row.isNew}
192
- onClick={() => onOpen(row.id)}
193
- >
194
- {children}
195
- </ListPageBoardCard>
196
- )
197
-
198
- if (visibleCols.length === 0) {
199
- return cardShell(
200
- "cursor-pointer",
201
- <ListPageBoardCardHeader className="gap-1 pb-2">
202
- <ListPageBoardCardTitleRow title={`Placement #${row.id}`} />
203
- <ListPageBoardCardSecondary>
204
- Unhide columns in Properties → Columns to show card fields.
205
- </ListPageBoardCardSecondary>
206
- </ListPageBoardCardHeader>,
207
- )
208
- }
209
-
210
- const titlePrimary = showStudent ? row.student : `Placement ${row.id}`
211
- const headerBadgeRow = showStatus || row.isNew
212
-
213
- return cardShell(
214
- "cursor-pointer",
215
- <ListPageBoardCardHeader>
216
- <ListPageBoardCardTitleRow
217
- title={titlePrimary}
218
- titleClassName={lc}
219
- trailing={
220
- showStudent ? (
221
- <AvatarInitials
222
- initials={row.initials}
223
- className="size-7 shrink-0 text-xs"
224
- fallbackClassName="text-xs"
225
- />
226
- ) : undefined
227
- }
228
- />
229
-
230
- {headerBadgeRow ? (
231
- <ListPageBoardCardBadgeRow>
232
- {showStatus ? <StatusBadge status={row.status} surface="board" /> : null}
233
- {row.isNew ? (
234
- <Badge variant="secondary" className="h-6 px-2 text-xs font-medium">
235
- New
236
- </Badge>
237
- ) : null}
238
- </ListPageBoardCardBadgeRow>
239
- ) : null}
240
-
241
- <ListPageBoardCardBody>
242
- {showSite && siteCol ? (
243
- <BoardCardIconRow iconClass="fa-hospital">
244
- <div className={cn(lc, "[&_.text-sm]:text-xs")}>{boardCellContent(row, siteCol)}</div>
245
- </BoardCardIconRow>
246
- ) : null}
247
-
248
- {renderScheduleSection(row, lifecycleTabId, hiddenColKeys, boardColumns)}
249
-
250
- {remainingCols.length > 0 ? (
251
- <div className="flex flex-col gap-2">
252
- {remainingCols.map(col => (
253
- <BoardCardIconRow key={col.key} iconClass={columnIconClass(col)}>
254
- <div className={cn(lc)}>{boardCellContent(row, col)}</div>
255
- </BoardCardIconRow>
256
- ))}
257
- </div>
258
- ) : null}
259
- </ListPageBoardCardBody>
260
- </ListPageBoardCardHeader>,
261
- )
262
- }