@exxatdesignux/ui 0.2.19 → 0.4.0

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 (716) hide show
  1. package/CHANGELOG.md +662 -7
  2. package/bin/sync-extras.mjs +116 -29
  3. package/consumer-extras/README.md +42 -7
  4. package/consumer-extras/cursor-rules/exxat-accessibility.mdc +39 -0
  5. package/consumer-extras/cursor-rules/exxat-board-cards.mdc +26 -0
  6. package/consumer-extras/cursor-rules/exxat-breadcrumbs-no-back.mdc +21 -0
  7. package/consumer-extras/cursor-rules/exxat-card-vs-list-rows.mdc +21 -0
  8. package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +44 -0
  9. package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +32 -0
  10. package/consumer-extras/cursor-rules/exxat-command-menu.mdc +22 -0
  11. package/consumer-extras/cursor-rules/exxat-dashboard-view-charts.mdc +53 -0
  12. package/consumer-extras/cursor-rules/exxat-data-tables.mdc +43 -0
  13. package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +25 -0
  14. package/consumer-extras/cursor-rules/exxat-drawer-vs-dialog.mdc +22 -0
  15. package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +56 -0
  16. package/consumer-extras/cursor-rules/exxat-fontawesome-icons.mdc +31 -0
  17. package/consumer-extras/cursor-rules/exxat-kbd-shortcuts.mdc +100 -0
  18. package/consumer-extras/cursor-rules/exxat-kpi-flat-band.mdc +28 -0
  19. package/consumer-extras/cursor-rules/exxat-kpi-max-four.mdc +21 -0
  20. package/consumer-extras/cursor-rules/exxat-kpi-trends.mdc +31 -0
  21. package/consumer-extras/cursor-rules/exxat-library-hub-header.mdc +28 -0
  22. package/consumer-extras/cursor-rules/exxat-list-page-connected-views.mdc +24 -0
  23. package/consumer-extras/cursor-rules/exxat-list-page-view-shells.mdc +31 -0
  24. package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +30 -0
  25. package/consumer-extras/cursor-rules/exxat-no-slds-leakage.mdc +78 -0
  26. package/consumer-extras/cursor-rules/exxat-no-toast.mdc +25 -0
  27. package/consumer-extras/cursor-rules/exxat-page-vs-drawer.mdc +23 -0
  28. package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +47 -0
  29. package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +52 -0
  30. package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +34 -0
  31. package/consumer-extras/cursor-rules/exxat-table-properties-drawer.mdc +77 -0
  32. package/consumer-extras/cursor-rules/exxat-token-discipline.mdc +103 -0
  33. package/consumer-extras/cursor-skills/exxat-accessibility/SKILL.md +1 -1
  34. package/consumer-extras/cursor-skills/exxat-board-cards/SKILL.md +3 -3
  35. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +5 -16
  36. package/consumer-extras/cursor-skills/exxat-collaboration-access/SKILL.md +3 -3
  37. package/consumer-extras/cursor-skills/exxat-dedicated-search-surfaces/SKILL.md +2 -2
  38. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +19 -34
  39. package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +1 -1
  40. package/consumer-extras/cursor-skills/exxat-kpi-flat-band/SKILL.md +1 -1
  41. package/consumer-extras/cursor-skills/exxat-list-page-view-shells/SKILL.md +1 -1
  42. package/consumer-extras/cursor-skills/exxat-mono-ids/SKILL.md +4 -4
  43. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +10 -12
  44. package/consumer-extras/cursor-skills/exxat-token-economy/SKILL.md +277 -0
  45. package/consumer-extras/handbook/HANDBOOK.md +187 -0
  46. package/consumer-extras/handbook/glossary.md +58 -0
  47. package/consumer-extras/handbook/reference-implementations.md +153 -0
  48. package/consumer-extras/handbook/voice-and-tone.md +262 -0
  49. package/consumer-extras/patterns/collaboration-access-pattern.md +7 -7
  50. package/consumer-extras/patterns/command-menu-pattern.md +1 -1
  51. package/consumer-extras/patterns/consumer-upgrade-checklist.md +0 -20
  52. package/consumer-extras/patterns/data-views-pattern.md +31 -66
  53. package/consumer-extras/patterns/kpi-flat-band-pattern.md +2 -2
  54. package/consumer-extras/patterns/shell-surface-elevation-pattern.md +3 -5
  55. package/dist/components/data-table/filter-date-calendar.d.ts +10 -0
  56. package/dist/components/data-table/filter-date-calendar.js +280 -0
  57. package/dist/components/data-table/filter-date-calendar.js.map +1 -0
  58. package/dist/components/data-table/filter-text-value-input.d.ts +15 -0
  59. package/dist/components/data-table/filter-text-value-input.js +561 -0
  60. package/dist/components/data-table/filter-text-value-input.js.map +1 -0
  61. package/dist/components/data-table/index.d.ts +45 -0
  62. package/dist/components/data-table/index.js +3085 -0
  63. package/dist/components/data-table/index.js.map +1 -0
  64. package/dist/components/data-table/pagination.d.ts +28 -0
  65. package/dist/components/data-table/pagination.js +3264 -0
  66. package/dist/components/data-table/pagination.js.map +1 -0
  67. package/dist/components/data-table/types.d.ts +84 -0
  68. package/dist/components/data-table/types.js +3 -0
  69. package/dist/components/data-table/types.js.map +1 -0
  70. package/dist/components/data-table/use-table-state.d.ts +116 -0
  71. package/dist/components/data-table/use-table-state.js +670 -0
  72. package/dist/components/data-table/use-table-state.js.map +1 -0
  73. package/dist/components/data-views/board-card-primitives.d.ts +22 -0
  74. package/dist/components/data-views/board-card-primitives.js +84 -0
  75. package/dist/components/data-views/board-card-primitives.js.map +1 -0
  76. package/dist/components/data-views/data-row-list.d.ts +33 -0
  77. package/dist/components/data-views/data-row-list.js +106 -0
  78. package/dist/components/data-views/data-row-list.js.map +1 -0
  79. package/dist/components/data-views/finder-panel-view.d.ts +54 -0
  80. package/dist/components/data-views/finder-panel-view.js +388 -0
  81. package/dist/components/data-views/finder-panel-view.js.map +1 -0
  82. package/dist/components/data-views/folder-grid-view.d.ts +22 -0
  83. package/dist/components/data-views/folder-grid-view.js +58 -0
  84. package/dist/components/data-views/folder-grid-view.js.map +1 -0
  85. package/dist/components/data-views/hub-table.d.ts +173 -0
  86. package/dist/components/data-views/hub-table.js +5783 -0
  87. package/dist/components/data-views/hub-table.js.map +1 -0
  88. package/dist/components/data-views/index.d.ts +27 -0
  89. package/dist/components/data-views/index.js +6797 -0
  90. package/dist/components/data-views/index.js.map +1 -0
  91. package/dist/components/data-views/list-page-board-card.d.ts +72 -0
  92. package/dist/components/data-views/list-page-board-card.js +264 -0
  93. package/dist/components/data-views/list-page-board-card.js.map +1 -0
  94. package/dist/components/data-views/list-page-board-template.d.ts +24 -0
  95. package/dist/components/data-views/list-page-board-template.js +137 -0
  96. package/dist/components/data-views/list-page-board-template.js.map +1 -0
  97. package/dist/components/data-views/list-page-connected-view-body.d.ts +19 -0
  98. package/dist/components/data-views/list-page-connected-view-body.js +116 -0
  99. package/dist/components/data-views/list-page-connected-view-body.js.map +1 -0
  100. package/dist/components/data-views/list-page-split-details-placeholder.d.ts +14 -0
  101. package/dist/components/data-views/list-page-split-details-placeholder.js +38 -0
  102. package/dist/components/data-views/list-page-split-details-placeholder.js.map +1 -0
  103. package/dist/components/data-views/list-page-split-hub-chrome.d.ts +17 -0
  104. package/dist/components/data-views/list-page-split-hub-chrome.js +54 -0
  105. package/dist/components/data-views/list-page-split-hub-chrome.js.map +1 -0
  106. package/dist/components/data-views/list-page-split-hub-tokens.d.ts +12 -0
  107. package/dist/components/data-views/list-page-split-hub-tokens.js +8 -0
  108. package/dist/components/data-views/list-page-split-hub-tokens.js.map +1 -0
  109. package/dist/components/data-views/list-page-tree-column-header.d.ts +15 -0
  110. package/dist/components/data-views/list-page-tree-column-header.js +22 -0
  111. package/dist/components/data-views/list-page-tree-column-header.js.map +1 -0
  112. package/dist/components/data-views/list-page-tree-panel-shell.d.ts +25 -0
  113. package/dist/components/data-views/list-page-tree-panel-shell.js +146 -0
  114. package/dist/components/data-views/list-page-tree-panel-shell.js.map +1 -0
  115. package/dist/components/data-views/os-folder-glyph.d.ts +35 -0
  116. package/dist/components/data-views/os-folder-glyph.js +104 -0
  117. package/dist/components/data-views/os-folder-glyph.js.map +1 -0
  118. package/dist/components/data-views/outline-tree-menu.d.ts +36 -0
  119. package/dist/components/data-views/outline-tree-menu.js +131 -0
  120. package/dist/components/data-views/outline-tree-menu.js.map +1 -0
  121. package/dist/components/table-properties/column-row.d.ts +22 -0
  122. package/dist/components/table-properties/column-row.js +153 -0
  123. package/dist/components/table-properties/column-row.js.map +1 -0
  124. package/dist/components/table-properties/draggable-list.d.ts +24 -0
  125. package/dist/components/table-properties/draggable-list.js +53 -0
  126. package/dist/components/table-properties/draggable-list.js.map +1 -0
  127. package/dist/components/table-properties/drawer-button.d.ts +110 -0
  128. package/dist/components/table-properties/drawer-button.js +2748 -0
  129. package/dist/components/table-properties/drawer-button.js.map +1 -0
  130. package/dist/components/table-properties/drawer.d.ts +100 -0
  131. package/dist/components/table-properties/drawer.js +2595 -0
  132. package/dist/components/table-properties/drawer.js.map +1 -0
  133. package/dist/components/table-properties/filter-card.d.ts +24 -0
  134. package/dist/components/table-properties/filter-card.js +854 -0
  135. package/dist/components/table-properties/filter-card.js.map +1 -0
  136. package/dist/components/table-properties/index.d.ts +14 -0
  137. package/dist/components/table-properties/index.js +2768 -0
  138. package/dist/components/table-properties/index.js.map +1 -0
  139. package/dist/components/table-properties/sort-card.d.ts +20 -0
  140. package/dist/components/table-properties/sort-card.js +102 -0
  141. package/dist/components/table-properties/sort-card.js.map +1 -0
  142. package/dist/components/templates/dedicated-search-landing-template.d.ts +21 -0
  143. package/dist/components/templates/dedicated-search-landing-template.js +254 -0
  144. package/dist/components/templates/dedicated-search-landing-template.js.map +1 -0
  145. package/dist/components/templates/dedicated-search-results-template.d.ts +15 -0
  146. package/dist/components/templates/dedicated-search-results-template.js +16 -0
  147. package/dist/components/templates/dedicated-search-results-template.js.map +1 -0
  148. package/dist/components/templates/index.d.ts +9 -0
  149. package/dist/components/templates/index.js +2720 -0
  150. package/dist/components/templates/index.js.map +1 -0
  151. package/dist/components/templates/list-page.d.ts +83 -0
  152. package/dist/components/templates/list-page.js +2433 -0
  153. package/dist/components/templates/list-page.js.map +1 -0
  154. package/dist/components/templates/nested-secondary-panel-shell.d.ts +20 -0
  155. package/dist/components/templates/nested-secondary-panel-shell.js +54 -0
  156. package/dist/components/templates/nested-secondary-panel-shell.js.map +1 -0
  157. package/dist/components/ui/accordion.d.ts +10 -0
  158. package/dist/components/ui/accordion.js +74 -0
  159. package/dist/components/ui/accordion.js.map +1 -0
  160. package/dist/components/ui/alert-dialog.d.ts +37 -0
  161. package/dist/components/ui/alert-dialog.js +201 -0
  162. package/dist/components/ui/alert-dialog.js.map +1 -0
  163. package/dist/components/ui/avatar.d.ts +84 -0
  164. package/dist/components/ui/avatar.js +328 -0
  165. package/dist/components/ui/avatar.js.map +1 -0
  166. package/dist/components/ui/badge.d.ts +13 -0
  167. package/dist/components/ui/badge.js +49 -0
  168. package/dist/components/ui/badge.js.map +1 -0
  169. package/dist/components/ui/banner.d.ts +62 -0
  170. package/dist/components/ui/banner.js +364 -0
  171. package/dist/components/ui/banner.js.map +1 -0
  172. package/dist/components/ui/breadcrumb.d.ts +14 -0
  173. package/dist/components/ui/breadcrumb.js +114 -0
  174. package/dist/components/ui/breadcrumb.js.map +1 -0
  175. package/dist/components/ui/button.d.ts +16 -0
  176. package/dist/components/ui/button.js +59 -0
  177. package/dist/components/ui/button.js.map +1 -0
  178. package/dist/components/ui/calendar.d.ts +13 -0
  179. package/dist/components/ui/calendar.js +238 -0
  180. package/dist/components/ui/calendar.js.map +1 -0
  181. package/dist/components/ui/card.d.ts +14 -0
  182. package/dist/components/ui/card.js +102 -0
  183. package/dist/components/ui/card.js.map +1 -0
  184. package/dist/components/ui/chart.d.ts +58 -0
  185. package/dist/components/ui/chart.js +292 -0
  186. package/dist/components/ui/chart.js.map +1 -0
  187. package/dist/components/ui/checkbox.d.ts +23 -0
  188. package/dist/components/ui/checkbox.js +155 -0
  189. package/dist/components/ui/checkbox.js.map +1 -0
  190. package/dist/components/ui/coach-mark.d.ts +27 -0
  191. package/dist/components/ui/coach-mark.js +306 -0
  192. package/dist/components/ui/coach-mark.js.map +1 -0
  193. package/dist/components/ui/collapsible.d.ts +8 -0
  194. package/dist/components/ui/collapsible.js +35 -0
  195. package/dist/components/ui/collapsible.js.map +1 -0
  196. package/dist/components/ui/command.d.ts +36 -0
  197. package/dist/components/ui/command.js +274 -0
  198. package/dist/components/ui/command.js.map +1 -0
  199. package/dist/components/ui/context-menu.d.ts +32 -0
  200. package/dist/components/ui/context-menu.js +245 -0
  201. package/dist/components/ui/context-menu.js.map +1 -0
  202. package/dist/components/ui/date-picker-field.d.ts +38 -0
  203. package/dist/components/ui/date-picker-field.js +550 -0
  204. package/dist/components/ui/date-picker-field.js.map +1 -0
  205. package/dist/components/ui/dialog.d.ts +22 -0
  206. package/dist/components/ui/dialog.js +200 -0
  207. package/dist/components/ui/dialog.js.map +1 -0
  208. package/dist/components/ui/dot-pattern.d.ts +21 -0
  209. package/dist/components/ui/dot-pattern.js +139 -0
  210. package/dist/components/ui/dot-pattern.js.map +1 -0
  211. package/dist/components/ui/drag-handle-grip.d.ts +10 -0
  212. package/dist/components/ui/drag-handle-grip.js +15 -0
  213. package/dist/components/ui/drag-handle-grip.js.map +1 -0
  214. package/dist/components/ui/drawer.d.ts +16 -0
  215. package/dist/components/ui/drawer.js +125 -0
  216. package/dist/components/ui/drawer.js.map +1 -0
  217. package/dist/components/ui/dropdown-menu.d.ts +45 -0
  218. package/dist/components/ui/dropdown-menu.js +353 -0
  219. package/dist/components/ui/dropdown-menu.js.map +1 -0
  220. package/dist/components/ui/export-drawer.d.ts +11 -0
  221. package/dist/components/ui/export-drawer.js +1658 -0
  222. package/dist/components/ui/export-drawer.js.map +1 -0
  223. package/dist/components/ui/field.d.ts +30 -0
  224. package/dist/components/ui/field.js +249 -0
  225. package/dist/components/ui/field.js.map +1 -0
  226. package/dist/components/ui/form.d.ts +28 -0
  227. package/dist/components/ui/form.js +110 -0
  228. package/dist/components/ui/form.js.map +1 -0
  229. package/dist/components/ui/hover-card.d.ts +9 -0
  230. package/dist/components/ui/hover-card.js +43 -0
  231. package/dist/components/ui/hover-card.js.map +1 -0
  232. package/dist/components/ui/input-group.d.ts +20 -0
  233. package/dist/components/ui/input-group.js +219 -0
  234. package/dist/components/ui/input-group.js.map +1 -0
  235. package/dist/components/ui/input-mask.d.ts +39 -0
  236. package/dist/components/ui/input-mask.js +118 -0
  237. package/dist/components/ui/input-mask.js.map +1 -0
  238. package/dist/components/ui/input.d.ts +5 -0
  239. package/dist/components/ui/input.js +30 -0
  240. package/dist/components/ui/input.js.map +1 -0
  241. package/dist/components/ui/kbd.d.ts +20 -0
  242. package/dist/components/ui/kbd.js +45 -0
  243. package/dist/components/ui/kbd.js.map +1 -0
  244. package/dist/components/ui/key-metrics-context.d.ts +19 -0
  245. package/dist/components/ui/key-metrics-context.js +26 -0
  246. package/dist/components/ui/key-metrics-context.js.map +1 -0
  247. package/dist/components/ui/key-metrics.d.ts +131 -0
  248. package/dist/components/ui/key-metrics.js +1015 -0
  249. package/dist/components/ui/key-metrics.js.map +1 -0
  250. package/dist/components/ui/label.d.ts +6 -0
  251. package/dist/components/ui/label.js +28 -0
  252. package/dist/components/ui/label.js.map +1 -0
  253. package/dist/components/ui/list-page-view-frame.d.ts +22 -0
  254. package/dist/components/ui/list-page-view-frame.js +24 -0
  255. package/dist/components/ui/list-page-view-frame.js.map +1 -0
  256. package/dist/components/ui/page-header.d.ts +51 -0
  257. package/dist/components/ui/page-header.js +372 -0
  258. package/dist/components/ui/page-header.js.map +1 -0
  259. package/dist/components/ui/payment-card-fields.d.ts +10 -0
  260. package/dist/components/ui/payment-card-fields.js +80 -0
  261. package/dist/components/ui/payment-card-fields.js.map +1 -0
  262. package/dist/components/ui/popover.d.ts +10 -0
  263. package/dist/components/ui/popover.js +47 -0
  264. package/dist/components/ui/popover.js.map +1 -0
  265. package/dist/components/ui/radio-group.d.ts +29 -0
  266. package/dist/components/ui/radio-group.js +190 -0
  267. package/dist/components/ui/radio-group.js.map +1 -0
  268. package/dist/components/ui/resizable.d.ts +16 -0
  269. package/dist/components/ui/resizable.js +51 -0
  270. package/dist/components/ui/resizable.js.map +1 -0
  271. package/dist/components/ui/scroll-area.d.ts +8 -0
  272. package/dist/components/ui/scroll-area.js +66 -0
  273. package/dist/components/ui/scroll-area.js.map +1 -0
  274. package/dist/components/ui/select.d.ts +18 -0
  275. package/dist/components/ui/select.js +186 -0
  276. package/dist/components/ui/select.js.map +1 -0
  277. package/dist/components/ui/selection-tile-grid.d.ts +52 -0
  278. package/dist/components/ui/selection-tile-grid.js +347 -0
  279. package/dist/components/ui/selection-tile-grid.js.map +1 -0
  280. package/dist/components/ui/separator.d.ts +7 -0
  281. package/dist/components/ui/separator.js +33 -0
  282. package/dist/components/ui/separator.js.map +1 -0
  283. package/dist/components/ui/sheet.d.ts +18 -0
  284. package/dist/components/ui/sheet.js +181 -0
  285. package/dist/components/ui/sheet.js.map +1 -0
  286. package/dist/components/ui/sidebar.d.ts +94 -0
  287. package/dist/components/ui/sidebar.js +805 -0
  288. package/dist/components/ui/sidebar.js.map +1 -0
  289. package/dist/components/ui/skeleton.d.ts +5 -0
  290. package/dist/components/ui/skeleton.js +22 -0
  291. package/dist/components/ui/skeleton.js.map +1 -0
  292. package/dist/components/ui/slider.d.ts +7 -0
  293. package/dist/components/ui/slider.js +66 -0
  294. package/dist/components/ui/slider.js.map +1 -0
  295. package/dist/components/ui/sonner.d.ts +6 -0
  296. package/dist/components/ui/sonner.js +38 -0
  297. package/dist/components/ui/sonner.js.map +1 -0
  298. package/dist/components/ui/status-badge.d.ts +38 -0
  299. package/dist/components/ui/status-badge.js +77 -0
  300. package/dist/components/ui/status-badge.js.map +1 -0
  301. package/dist/components/ui/table.d.ts +13 -0
  302. package/dist/components/ui/table.js +115 -0
  303. package/dist/components/ui/table.js.map +1 -0
  304. package/dist/components/ui/tabs.d.ts +15 -0
  305. package/dist/components/ui/tabs.js +93 -0
  306. package/dist/components/ui/tabs.js.map +1 -0
  307. package/dist/components/ui/textarea.d.ts +6 -0
  308. package/dist/components/ui/textarea.js +25 -0
  309. package/dist/components/ui/textarea.js.map +1 -0
  310. package/dist/components/ui/tip.d.ts +12 -0
  311. package/dist/components/ui/tip.js +61 -0
  312. package/dist/components/ui/tip.js.map +1 -0
  313. package/dist/components/ui/toggle-group.d.ts +14 -0
  314. package/dist/components/ui/toggle-group.js +104 -0
  315. package/dist/components/ui/toggle-group.js.map +1 -0
  316. package/dist/components/ui/toggle-switch.d.ts +10 -0
  317. package/dist/components/ui/toggle-switch.js +33 -0
  318. package/dist/components/ui/toggle-switch.js.map +1 -0
  319. package/dist/components/ui/toggle.d.ts +13 -0
  320. package/dist/components/ui/toggle.js +51 -0
  321. package/dist/components/ui/toggle.js.map +1 -0
  322. package/dist/components/ui/tooltip.d.ts +10 -0
  323. package/dist/components/ui/tooltip.js +68 -0
  324. package/dist/components/ui/tooltip.js.map +1 -0
  325. package/dist/components/ui/view-segmented-control.d.ts +31 -0
  326. package/dist/components/ui/view-segmented-control.js +167 -0
  327. package/dist/components/ui/view-segmented-control.js.map +1 -0
  328. package/dist/data-list-view-registry-CyBoBML4.d.ts +73 -0
  329. package/dist/hooks/use-app-theme.d.ts +24 -0
  330. package/dist/hooks/use-app-theme.js +286 -0
  331. package/dist/hooks/use-app-theme.js.map +1 -0
  332. package/dist/hooks/use-coach-mark.d.ts +86 -0
  333. package/dist/hooks/use-coach-mark.js +218 -0
  334. package/dist/hooks/use-coach-mark.js.map +1 -0
  335. package/dist/hooks/use-mobile.d.ts +3 -0
  336. package/dist/hooks/use-mobile.js +29 -0
  337. package/dist/hooks/use-mobile.js.map +1 -0
  338. package/dist/hooks/use-mod-key-label.d.ts +6 -0
  339. package/dist/hooks/use-mod-key-label.js +25 -0
  340. package/dist/hooks/use-mod-key-label.js.map +1 -0
  341. package/dist/index.d.ts +120 -0
  342. package/dist/index.js +13421 -0
  343. package/dist/index.js.map +1 -0
  344. package/dist/lib/compose-refs.d.ts +6 -0
  345. package/dist/lib/compose-refs.js +17 -0
  346. package/dist/lib/compose-refs.js.map +1 -0
  347. package/dist/lib/conditional-rule-match.d.ts +30 -0
  348. package/dist/lib/conditional-rule-match.js +66 -0
  349. package/dist/lib/conditional-rule-match.js.map +1 -0
  350. package/dist/lib/data-list-display-options.d.ts +26 -0
  351. package/dist/lib/data-list-display-options.js +14 -0
  352. package/dist/lib/data-list-display-options.js.map +1 -0
  353. package/dist/lib/data-list-view-registry.d.ts +2 -0
  354. package/dist/lib/data-list-view-registry.js +102 -0
  355. package/dist/lib/data-list-view-registry.js.map +1 -0
  356. package/dist/lib/data-list-view-surface.d.ts +2 -0
  357. package/dist/lib/data-list-view-surface.js +80 -0
  358. package/dist/lib/data-list-view-surface.js.map +1 -0
  359. package/dist/lib/data-list-view.d.ts +21 -0
  360. package/dist/lib/data-list-view.js +25 -0
  361. package/dist/lib/data-list-view.js.map +1 -0
  362. package/dist/lib/date-filter.d.ts +22 -0
  363. package/dist/lib/date-filter.js +61 -0
  364. package/dist/lib/date-filter.js.map +1 -0
  365. package/dist/lib/dev-log.d.ts +8 -0
  366. package/dist/lib/dev-log.js +10 -0
  367. package/dist/lib/dev-log.js.map +1 -0
  368. package/dist/lib/dropdown-menu-surface.d.ts +14 -0
  369. package/dist/lib/dropdown-menu-surface.js +6 -0
  370. package/dist/lib/dropdown-menu-surface.js.map +1 -0
  371. package/dist/lib/editable-target.d.ts +12 -0
  372. package/dist/lib/editable-target.js +12 -0
  373. package/dist/lib/editable-target.js.map +1 -0
  374. package/dist/lib/list-page-table-properties.d.ts +35 -0
  375. package/dist/lib/list-page-table-properties.js +81 -0
  376. package/dist/lib/list-page-table-properties.js.map +1 -0
  377. package/dist/lib/raf-throttle.d.ts +23 -0
  378. package/dist/lib/raf-throttle.js +27 -0
  379. package/dist/lib/raf-throttle.js.map +1 -0
  380. package/dist/lib/row-height.d.ts +16 -0
  381. package/dist/lib/row-height.js +10 -0
  382. package/dist/lib/row-height.js.map +1 -0
  383. package/dist/lib/table-properties-types.d.ts +83 -0
  384. package/dist/lib/table-properties-types.js +19 -0
  385. package/dist/lib/table-properties-types.js.map +1 -0
  386. package/dist/lib/utils.d.ts +5 -0
  387. package/dist/lib/utils.js +11 -0
  388. package/dist/lib/utils.js.map +1 -0
  389. package/package.json +83 -19
  390. package/src/components/data-table/filter-date-calendar.tsx +38 -0
  391. package/src/components/data-table/filter-text-value-input.tsx +77 -0
  392. package/src/components/data-table/index.tsx +1678 -0
  393. package/src/components/data-table/pagination.tsx +259 -0
  394. package/src/components/data-table/types.ts +96 -0
  395. package/src/components/data-table/use-table-state.ts +767 -0
  396. package/src/components/data-views/board-card-primitives.tsx +93 -0
  397. package/src/components/data-views/data-row-list.tsx +183 -0
  398. package/src/components/data-views/finder-panel-view.tsx +405 -0
  399. package/src/components/data-views/folder-grid-view.tsx +86 -0
  400. package/src/components/data-views/hub-table.tsx +606 -0
  401. package/src/components/data-views/index.ts +28 -0
  402. package/src/components/data-views/list-page-board-card.tsx +192 -0
  403. package/src/components/data-views/list-page-board-template.tsx +122 -0
  404. package/src/components/data-views/list-page-connected-view-body.tsx +66 -0
  405. package/src/components/data-views/list-page-split-details-placeholder.tsx +39 -0
  406. package/src/components/data-views/list-page-split-hub-chrome.tsx +60 -0
  407. package/src/components/data-views/list-page-split-hub-tokens.ts +16 -0
  408. package/src/components/data-views/list-page-tree-column-header.tsx +31 -0
  409. package/src/components/data-views/list-page-tree-panel-shell.tsx +91 -0
  410. package/src/components/data-views/os-folder-glyph.tsx +141 -0
  411. package/src/components/data-views/outline-tree-menu.tsx +157 -0
  412. package/src/components/table-properties/column-row.tsx +90 -0
  413. package/src/components/table-properties/draggable-list.ts +54 -0
  414. package/src/components/table-properties/drawer-button.tsx +300 -0
  415. package/src/components/table-properties/drawer.tsx +1148 -0
  416. package/src/components/table-properties/filter-card.tsx +251 -0
  417. package/src/components/table-properties/index.ts +36 -0
  418. package/src/components/table-properties/sort-card.tsx +63 -0
  419. package/src/components/templates/dedicated-search-landing-template.tsx +124 -0
  420. package/src/components/templates/dedicated-search-results-template.tsx +19 -0
  421. package/src/components/templates/index.ts +33 -0
  422. package/src/components/templates/list-page.tsx +602 -0
  423. package/src/components/templates/nested-secondary-panel-shell.tsx +70 -0
  424. package/src/components/ui/accordion.tsx +92 -0
  425. package/src/components/ui/alert-dialog.tsx +221 -0
  426. package/src/components/ui/avatar.tsx +13 -2
  427. package/src/components/ui/banner.tsx +2 -2
  428. package/src/components/ui/button.tsx +4 -4
  429. package/src/components/ui/calendar.tsx +1 -1
  430. package/src/components/ui/coach-mark.tsx +1 -1
  431. package/src/components/ui/context-menu.tsx +291 -0
  432. package/src/components/ui/date-picker-field.tsx +2 -2
  433. package/src/components/ui/dot-pattern.tsx +183 -0
  434. package/src/components/ui/export-drawer.tsx +375 -0
  435. package/src/components/ui/hover-card.tsx +66 -0
  436. package/src/components/ui/key-metrics-context.tsx +78 -0
  437. package/src/components/ui/key-metrics.tsx +1133 -0
  438. package/src/components/ui/list-page-view-frame.tsx +64 -0
  439. package/src/components/ui/page-header.tsx +244 -0
  440. package/src/components/ui/payment-card-fields.tsx +2 -2
  441. package/src/components/ui/resizable.tsx +68 -0
  442. package/src/components/ui/scroll-area.tsx +72 -0
  443. package/src/components/ui/selection-tile-grid.tsx +9 -2
  444. package/src/components/ui/sidebar.tsx +84 -12
  445. package/src/components/ui/slider.tsx +83 -0
  446. package/src/globals.css +2201 -7
  447. package/src/globals.d.ts +20 -0
  448. package/src/index.ts +68 -1
  449. package/src/lib/conditional-rule-match.ts +119 -0
  450. package/src/lib/data-list-display-options.ts +35 -0
  451. package/src/lib/data-list-view-registry.ts +104 -0
  452. package/src/lib/data-list-view-surface.ts +83 -0
  453. package/src/lib/data-list-view.ts +47 -0
  454. package/src/lib/dev-log.ts +10 -0
  455. package/src/lib/editable-target.ts +20 -0
  456. package/src/lib/list-page-table-properties.ts +48 -0
  457. package/src/lib/raf-throttle.ts +45 -0
  458. package/src/lib/row-height.ts +19 -0
  459. package/src/lib/table-properties-types.ts +98 -0
  460. package/template/.claude/skills/exxat-ds-skill/SKILL.md +8 -7
  461. package/template/.cursor/rules/exxat-accessibility.mdc +1 -1
  462. package/template/.cursor/rules/exxat-command-menu.mdc +2 -2
  463. package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +7 -7
  464. package/template/.cursor/rules/exxat-data-tables.mdc +3 -3
  465. package/template/.cursor/rules/exxat-ds-agents.mdc +2 -2
  466. package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +7 -7
  467. package/template/.cursor/rules/exxat-mono-ids.mdc +1 -1
  468. package/template/.cursor/rules/exxat-page-vs-drawer.mdc +1 -1
  469. package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
  470. package/template/AGENTS.md +135 -103
  471. package/template/app/(app)/columns/page.tsx +11 -0
  472. package/template/app/(app)/dashboard/loading.tsx +15 -3
  473. package/template/app/(app)/dashboard/page.tsx +14 -2
  474. package/template/app/(app)/layout.tsx +17 -4
  475. package/template/app/(app)/library/all/page.tsx +11 -0
  476. package/template/app/(app)/library/find/page.tsx +12 -0
  477. package/template/app/(app)/{question-bank → library}/layout.tsx +17 -17
  478. package/template/app/(app)/library/list/page.tsx +12 -0
  479. package/template/app/(app)/library/new/page.tsx +45 -0
  480. package/template/app/(app)/library/page.tsx +11 -0
  481. package/template/app/(app)/loading.tsx +18 -1
  482. package/template/app/(app)/settings/page.tsx +5 -4
  483. package/template/app/(app)/tokens-themes/page.tsx +11 -0
  484. package/template/app/globals.css +14 -16
  485. package/template/components/ask-leo-composer.tsx +2 -2
  486. package/template/components/ask-leo-sidebar.tsx +5 -1
  487. package/template/components/brand-color-picker.tsx +2 -2
  488. package/template/components/charts-overview.tsx +1 -1
  489. package/template/components/columns-client.tsx +158 -0
  490. package/template/components/columns-showcase.tsx +541 -0
  491. package/template/components/dashboard-report-charts.tsx +1 -1
  492. package/template/components/dashboard-tabs.tsx +1 -1
  493. package/template/components/data-table/filter-date-calendar.tsx +1 -38
  494. package/template/components/data-table/filter-text-value-input.tsx +1 -77
  495. package/template/components/data-table/index.tsx +1 -1634
  496. package/template/components/data-table/pagination.tsx +1 -255
  497. package/template/components/data-table/types.ts +1 -94
  498. package/template/components/data-table/use-table-state.test.ts +420 -0
  499. package/template/components/data-table/use-table-state.ts +1 -758
  500. package/template/components/data-views/board-card-primitives.tsx +1 -93
  501. package/template/components/data-views/data-row-list.tsx +1 -183
  502. package/template/components/data-views/finder-panel-view.tsx +1 -405
  503. package/template/components/data-views/folder-grid-view.tsx +1 -86
  504. package/template/components/data-views/hub-table.tsx +1 -0
  505. package/template/components/data-views/index.ts +77 -38
  506. package/template/components/data-views/{question-bank-folder-tree-branch.tsx → library-folder-tree-branch.tsx} +19 -19
  507. package/template/components/data-views/list-page-board-card.tsx +1 -192
  508. package/template/components/data-views/list-page-board-template.tsx +1 -122
  509. package/template/components/data-views/list-page-connected-view-body.tsx +1 -66
  510. package/template/components/data-views/list-page-split-details-placeholder.tsx +1 -39
  511. package/template/components/data-views/list-page-split-hub-chrome.tsx +1 -68
  512. package/template/components/data-views/list-page-split-hub-tokens.ts +1 -16
  513. package/template/components/data-views/list-page-tree-column-header.tsx +1 -31
  514. package/template/components/data-views/list-page-tree-panel-shell.tsx +1 -91
  515. package/template/components/data-views/list-page-view-frame.tsx +5 -53
  516. package/template/components/data-views/os-folder-glyph.tsx +1 -129
  517. package/template/components/data-views/outline-tree-menu.tsx +1 -157
  518. package/template/components/data-views/table-cells.tsx +673 -0
  519. package/template/components/export-drawer.test.tsx +71 -0
  520. package/template/components/export-drawer.tsx +1 -375
  521. package/template/components/exxat-product-logo.tsx +5 -5
  522. package/template/components/folder-details-shell.tsx +11 -11
  523. package/template/components/hub-tree-panel-view.tsx +26 -26
  524. package/template/components/invite-collaborators-drawer.tsx +3 -3
  525. package/template/components/key-metrics-ask-leo-bridge.tsx +40 -0
  526. package/template/components/key-metrics.tsx +1 -1063
  527. package/template/components/leo-insight-indicator.tsx +2 -2
  528. package/template/components/{question-bank-board-view.tsx → library-board-view.tsx} +44 -44
  529. package/template/components/{question-bank-client.tsx → library-client.tsx} +83 -83
  530. package/template/components/{question-bank-dashboard-charts.tsx → library-dashboard-charts.tsx} +14 -14
  531. package/template/components/{question-bank-favorite-button.tsx → library-favorite-button.tsx} +7 -7
  532. package/template/components/{question-bank-hub-client.tsx → library-hub-client.tsx} +44 -44
  533. package/template/components/{question-bank-new-folder-sheet.tsx → library-new-folder-sheet.tsx} +16 -16
  534. package/template/components/{question-bank-os-folder-view.tsx → library-os-folder-view.tsx} +31 -31
  535. package/template/components/{question-bank-page-header.tsx → library-page-header.tsx} +6 -6
  536. package/template/components/library-panel-activator.tsx +8 -0
  537. package/template/components/{question-bank-secondary-nav.tsx → library-secondary-nav.tsx} +63 -63
  538. package/template/components/library-table.tsx +839 -0
  539. package/template/components/list-hub-status-badge.tsx +2 -2
  540. package/template/components/{new-question-composer.tsx → new-library-item-form.tsx} +489 -441
  541. package/template/components/onboarding/index.ts +9 -0
  542. package/template/components/onboarding/onboarding-01.tsx +1 -1
  543. package/template/components/onboarding/onboarding-02.tsx +1 -1
  544. package/template/components/onboarding/onboarding-03.tsx +1 -1
  545. package/template/components/onboarding/onboarding-04.tsx +1 -1
  546. package/template/components/page-header.tsx +8 -226
  547. package/template/components/product-switcher.tsx +3 -4
  548. package/template/components/product-wordmark.tsx +2 -1
  549. package/template/components/settings-appearance-card.tsx +3 -4
  550. package/template/components/settings-client.tsx +15 -59
  551. package/template/components/settings-form-row.tsx +4 -9
  552. package/template/components/{app-sidebar-dynamic.tsx → sidebar/app-sidebar-dynamic.tsx} +1 -1
  553. package/template/components/{app-sidebar.tsx → sidebar/app-sidebar.tsx} +114 -73
  554. package/template/components/sidebar/index.ts +16 -0
  555. package/template/components/{secondary-nav.tsx → sidebar/secondary-nav.tsx} +2 -2
  556. package/template/components/sidebar/secondary-panel.tsx +316 -0
  557. package/template/components/sidebar/sidebar-auto-collapse.tsx +27 -0
  558. package/template/components/{sidebar-auto-open.tsx → sidebar/sidebar-auto-open.tsx} +2 -1
  559. package/template/components/{sidebar-shell.tsx → sidebar/sidebar-shell.tsx} +1 -1
  560. package/template/components/site-header.tsx +1 -1
  561. package/template/components/table-properties/column-row.tsx +1 -90
  562. package/template/components/table-properties/draggable-list.ts +1 -49
  563. package/template/components/table-properties/drawer-button.tsx +1 -262
  564. package/template/components/table-properties/drawer.tsx +1 -1166
  565. package/template/components/table-properties/filter-card.tsx +1 -251
  566. package/template/components/table-properties/sort-card.tsx +1 -59
  567. package/template/components/table-properties/types.ts +28 -71
  568. package/template/components/templates/dedicated-search-landing-template.tsx +1 -124
  569. package/template/components/templates/dedicated-search-results-template.tsx +1 -19
  570. package/template/components/templates/discovery-hub-template.tsx +1 -1
  571. package/template/components/templates/list-page.tsx +1 -608
  572. package/template/components/templates/nested-secondary-panel-shell.tsx +1 -63
  573. package/template/components/templates/new-focus-template.tsx +659 -0
  574. package/template/components/templates/secondary-panel-hub-template.tsx +2 -2
  575. package/template/components/tokens-secondary-nav.tsx +192 -0
  576. package/template/components/tokens-themes-client.tsx +476 -0
  577. package/template/components/tokens-themes-section.tsx +386 -0
  578. package/template/components/ui/accordion.tsx +1 -0
  579. package/template/components/ui/alert-dialog.tsx +1 -0
  580. package/template/components/ui/context-menu.tsx +1 -0
  581. package/template/components/ui/dot-pattern.tsx +1 -183
  582. package/template/components/ui/hover-card.tsx +1 -0
  583. package/template/components/ui/resizable.tsx +1 -68
  584. package/template/components/ui/scroll-area.tsx +1 -0
  585. package/template/components/ui/slider.tsx +1 -0
  586. package/template/docs/HANDBOOK.md +187 -0
  587. package/template/docs/blueprints/README.md +86 -0
  588. package/template/docs/blueprints/_template.md +91 -0
  589. package/template/docs/blueprints/board-card.md +123 -0
  590. package/template/docs/blueprints/data-table.md +139 -0
  591. package/template/docs/blueprints/key-metrics.md +128 -0
  592. package/template/docs/blueprints/list-page-template.md +123 -0
  593. package/template/docs/blueprints/page-header.md +130 -0
  594. package/template/docs/collaboration-access-pattern.md +7 -7
  595. package/template/docs/command-menu-pattern.md +1 -1
  596. package/template/docs/component-selection-guide.md +224 -0
  597. package/template/docs/components-audit-2026-05.md +158 -0
  598. package/template/docs/data-views-pattern.md +31 -66
  599. package/template/docs/drawer-vs-dialog-pattern.md +1 -3
  600. package/template/docs/glossary.md +58 -0
  601. package/template/docs/kpi-flat-band-pattern.md +3 -3
  602. package/template/docs/kpi-trend-pattern.md +18 -3
  603. package/template/docs/large-dataset-strategy.md +155 -0
  604. package/template/docs/library-hub-header-pattern.md +25 -0
  605. package/template/docs/migrations/0001-brand-deep-alias-stabilization.md +95 -0
  606. package/template/docs/migrations/0002-exxat-token-namespace.md +154 -0
  607. package/template/docs/migrations/0003-globals-css-canonical.md +110 -0
  608. package/template/docs/migrations/README.md +100 -0
  609. package/template/docs/migrations/_template.md +64 -0
  610. package/template/docs/reference-implementations.md +151 -0
  611. package/template/docs/shell-surface-elevation-pattern.md +3 -5
  612. package/template/docs/token-taxonomy.md +416 -0
  613. package/template/docs/voice-and-tone.md +262 -0
  614. package/template/eslint.config.mjs +27 -0
  615. package/template/hooks/use-secondary-panel-hub-nav.ts +11 -11
  616. package/template/lib/ask-leo-route-context.ts +6 -18
  617. package/template/lib/coach-mark-registry.ts +0 -16
  618. package/template/lib/command-menu-config.ts +5 -13
  619. package/template/lib/command-menu-search-data.ts +8 -23
  620. package/template/lib/conditional-rule-match.ts +6 -97
  621. package/template/lib/data-list-display-options.ts +1 -49
  622. package/template/lib/data-list-view-registry.ts +1 -104
  623. package/template/lib/data-list-view-surface.ts +1 -83
  624. package/template/lib/data-list-view.ts +1 -47
  625. package/template/lib/data-view-dashboard-storage.ts +35 -38
  626. package/template/lib/dev-log.ts +1 -8
  627. package/template/lib/editable-target.ts +1 -10
  628. package/template/lib/{question-bank-authoring.ts → library-authoring.ts} +89 -88
  629. package/template/lib/library-dedicated-search.ts +19 -0
  630. package/template/lib/library-hub-search.ts +90 -0
  631. package/template/lib/library-nav.ts +477 -0
  632. package/template/lib/library-recent-searches.ts +22 -0
  633. package/template/lib/{question-bank-supported-views.ts → library-supported-views.ts} +2 -3
  634. package/template/lib/list-page-table-properties.ts +1 -48
  635. package/template/lib/list-status-badges.ts +16 -11
  636. package/template/lib/mock/dashboard.ts +1 -1
  637. package/template/lib/mock/{question-bank-folders.ts → library-folders.ts} +30 -30
  638. package/template/lib/mock/library-header-collaborators.ts +54 -0
  639. package/template/lib/mock/{question-bank-inspector.ts → library-inspector.ts} +29 -29
  640. package/template/lib/mock/{question-bank-kpi.ts → library-kpi.ts} +20 -20
  641. package/template/lib/mock/library.ts +249 -0
  642. package/template/lib/mock/navigation.tsx +32 -35
  643. package/template/lib/raf-throttle.ts +1 -45
  644. package/template/lib/row-height.ts +4 -10
  645. package/template/lib/sidebar-state-cookie.ts +11 -2
  646. package/template/lib/table-state-lifecycle.ts +3 -3
  647. package/template/next.config.mjs +7 -4
  648. package/template/package.json +1 -0
  649. package/template/tests/setup.ts +25 -0
  650. package/consumer-extras/AGENTS.md +0 -76
  651. package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +0 -37
  652. package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +0 -57
  653. package/consumer-extras/patterns/consumer-app-pattern.md +0 -39
  654. package/consumer-extras/patterns/focused-workflow-page-pattern.md +0 -84
  655. package/src/components/ui/button-group.tsx +0 -81
  656. package/src/theme.css +0 -16
  657. package/src/tokens/README.md +0 -15
  658. package/src/tokens/base.css +0 -337
  659. package/src/tokens/high-contrast.css +0 -1195
  660. package/src/tokens/layers.css +0 -224
  661. package/src/tokens/tailwind-bridge.css +0 -118
  662. package/src/tokens/themes.css +0 -201
  663. package/template/app/(app)/data-list/layout.tsx +0 -43
  664. package/template/app/(app)/data-list/page.tsx +0 -10
  665. package/template/app/(app)/examples/focused-workflow/page.tsx +0 -5
  666. package/template/app/(app)/examples/page.tsx +0 -43
  667. package/template/app/(app)/question-bank/find/page.tsx +0 -13
  668. package/template/app/(app)/question-bank/library/page.tsx +0 -12
  669. package/template/app/(app)/question-bank/list/page.tsx +0 -13
  670. package/template/app/(app)/question-bank/new/page.tsx +0 -50
  671. package/template/app/(app)/question-bank/page.tsx +0 -12
  672. package/template/components/app-route-loading.tsx +0 -14
  673. package/template/components/dashboard-onboarding-gallery.tsx +0 -13
  674. package/template/components/dashboard-onboarding.tsx +0 -21
  675. package/template/components/data-views/list-page-calendar-view.tsx +0 -593
  676. package/template/components/data-views/list-page-folder-columns-panel.tsx +0 -345
  677. package/template/components/examples/focused-workflow-showcase.tsx +0 -183
  678. package/template/components/list-hub-board-view.tsx +0 -68
  679. package/template/components/list-hub-client.tsx +0 -186
  680. package/template/components/list-hub-list-view.tsx +0 -36
  681. package/template/components/list-hub-panel-activator.tsx +0 -8
  682. package/template/components/list-hub-secondary-nav.tsx +0 -121
  683. package/template/components/list-hub-table.tsx +0 -336
  684. package/template/components/question-bank-folder-columns-panel.tsx +0 -104
  685. package/template/components/question-bank-list-view.tsx +0 -53
  686. package/template/components/question-bank-panel-activator.tsx +0 -8
  687. package/template/components/question-bank-table.tsx +0 -729
  688. package/template/components/secondary-panel/nav-link-rows.tsx +0 -83
  689. package/template/components/secondary-panel.tsx +0 -220
  690. package/template/components/secondary-panels/list-hub-panel.tsx +0 -39
  691. package/template/components/secondary-panels/question-bank-panel.tsx +0 -39
  692. package/template/components/secondary-panels/registry.tsx +0 -15
  693. package/template/components/section-cards.tsx +0 -106
  694. package/template/components/sidebar-auto-collapse.tsx +0 -23
  695. package/template/components/templates/focused-workflow-layouts.tsx +0 -448
  696. package/template/components/templates/focused-workflow-page-template.tsx +0 -69
  697. package/template/components/templates/page-loading-shell.tsx +0 -262
  698. package/template/components/ui/button-group.tsx +0 -1
  699. package/template/docs/consumer-app-pattern.md +0 -39
  700. package/template/docs/focused-workflow-page-pattern.md +0 -84
  701. package/template/docs/question-bank-hub-header-pattern.md +0 -25
  702. package/template/lib/list-hub-nav.ts +0 -121
  703. package/template/lib/mock/list-hub-directory.ts +0 -27
  704. package/template/lib/mock/list-hub-kpi.ts +0 -27
  705. package/template/lib/mock/question-bank-header-collaborators.ts +0 -54
  706. package/template/lib/mock/question-bank.ts +0 -249
  707. package/template/lib/page-loading-variant.ts +0 -40
  708. package/template/lib/question-bank-dedicated-search.ts +0 -19
  709. package/template/lib/question-bank-hub-search.ts +0 -90
  710. package/template/lib/question-bank-nav.ts +0 -477
  711. package/template/lib/question-bank-recent-searches.ts +0 -22
  712. /package/template/components/{getting-started.tsx → onboarding/getting-started.tsx} +0 -0
  713. /package/template/components/{nav-documents.tsx → sidebar/nav-documents.tsx} +0 -0
  714. /package/template/components/{nav-main.tsx → sidebar/nav-main.tsx} +0 -0
  715. /package/template/components/{nav-secondary.tsx → sidebar/nav-secondary.tsx} +0 -0
  716. /package/template/components/{nav-user.tsx → sidebar/nav-user.tsx} +0 -0
@@ -0,0 +1,1133 @@
1
+ "use client"
2
+
3
+ /**
4
+ * KeyMetrics — WCAG 2.1 AA reusable KPI panel
5
+ *
6
+ * Variants:
7
+ * "card" (default) — shadcn Card wrapper with brand gradient fill
8
+ * "flat" — full-width soft tint band (brand-tint → background) + bottom glow, no card chrome
9
+ *
10
+ * AA checklist:
11
+ * ✓ Trend text never relies on colour alone — icon + label (WCAG 1.4.1)
12
+ * ✓ `trend` matches signed change; `trendPolarity` flips sentiment when “up” is bad (see `docs/kpi-trend-pattern.md`)
13
+ * ✓ Trend icons have aria-hidden; chip `aria-label` uses `metricTrendAriaQualifier` (1.1.1)
14
+ * ✓ Select has accessible label via aria-label (4.1.2)
15
+ * ✓ Insight action button has descriptive text (4.1.2)
16
+ * ✓ Decorative dividers are aria-hidden (1.1.1)
17
+ * ✓ Contrast: value text foreground ≥ 17:1, trend colours ≥ 4.5:1 (1.4.3)
18
+ */
19
+
20
+ import * as React from "react"
21
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./card"
22
+ import {
23
+ Select,
24
+ SelectContent,
25
+ SelectItem,
26
+ SelectTrigger,
27
+ SelectValue,
28
+ } from "./select"
29
+ import { Separator } from "./separator"
30
+ import { Button } from "./button"
31
+ import {
32
+ Tooltip,
33
+ TooltipContent,
34
+ TooltipTrigger,
35
+ } from "./tooltip"
36
+ import { cn } from "../../lib/utils"
37
+ import { useKeyMetricsContext } from "./key-metrics-context"
38
+
39
+ export {
40
+ KeyMetricsProvider,
41
+ useKeyMetricsContext,
42
+ type KeyMetricsContextValue,
43
+ } from "./key-metrics-context"
44
+
45
+ /**
46
+ * Tooltip around the default insight CTA. Renders the shortcut hint
47
+ * injected by `KeyMetricsProvider` (typically `⌘⌥K` for Ask Leo) when
48
+ * the CTA still uses the default action label — i.e. the consumer did
49
+ * NOT override `actionLabel` on the individual `MetricInsight`. Any
50
+ * custom `actionLabel` ("Open ticket", "Acknowledge", …) suppresses
51
+ * the shortcut hint since it no longer maps to the default chord.
52
+ */
53
+ function InsightDefaultTooltip({
54
+ actionLabel,
55
+ children,
56
+ }: {
57
+ actionLabel?: string
58
+ children: React.ReactNode
59
+ }) {
60
+ const { shortcutHint, defaultActionLabel = "Ask Leo" } = useKeyMetricsContext()
61
+ const label = actionLabel ?? defaultActionLabel
62
+ const showShortcut =
63
+ !!shortcutHint && (!actionLabel || actionLabel === defaultActionLabel)
64
+ if (!showShortcut) {
65
+ return (
66
+ <Tooltip>
67
+ <TooltipTrigger asChild>{children}</TooltipTrigger>
68
+ <TooltipContent side="top">{label}</TooltipContent>
69
+ </Tooltip>
70
+ )
71
+ }
72
+ return (
73
+ <Tooltip>
74
+ <TooltipTrigger asChild>{children}</TooltipTrigger>
75
+ <TooltipContent side="top" className="flex flex-wrap items-center gap-1.5">
76
+ <span>{label}</span>
77
+ {shortcutHint}
78
+ </TooltipContent>
79
+ </Tooltip>
80
+ )
81
+ }
82
+
83
+ /* ── Types ────────────────────────────────────────────────────────────────── */
84
+
85
+ /**
86
+ * Whether an **up** arrow should read as “good news” for tinting and assistive text.
87
+ * - **`higher_is_better`** (default) — revenue, pass rate, approved count: up = favorable.
88
+ * - **`lower_is_better`** — defects, overdue, **low PBI / quality flags**: more flags + up arrow = unfavorable.
89
+ * - **`informational`** — volume or mix only; keep arrows **muted** (direction without value judgment).
90
+ */
91
+ export type MetricTrendPolarity = "higher_is_better" | "lower_is_better" | "informational"
92
+
93
+ export type MetricTrendTone = "positive" | "negative" | "muted"
94
+
95
+ /** Maps `trend` + polarity to semantic tone for colours (arrow direction still follows `trend`). */
96
+ export function metricTrendTone(
97
+ trend: "up" | "down" | "neutral",
98
+ polarity: MetricTrendPolarity = "higher_is_better",
99
+ ): MetricTrendTone {
100
+ if (trend === "neutral") return "muted"
101
+ if (polarity === "informational") return "muted"
102
+ if (polarity === "higher_is_better") {
103
+ return trend === "up" ? "positive" : "negative"
104
+ }
105
+ return trend === "up" ? "negative" : "positive"
106
+ }
107
+
108
+ /** Short clause for `aria-label` on the trend chip (paired with the delta string). */
109
+ export function metricTrendAriaQualifier(
110
+ trend: "up" | "down" | "neutral",
111
+ polarity: MetricTrendPolarity = "higher_is_better",
112
+ ): string {
113
+ if (trend === "neutral") return "no net change"
114
+ if (polarity === "informational") {
115
+ return trend === "up" ? "increased" : "decreased"
116
+ }
117
+ if (polarity === "higher_is_better") {
118
+ return trend === "up" ? "increased, favorable" : "decreased, unfavorable"
119
+ }
120
+ return trend === "up" ? "increased, unfavorable" : "decreased, favorable"
121
+ }
122
+
123
+ export interface MetricItem {
124
+ /** Unique identifier for React keying */
125
+ id: string
126
+ /** Short label shown above the value */
127
+ label: string
128
+ /** Displayed value — e.g. "23", "98%", "1,250" */
129
+ value: string | number
130
+ /**
131
+ * Change **count** for the trend chip — e.g. `"+5"`, `"-3"`, `"+12%"`.
132
+ *
133
+ * Pass an **empty string** (or `0`) when there is no comparison delta to show.
134
+ * In that case the **trend chip is hidden entirely** (the previous `—` placeholder
135
+ * is dropped) — see `MetricCell`. Put contextual prose like
136
+ * `"left + right"` / `"vs last week"` in `description`, **never** here.
137
+ */
138
+ delta: string | number
139
+ /**
140
+ * Visual trend direction (arrow follows the signed change in the underlying metric).
141
+ * `"neutral"` paired with an empty `delta` suppresses the chip — use `description`
142
+ * for any caption you want to show below the value instead.
143
+ */
144
+ trend: "up" | "down" | "neutral"
145
+ /**
146
+ * Optional short caption rendered **below** the value + trend row (muted, small).
147
+ * Use for **what** the number means or **how** it breaks down
148
+ * (e.g. `"left + right"`, `"vs last week"`, `"across 4 sites"`) — NOT for delta counts.
149
+ */
150
+ description?: string
151
+ /**
152
+ * How to **tint** the trend chip. Omit = **`higher_is_better`** (legacy behaviour).
153
+ * Arrows always match `trend`; sentiment colours flip for **`lower_is_better`**.
154
+ */
155
+ trendPolarity?: MetricTrendPolarity
156
+ /** Makes the cell a link */
157
+ href?: string
158
+ /** Makes the cell a button */
159
+ onClick?: () => void
160
+ /**
161
+ * "hero" — primary KPI (e.g. total count): larger value, same structure as siblings.
162
+ * "default" — standard KPI strip cell.
163
+ */
164
+ metricVariant?: "default" | "hero"
165
+ }
166
+
167
+ export interface MetricInsight {
168
+ /** Optional single line for custom copy; rail prefers `title` + `description` when both are set */
169
+ statement?: string
170
+ /** Card headline */
171
+ title: string
172
+ /** Supporting body copy */
173
+ description?: string
174
+ /** Optional deep-link for the ↗ button */
175
+ href?: string
176
+ /** CTA label — defaults to "Ask Leo" */
177
+ actionLabel?: string
178
+ /** Font Awesome class for the CTA icon — defaults to fa-wand-magic-sparkles */
179
+ actionIcon?: string
180
+ /** Callback for the CTA button */
181
+ onAction?: () => void
182
+ /** Severity determines the badge colour (default: warning) */
183
+ severity?: "warning" | "info" | "error"
184
+ }
185
+
186
+ export interface PeriodOption {
187
+ value: string
188
+ label: string
189
+ }
190
+
191
+ export interface KeyMetricsProps {
192
+ /**
193
+ * "card" — shadcn Card with brand gradient (default)
194
+ * "flat" — full-width gradient band, no card chrome
195
+ */
196
+ variant?: "card" | "flat" | "compact"
197
+ /** Panel title */
198
+ title?: string
199
+ /** Subtitle / description below title */
200
+ description?: string
201
+ /** Array of KPI items — by default split into rows of 3 */
202
+ metrics: MetricItem[]
203
+ /** When true, all metrics share one horizontal row (md+ and compact mobile grid) */
204
+ metricsSingleRow?: boolean
205
+ /**
206
+ * When true with `metricsSingleRow`, use a 2-column KPI grid so half-width dashboard cards
207
+ * fit 1–4 KPIs without horizontal overflow (pair rows on md+; 2-col grid on small screens).
208
+ * The insight rail (if any) stacks below the KPI grid instead of sitting beside it on md+.
209
+ */
210
+ metricsHalfWidthLayout?: boolean
211
+ /** Optional insight card — see `insightFullWidth` */
212
+ insight?: MetricInsight
213
+ /**
214
+ * When true, the insight sits on its own full-width row under the metrics (not a narrow side rail).
215
+ */
216
+ insightFullWidth?: boolean
217
+ /** Comparison-period options for the Select */
218
+ periods?: PeriodOption[]
219
+ /** Initially-selected period value */
220
+ defaultPeriod?: string
221
+ /** Called with the new period value when the Select changes */
222
+ onPeriodChange?: (period: string) => void
223
+ /** When false, hides the title/description/period-selector header row (default: true) */
224
+ showHeader?: boolean
225
+ /**
226
+ * Tighter insight card: one short title + line of body, no vertical filler;
227
+ * aligns visually with a single-row KPI band.
228
+ */
229
+ insightCompact?: boolean
230
+ className?: string
231
+ }
232
+
233
+ /**
234
+ * KPI grid column step patterns — Tailwind v4 container-query classes.
235
+ *
236
+ * We deliberately AVOID `repeat(auto-fit, minmax(...))` here because it
237
+ * produces awkward "N + leftover" layouts at intermediate widths (e.g. 3
238
+ * tiles in row 1 + 1 lonely tile in row 2 for a 4-KPI strip). Instead we
239
+ * step the column count through values that evenly divide the row size:
240
+ * 1 → 2 → 4 for a 4-KPI strip (3 is skipped on purpose).
241
+ *
242
+ * The breakpoints are container-query based (`@[Xrem]:…`) so they react to
243
+ * the metrics strip's OWN width, not the viewport — that's what makes the
244
+ * 2×2 fallback kick in when the primary sidebar + secondary panel are
245
+ * both open and the strip column is ~360 px wide, even on a 1280 px display.
246
+ *
247
+ * `metricsHalfWidthLayout` = strip shares its row with the insight rail
248
+ * (3fr / 2fr split). Tighter breakpoints because available width is ~60%
249
+ * of the section.
250
+ */
251
+ /**
252
+ * Flat KPI hairlines — cell borders only (no grid gap fill / no surface).
253
+ * Four tiles: default 4-across verticals; 2×2 hairlines only when @container is narrow.
254
+ */
255
+ function flatMetricsHairlineClass(
256
+ itemCount: number,
257
+ metricsHalfWidthLayout: boolean,
258
+ ): string {
259
+ if (itemCount <= 1) return "gap-0"
260
+
261
+ const childBorder = "[&>*]:border-[color:var(--key-metrics-flat-divider)]"
262
+
263
+ if (itemCount === 2) {
264
+ return cn("gap-0", childBorder, "[&>*:first-child]:border-r")
265
+ }
266
+
267
+ if (itemCount === 4) {
268
+ const narrow2x2 = metricsHalfWidthLayout
269
+ ? "@[max-width:25.99rem]"
270
+ : "@[max-width:29.99rem]"
271
+ return cn(
272
+ "gap-0",
273
+ childBorder,
274
+ /* Wide strip (matches `@[30rem]:grid-cols-4`) — verticals between all tiles, no horizontal */
275
+ "[&>*:not(:last-child)]:border-r",
276
+ /* Narrow strip (`@[18rem]`–`@[30rem]` 2×2) */
277
+ `${narrow2x2}:[&>*:not(:last-child)]:border-e-0`,
278
+ `${narrow2x2}:[&>*:nth-child(odd)]:border-r`,
279
+ `${narrow2x2}:[&>*:not(:nth-last-child(-n+2))]:border-b`,
280
+ )
281
+ }
282
+
283
+ return cn("gap-0", childBorder, "[&>*:not(:last-child)]:border-r")
284
+ }
285
+
286
+ function metricsRowColumnsClass(rowLength: number, metricsHalfWidthLayout: boolean): string {
287
+ const half = metricsHalfWidthLayout
288
+ switch (rowLength) {
289
+ case 1:
290
+ return "grid-cols-1"
291
+ case 2:
292
+ return half
293
+ ? "grid-cols-1 @[14rem]:grid-cols-2"
294
+ : "grid-cols-1 @[18rem]:grid-cols-2"
295
+ case 3:
296
+ // 3 tiles divide evenly already — step 1 → 3.
297
+ return half
298
+ ? "grid-cols-1 @[18rem]:grid-cols-3"
299
+ : "grid-cols-1 @[24rem]:grid-cols-3"
300
+ case 4:
301
+ // Step 1 → 2 (2×2 grid) → 4. Skip 3 — that's the awkward 3+1 layout.
302
+ // Aggressive 4-col thresholds so the strip fits all four tiles even
303
+ // when the primary sidebar + secondary panel + insight rail are all
304
+ // expanded (typical library layout puts the KPI grid at ~27rem).
305
+ return half
306
+ ? "grid-cols-1 @[14rem]:grid-cols-2 @[26rem]:grid-cols-4"
307
+ : "grid-cols-1 @[18rem]:grid-cols-2 @[30rem]:grid-cols-4"
308
+ default:
309
+ // 5+ KPIs (`exxat-kpi-max-four` caps the strip at 4, but key-metrics
310
+ // is a generic primitive — fall back to a sensible step). 1 → 2 → 3 → 6.
311
+ return half
312
+ ? "grid-cols-1 @[14rem]:grid-cols-2 @[26rem]:grid-cols-3 @[40rem]:grid-cols-6"
313
+ : "grid-cols-1 @[18rem]:grid-cols-2 @[30rem]:grid-cols-3 @[56rem]:grid-cols-6"
314
+ }
315
+ }
316
+
317
+ /* ── Default data ─────────────────────────────────────────────────────────── */
318
+
319
+ const DEFAULT_PERIODS: PeriodOption[] = [
320
+ { value: "week", label: "vs last week" },
321
+ { value: "month", label: "vs last month" },
322
+ { value: "quarter", label: "vs last quarter" },
323
+ { value: "year", label: "vs last year" },
324
+ ]
325
+
326
+ /* ── Sub-components ───────────────────────────────────────────────────────── */
327
+
328
+ /** Single KPI cell inside the metrics grid */
329
+ const MetricCell = React.memo(function MetricCell({
330
+ label,
331
+ value,
332
+ delta,
333
+ trend,
334
+ trendPolarity = "higher_is_better",
335
+ description,
336
+ href,
337
+ onClick,
338
+ metricVariant = "default",
339
+ dense = false,
340
+ edgeGutter = true,
341
+ }: Omit<MetricItem, "id"> & { dense?: boolean; edgeGutter?: boolean }) {
342
+ const isUp = trend === "up"
343
+ const isDown = trend === "down"
344
+ const tone = metricTrendTone(trend, trendPolarity)
345
+ const isInteractive = !!(href || onClick)
346
+ const isHero = metricVariant === "hero"
347
+
348
+ // Hide the trend chip entirely when there's no direction *and* no count to
349
+ // surface. This avoids the noisy `—` placeholder for purely informational
350
+ // metrics — see `docs/kpi-trend-pattern.md`.
351
+ const deltaText = typeof delta === "number"
352
+ ? (delta === 0 ? "" : String(delta))
353
+ : String(delta ?? "").trim()
354
+ const showTrendChip = isUp || isDown || deltaText.length > 0
355
+
356
+ const inner = (
357
+ <>
358
+ {/* Label row — min-height = 2 lines so values align when some titles wrap */}
359
+ <div
360
+ className={cn(
361
+ "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-x-2 gap-y-0.5",
362
+ dense ? "min-h-[2.125rem]" : "min-h-[2.625rem]",
363
+ )}
364
+ >
365
+ <p
366
+ className={cn(
367
+ "min-w-0 text-muted-foreground leading-snug wrap-break-word",
368
+ dense ? "text-xs" : "text-sm",
369
+ isHero && "font-medium",
370
+ )}
371
+ >
372
+ {label}
373
+ </p>
374
+ {isInteractive ? (
375
+ <span className="mt-0.5 inline-flex shrink-0" aria-hidden="true">
376
+ <i className="fa-light fa-arrow-right text-xs text-foreground/70 transition-colors duration-150 group-hover:text-interactive-hover-foreground sm:group-hover:translate-x-0.5" />
377
+ </span>
378
+ ) : null}
379
+ </div>
380
+
381
+ {/* Value + trend badge */}
382
+ <div className="flex items-baseline gap-2 flex-wrap">
383
+ <span
384
+ className={cn(
385
+ "font-bold tabular-nums leading-none text-foreground",
386
+ dense
387
+ ? isHero
388
+ ? "text-lg sm:text-xl"
389
+ : "text-base sm:text-lg"
390
+ : isHero
391
+ ? "text-2xl sm:text-[1.625rem]"
392
+ : "text-xl sm:text-2xl",
393
+ )}
394
+ >
395
+ {value}
396
+ </span>
397
+
398
+ {/* Trend chip — icon + count, never colour-only (WCAG 1.4.1).
399
+ Suppressed when both `trend === "neutral"` and `delta` is empty
400
+ (see `showTrendChip` above) so informational KPIs render cleanly. */}
401
+ {showTrendChip && (
402
+ <span
403
+ className={cn(
404
+ "inline-flex items-center gap-1 font-medium leading-none",
405
+ dense ? "text-xs sm:text-xs" : "text-xs sm:text-sm",
406
+ tone === "positive" && "text-chart-2",
407
+ tone === "negative" && "text-destructive",
408
+ tone === "muted" && "text-muted-foreground",
409
+ )}
410
+ aria-label={`${metricTrendAriaQualifier(trend, trendPolarity)} ${deltaText}`.trim()}
411
+ >
412
+ {isUp && <i className="fa-light fa-arrow-trend-up text-[0.8rem]" aria-hidden="true" />}
413
+ {isDown && <i className="fa-light fa-arrow-trend-down text-[0.8rem]" aria-hidden="true" />}
414
+ {!isUp && !isDown && deltaText.length > 0 && (
415
+ <i className="fa-light fa-minus text-[0.8rem]" aria-hidden="true" />
416
+ )}
417
+ {deltaText.length > 0 && <span>{deltaText}</span>}
418
+ </span>
419
+ )}
420
+ </div>
421
+
422
+ {/* Caption — below the value row. Use for what the number means or how
423
+ it breaks down (e.g. "left + right", "vs last week"). Never the delta. */}
424
+ {description ? (
425
+ <p
426
+ className={cn(
427
+ "min-w-0 text-muted-foreground leading-snug wrap-break-word",
428
+ dense ? "text-[11px]" : "text-xs",
429
+ )}
430
+ >
431
+ {description}
432
+ </p>
433
+ ) : null}
434
+ </>
435
+ )
436
+
437
+ const sharedClass = cn(
438
+ "group flex min-w-0 flex-col gap-2 text-start outline-none",
439
+ edgeGutter && "first:ps-0 last:pe-0",
440
+ dense ? "gap-1.5 px-2 py-2 sm:px-3 sm:py-3" : "gap-2 px-3 py-3 sm:px-5 sm:py-4",
441
+ isHero && "gap-2.5",
442
+ isInteractive && [
443
+ "cursor-pointer transition-colors duration-150",
444
+ "hover:bg-foreground/5",
445
+ "focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
446
+ ]
447
+ )
448
+
449
+ if (href) {
450
+ return (
451
+ <a href={href} className={sharedClass} aria-label={`${label}: ${value}`}>
452
+ {inner}
453
+ </a>
454
+ )
455
+ }
456
+
457
+ if (onClick) {
458
+ return (
459
+ <button type="button" onClick={onClick} className={sharedClass} aria-label={`${label}: ${value}`}>
460
+ {inner}
461
+ </button>
462
+ )
463
+ }
464
+
465
+ return <div className={sharedClass}>{inner}</div>
466
+ })
467
+
468
+ /** Body line for rail: `description`, else optional `statement` */
469
+ function insightRailBody(insight: MetricInsight): string {
470
+ const d = insight.description?.trim()
471
+ if (d) return d
472
+ return insight.statement?.trim() ?? ""
473
+ }
474
+
475
+ /**
476
+ * Rail insight: severity badge + title + description + optional ↗, Ask Leo (no rule between copy and action).
477
+ */
478
+ function InsightRailStatementAction({
479
+ insight,
480
+ compact,
481
+ }: {
482
+ insight: MetricInsight
483
+ compact: boolean
484
+ }) {
485
+ const badgeSize = compact ? "sm" : "default"
486
+ const surface = compact
487
+ ? "border border-border/50 bg-gradient-to-b from-muted/35 to-card"
488
+ : "bg-card"
489
+ const body = insightRailBody(insight)
490
+
491
+ return (
492
+ <Card
493
+ role="region"
494
+ aria-label="Insight"
495
+ className={cn(
496
+ "flex h-full min-h-0 flex-col overflow-hidden rounded-lg border-0 p-0 shadow-none ring-1 ring-foreground/8",
497
+ surface
498
+ )}
499
+ >
500
+ {/* flex-1 + mt-auto on the CTA: copy stays top-aligned when the rail stretches to KPI height */}
501
+ <div className="flex min-h-0 flex-1 flex-col px-3 py-3 sm:px-4 sm:py-4">
502
+ <div className="flex items-start gap-2.5">
503
+ <InsightBadge severity={insight.severity} size={badgeSize} />
504
+ <div className="min-w-0 flex-1">
505
+ <p className="text-sm font-semibold leading-snug text-foreground">{insight.title}</p>
506
+ {body ? (
507
+ <p className="mt-1 text-sm leading-snug text-muted-foreground">{body}</p>
508
+ ) : null}
509
+ </div>
510
+ {insight.href && (
511
+ <a
512
+ href={insight.href}
513
+ className="mt-0.5 shrink-0 text-muted-foreground transition-colors hover:text-interactive-hover-foreground focus-visible:rounded-sm focus-visible:outline-2 focus-visible:outline-ring"
514
+ aria-label={`Open ${insight.title} — details`}
515
+ >
516
+ <i className="fa-light fa-arrow-up-right text-xs" aria-hidden="true" />
517
+ </a>
518
+ )}
519
+ </div>
520
+
521
+ <div className="mt-auto flex shrink-0 justify-end pt-3">
522
+ <InsightDefaultTooltip actionLabel={insight.actionLabel}>
523
+ <Button
524
+ variant={compact ? "outline" : "ghost"}
525
+ size="sm"
526
+ className={cn(
527
+ "h-8 w-full gap-1.5 text-xs sm:w-auto",
528
+ compact
529
+ ? "border-border/60 bg-background px-3 text-foreground hover:bg-background"
530
+ : "px-3 text-muted-foreground hover:text-interactive-hover-foreground"
531
+ )}
532
+ onClick={insight.onAction}
533
+ aria-label={insight.actionLabel ?? "Ask Leo"}
534
+ >
535
+ <i
536
+ className={
537
+ insight.actionIcon
538
+ ? `fa-light ${insight.actionIcon} text-xs`
539
+ : "fa-duotone fa-solid fa-star-christmas text-xs text-brand"
540
+ }
541
+ aria-hidden="true"
542
+ />
543
+ {insight.actionLabel ?? "Ask Leo"}
544
+ </Button>
545
+ </InsightDefaultTooltip>
546
+ </div>
547
+ </div>
548
+ </Card>
549
+ )
550
+ }
551
+
552
+ /** Severity icon badge for the insight card */
553
+
554
+ function InsightBadge({
555
+ severity = "warning",
556
+ size = "default",
557
+ }: {
558
+ severity?: MetricInsight["severity"]
559
+ size?: "default" | "sm"
560
+ }) {
561
+ const styles = {
562
+ warning: {
563
+ bg: "bg-[var(--insight-severity-warning-bg)]",
564
+ icon: "fa-circle-exclamation",
565
+ color: "text-[var(--insight-severity-warning-fg)]",
566
+ },
567
+ info: {
568
+ bg: "bg-[var(--insight-severity-info-bg)]",
569
+ icon: "fa-circle-info",
570
+ color: "text-[var(--insight-severity-info-fg)]",
571
+ },
572
+ error: { bg: "bg-destructive/15", icon: "fa-circle-xmark", color: "text-destructive" },
573
+ }[severity]
574
+
575
+ return (
576
+ <span
577
+ className={cn(
578
+ "inline-flex shrink-0 items-center justify-center rounded-full",
579
+ size === "sm" ? "h-6 w-6 text-xs" : "h-7 w-7 text-sm",
580
+ styles.bg,
581
+ styles.color
582
+ )}
583
+ aria-hidden="true"
584
+ >
585
+ <i className={`fa-light ${styles.icon}`} />
586
+ </span>
587
+ )
588
+ }
589
+
590
+ /* ── Shared inner content ─────────────────────────────────────────────────── */
591
+
592
+ interface InnerProps {
593
+ title: string
594
+ description: string
595
+ period: string
596
+ periods: PeriodOption[]
597
+ metrics: MetricItem[]
598
+ rows: MetricItem[][]
599
+ insight?: MetricInsight
600
+ onPeriodChange: (v: string) => void
601
+ /** Extra padding class injected by flat variant */
602
+ innerPadding?: string
603
+ /** When false, the header (title/description/period select) is hidden */
604
+ showHeader?: boolean
605
+ insightCompact?: boolean
606
+ insightFullWidth?: boolean
607
+ metricsSingleRow?: boolean
608
+ /** Tighter KPI cells + 2-col mobile grid (half-width dashboard card). */
609
+ metricsHalfWidthLayout?: boolean
610
+ /** Opaque fill behind each KPI cell when using hairline grid gaps (below `lg`). */
611
+ metricsCellSurfaceClassName?: string
612
+ /** Flat list-page band: softer dividers + tinted cells on a lavender-tinted surface */
613
+ surfaceVariant?: "default" | "flat"
614
+ }
615
+
616
+ function KeyMetricsInner({
617
+ title,
618
+ description,
619
+ period,
620
+ periods,
621
+ metrics,
622
+ rows,
623
+ insight,
624
+ onPeriodChange,
625
+ innerPadding = "",
626
+ showHeader = true,
627
+ insightCompact = false,
628
+ insightFullWidth = false,
629
+ metricsSingleRow = false,
630
+ metricsHalfWidthLayout = false,
631
+ metricsCellSurfaceClassName = "bg-background",
632
+ surfaceVariant = "default",
633
+ }: InnerProps) {
634
+ const isFlatBand = surfaceVariant === "flat"
635
+ const metricsGridClassName = isFlatBand
636
+ ? flatMetricsHairlineClass(metrics.length, metricsHalfWidthLayout)
637
+ : "gap-px bg-border"
638
+ /** Side-by-side KPI + insight rail (md+). Disabled for half-width dashboard cards — insight stacks below. */
639
+ const insightSideBySide = insight && !insightFullWidth && !metricsHalfWidthLayout
640
+ const stackedRailInsight = insight && !insightFullWidth && metricsHalfWidthLayout
641
+
642
+ return (
643
+ <div data-slot="key-metrics" className="contents">
644
+ {/* ── Header ──────────────────────────────────────────────────── */}
645
+ {showHeader && (
646
+ <div className={cn(
647
+ "flex flex-col gap-2 pb-3",
648
+ "sm:flex-row sm:items-center sm:justify-between sm:gap-4",
649
+ innerPadding
650
+ )}>
651
+ <div>
652
+ <p className="text-base font-semibold text-foreground leading-tight">{title}</p>
653
+ <p className="mt-0.5 text-sm text-muted-foreground">{description}</p>
654
+ </div>
655
+
656
+ {/* Period selector — align="end" keeps dropdown flush-right */}
657
+ <Select value={period} onValueChange={onPeriodChange}>
658
+ <SelectTrigger
659
+ className="h-8 w-full sm:w-auto sm:min-w-[9rem] shrink-0 text-sm"
660
+ aria-label="Select comparison period"
661
+ >
662
+ <SelectValue />
663
+ </SelectTrigger>
664
+ <SelectContent align="end" sideOffset={4}>
665
+ {periods.map((p) => (
666
+ <SelectItem key={p.value} value={p.value}>
667
+ {p.label}
668
+ </SelectItem>
669
+ ))}
670
+ </SelectContent>
671
+ </Select>
672
+ </div>
673
+ )}
674
+
675
+ {/* ── Body: metrics grid + optional insight ───────────────────── */}
676
+ <div
677
+ className={cn(
678
+ "flex flex-col gap-0",
679
+ /* 60% KPIs / 40% insight (3fr:2fr); lg+ only so phones/tablets stack KPIs + insight */
680
+ insightSideBySide &&
681
+ "lg:grid lg:grid-cols-[minmax(0,3fr)_minmax(13rem,2fr)] lg:items-stretch lg:gap-x-6 lg:gap-y-0",
682
+ innerPadding
683
+ )}
684
+ >
685
+
686
+ {/* Metrics section — self-start so KPI cells don’t stretch when the insight column is taller */}
687
+ <div
688
+ className={cn(
689
+ "min-w-0 lg:flex lg:min-h-0 lg:flex-col",
690
+ !insightSideBySide && "w-full",
691
+ insightSideBySide && "lg:self-start"
692
+ )}
693
+ >
694
+ {/*
695
+ Phone (<md): one column. Tablet (md–lg): 2-column grid (e.g. 2×2 for four KPIs).
696
+ Hairline separators use gap-px + opaque cell surfaces (divide-* breaks for 2-col order).
697
+ Half-width dashboard cards keep divide-x + optional template columns.
698
+ */}
699
+ {metricsHalfWidthLayout ? (
700
+ <div
701
+ className={cn(
702
+ "@container/metrics-strip grid lg:hidden",
703
+ metricsSingleRow
704
+ ? metricsRowColumnsClass(metrics.length, /* half */ true)
705
+ : "grid-cols-2",
706
+ metricsGridClassName,
707
+ )}
708
+ >
709
+ {metrics.map((m) => (
710
+ <div key={m.id} className={cn("min-w-0", metricsCellSurfaceClassName)}>
711
+ <MetricCell {...m} dense edgeGutter={false} />
712
+ </div>
713
+ ))}
714
+ </div>
715
+ ) : (
716
+ <div
717
+ className={cn(
718
+ "@container/metrics-strip grid lg:hidden",
719
+ metricsRowColumnsClass(metrics.length, /* half */ false),
720
+ metricsGridClassName,
721
+ )}
722
+ >
723
+ {metrics.map((m) => (
724
+ <div key={m.id} className={cn("min-w-0", metricsCellSurfaceClassName)}>
725
+ <MetricCell {...m} dense={false} edgeGutter={false} />
726
+ </div>
727
+ ))}
728
+ </div>
729
+ )}
730
+
731
+ {/*
732
+ lg+: row-by-row container-queried grid. Uses a `gap-px + bg` hairline
733
+ instead of `divide-x` so dividers render correctly when the row wraps
734
+ from 4-across to a 2×2 grid (the awkward 3+1 layout is skipped — see
735
+ `metricsRowColumnsClass`).
736
+ */}
737
+ <div className="@container/metrics-strip hidden lg:block">
738
+ {rows.map((row, rowIdx) => (
739
+ <React.Fragment key={rowIdx}>
740
+ {rowIdx > 0 && !isFlatBand && (
741
+ <Separator aria-hidden="true" className="my-1" />
742
+ )}
743
+ <div
744
+ className={cn(
745
+ "grid",
746
+ metricsRowColumnsClass(row.length, metricsHalfWidthLayout),
747
+ isFlatBand
748
+ ? flatMetricsHairlineClass(row.length, metricsHalfWidthLayout)
749
+ : metricsGridClassName,
750
+ )}
751
+ >
752
+ {row.map((m) => (
753
+ <div key={m.id} className={cn("min-w-0", metricsCellSurfaceClassName)}>
754
+ <MetricCell {...m} dense={metricsHalfWidthLayout} edgeGutter={false} />
755
+ </div>
756
+ ))}
757
+ </div>
758
+ </React.Fragment>
759
+ ))}
760
+ </div>
761
+ </div>
762
+
763
+ {/* Insight card — only rendered when data provided */}
764
+ {insight && (
765
+ <>
766
+ {insightFullWidth ? (
767
+ <Separator
768
+ aria-hidden="true"
769
+ className={cn("my-4 w-full", isFlatBand && "bg-foreground/[0.06]")}
770
+ />
771
+ ) : stackedRailInsight ? (
772
+ <Separator
773
+ aria-hidden="true"
774
+ className={cn("my-4 w-full", isFlatBand && "bg-foreground/[0.06]")}
775
+ />
776
+ ) : (
777
+ <Separator
778
+ aria-hidden="true"
779
+ className={cn("my-3 lg:hidden", isFlatBand && "bg-foreground/[0.055]")}
780
+ />
781
+ )}
782
+
783
+ <div
784
+ className={cn(
785
+ "flex min-h-0 min-w-0 w-full flex-col",
786
+ /* Divider + padding replace vertical Separator so grid stays 2 columns */
787
+ insightSideBySide &&
788
+ !insightFullWidth &&
789
+ cn(
790
+ "lg:h-full lg:ps-6",
791
+ /* Flat band: insight card ring is the divider — skip `border-l` (double line). */
792
+ !isFlatBand && "lg:border-s lg:border-border",
793
+ )
794
+ )}
795
+ >
796
+ {insight && !insightFullWidth ? (
797
+ <InsightRailStatementAction insight={insight} compact={insightCompact} />
798
+ ) : (
799
+ <Card
800
+ role="region"
801
+ aria-label="Insight"
802
+ className={cn(
803
+ "overflow-hidden rounded-lg p-0 ring-1 ring-foreground/8 shadow-none",
804
+ "flex min-h-0 flex-col bg-muted/25"
805
+ )}
806
+ >
807
+ {insightCompact ? (
808
+ <div className="flex min-h-0 flex-1 flex-col gap-4 p-4 md:flex-row md:items-center md:justify-between md:gap-8 md:p-5">
809
+ <div className="flex min-w-0 flex-1 flex-col gap-2">
810
+ <div className="flex items-start gap-2.5">
811
+ <InsightBadge severity={insight.severity} size="sm" />
812
+ <div className="flex min-w-0 flex-1 items-start justify-between gap-2">
813
+ <p className="text-base font-semibold leading-tight text-foreground">
814
+ {insight.title}
815
+ </p>
816
+ {insight.href && (
817
+ <a
818
+ href={insight.href}
819
+ className="shrink-0 text-muted-foreground transition-colors hover:text-interactive-hover-foreground focus-visible:rounded-sm focus-visible:outline-2 focus-visible:outline-ring"
820
+ aria-label={`Open ${insight.title} — details`}
821
+ >
822
+ <i className="fa-light fa-arrow-up-right text-xs" aria-hidden="true" />
823
+ </a>
824
+ )}
825
+ </div>
826
+ </div>
827
+ {insight.description ? (
828
+ <p className="text-sm leading-relaxed text-muted-foreground">
829
+ {insight.description}
830
+ </p>
831
+ ) : null}
832
+ </div>
833
+ <div className="flex w-full shrink-0 md:w-auto">
834
+ <InsightDefaultTooltip actionLabel={insight.actionLabel}>
835
+ <Button
836
+ variant="ghost"
837
+ size="sm"
838
+ className="h-9 w-full gap-1.5 px-4 text-xs text-muted-foreground hover:text-interactive-hover-foreground md:min-w-[8.5rem]"
839
+ onClick={insight.onAction}
840
+ aria-label={insight.actionLabel ?? "Ask Leo"}
841
+ >
842
+ <i
843
+ className={insight.actionIcon ? `fa-light ${insight.actionIcon} text-xs` : "fa-duotone fa-solid fa-star-christmas text-xs text-brand"}
844
+ aria-hidden="true"
845
+ />
846
+ {insight.actionLabel ?? "Ask Leo"}
847
+ </Button>
848
+ </InsightDefaultTooltip>
849
+ </div>
850
+ </div>
851
+ ) : (
852
+ <div className="flex min-h-0 flex-1 flex-col gap-4 p-4 md:flex-row md:items-center md:justify-between md:gap-8 md:p-5">
853
+ <div className="flex min-w-0 flex-1 flex-col gap-3">
854
+ <div className="flex items-start gap-3">
855
+ <InsightBadge severity={insight.severity} />
856
+ <div className="flex min-w-0 flex-1 items-start justify-between gap-2">
857
+ <p className="text-base font-semibold leading-snug text-foreground">
858
+ {insight.title}
859
+ </p>
860
+ {insight.href && (
861
+ <a
862
+ href={insight.href}
863
+ className="shrink-0 text-muted-foreground transition-colors hover:text-interactive-hover-foreground focus-visible:rounded-sm focus-visible:outline-2 focus-visible:outline-ring"
864
+ aria-label={`Open ${insight.title} — details`}
865
+ >
866
+ <i className="fa-light fa-arrow-up-right text-xs" aria-hidden="true" />
867
+ </a>
868
+ )}
869
+ </div>
870
+ </div>
871
+ {insight.description ? (
872
+ <p className="text-sm leading-relaxed text-muted-foreground">
873
+ {insight.description}
874
+ </p>
875
+ ) : null}
876
+ </div>
877
+ <div className="flex w-full shrink-0 md:w-auto">
878
+ <InsightDefaultTooltip actionLabel={insight.actionLabel}>
879
+ <Button
880
+ variant="ghost"
881
+ size="sm"
882
+ className="h-9 w-full gap-1.5 px-4 text-xs text-muted-foreground hover:text-interactive-hover-foreground md:min-w-[8.5rem]"
883
+ onClick={insight.onAction}
884
+ aria-label={insight.actionLabel ?? "Ask Leo"}
885
+ >
886
+ <i
887
+ className={insight.actionIcon ? `fa-light ${insight.actionIcon} text-xs` : "fa-duotone fa-solid fa-star-christmas text-xs text-brand"}
888
+ aria-hidden="true"
889
+ />
890
+ {insight.actionLabel ?? "Ask Leo"}
891
+ </Button>
892
+ </InsightDefaultTooltip>
893
+ </div>
894
+ </div>
895
+ )}
896
+ </Card>
897
+ )}
898
+ </div>
899
+ </>
900
+ )}
901
+ </div>
902
+ </div>
903
+ )
904
+ }
905
+
906
+ function chunkMetricPairs(metrics: MetricItem[]): MetricItem[][] {
907
+ const out: MetricItem[][] = []
908
+ for (let i = 0; i < metrics.length; i += 2) out.push(metrics.slice(i, i + 2))
909
+ return out
910
+ }
911
+
912
+ /* ── Main component ───────────────────────────────────────────────────────── */
913
+
914
+ export function KeyMetrics({
915
+ variant = "card",
916
+ title = "Key Metrics",
917
+ description = "Overview of performance indicators",
918
+ metrics = [],
919
+ insight,
920
+ periods = DEFAULT_PERIODS,
921
+ defaultPeriod = "week",
922
+ onPeriodChange,
923
+ showHeader = true,
924
+ insightCompact = false,
925
+ insightFullWidth = false,
926
+ metricsSingleRow = false,
927
+ metricsHalfWidthLayout = false,
928
+ className,
929
+ }: KeyMetricsProps) {
930
+ const [period, setPeriod] = React.useState(defaultPeriod)
931
+ // Pull the host app's "default insight action" (e.g. toggle Ask Leo
932
+ // sidebar) and label out of `KeyMetricsProvider` instead of
933
+ // hardcoding `useAskLeo()`. When no provider is mounted, the default
934
+ // action button hides entirely so the strip stays useful in apps
935
+ // without an AI assistant.
936
+ const {
937
+ defaultInsightAction,
938
+ defaultActionLabel = "Ask Leo",
939
+ } = useKeyMetricsContext()
940
+
941
+ function handlePeriodChange(v: string) {
942
+ setPeriod(v)
943
+ onPeriodChange?.(v)
944
+ }
945
+
946
+ /* Split metrics into rows of 3, or paired rows when half-width + single row, else one row */
947
+ const rows: MetricItem[][] = metricsSingleRow
948
+ ? metrics.length
949
+ ? metricsHalfWidthLayout
950
+ ? chunkMetricPairs(metrics)
951
+ : [metrics]
952
+ : []
953
+ : (() => {
954
+ const out: MetricItem[][] = []
955
+ for (let i = 0; i < metrics.length; i += 3) {
956
+ out.push(metrics.slice(i, i + 3))
957
+ }
958
+ return out
959
+ })()
960
+
961
+ const metricsCellSurfaceClassName =
962
+ variant === "flat"
963
+ ? "bg-transparent"
964
+ : "bg-card dark:bg-transparent"
965
+
966
+ const innerProps: InnerProps = {
967
+ title,
968
+ description,
969
+ period,
970
+ periods,
971
+ metrics,
972
+ rows,
973
+ insight,
974
+ onPeriodChange: handlePeriodChange,
975
+ insightCompact,
976
+ insightFullWidth,
977
+ metricsSingleRow,
978
+ metricsHalfWidthLayout,
979
+ metricsCellSurfaceClassName,
980
+ surfaceVariant: variant === "flat" ? "flat" : "default",
981
+ }
982
+
983
+ /*
984
+ * ── GLOW GUIDELINE ────────────────────────────────────────────────────────
985
+ * The bottom-glow treatment is a deliberate design signal. Use it only for:
986
+ *
987
+ * 1. AI / intelligence surfaces — e.g. AI Insights, Ask Leo responses,
988
+ * any card that surfaces machine-generated content.
989
+ * Opacity: 0.12–0.16 (subtle; the glow should not dominate)
990
+ *
991
+ * 2. Designer-designated hero sections — e.g. Key Metrics (the primary
992
+ * KPI band), onboarding completion, or any section the product team
993
+ * explicitly wants to "elevate" visually.
994
+ * Opacity: 0.18–0.24 (more pronounced; intentional focal point)
995
+ *
996
+ * Do NOT add glow to:
997
+ * • Standard data/content cards (Tasks, Activity, Learn, Charts…)
998
+ * • Navigation or shell elements
999
+ * • Cards that already use a coloured border or badge for status
1000
+ *
1001
+ * Implementation:
1002
+ * style={{ background: "radial-gradient(ellipse 110% 90% at 50% 100%,
1003
+ * oklch(from var(--brand-color) l c h / <opacity>) 0%, transparent 68%)" }}
1004
+ * + className="overflow-hidden" ← required to clip the gradient
1005
+ * ─────────────────────────────────────────────────────────────────────────
1006
+ */
1007
+ const glowStyle: React.CSSProperties = {
1008
+ background: "var(--key-metrics-card-glow-radial)",
1009
+ }
1010
+
1011
+ /** List-page KPI band — transparent; only `--key-metrics-flat-band-radial` glow. */
1012
+ const flatBandStyle: React.CSSProperties = {
1013
+ background: "var(--key-metrics-flat-band-radial)",
1014
+ boxShadow: "var(--key-metrics-flat-band-shadow)",
1015
+ }
1016
+
1017
+ /* ── Card variant — ChartCard-style chrome ───────────────────────────── */
1018
+ if (variant === "card") {
1019
+ return (
1020
+ <Card className={cn("shadow-xs overflow-hidden flex flex-col", className)} style={glowStyle}>
1021
+ <CardHeader className={cn("shrink-0 pb-2", metricsHalfWidthLayout && "space-y-2")}>
1022
+ <div
1023
+ className={cn(
1024
+ "flex gap-2",
1025
+ metricsHalfWidthLayout
1026
+ ? "flex-col min-[400px]:flex-row min-[400px]:items-start min-[400px]:justify-between"
1027
+ : "items-start",
1028
+ )}
1029
+ >
1030
+ <div className="flex-1 min-w-0">
1031
+ <CardTitle className="text-sm font-semibold leading-tight">{title}</CardTitle>
1032
+ <CardDescription className="text-xs mt-0.5">{description}</CardDescription>
1033
+ </div>
1034
+ <div className="flex flex-wrap items-center gap-1.5 shrink-0">
1035
+ {defaultInsightAction ? (
1036
+ <InsightDefaultTooltip actionLabel={defaultActionLabel}>
1037
+ <Button
1038
+ size="sm"
1039
+ variant="outline"
1040
+ className="h-7 shrink-0 text-xs gap-1.5 px-2"
1041
+ aria-label={`${defaultActionLabel} about these metrics`}
1042
+ onClick={defaultInsightAction}
1043
+ type="button"
1044
+ >
1045
+ <i className="fa-duotone fa-solid fa-star-christmas text-xs text-brand" aria-hidden="true" />
1046
+ <span>{defaultActionLabel}</span>
1047
+ </Button>
1048
+ </InsightDefaultTooltip>
1049
+ ) : null}
1050
+ <Select value={period} onValueChange={handlePeriodChange}>
1051
+ <SelectTrigger
1052
+ size="sm"
1053
+ className="w-auto min-w-[9rem] shrink-0 text-sm"
1054
+ aria-label="Select comparison period"
1055
+ >
1056
+ <SelectValue />
1057
+ </SelectTrigger>
1058
+ <SelectContent align="end" sideOffset={4}>
1059
+ {periods.map((p) => (
1060
+ <SelectItem key={p.value} value={p.value}>
1061
+ {p.label}
1062
+ </SelectItem>
1063
+ ))}
1064
+ </SelectContent>
1065
+ </Select>
1066
+ </div>
1067
+ </div>
1068
+ </CardHeader>
1069
+ <CardContent className="flex-1 pb-4">
1070
+ <KeyMetricsInner {...innerProps} showHeader={false} />
1071
+ </CardContent>
1072
+ </Card>
1073
+ )
1074
+ }
1075
+
1076
+ /* ── Compact variant — card chrome, no header, metrics only ──────────── */
1077
+ if (variant === "compact") {
1078
+ return (
1079
+ <Card className={cn("shadow-xs overflow-hidden", className)} style={glowStyle}>
1080
+ <CardContent className="py-3 px-4">
1081
+ <KeyMetricsInner {...innerProps} showHeader={false} />
1082
+ </CardContent>
1083
+ </Card>
1084
+ )
1085
+ }
1086
+
1087
+ /* ── Flat variant — no surface; bottom brand glow only ── */
1088
+ return (
1089
+ <section
1090
+ aria-label={title}
1091
+ className={cn("relative w-full overflow-hidden pt-5 pb-8", className)}
1092
+ style={flatBandStyle}
1093
+ >
1094
+ <KeyMetricsInner
1095
+ {...innerProps}
1096
+ innerPadding="px-4 lg:px-6"
1097
+ showHeader={showHeader}
1098
+ />
1099
+ </section>
1100
+ )
1101
+ }
1102
+
1103
+ /**
1104
+ * KeyMetricsContent — renders just the metrics grid + optional insight panel.
1105
+ * No card wrapper, no header, no period selector.
1106
+ * Designed for embedding inside a ChartCard with tabOptions period tabs.
1107
+ */
1108
+ export function KeyMetricsContent({
1109
+ metrics = [],
1110
+ insight,
1111
+ insightCompact = false,
1112
+ insightFullWidth = false,
1113
+ }: Pick<KeyMetricsProps, "metrics" | "insight" | "insightCompact" | "insightFullWidth">) {
1114
+ const rows: MetricItem[][] = []
1115
+ for (let i = 0; i < metrics.length; i += 3) rows.push(metrics.slice(i, i + 3))
1116
+
1117
+ return (
1118
+ <KeyMetricsInner
1119
+ title=""
1120
+ description=""
1121
+ period=""
1122
+ periods={[]}
1123
+ metrics={metrics}
1124
+ rows={rows}
1125
+ insight={insight}
1126
+ onPeriodChange={() => {}}
1127
+ showHeader={false}
1128
+ insightCompact={insightCompact}
1129
+ insightFullWidth={insightFullWidth}
1130
+ metricsCellSurfaceClassName="bg-card dark:bg-transparent"
1131
+ />
1132
+ )
1133
+ }