@freighttech/ui 0.13.18

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 (1477) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/dist/backend/AppShell.js +1289 -0
  4. package/dist/backend/AppShell.js.map +7 -0
  5. package/dist/backend/BackendChromeProvider.js +83 -0
  6. package/dist/backend/BackendChromeProvider.js.map +7 -0
  7. package/dist/backend/BackendPageSurface.js +19 -0
  8. package/dist/backend/BackendPageSurface.js.map +7 -0
  9. package/dist/backend/CollapsibleNavSection.js +149 -0
  10. package/dist/backend/CollapsibleNavSection.js.map +7 -0
  11. package/dist/backend/ContextHelp.js +33 -0
  12. package/dist/backend/ContextHelp.js.map +7 -0
  13. package/dist/backend/CrudForm.js +3163 -0
  14. package/dist/backend/CrudForm.js.map +7 -0
  15. package/dist/backend/DataTable.js +2091 -0
  16. package/dist/backend/DataTable.js.map +7 -0
  17. package/dist/backend/EmptyState.js +52 -0
  18. package/dist/backend/EmptyState.js.map +7 -0
  19. package/dist/backend/FilterBar.js +147 -0
  20. package/dist/backend/FilterBar.js.map +7 -0
  21. package/dist/backend/FilterOverlay.js +337 -0
  22. package/dist/backend/FilterOverlay.js.map +7 -0
  23. package/dist/backend/FlashMessages.js +158 -0
  24. package/dist/backend/FlashMessages.js.map +7 -0
  25. package/dist/backend/HtmlRichTextEditor.js +129 -0
  26. package/dist/backend/HtmlRichTextEditor.js.map +7 -0
  27. package/dist/backend/IntegrationsButton.js +25 -0
  28. package/dist/backend/IntegrationsButton.js.map +7 -0
  29. package/dist/backend/JsonBuilder.js +323 -0
  30. package/dist/backend/JsonBuilder.js.map +7 -0
  31. package/dist/backend/JsonDisplay.js +207 -0
  32. package/dist/backend/JsonDisplay.js.map +7 -0
  33. package/dist/backend/NextStepCallout.js +79 -0
  34. package/dist/backend/NextStepCallout.js.map +7 -0
  35. package/dist/backend/Page.js +27 -0
  36. package/dist/backend/Page.js.map +7 -0
  37. package/dist/backend/PerspectiveSidebar.js +456 -0
  38. package/dist/backend/PerspectiveSidebar.js.map +7 -0
  39. package/dist/backend/ProfileDropdown.js +341 -0
  40. package/dist/backend/ProfileDropdown.js.map +7 -0
  41. package/dist/backend/RowActions.js +155 -0
  42. package/dist/backend/RowActions.js.map +7 -0
  43. package/dist/backend/SectionHeader.js +74 -0
  44. package/dist/backend/SectionHeader.js.map +7 -0
  45. package/dist/backend/SettingsButton.js +25 -0
  46. package/dist/backend/SettingsButton.js.map +7 -0
  47. package/dist/backend/Sidebar.js +510 -0
  48. package/dist/backend/Sidebar.js.map +7 -0
  49. package/dist/backend/Sidebar.stories.js +166 -0
  50. package/dist/backend/Sidebar.stories.js.map +7 -0
  51. package/dist/backend/TruncatedCell.js +92 -0
  52. package/dist/backend/TruncatedCell.js.map +7 -0
  53. package/dist/backend/UserMenu.js +146 -0
  54. package/dist/backend/UserMenu.js.map +7 -0
  55. package/dist/backend/ValueIcons.js +34 -0
  56. package/dist/backend/ValueIcons.js.map +7 -0
  57. package/dist/backend/WebhookSetupGuide.js +79 -0
  58. package/dist/backend/WebhookSetupGuide.js.map +7 -0
  59. package/dist/backend/charts/BarChart.js +154 -0
  60. package/dist/backend/charts/BarChart.js.map +7 -0
  61. package/dist/backend/charts/ChartUtils.js +141 -0
  62. package/dist/backend/charts/ChartUtils.js.map +7 -0
  63. package/dist/backend/charts/ComboChart.js +261 -0
  64. package/dist/backend/charts/ComboChart.js.map +7 -0
  65. package/dist/backend/charts/ComboChart.stories.js +88 -0
  66. package/dist/backend/charts/ComboChart.stories.js.map +7 -0
  67. package/dist/backend/charts/KpiCard.js +96 -0
  68. package/dist/backend/charts/KpiCard.js.map +7 -0
  69. package/dist/backend/charts/LineChart.js +165 -0
  70. package/dist/backend/charts/LineChart.js.map +7 -0
  71. package/dist/backend/charts/PieChart.js +121 -0
  72. package/dist/backend/charts/PieChart.js.map +7 -0
  73. package/dist/backend/charts/ScatterChart.js +320 -0
  74. package/dist/backend/charts/ScatterChart.js.map +7 -0
  75. package/dist/backend/charts/TopNTable.js +91 -0
  76. package/dist/backend/charts/TopNTable.js.map +7 -0
  77. package/dist/backend/charts/WaterfallChart.js +136 -0
  78. package/dist/backend/charts/WaterfallChart.js.map +7 -0
  79. package/dist/backend/charts/index.js +30 -0
  80. package/dist/backend/charts/index.js.map +7 -0
  81. package/dist/backend/columns/ColumnChooserPanel.js +244 -0
  82. package/dist/backend/columns/ColumnChooserPanel.js.map +7 -0
  83. package/dist/backend/confirm-dialog/ConfirmDialog.js +214 -0
  84. package/dist/backend/confirm-dialog/ConfirmDialog.js.map +7 -0
  85. package/dist/backend/confirm-dialog/index.js +7 -0
  86. package/dist/backend/confirm-dialog/index.js.map +7 -0
  87. package/dist/backend/confirm-dialog/useConfirmDialog.js +113 -0
  88. package/dist/backend/confirm-dialog/useConfirmDialog.js.map +7 -0
  89. package/dist/backend/custom-fields/FieldDefinitionsEditor.js +1352 -0
  90. package/dist/backend/custom-fields/FieldDefinitionsEditor.js.map +7 -0
  91. package/dist/backend/custom-fields/FieldDefinitionsManager.js +332 -0
  92. package/dist/backend/custom-fields/FieldDefinitionsManager.js.map +7 -0
  93. package/dist/backend/dashboard/DashboardScreen.js +598 -0
  94. package/dist/backend/dashboard/DashboardScreen.js.map +7 -0
  95. package/dist/backend/dashboard/index.js +5 -0
  96. package/dist/backend/dashboard/index.js.map +7 -0
  97. package/dist/backend/dashboard/widgetRegistry.js +55 -0
  98. package/dist/backend/dashboard/widgetRegistry.js.map +7 -0
  99. package/dist/backend/date-range/DateRangeSelect.js +39 -0
  100. package/dist/backend/date-range/DateRangeSelect.js.map +7 -0
  101. package/dist/backend/date-range/InlineDateRangeSelect.js +42 -0
  102. package/dist/backend/date-range/InlineDateRangeSelect.js.map +7 -0
  103. package/dist/backend/date-range/InlineGranularitySelect.js +40 -0
  104. package/dist/backend/date-range/InlineGranularitySelect.js.map +7 -0
  105. package/dist/backend/date-range/dateRanges.js +176 -0
  106. package/dist/backend/date-range/dateRanges.js.map +7 -0
  107. package/dist/backend/date-range/index.js +25 -0
  108. package/dist/backend/date-range/index.js.map +7 -0
  109. package/dist/backend/detail/AccessDeniedMessage.js +28 -0
  110. package/dist/backend/detail/AccessDeniedMessage.js.map +7 -0
  111. package/dist/backend/detail/ActivitiesSection.js +947 -0
  112. package/dist/backend/detail/ActivitiesSection.js.map +7 -0
  113. package/dist/backend/detail/AddressEditor.js +417 -0
  114. package/dist/backend/detail/AddressEditor.js.map +7 -0
  115. package/dist/backend/detail/AddressTiles.js +441 -0
  116. package/dist/backend/detail/AddressTiles.js.map +7 -0
  117. package/dist/backend/detail/AddressesSection.js +274 -0
  118. package/dist/backend/detail/AddressesSection.js.map +7 -0
  119. package/dist/backend/detail/AttachmentDeleteDialog.js +41 -0
  120. package/dist/backend/detail/AttachmentDeleteDialog.js.map +7 -0
  121. package/dist/backend/detail/AttachmentMetadataDialog.js +519 -0
  122. package/dist/backend/detail/AttachmentMetadataDialog.js.map +7 -0
  123. package/dist/backend/detail/AttachmentVisualPreview.js +132 -0
  124. package/dist/backend/detail/AttachmentVisualPreview.js.map +7 -0
  125. package/dist/backend/detail/AttachmentsSection.js +281 -0
  126. package/dist/backend/detail/AttachmentsSection.js.map +7 -0
  127. package/dist/backend/detail/CustomDataSection.js +575 -0
  128. package/dist/backend/detail/CustomDataSection.js.map +7 -0
  129. package/dist/backend/detail/DetailFieldsSection.js +85 -0
  130. package/dist/backend/detail/DetailFieldsSection.js.map +7 -0
  131. package/dist/backend/detail/ErrorMessage.js +28 -0
  132. package/dist/backend/detail/ErrorMessage.js.map +7 -0
  133. package/dist/backend/detail/InlineEditors.js +697 -0
  134. package/dist/backend/detail/InlineEditors.js.map +7 -0
  135. package/dist/backend/detail/LoadingMessage.js +14 -0
  136. package/dist/backend/detail/LoadingMessage.js.map +7 -0
  137. package/dist/backend/detail/NotesSection.js +1022 -0
  138. package/dist/backend/detail/NotesSection.js.map +7 -0
  139. package/dist/backend/detail/TabEmptyState.js +25 -0
  140. package/dist/backend/detail/TabEmptyState.js.map +7 -0
  141. package/dist/backend/detail/TagsSection.js +375 -0
  142. package/dist/backend/detail/TagsSection.js.map +7 -0
  143. package/dist/backend/detail/addressFormat.js +77 -0
  144. package/dist/backend/detail/addressFormat.js.map +7 -0
  145. package/dist/backend/detail/index.js +36 -0
  146. package/dist/backend/detail/index.js.map +7 -0
  147. package/dist/backend/devtools/UmesDevToolsPanel.js +109 -0
  148. package/dist/backend/devtools/UmesDevToolsPanel.js.map +7 -0
  149. package/dist/backend/devtools/components/ConflictWarnings.js +32 -0
  150. package/dist/backend/devtools/components/ConflictWarnings.js.map +7 -0
  151. package/dist/backend/devtools/components/EnricherTiming.js +46 -0
  152. package/dist/backend/devtools/components/EnricherTiming.js.map +7 -0
  153. package/dist/backend/devtools/components/EventFlow.js +34 -0
  154. package/dist/backend/devtools/components/EventFlow.js.map +7 -0
  155. package/dist/backend/devtools/components/ExtensionPointList.js +58 -0
  156. package/dist/backend/devtools/components/ExtensionPointList.js.map +7 -0
  157. package/dist/backend/devtools/components/InterceptorActivity.js +36 -0
  158. package/dist/backend/devtools/components/InterceptorActivity.js.map +7 -0
  159. package/dist/backend/devtools/index.js +7 -0
  160. package/dist/backend/devtools/index.js.map +7 -0
  161. package/dist/backend/devtools/useUmesDevTools.js +125 -0
  162. package/dist/backend/devtools/useUmesDevTools.js.map +7 -0
  163. package/dist/backend/document-viewer/RegionOverlay.js +92 -0
  164. package/dist/backend/document-viewer/RegionOverlay.js.map +7 -0
  165. package/dist/backend/document-viewer/index.js +6 -0
  166. package/dist/backend/document-viewer/index.js.map +7 -0
  167. package/dist/backend/dynamic-grid/DynamicGrid.js +130 -0
  168. package/dist/backend/dynamic-grid/DynamicGrid.js.map +7 -0
  169. package/dist/backend/dynamic-grid/adapters.js +34 -0
  170. package/dist/backend/dynamic-grid/adapters.js.map +7 -0
  171. package/dist/backend/dynamic-grid/components/GridCell.js +149 -0
  172. package/dist/backend/dynamic-grid/components/GridCell.js.map +7 -0
  173. package/dist/backend/dynamic-grid/components/GridRow.js +32 -0
  174. package/dist/backend/dynamic-grid/components/GridRow.js.map +7 -0
  175. package/dist/backend/dynamic-grid/hooks/useGridKeyboardNav.js +115 -0
  176. package/dist/backend/dynamic-grid/hooks/useGridKeyboardNav.js.map +7 -0
  177. package/dist/backend/dynamic-grid/index.js +9 -0
  178. package/dist/backend/dynamic-grid/index.js.map +7 -0
  179. package/dist/backend/dynamic-grid/types.js +1 -0
  180. package/dist/backend/dynamic-grid/types.js.map +7 -0
  181. package/dist/backend/dynamic-table/DynamicTable.js +2607 -0
  182. package/dist/backend/dynamic-table/DynamicTable.js.map +7 -0
  183. package/dist/backend/dynamic-table/DynamicTable.stories.js +233 -0
  184. package/dist/backend/dynamic-table/DynamicTable.stories.js.map +7 -0
  185. package/dist/backend/dynamic-table/components/ActiveFilterChips.js +60 -0
  186. package/dist/backend/dynamic-table/components/ActiveFilterChips.js.map +7 -0
  187. package/dist/backend/dynamic-table/components/BulkActionsBar.js +58 -0
  188. package/dist/backend/dynamic-table/components/BulkActionsBar.js.map +7 -0
  189. package/dist/backend/dynamic-table/components/Cell.js +155 -0
  190. package/dist/backend/dynamic-table/components/Cell.js.map +7 -0
  191. package/dist/backend/dynamic-table/components/CellCommentDialog.js +726 -0
  192. package/dist/backend/dynamic-table/components/CellCommentDialog.js.map +7 -0
  193. package/dist/backend/dynamic-table/components/CellCommentHoverPopup.js +77 -0
  194. package/dist/backend/dynamic-table/components/CellCommentHoverPopup.js.map +7 -0
  195. package/dist/backend/dynamic-table/components/ColumnFilterPopover.js +485 -0
  196. package/dist/backend/dynamic-table/components/ColumnFilterPopover.js.map +7 -0
  197. package/dist/backend/dynamic-table/components/ColumnHeaderMenu.js +173 -0
  198. package/dist/backend/dynamic-table/components/ColumnHeaderMenu.js.map +7 -0
  199. package/dist/backend/dynamic-table/components/ColumnHeaders.js +528 -0
  200. package/dist/backend/dynamic-table/components/ColumnHeaders.js.map +7 -0
  201. package/dist/backend/dynamic-table/components/CompactLabelCell.js +64 -0
  202. package/dist/backend/dynamic-table/components/CompactLabelCell.js.map +7 -0
  203. package/dist/backend/dynamic-table/components/ConfigureViewFields.js +139 -0
  204. package/dist/backend/dynamic-table/components/ConfigureViewFields.js.map +7 -0
  205. package/dist/backend/dynamic-table/components/ConfigureViewFilters.js +305 -0
  206. package/dist/backend/dynamic-table/components/ConfigureViewFilters.js.map +7 -0
  207. package/dist/backend/dynamic-table/components/ConfigureViewFormatting.js +169 -0
  208. package/dist/backend/dynamic-table/components/ConfigureViewFormatting.js.map +7 -0
  209. package/dist/backend/dynamic-table/components/ConfigureViewFormulas.js +298 -0
  210. package/dist/backend/dynamic-table/components/ConfigureViewFormulas.js.map +7 -0
  211. package/dist/backend/dynamic-table/components/ConfigureViewGrouping.js +224 -0
  212. package/dist/backend/dynamic-table/components/ConfigureViewGrouping.js.map +7 -0
  213. package/dist/backend/dynamic-table/components/ConfigureViewLinkedColumns.js +167 -0
  214. package/dist/backend/dynamic-table/components/ConfigureViewLinkedColumns.js.map +7 -0
  215. package/dist/backend/dynamic-table/components/ConfigureViewPanel.js +592 -0
  216. package/dist/backend/dynamic-table/components/ConfigureViewPanel.js.map +7 -0
  217. package/dist/backend/dynamic-table/components/ConfigureViewRollups.js +269 -0
  218. package/dist/backend/dynamic-table/components/ConfigureViewRollups.js.map +7 -0
  219. package/dist/backend/dynamic-table/components/ConfigureViewSorting.js +107 -0
  220. package/dist/backend/dynamic-table/components/ConfigureViewSorting.js.map +7 -0
  221. package/dist/backend/dynamic-table/components/ContextMenu.js +91 -0
  222. package/dist/backend/dynamic-table/components/ContextMenu.js.map +7 -0
  223. package/dist/backend/dynamic-table/components/DateCell.js +80 -0
  224. package/dist/backend/dynamic-table/components/DateCell.js.map +7 -0
  225. package/dist/backend/dynamic-table/components/Debugger.js +311 -0
  226. package/dist/backend/dynamic-table/components/Debugger.js.map +7 -0
  227. package/dist/backend/dynamic-table/components/DensityControl.js +289 -0
  228. package/dist/backend/dynamic-table/components/DensityControl.js.map +7 -0
  229. package/dist/backend/dynamic-table/components/EmptyState.js +42 -0
  230. package/dist/backend/dynamic-table/components/EmptyState.js.map +7 -0
  231. package/dist/backend/dynamic-table/components/EntitySearchEditor.js +369 -0
  232. package/dist/backend/dynamic-table/components/EntitySearchEditor.js.map +7 -0
  233. package/dist/backend/dynamic-table/components/ExportMenu.js +63 -0
  234. package/dist/backend/dynamic-table/components/ExportMenu.js.map +7 -0
  235. package/dist/backend/dynamic-table/components/FilterDatePicker.js +165 -0
  236. package/dist/backend/dynamic-table/components/FilterDatePicker.js.map +7 -0
  237. package/dist/backend/dynamic-table/components/FilterValueInput.js +200 -0
  238. package/dist/backend/dynamic-table/components/FilterValueInput.js.map +7 -0
  239. package/dist/backend/dynamic-table/components/FooterTotalsRow.js +165 -0
  240. package/dist/backend/dynamic-table/components/FooterTotalsRow.js.map +7 -0
  241. package/dist/backend/dynamic-table/components/FullscreenOverlay.js +51 -0
  242. package/dist/backend/dynamic-table/components/FullscreenOverlay.js.map +7 -0
  243. package/dist/backend/dynamic-table/components/GroupHeaderRow.js +108 -0
  244. package/dist/backend/dynamic-table/components/GroupHeaderRow.js.map +7 -0
  245. package/dist/backend/dynamic-table/components/GroupSummaryRow.js +134 -0
  246. package/dist/backend/dynamic-table/components/GroupSummaryRow.js.map +7 -0
  247. package/dist/backend/dynamic-table/components/ImportPanel.js +341 -0
  248. package/dist/backend/dynamic-table/components/ImportPanel.js.map +7 -0
  249. package/dist/backend/dynamic-table/components/ImportPreviewGrid.js +114 -0
  250. package/dist/backend/dynamic-table/components/ImportPreviewGrid.js.map +7 -0
  251. package/dist/backend/dynamic-table/components/LoadErrorState.js +23 -0
  252. package/dist/backend/dynamic-table/components/LoadErrorState.js.map +7 -0
  253. package/dist/backend/dynamic-table/components/MentionPopup.js +149 -0
  254. package/dist/backend/dynamic-table/components/MentionPopup.js.map +7 -0
  255. package/dist/backend/dynamic-table/components/PerspectiveDeleteDialog.js +57 -0
  256. package/dist/backend/dynamic-table/components/PerspectiveDeleteDialog.js.map +7 -0
  257. package/dist/backend/dynamic-table/components/PerspectivePublishDialog.js +100 -0
  258. package/dist/backend/dynamic-table/components/PerspectivePublishDialog.js.map +7 -0
  259. package/dist/backend/dynamic-table/components/PerspectiveTabMenu.js +159 -0
  260. package/dist/backend/dynamic-table/components/PerspectiveTabMenu.js.map +7 -0
  261. package/dist/backend/dynamic-table/components/PerspectiveTabs.js +319 -0
  262. package/dist/backend/dynamic-table/components/PerspectiveTabs.js.map +7 -0
  263. package/dist/backend/dynamic-table/components/PerspectiveTemplateMenu.js +90 -0
  264. package/dist/backend/dynamic-table/components/PerspectiveTemplateMenu.js.map +7 -0
  265. package/dist/backend/dynamic-table/components/RowHeaderCell.js +58 -0
  266. package/dist/backend/dynamic-table/components/RowHeaderCell.js.map +7 -0
  267. package/dist/backend/dynamic-table/components/SearchBar.js +221 -0
  268. package/dist/backend/dynamic-table/components/SearchBar.js.map +7 -0
  269. package/dist/backend/dynamic-table/components/SelectMenu.js +186 -0
  270. package/dist/backend/dynamic-table/components/SelectMenu.js.map +7 -0
  271. package/dist/backend/dynamic-table/components/StaleDataBar.js +26 -0
  272. package/dist/backend/dynamic-table/components/StaleDataBar.js.map +7 -0
  273. package/dist/backend/dynamic-table/components/TableDeleteDialog.js +48 -0
  274. package/dist/backend/dynamic-table/components/TableDeleteDialog.js.map +7 -0
  275. package/dist/backend/dynamic-table/components/TablePagination.js +69 -0
  276. package/dist/backend/dynamic-table/components/TablePagination.js.map +7 -0
  277. package/dist/backend/dynamic-table/components/TableSkeleton.js +43 -0
  278. package/dist/backend/dynamic-table/components/TableSkeleton.js.map +7 -0
  279. package/dist/backend/dynamic-table/components/ToolbarOverflow.js +204 -0
  280. package/dist/backend/dynamic-table/components/ToolbarOverflow.js.map +7 -0
  281. package/dist/backend/dynamic-table/components/ViewModeSwitch.js +50 -0
  282. package/dist/backend/dynamic-table/components/ViewModeSwitch.js.map +7 -0
  283. package/dist/backend/dynamic-table/components/VirtualRow.js +214 -0
  284. package/dist/backend/dynamic-table/components/VirtualRow.js.map +7 -0
  285. package/dist/backend/dynamic-table/components/editors.js +1483 -0
  286. package/dist/backend/dynamic-table/components/editors.js.map +7 -0
  287. package/dist/backend/dynamic-table/components/renderers.js +131 -0
  288. package/dist/backend/dynamic-table/components/renderers.js.map +7 -0
  289. package/dist/backend/dynamic-table/events/events.js +73 -0
  290. package/dist/backend/dynamic-table/events/events.js.map +7 -0
  291. package/dist/backend/dynamic-table/formula/check.js +243 -0
  292. package/dist/backend/dynamic-table/formula/check.js.map +7 -0
  293. package/dist/backend/dynamic-table/formula/evaluate.js +302 -0
  294. package/dist/backend/dynamic-table/formula/evaluate.js.map +7 -0
  295. package/dist/backend/dynamic-table/formula/functions.js +602 -0
  296. package/dist/backend/dynamic-table/formula/functions.js.map +7 -0
  297. package/dist/backend/dynamic-table/formula/index.js +47 -0
  298. package/dist/backend/dynamic-table/formula/index.js.map +7 -0
  299. package/dist/backend/dynamic-table/formula/parse.js +293 -0
  300. package/dist/backend/dynamic-table/formula/parse.js.map +7 -0
  301. package/dist/backend/dynamic-table/formula/types.js +24 -0
  302. package/dist/backend/dynamic-table/formula/types.js.map +7 -0
  303. package/dist/backend/dynamic-table/handlers/cellWrites.js +143 -0
  304. package/dist/backend/dynamic-table/handlers/cellWrites.js.map +7 -0
  305. package/dist/backend/dynamic-table/handlers/index.js +504 -0
  306. package/dist/backend/dynamic-table/handlers/index.js.map +7 -0
  307. package/dist/backend/dynamic-table/handlers/perspectiveHandlers.js +377 -0
  308. package/dist/backend/dynamic-table/handlers/perspectiveHandlers.js.map +7 -0
  309. package/dist/backend/dynamic-table/hooks/index.js +524 -0
  310. package/dist/backend/dynamic-table/hooks/index.js.map +7 -0
  311. package/dist/backend/dynamic-table/hooks/useAbbreviations.js +19 -0
  312. package/dist/backend/dynamic-table/hooks/useAbbreviations.js.map +7 -0
  313. package/dist/backend/dynamic-table/hooks/useAnnotations.js +115 -0
  314. package/dist/backend/dynamic-table/hooks/useAnnotations.js.map +7 -0
  315. package/dist/backend/dynamic-table/hooks/useColumnVirtualizer.js +155 -0
  316. package/dist/backend/dynamic-table/hooks/useColumnVirtualizer.js.map +7 -0
  317. package/dist/backend/dynamic-table/hooks/useColumnWidthPersistence.js +135 -0
  318. package/dist/backend/dynamic-table/hooks/useColumnWidthPersistence.js.map +7 -0
  319. package/dist/backend/dynamic-table/hooks/useDensityPreference.js +203 -0
  320. package/dist/backend/dynamic-table/hooks/useDensityPreference.js.map +7 -0
  321. package/dist/backend/dynamic-table/hooks/useDynamicTablePage.js +1005 -0
  322. package/dist/backend/dynamic-table/hooks/useDynamicTablePage.js.map +7 -0
  323. package/dist/backend/dynamic-table/hooks/useEscapeLayer.js +32 -0
  324. package/dist/backend/dynamic-table/hooks/useEscapeLayer.js.map +7 -0
  325. package/dist/backend/dynamic-table/hooks/useGrouping.js +256 -0
  326. package/dist/backend/dynamic-table/hooks/useGrouping.js.map +7 -0
  327. package/dist/backend/dynamic-table/hooks/useSuggestionFetch.js +63 -0
  328. package/dist/backend/dynamic-table/hooks/useSuggestionFetch.js.map +7 -0
  329. package/dist/backend/dynamic-table/hooks/useTableDateFormat.js +11 -0
  330. package/dist/backend/dynamic-table/hooks/useTableDateFormat.js.map +7 -0
  331. package/dist/backend/dynamic-table/index.js +476 -0
  332. package/dist/backend/dynamic-table/index.js.map +7 -0
  333. package/dist/backend/dynamic-table/registry/ContentRegistryContext.js +126 -0
  334. package/dist/backend/dynamic-table/registry/ContentRegistryContext.js.map +7 -0
  335. package/dist/backend/dynamic-table/registry/TableRegistryContext.js +43 -0
  336. package/dist/backend/dynamic-table/registry/TableRegistryContext.js.map +7 -0
  337. package/dist/backend/dynamic-table/registry/filterAccessibleTables.js +13 -0
  338. package/dist/backend/dynamic-table/registry/filterAccessibleTables.js.map +7 -0
  339. package/dist/backend/dynamic-table/registry/index.js +44 -0
  340. package/dist/backend/dynamic-table/registry/index.js.map +7 -0
  341. package/dist/backend/dynamic-table/registry/lazyTable.js +55 -0
  342. package/dist/backend/dynamic-table/registry/lazyTable.js.map +7 -0
  343. package/dist/backend/dynamic-table/registry/types.js +1 -0
  344. package/dist/backend/dynamic-table/registry/types.js.map +7 -0
  345. package/dist/backend/dynamic-table/registry/widgetCatalog.js +96 -0
  346. package/dist/backend/dynamic-table/registry/widgetCatalog.js.map +7 -0
  347. package/dist/backend/dynamic-table/server/aggregate.js +261 -0
  348. package/dist/backend/dynamic-table/server/aggregate.js.map +7 -0
  349. package/dist/backend/dynamic-table/server/dateWindows.js +105 -0
  350. package/dist/backend/dynamic-table/server/dateWindows.js.map +7 -0
  351. package/dist/backend/dynamic-table/server/filterParser.js +97 -0
  352. package/dist/backend/dynamic-table/server/filterParser.js.map +7 -0
  353. package/dist/backend/dynamic-table/server/filterSuggestions.js +70 -0
  354. package/dist/backend/dynamic-table/server/filterSuggestions.js.map +7 -0
  355. package/dist/backend/dynamic-table/server/index.js +47 -0
  356. package/dist/backend/dynamic-table/server/index.js.map +7 -0
  357. package/dist/backend/dynamic-table/server/makeDynamicTableRoute.js +270 -0
  358. package/dist/backend/dynamic-table/server/makeDynamicTableRoute.js.map +7 -0
  359. package/dist/backend/dynamic-table/split-view/ContentPicker.js +171 -0
  360. package/dist/backend/dynamic-table/split-view/ContentPicker.js.map +7 -0
  361. package/dist/backend/dynamic-table/split-view/EmptySlot.js +47 -0
  362. package/dist/backend/dynamic-table/split-view/EmptySlot.js.map +7 -0
  363. package/dist/backend/dynamic-table/split-view/SplitViewHost.js +863 -0
  364. package/dist/backend/dynamic-table/split-view/SplitViewHost.js.map +7 -0
  365. package/dist/backend/dynamic-table/split-view/WidgetPane.js +280 -0
  366. package/dist/backend/dynamic-table/split-view/WidgetPane.js.map +7 -0
  367. package/dist/backend/dynamic-table/split-view/WorkspaceFilterBar.js +524 -0
  368. package/dist/backend/dynamic-table/split-view/WorkspaceFilterBar.js.map +7 -0
  369. package/dist/backend/dynamic-table/split-view/index.js +61 -0
  370. package/dist/backend/dynamic-table/split-view/index.js.map +7 -0
  371. package/dist/backend/dynamic-table/split-view/layoutPersistence.js +91 -0
  372. package/dist/backend/dynamic-table/split-view/layoutPersistence.js.map +7 -0
  373. package/dist/backend/dynamic-table/split-view/sharedCriteria.js +62 -0
  374. package/dist/backend/dynamic-table/split-view/sharedCriteria.js.map +7 -0
  375. package/dist/backend/dynamic-table/split-view/types.js +426 -0
  376. package/dist/backend/dynamic-table/split-view/types.js.map +7 -0
  377. package/dist/backend/dynamic-table/split-view/useSplitViewLayouts.js +79 -0
  378. package/dist/backend/dynamic-table/split-view/useSplitViewLayouts.js.map +7 -0
  379. package/dist/backend/dynamic-table/store/index.js +611 -0
  380. package/dist/backend/dynamic-table/store/index.js.map +7 -0
  381. package/dist/backend/dynamic-table/styles/ContextMenu.css.js +2 -0
  382. package/dist/backend/dynamic-table/styles/DynamicTable.css.js +2 -0
  383. package/dist/backend/dynamic-table/styles/DynamicTable.v2.css.js +2 -0
  384. package/dist/backend/dynamic-table/styles/SearchBar.css.js +2 -0
  385. package/dist/backend/dynamic-table/styles/dateCell.css.js +2 -0
  386. package/dist/backend/dynamic-table/styles/density.css.js +2 -0
  387. package/dist/backend/dynamic-table/types/density.js +29 -0
  388. package/dist/backend/dynamic-table/types/density.js.map +7 -0
  389. package/dist/backend/dynamic-table/types/filters.js +124 -0
  390. package/dist/backend/dynamic-table/types/filters.js.map +7 -0
  391. package/dist/backend/dynamic-table/types/grouping.js +34 -0
  392. package/dist/backend/dynamic-table/types/grouping.js.map +7 -0
  393. package/dist/backend/dynamic-table/types/import.js +7 -0
  394. package/dist/backend/dynamic-table/types/import.js.map +7 -0
  395. package/dist/backend/dynamic-table/types/index.js +46 -0
  396. package/dist/backend/dynamic-table/types/index.js.map +7 -0
  397. package/dist/backend/dynamic-table/types/perspective.js +79 -0
  398. package/dist/backend/dynamic-table/types/perspective.js.map +7 -0
  399. package/dist/backend/dynamic-table/types/rollup.js +13 -0
  400. package/dist/backend/dynamic-table/types/rollup.js.map +7 -0
  401. package/dist/backend/dynamic-table/utils/abbreviations.js +228 -0
  402. package/dist/backend/dynamic-table/utils/abbreviations.js.map +7 -0
  403. package/dist/backend/dynamic-table/utils/anchoredPosition.js +21 -0
  404. package/dist/backend/dynamic-table/utils/anchoredPosition.js.map +7 -0
  405. package/dist/backend/dynamic-table/utils/badgeAutoFit.js +35 -0
  406. package/dist/backend/dynamic-table/utils/badgeAutoFit.js.map +7 -0
  407. package/dist/backend/dynamic-table/utils/clipboard.js +172 -0
  408. package/dist/backend/dynamic-table/utils/clipboard.js.map +7 -0
  409. package/dist/backend/dynamic-table/utils/coerceCellValue.js +52 -0
  410. package/dist/backend/dynamic-table/utils/coerceCellValue.js.map +7 -0
  411. package/dist/backend/dynamic-table/utils/colorAdjacency.js +27 -0
  412. package/dist/backend/dynamic-table/utils/colorAdjacency.js.map +7 -0
  413. package/dist/backend/dynamic-table/utils/columnFill.js +36 -0
  414. package/dist/backend/dynamic-table/utils/columnFill.js.map +7 -0
  415. package/dist/backend/dynamic-table/utils/columnSpacerStyle.js +21 -0
  416. package/dist/backend/dynamic-table/utils/columnSpacerStyle.js.map +7 -0
  417. package/dist/backend/dynamic-table/utils/columnWindow.js +293 -0
  418. package/dist/backend/dynamic-table/utils/columnWindow.js.map +7 -0
  419. package/dist/backend/dynamic-table/utils/conditionalFormat.js +188 -0
  420. package/dist/backend/dynamic-table/utils/conditionalFormat.js.map +7 -0
  421. package/dist/backend/dynamic-table/utils/exportTable.js +82 -0
  422. package/dist/backend/dynamic-table/utils/exportTable.js.map +7 -0
  423. package/dist/backend/dynamic-table/utils/fillGeometry.js +92 -0
  424. package/dist/backend/dynamic-table/utils/fillGeometry.js.map +7 -0
  425. package/dist/backend/dynamic-table/utils/fillPatterns.js +241 -0
  426. package/dist/backend/dynamic-table/utils/fillPatterns.js.map +7 -0
  427. package/dist/backend/dynamic-table/utils/formatAggregate.js +82 -0
  428. package/dist/backend/dynamic-table/utils/formatAggregate.js.map +7 -0
  429. package/dist/backend/dynamic-table/utils/formatDateCompact.js +508 -0
  430. package/dist/backend/dynamic-table/utils/formatDateCompact.js.map +7 -0
  431. package/dist/backend/dynamic-table/utils/formatLabelCompact.js +312 -0
  432. package/dist/backend/dynamic-table/utils/formatLabelCompact.js.map +7 -0
  433. package/dist/backend/dynamic-table/utils/formulaColumns.js +158 -0
  434. package/dist/backend/dynamic-table/utils/formulaColumns.js.map +7 -0
  435. package/dist/backend/dynamic-table/utils/importMapping.js +118 -0
  436. package/dist/backend/dynamic-table/utils/importMapping.js.map +7 -0
  437. package/dist/backend/dynamic-table/utils/importParse.js +121 -0
  438. package/dist/backend/dynamic-table/utils/importParse.js.map +7 -0
  439. package/dist/backend/dynamic-table/utils/lookupColumns.js +28 -0
  440. package/dist/backend/dynamic-table/utils/lookupColumns.js.map +7 -0
  441. package/dist/backend/dynamic-table/utils/perspectiveTransforms.js +191 -0
  442. package/dist/backend/dynamic-table/utils/perspectiveTransforms.js.map +7 -0
  443. package/dist/backend/dynamic-table/utils/rollupColumns.js +81 -0
  444. package/dist/backend/dynamic-table/utils/rollupColumns.js.map +7 -0
  445. package/dist/backend/dynamic-table/validators/index.js +36 -0
  446. package/dist/backend/dynamic-table/validators/index.js.map +7 -0
  447. package/dist/backend/fields/registry.generated.js +8 -0
  448. package/dist/backend/fields/registry.generated.js.map +7 -0
  449. package/dist/backend/fields/registry.js +29 -0
  450. package/dist/backend/fields/registry.js.map +7 -0
  451. package/dist/backend/filters/AdvancedFilterBuilder.js +280 -0
  452. package/dist/backend/filters/AdvancedFilterBuilder.js.map +7 -0
  453. package/dist/backend/forms/ActionsDropdown.js +160 -0
  454. package/dist/backend/forms/ActionsDropdown.js.map +7 -0
  455. package/dist/backend/forms/FormActionButtons.js +59 -0
  456. package/dist/backend/forms/FormActionButtons.js.map +7 -0
  457. package/dist/backend/forms/FormFooter.js +18 -0
  458. package/dist/backend/forms/FormFooter.js.map +7 -0
  459. package/dist/backend/forms/FormHeader.js +125 -0
  460. package/dist/backend/forms/FormHeader.js.map +7 -0
  461. package/dist/backend/forms/index.js +11 -0
  462. package/dist/backend/forms/index.js.map +7 -0
  463. package/dist/backend/hooks/useAdvancedFilter.js +64 -0
  464. package/dist/backend/hooks/useAdvancedFilter.js.map +7 -0
  465. package/dist/backend/indexes/PartialIndexBanner.js +74 -0
  466. package/dist/backend/indexes/PartialIndexBanner.js.map +7 -0
  467. package/dist/backend/indexes/store.js +62 -0
  468. package/dist/backend/indexes/store.js.map +7 -0
  469. package/dist/backend/injection/ComponentOverrideProvider.js +55 -0
  470. package/dist/backend/injection/ComponentOverrideProvider.js.map +7 -0
  471. package/dist/backend/injection/InjectedField.js +166 -0
  472. package/dist/backend/injection/InjectedField.js.map +7 -0
  473. package/dist/backend/injection/InjectionSpot.js +323 -0
  474. package/dist/backend/injection/InjectionSpot.js.map +7 -0
  475. package/dist/backend/injection/InjectionWizard.js +256 -0
  476. package/dist/backend/injection/InjectionWizard.js.map +7 -0
  477. package/dist/backend/injection/PageInjectionBoundary.js +26 -0
  478. package/dist/backend/injection/PageInjectionBoundary.js.map +7 -0
  479. package/dist/backend/injection/SseEventIndicator.js +8 -0
  480. package/dist/backend/injection/SseEventIndicator.js.map +7 -0
  481. package/dist/backend/injection/StatusBadgeInjectionSpot.js +34 -0
  482. package/dist/backend/injection/StatusBadgeInjectionSpot.js.map +7 -0
  483. package/dist/backend/injection/StatusBadgeRenderer.js +58 -0
  484. package/dist/backend/injection/StatusBadgeRenderer.js.map +7 -0
  485. package/dist/backend/injection/WidgetSharedState.js +49 -0
  486. package/dist/backend/injection/WidgetSharedState.js.map +7 -0
  487. package/dist/backend/injection/WizardInjectionSpot.js +30 -0
  488. package/dist/backend/injection/WizardInjectionSpot.js.map +7 -0
  489. package/dist/backend/injection/eventBridge.js +129 -0
  490. package/dist/backend/injection/eventBridge.js.map +7 -0
  491. package/dist/backend/injection/helpers.js +26 -0
  492. package/dist/backend/injection/helpers.js.map +7 -0
  493. package/dist/backend/injection/mergeMenuItems.js +49 -0
  494. package/dist/backend/injection/mergeMenuItems.js.map +7 -0
  495. package/dist/backend/injection/mutationEvents.js +20 -0
  496. package/dist/backend/injection/mutationEvents.js.map +7 -0
  497. package/dist/backend/injection/resolveInjectedIcon.js +23 -0
  498. package/dist/backend/injection/resolveInjectedIcon.js.map +7 -0
  499. package/dist/backend/injection/spotIds.js +72 -0
  500. package/dist/backend/injection/spotIds.js.map +7 -0
  501. package/dist/backend/injection/useAppEvent.js +35 -0
  502. package/dist/backend/injection/useAppEvent.js.map +7 -0
  503. package/dist/backend/injection/useGuardedMutation.js +71 -0
  504. package/dist/backend/injection/useGuardedMutation.js.map +7 -0
  505. package/dist/backend/injection/useInjectedMenuItems.js +49 -0
  506. package/dist/backend/injection/useInjectedMenuItems.js.map +7 -0
  507. package/dist/backend/injection/useInjectionDataWidgets.js +36 -0
  508. package/dist/backend/injection/useInjectionDataWidgets.js.map +7 -0
  509. package/dist/backend/injection/useOperationProgress.js +64 -0
  510. package/dist/backend/injection/useOperationProgress.js.map +7 -0
  511. package/dist/backend/injection/useRegisteredComponent.js +89 -0
  512. package/dist/backend/injection/useRegisteredComponent.js.map +7 -0
  513. package/dist/backend/injection/useWidgetSharedState.js +26 -0
  514. package/dist/backend/injection/useWidgetSharedState.js.map +7 -0
  515. package/dist/backend/injection/visibility-utils.js +29 -0
  516. package/dist/backend/injection/visibility-utils.js.map +7 -0
  517. package/dist/backend/injection/widgetRegistry.js +55 -0
  518. package/dist/backend/injection/widgetRegistry.js.map +7 -0
  519. package/dist/backend/inputs/ComboboxInput.js +228 -0
  520. package/dist/backend/inputs/ComboboxInput.js.map +7 -0
  521. package/dist/backend/inputs/DatePicker.js +148 -0
  522. package/dist/backend/inputs/DatePicker.js.map +7 -0
  523. package/dist/backend/inputs/DateTimePicker.js +188 -0
  524. package/dist/backend/inputs/DateTimePicker.js.map +7 -0
  525. package/dist/backend/inputs/EventPatternInput.js +38 -0
  526. package/dist/backend/inputs/EventPatternInput.js.map +7 -0
  527. package/dist/backend/inputs/EventSelect.js +115 -0
  528. package/dist/backend/inputs/EventSelect.js.map +7 -0
  529. package/dist/backend/inputs/LookupSelect.js +197 -0
  530. package/dist/backend/inputs/LookupSelect.js.map +7 -0
  531. package/dist/backend/inputs/PhoneNumberField.js +133 -0
  532. package/dist/backend/inputs/PhoneNumberField.js.map +7 -0
  533. package/dist/backend/inputs/SwitchableMarkdownInput.js +73 -0
  534. package/dist/backend/inputs/SwitchableMarkdownInput.js.map +7 -0
  535. package/dist/backend/inputs/TagsInput.js +258 -0
  536. package/dist/backend/inputs/TagsInput.js.map +7 -0
  537. package/dist/backend/inputs/TimeInput.js +132 -0
  538. package/dist/backend/inputs/TimeInput.js.map +7 -0
  539. package/dist/backend/inputs/TimePicker.js +105 -0
  540. package/dist/backend/inputs/TimePicker.js.map +7 -0
  541. package/dist/backend/inputs/index.js +12 -0
  542. package/dist/backend/inputs/index.js.map +7 -0
  543. package/dist/backend/markdown/MarkdownContent.js +20 -0
  544. package/dist/backend/markdown/MarkdownContent.js.map +7 -0
  545. package/dist/backend/markdown/index.js +3 -0
  546. package/dist/backend/markdown/index.js.map +7 -0
  547. package/dist/backend/markdown/useMarkdownRemarkPlugins.js +33 -0
  548. package/dist/backend/markdown/useMarkdownRemarkPlugins.js.map +7 -0
  549. package/dist/backend/messages/MessageComposer.js +158 -0
  550. package/dist/backend/messages/MessageComposer.js.map +7 -0
  551. package/dist/backend/messages/MessageObjectDetail.js +81 -0
  552. package/dist/backend/messages/MessageObjectDetail.js.map +7 -0
  553. package/dist/backend/messages/MessageObjectPreview.js +48 -0
  554. package/dist/backend/messages/MessageObjectPreview.js.map +7 -0
  555. package/dist/backend/messages/MessageObjectRecordPicker.js +89 -0
  556. package/dist/backend/messages/MessageObjectRecordPicker.js.map +7 -0
  557. package/dist/backend/messages/MessagePrioritySelector.js +74 -0
  558. package/dist/backend/messages/MessagePrioritySelector.js.map +7 -0
  559. package/dist/backend/messages/MessagesIcon.js +22 -0
  560. package/dist/backend/messages/MessagesIcon.js.map +7 -0
  561. package/dist/backend/messages/SendObjectMessageDialog.js +65 -0
  562. package/dist/backend/messages/SendObjectMessageDialog.js.map +7 -0
  563. package/dist/backend/messages/index.js +17 -0
  564. package/dist/backend/messages/index.js.map +7 -0
  565. package/dist/backend/messages/message-compose-form-groups.js +333 -0
  566. package/dist/backend/messages/message-compose-form-groups.js.map +7 -0
  567. package/dist/backend/messages/message-composer.types.js +1 -0
  568. package/dist/backend/messages/message-composer.types.js.map +7 -0
  569. package/dist/backend/messages/message-priority.js +40 -0
  570. package/dist/backend/messages/message-priority.js.map +7 -0
  571. package/dist/backend/messages/useMessageCompose.js +488 -0
  572. package/dist/backend/messages/useMessageCompose.js.map +7 -0
  573. package/dist/backend/messages/useMessageComposeOperations.js +163 -0
  574. package/dist/backend/messages/useMessageComposeOperations.js.map +7 -0
  575. package/dist/backend/messages/useMessagesPoll.js +79 -0
  576. package/dist/backend/messages/useMessagesPoll.js.map +7 -0
  577. package/dist/backend/notifications/NotificationBell.js +83 -0
  578. package/dist/backend/notifications/NotificationBell.js.map +7 -0
  579. package/dist/backend/notifications/NotificationCountBadge.js +9 -0
  580. package/dist/backend/notifications/NotificationCountBadge.js.map +7 -0
  581. package/dist/backend/notifications/NotificationDispatcher.js +165 -0
  582. package/dist/backend/notifications/NotificationDispatcher.js.map +7 -0
  583. package/dist/backend/notifications/NotificationItem.js +187 -0
  584. package/dist/backend/notifications/NotificationItem.js.map +7 -0
  585. package/dist/backend/notifications/NotificationPanel.js +146 -0
  586. package/dist/backend/notifications/NotificationPanel.js.map +7 -0
  587. package/dist/backend/notifications/index.js +19 -0
  588. package/dist/backend/notifications/index.js.map +7 -0
  589. package/dist/backend/notifications/useNotificationActions.js +124 -0
  590. package/dist/backend/notifications/useNotificationActions.js.map +7 -0
  591. package/dist/backend/notifications/useNotificationEffect.js +13 -0
  592. package/dist/backend/notifications/useNotificationEffect.js.map +7 -0
  593. package/dist/backend/notifications/useNotifications.js +11 -0
  594. package/dist/backend/notifications/useNotifications.js.map +7 -0
  595. package/dist/backend/notifications/useNotificationsPoll.js +135 -0
  596. package/dist/backend/notifications/useNotificationsPoll.js.map +7 -0
  597. package/dist/backend/notifications/useNotificationsSse.js +173 -0
  598. package/dist/backend/notifications/useNotificationsSse.js.map +7 -0
  599. package/dist/backend/operations/LastOperationBanner.js +80 -0
  600. package/dist/backend/operations/LastOperationBanner.js.map +7 -0
  601. package/dist/backend/operations/store.js +183 -0
  602. package/dist/backend/operations/store.js.map +7 -0
  603. package/dist/backend/progress/ProgressTopBar.js +129 -0
  604. package/dist/backend/progress/ProgressTopBar.js.map +7 -0
  605. package/dist/backend/progress/useProgress.js +11 -0
  606. package/dist/backend/progress/useProgress.js.map +7 -0
  607. package/dist/backend/progress/useProgressPoll.js +69 -0
  608. package/dist/backend/progress/useProgressPoll.js.map +7 -0
  609. package/dist/backend/progress/useProgressSse.js +104 -0
  610. package/dist/backend/progress/useProgressSse.js.map +7 -0
  611. package/dist/backend/schedule/ScheduleAgenda.js +107 -0
  612. package/dist/backend/schedule/ScheduleAgenda.js.map +7 -0
  613. package/dist/backend/schedule/ScheduleGrid.js +107 -0
  614. package/dist/backend/schedule/ScheduleGrid.js.map +7 -0
  615. package/dist/backend/schedule/ScheduleToolbar.js +166 -0
  616. package/dist/backend/schedule/ScheduleToolbar.js.map +7 -0
  617. package/dist/backend/schedule/ScheduleView.js +168 -0
  618. package/dist/backend/schedule/ScheduleView.js.map +7 -0
  619. package/dist/backend/schedule/index.js +6 -0
  620. package/dist/backend/schedule/index.js.map +7 -0
  621. package/dist/backend/schedule/recurrence.js +83 -0
  622. package/dist/backend/schedule/recurrence.js.map +7 -0
  623. package/dist/backend/schedule/types.js +1 -0
  624. package/dist/backend/schedule/types.js.map +7 -0
  625. package/dist/backend/section-page/SectionNav.js +104 -0
  626. package/dist/backend/section-page/SectionNav.js.map +7 -0
  627. package/dist/backend/section-page/SectionPage.js +33 -0
  628. package/dist/backend/section-page/SectionPage.js.map +7 -0
  629. package/dist/backend/section-page/index.js +7 -0
  630. package/dist/backend/section-page/index.js.map +7 -0
  631. package/dist/backend/section-page/types.js +1 -0
  632. package/dist/backend/section-page/types.js.map +7 -0
  633. package/dist/backend/settings/SettingsNavigation.js +43 -0
  634. package/dist/backend/settings/SettingsNavigation.js.map +7 -0
  635. package/dist/backend/settings/SettingsPageWrapper.js +66 -0
  636. package/dist/backend/settings/SettingsPageWrapper.js.map +7 -0
  637. package/dist/backend/settings/index.js +6 -0
  638. package/dist/backend/settings/index.js.map +7 -0
  639. package/dist/backend/upgrades/UpgradeActionBanner.js +91 -0
  640. package/dist/backend/upgrades/UpgradeActionBanner.js.map +7 -0
  641. package/dist/backend/utils/api.js +178 -0
  642. package/dist/backend/utils/api.js.map +7 -0
  643. package/dist/backend/utils/apiCall.js +68 -0
  644. package/dist/backend/utils/apiCall.js.map +7 -0
  645. package/dist/backend/utils/crud.js +126 -0
  646. package/dist/backend/utils/crud.js.map +7 -0
  647. package/dist/backend/utils/customFieldColumns.js +100 -0
  648. package/dist/backend/utils/customFieldColumns.js.map +7 -0
  649. package/dist/backend/utils/customFieldDefs.js +143 -0
  650. package/dist/backend/utils/customFieldDefs.js.map +7 -0
  651. package/dist/backend/utils/customFieldFilters.js +151 -0
  652. package/dist/backend/utils/customFieldFilters.js.map +7 -0
  653. package/dist/backend/utils/customFieldForms.js +169 -0
  654. package/dist/backend/utils/customFieldForms.js.map +7 -0
  655. package/dist/backend/utils/customFieldRelationDisplay.js +239 -0
  656. package/dist/backend/utils/customFieldRelationDisplay.js.map +7 -0
  657. package/dist/backend/utils/customFieldValues.js +26 -0
  658. package/dist/backend/utils/customFieldValues.js.map +7 -0
  659. package/dist/backend/utils/flash.js +16 -0
  660. package/dist/backend/utils/flash.js.map +7 -0
  661. package/dist/backend/utils/nav.js +280 -0
  662. package/dist/backend/utils/nav.js.map +7 -0
  663. package/dist/backend/utils/richTextSanitizer.js +11 -0
  664. package/dist/backend/utils/richTextSanitizer.js.map +7 -0
  665. package/dist/backend/utils/scopedHeaderStack.js +44 -0
  666. package/dist/backend/utils/scopedHeaderStack.js.map +7 -0
  667. package/dist/backend/utils/serverErrors.js +237 -0
  668. package/dist/backend/utils/serverErrors.js.map +7 -0
  669. package/dist/backend/utils/useAutoDiscoveredFields.js +96 -0
  670. package/dist/backend/utils/useAutoDiscoveredFields.js.map +7 -0
  671. package/dist/backend/version-history/VersionHistoryAction.js +67 -0
  672. package/dist/backend/version-history/VersionHistoryAction.js.map +7 -0
  673. package/dist/backend/version-history/VersionHistoryDetail.js +57 -0
  674. package/dist/backend/version-history/VersionHistoryDetail.js.map +7 -0
  675. package/dist/backend/version-history/VersionHistoryPanel.js +270 -0
  676. package/dist/backend/version-history/VersionHistoryPanel.js.map +7 -0
  677. package/dist/backend/version-history/index.js +13 -0
  678. package/dist/backend/version-history/index.js.map +7 -0
  679. package/dist/backend/version-history/labels.js +52 -0
  680. package/dist/backend/version-history/labels.js.map +7 -0
  681. package/dist/backend/version-history/types.js +1 -0
  682. package/dist/backend/version-history/types.js.map +7 -0
  683. package/dist/backend/version-history/useAuditPermissions.js +72 -0
  684. package/dist/backend/version-history/useAuditPermissions.js.map +7 -0
  685. package/dist/backend/version-history/useVersionHistory.js +141 -0
  686. package/dist/backend/version-history/useVersionHistory.js.map +7 -0
  687. package/dist/backend/views/NewViewForm.js +56 -0
  688. package/dist/backend/views/NewViewForm.js.map +7 -0
  689. package/dist/backend/views/ShareForm.js +49 -0
  690. package/dist/backend/views/ShareForm.js.map +7 -0
  691. package/dist/backend/views/ViewChip.js +112 -0
  692. package/dist/backend/views/ViewChip.js.map +7 -0
  693. package/dist/backend/views/ViewChipMenu.js +89 -0
  694. package/dist/backend/views/ViewChipMenu.js.map +7 -0
  695. package/dist/backend/views/types.js +1 -0
  696. package/dist/backend/views/types.js.map +7 -0
  697. package/dist/frontend/AuthFooter.js +23 -0
  698. package/dist/frontend/AuthFooter.js.map +7 -0
  699. package/dist/frontend/LanguageSwitcher.js +57 -0
  700. package/dist/frontend/LanguageSwitcher.js.map +7 -0
  701. package/dist/frontend/Layout.js +14 -0
  702. package/dist/frontend/Layout.js.map +7 -0
  703. package/dist/hooks/useIsMobile.js +21 -0
  704. package/dist/hooks/useIsMobile.js.map +7 -0
  705. package/dist/index.js +40 -0
  706. package/dist/index.js.map +7 -0
  707. package/dist/portal/PortalContext.js +113 -0
  708. package/dist/portal/PortalContext.js.map +7 -0
  709. package/dist/portal/PortalLayoutProvider.js +5 -0
  710. package/dist/portal/PortalLayoutProvider.js.map +7 -0
  711. package/dist/portal/PortalLayoutShell.js +44 -0
  712. package/dist/portal/PortalLayoutShell.js.map +7 -0
  713. package/dist/portal/PortalShell.js +229 -0
  714. package/dist/portal/PortalShell.js.map +7 -0
  715. package/dist/portal/components/PortalCard.js +31 -0
  716. package/dist/portal/components/PortalCard.js.map +7 -0
  717. package/dist/portal/components/PortalEmptyState.js +16 -0
  718. package/dist/portal/components/PortalEmptyState.js.map +7 -0
  719. package/dist/portal/components/PortalFeatureCard.js +21 -0
  720. package/dist/portal/components/PortalFeatureCard.js.map +7 -0
  721. package/dist/portal/components/PortalNotificationBell.js +67 -0
  722. package/dist/portal/components/PortalNotificationBell.js.map +7 -0
  723. package/dist/portal/components/PortalNotificationPanel.js +158 -0
  724. package/dist/portal/components/PortalNotificationPanel.js.map +7 -0
  725. package/dist/portal/components/PortalPageHeader.js +16 -0
  726. package/dist/portal/components/PortalPageHeader.js.map +7 -0
  727. package/dist/portal/components/index.js +18 -0
  728. package/dist/portal/components/index.js.map +7 -0
  729. package/dist/portal/hooks/useCustomerAuth.js +69 -0
  730. package/dist/portal/hooks/useCustomerAuth.js.map +7 -0
  731. package/dist/portal/hooks/usePortalAppEvent.js +26 -0
  732. package/dist/portal/hooks/usePortalAppEvent.js.map +7 -0
  733. package/dist/portal/hooks/usePortalDashboardWidgets.js +37 -0
  734. package/dist/portal/hooks/usePortalDashboardWidgets.js.map +7 -0
  735. package/dist/portal/hooks/usePortalEventBridge.js +129 -0
  736. package/dist/portal/hooks/usePortalEventBridge.js.map +7 -0
  737. package/dist/portal/hooks/usePortalInjectedMenuItems.js +88 -0
  738. package/dist/portal/hooks/usePortalInjectedMenuItems.js.map +7 -0
  739. package/dist/portal/hooks/usePortalNotifications.js +85 -0
  740. package/dist/portal/hooks/usePortalNotifications.js.map +7 -0
  741. package/dist/portal/hooks/useTenantContext.js +53 -0
  742. package/dist/portal/hooks/useTenantContext.js.map +7 -0
  743. package/dist/primitives/DataLoader.js +68 -0
  744. package/dist/primitives/DataLoader.js.map +7 -0
  745. package/dist/primitives/ErrorNotice.js +14 -0
  746. package/dist/primitives/ErrorNotice.js.map +7 -0
  747. package/dist/primitives/Notice.js +55 -0
  748. package/dist/primitives/Notice.js.map +7 -0
  749. package/dist/primitives/alert.js +38 -0
  750. package/dist/primitives/alert.js.map +7 -0
  751. package/dist/primitives/badge.js +33 -0
  752. package/dist/primitives/badge.js.map +7 -0
  753. package/dist/primitives/button.js +45 -0
  754. package/dist/primitives/button.js.map +7 -0
  755. package/dist/primitives/calendar.js +62 -0
  756. package/dist/primitives/calendar.js.map +7 -0
  757. package/dist/primitives/card.js +91 -0
  758. package/dist/primitives/card.js.map +7 -0
  759. package/dist/primitives/checkbox.js +28 -0
  760. package/dist/primitives/checkbox.js.map +7 -0
  761. package/dist/primitives/dialog.js +101 -0
  762. package/dist/primitives/dialog.js.map +7 -0
  763. package/dist/primitives/form-field.js +78 -0
  764. package/dist/primitives/form-field.js.map +7 -0
  765. package/dist/primitives/icon-button.js +40 -0
  766. package/dist/primitives/icon-button.js.map +7 -0
  767. package/dist/primitives/input.js +22 -0
  768. package/dist/primitives/input.js.map +7 -0
  769. package/dist/primitives/label.js +21 -0
  770. package/dist/primitives/label.js.map +7 -0
  771. package/dist/primitives/popover.js +36 -0
  772. package/dist/primitives/popover.js.map +7 -0
  773. package/dist/primitives/primitives.stories.js +187 -0
  774. package/dist/primitives/primitives.stories.js.map +7 -0
  775. package/dist/primitives/progress.js +28 -0
  776. package/dist/primitives/progress.js.map +7 -0
  777. package/dist/primitives/separator.js +9 -0
  778. package/dist/primitives/separator.js.map +7 -0
  779. package/dist/primitives/sheet.js +103 -0
  780. package/dist/primitives/sheet.js.map +7 -0
  781. package/dist/primitives/spinner.js +24 -0
  782. package/dist/primitives/spinner.js.map +7 -0
  783. package/dist/primitives/status-badge.js +38 -0
  784. package/dist/primitives/status-badge.js.map +7 -0
  785. package/dist/primitives/switch.js +80 -0
  786. package/dist/primitives/switch.js.map +7 -0
  787. package/dist/primitives/table.js +29 -0
  788. package/dist/primitives/table.js.map +7 -0
  789. package/dist/primitives/tabs.js +90 -0
  790. package/dist/primitives/tabs.js.map +7 -0
  791. package/dist/primitives/textarea.js +21 -0
  792. package/dist/primitives/textarea.js.map +7 -0
  793. package/dist/primitives/tooltip.js +60 -0
  794. package/dist/primitives/tooltip.js.map +7 -0
  795. package/dist/primitives-v2/Alert.js +69 -0
  796. package/dist/primitives-v2/Alert.js.map +7 -0
  797. package/dist/primitives-v2/Alert.stories.js +42 -0
  798. package/dist/primitives-v2/Alert.stories.js.map +7 -0
  799. package/dist/primitives-v2/Avatar.js +93 -0
  800. package/dist/primitives-v2/Avatar.js.map +7 -0
  801. package/dist/primitives-v2/Avatar.stories.js +57 -0
  802. package/dist/primitives-v2/Avatar.stories.js.map +7 -0
  803. package/dist/primitives-v2/Badge.js +50 -0
  804. package/dist/primitives-v2/Badge.js.map +7 -0
  805. package/dist/primitives-v2/Badge.stories.js +66 -0
  806. package/dist/primitives-v2/Badge.stories.js.map +7 -0
  807. package/dist/primitives-v2/Breadcrumb.js +53 -0
  808. package/dist/primitives-v2/Breadcrumb.js.map +7 -0
  809. package/dist/primitives-v2/Breadcrumb.stories.js +70 -0
  810. package/dist/primitives-v2/Breadcrumb.stories.js.map +7 -0
  811. package/dist/primitives-v2/Button.js +85 -0
  812. package/dist/primitives-v2/Button.js.map +7 -0
  813. package/dist/primitives-v2/Button.stories.js +48 -0
  814. package/dist/primitives-v2/Button.stories.js.map +7 -0
  815. package/dist/primitives-v2/Checkbox.js +65 -0
  816. package/dist/primitives-v2/Checkbox.js.map +7 -0
  817. package/dist/primitives-v2/Checkbox.stories.js +31 -0
  818. package/dist/primitives-v2/Checkbox.stories.js.map +7 -0
  819. package/dist/primitives-v2/IconButton.js +13 -0
  820. package/dist/primitives-v2/IconButton.js.map +7 -0
  821. package/dist/primitives-v2/IconButton.stories.js +37 -0
  822. package/dist/primitives-v2/IconButton.stories.js.map +7 -0
  823. package/dist/primitives-v2/Input.js +74 -0
  824. package/dist/primitives-v2/Input.js.map +7 -0
  825. package/dist/primitives-v2/Input.stories.js +56 -0
  826. package/dist/primitives-v2/Input.stories.js.map +7 -0
  827. package/dist/primitives-v2/NavGroup.js +54 -0
  828. package/dist/primitives-v2/NavGroup.js.map +7 -0
  829. package/dist/primitives-v2/NavGroup.stories.js +39 -0
  830. package/dist/primitives-v2/NavGroup.stories.js.map +7 -0
  831. package/dist/primitives-v2/NavItem.js +61 -0
  832. package/dist/primitives-v2/NavItem.js.map +7 -0
  833. package/dist/primitives-v2/NavItem.stories.js +56 -0
  834. package/dist/primitives-v2/NavItem.stories.js.map +7 -0
  835. package/dist/primitives-v2/SearchInput.js +93 -0
  836. package/dist/primitives-v2/SearchInput.js.map +7 -0
  837. package/dist/primitives-v2/SearchInput.stories.js +41 -0
  838. package/dist/primitives-v2/SearchInput.stories.js.map +7 -0
  839. package/dist/primitives-v2/Sidebar.js +48 -0
  840. package/dist/primitives-v2/Sidebar.js.map +7 -0
  841. package/dist/primitives-v2/Sidebar.stories.js +76 -0
  842. package/dist/primitives-v2/Sidebar.stories.js.map +7 -0
  843. package/dist/primitives-v2/Tab.js +78 -0
  844. package/dist/primitives-v2/Tab.js.map +7 -0
  845. package/dist/primitives-v2/Tab.stories.js +51 -0
  846. package/dist/primitives-v2/Tab.stories.js.map +7 -0
  847. package/dist/primitives-v2/Tag.js +61 -0
  848. package/dist/primitives-v2/Tag.js.map +7 -0
  849. package/dist/primitives-v2/Tag.stories.js +121 -0
  850. package/dist/primitives-v2/Tag.stories.js.map +7 -0
  851. package/dist/primitives-v2/Toggle.js +72 -0
  852. package/dist/primitives-v2/Toggle.js.map +7 -0
  853. package/dist/primitives-v2/Toggle.stories.js +34 -0
  854. package/dist/primitives-v2/Toggle.stories.js.map +7 -0
  855. package/dist/primitives-v2/Topbar.js +24 -0
  856. package/dist/primitives-v2/Topbar.js.map +7 -0
  857. package/dist/primitives-v2/Topbar.stories.js +58 -0
  858. package/dist/primitives-v2/Topbar.stories.js.map +7 -0
  859. package/dist/primitives-v2/index.js +41 -0
  860. package/dist/primitives-v2/index.js.map +7 -0
  861. package/dist/primitives-v2/utils.js +7 -0
  862. package/dist/primitives-v2/utils.js.map +7 -0
  863. package/dist/theme/BrandThemeProvider.js +49 -0
  864. package/dist/theme/BrandThemeProvider.js.map +7 -0
  865. package/dist/theme/QueryProvider.js +44 -0
  866. package/dist/theme/QueryProvider.js.map +7 -0
  867. package/dist/theme/ThemeProvider.js +96 -0
  868. package/dist/theme/ThemeProvider.js.map +7 -0
  869. package/dist/theme/ThemeToggle.js +88 -0
  870. package/dist/theme/ThemeToggle.js.map +7 -0
  871. package/dist/theme/index.js +12 -0
  872. package/dist/theme/index.js.map +7 -0
  873. package/dist/theme/m3.css.js +2 -0
  874. package/dist/theme/tokens.v2.css.js +2 -0
  875. package/dist/theme/typography.css.js +2 -0
  876. package/dist/theme/typography.stories.js +123 -0
  877. package/dist/theme/typography.stories.js.map +7 -0
  878. package/dist/theme/typography.tokens.js +281 -0
  879. package/dist/theme/typography.tokens.js.map +7 -0
  880. package/dist/types/react-big-calendar.d.js +1 -0
  881. package/dist/types/react-big-calendar.d.js.map +7 -0
  882. package/package.json +411 -0
  883. package/src/backend/AppShell.tsx +1606 -0
  884. package/src/backend/BackendChromeProvider.tsx +99 -0
  885. package/src/backend/BackendPageSurface.tsx +57 -0
  886. package/src/backend/CollapsibleNavSection.tsx +201 -0
  887. package/src/backend/ContextHelp.tsx +40 -0
  888. package/src/backend/CrudForm.tsx +3796 -0
  889. package/src/backend/DataTable.tsx +2584 -0
  890. package/src/backend/EmptyState.tsx +65 -0
  891. package/src/backend/FilterBar.tsx +171 -0
  892. package/src/backend/FilterOverlay.tsx +388 -0
  893. package/src/backend/FlashMessages.tsx +189 -0
  894. package/src/backend/HtmlRichTextEditor.tsx +124 -0
  895. package/src/backend/IntegrationsButton.tsx +29 -0
  896. package/src/backend/JsonBuilder.tsx +359 -0
  897. package/src/backend/JsonDisplay.tsx +258 -0
  898. package/src/backend/NextStepCallout.tsx +134 -0
  899. package/src/backend/Page.tsx +30 -0
  900. package/src/backend/PerspectiveSidebar.tsx +506 -0
  901. package/src/backend/ProfileDropdown.tsx +369 -0
  902. package/src/backend/RowActions.tsx +158 -0
  903. package/src/backend/SectionHeader.tsx +119 -0
  904. package/src/backend/SettingsButton.tsx +28 -0
  905. package/src/backend/Sidebar.stories.tsx +183 -0
  906. package/src/backend/Sidebar.tsx +737 -0
  907. package/src/backend/TruncatedCell.tsx +133 -0
  908. package/src/backend/UserMenu.tsx +153 -0
  909. package/src/backend/ValueIcons.tsx +48 -0
  910. package/src/backend/WebhookSetupGuide.tsx +127 -0
  911. package/src/backend/__tests__/AppShell.test.tsx +392 -0
  912. package/src/backend/__tests__/CrudForm.custom-fields.test.tsx +180 -0
  913. package/src/backend/__tests__/CrudForm.datetime.test.tsx +95 -0
  914. package/src/backend/__tests__/CrudForm.defaults.test.tsx +195 -0
  915. package/src/backend/__tests__/CrudForm.navigation.test.tsx +156 -0
  916. package/src/backend/__tests__/CrudForm.render.test.tsx +146 -0
  917. package/src/backend/__tests__/CrudForm.transformData.test.tsx +231 -0
  918. package/src/backend/__tests__/CrudForm.unsavedNavigation.test.tsx +145 -0
  919. package/src/backend/__tests__/CrudForm.validation.test.tsx +178 -0
  920. package/src/backend/__tests__/CustomDataSection.test.tsx +465 -0
  921. package/src/backend/__tests__/DataTable.extensions.test.tsx +339 -0
  922. package/src/backend/__tests__/DataTable.namespaces.test.ts +32 -0
  923. package/src/backend/__tests__/DataTable.render.test.tsx +48 -0
  924. package/src/backend/__tests__/FieldDefinitionsEditor.test.tsx +64 -0
  925. package/src/backend/__tests__/FlashMessages.test.tsx +105 -0
  926. package/src/backend/__tests__/FormHeader.test.tsx +81 -0
  927. package/src/backend/__tests__/NotificationDispatcher.test.ts +172 -0
  928. package/src/backend/__tests__/VersionHistoryPanel.test.tsx +109 -0
  929. package/src/backend/__tests__/component-replacement.test.tsx +270 -0
  930. package/src/backend/__tests__/custom-field-filters.test.ts +72 -0
  931. package/src/backend/__tests__/custom-field-forms.test.ts +54 -0
  932. package/src/backend/__tests__/nav-utils.test.ts +175 -0
  933. package/src/backend/__tests__/serverErrors.test.ts +83 -0
  934. package/src/backend/__tests__/useAuditPermissions.test.tsx +51 -0
  935. package/src/backend/__tests__/useNotifications.strategy.test.ts +58 -0
  936. package/src/backend/__tests__/useProgress.strategy.test.ts +62 -0
  937. package/src/backend/charts/BarChart.tsx +208 -0
  938. package/src/backend/charts/ChartUtils.tsx +196 -0
  939. package/src/backend/charts/ComboChart.stories.tsx +103 -0
  940. package/src/backend/charts/ComboChart.tsx +350 -0
  941. package/src/backend/charts/KpiCard.tsx +155 -0
  942. package/src/backend/charts/LineChart.tsx +207 -0
  943. package/src/backend/charts/PieChart.tsx +157 -0
  944. package/src/backend/charts/ScatterChart.tsx +498 -0
  945. package/src/backend/charts/TopNTable.tsx +141 -0
  946. package/src/backend/charts/WaterfallChart.tsx +213 -0
  947. package/src/backend/charts/index.ts +21 -0
  948. package/src/backend/columns/ColumnChooserPanel.tsx +293 -0
  949. package/src/backend/confirm-dialog/ConfirmDialog.tsx +275 -0
  950. package/src/backend/confirm-dialog/index.ts +7 -0
  951. package/src/backend/confirm-dialog/useConfirmDialog.tsx +146 -0
  952. package/src/backend/custom-fields/FieldDefinitionsEditor.tsx +1413 -0
  953. package/src/backend/custom-fields/FieldDefinitionsManager.tsx +381 -0
  954. package/src/backend/dashboard/DashboardScreen.tsx +706 -0
  955. package/src/backend/dashboard/__tests__/DashboardScreen.test.tsx +114 -0
  956. package/src/backend/dashboard/index.ts +1 -0
  957. package/src/backend/dashboard/widgetRegistry.ts +68 -0
  958. package/src/backend/date-range/DateRangeSelect.tsx +50 -0
  959. package/src/backend/date-range/InlineDateRangeSelect.tsx +52 -0
  960. package/src/backend/date-range/InlineGranularitySelect.tsx +63 -0
  961. package/src/backend/date-range/__tests__/dateRanges.test.ts +224 -0
  962. package/src/backend/date-range/dateRanges.ts +218 -0
  963. package/src/backend/date-range/index.ts +15 -0
  964. package/src/backend/detail/AccessDeniedMessage.tsx +32 -0
  965. package/src/backend/detail/ActivitiesSection.tsx +1261 -0
  966. package/src/backend/detail/AddressEditor.tsx +479 -0
  967. package/src/backend/detail/AddressTiles.tsx +591 -0
  968. package/src/backend/detail/AddressesSection.tsx +362 -0
  969. package/src/backend/detail/AttachmentDeleteDialog.tsx +56 -0
  970. package/src/backend/detail/AttachmentMetadataDialog.tsx +674 -0
  971. package/src/backend/detail/AttachmentVisualPreview.tsx +155 -0
  972. package/src/backend/detail/AttachmentsSection.tsx +327 -0
  973. package/src/backend/detail/CustomDataSection.tsx +700 -0
  974. package/src/backend/detail/DetailFieldsSection.tsx +163 -0
  975. package/src/backend/detail/ErrorMessage.tsx +32 -0
  976. package/src/backend/detail/InlineEditors.tsx +894 -0
  977. package/src/backend/detail/LoadingMessage.tsx +14 -0
  978. package/src/backend/detail/NotesSection.tsx +1266 -0
  979. package/src/backend/detail/TabEmptyState.tsx +48 -0
  980. package/src/backend/detail/TagsSection.tsx +470 -0
  981. package/src/backend/detail/__tests__/InlineEditors.test.tsx +59 -0
  982. package/src/backend/detail/addressFormat.tsx +121 -0
  983. package/src/backend/detail/index.ts +46 -0
  984. package/src/backend/devtools/UmesDevToolsPanel.tsx +122 -0
  985. package/src/backend/devtools/components/ConflictWarnings.tsx +36 -0
  986. package/src/backend/devtools/components/EnricherTiming.tsx +50 -0
  987. package/src/backend/devtools/components/EventFlow.tsx +45 -0
  988. package/src/backend/devtools/components/ExtensionPointList.tsx +59 -0
  989. package/src/backend/devtools/components/InterceptorActivity.tsx +48 -0
  990. package/src/backend/devtools/index.ts +2 -0
  991. package/src/backend/devtools/useUmesDevTools.ts +188 -0
  992. package/src/backend/document-viewer/RegionOverlay.tsx +150 -0
  993. package/src/backend/document-viewer/index.ts +2 -0
  994. package/src/backend/dynamic-grid/DynamicGrid.tsx +161 -0
  995. package/src/backend/dynamic-grid/adapters.ts +57 -0
  996. package/src/backend/dynamic-grid/components/GridCell.tsx +194 -0
  997. package/src/backend/dynamic-grid/components/GridRow.tsx +40 -0
  998. package/src/backend/dynamic-grid/hooks/useGridKeyboardNav.ts +134 -0
  999. package/src/backend/dynamic-grid/index.ts +4 -0
  1000. package/src/backend/dynamic-grid/types.ts +55 -0
  1001. package/src/backend/dynamic-table/DynamicTable.stories.tsx +274 -0
  1002. package/src/backend/dynamic-table/DynamicTable.tsx +4419 -0
  1003. package/src/backend/dynamic-table/README.md +1199 -0
  1004. package/src/backend/dynamic-table/URL_FILTERING.md +217 -0
  1005. package/src/backend/dynamic-table/__e2e__/GridHarness.ts +1155 -0
  1006. package/src/backend/dynamic-table/__e2e__/SplitViewHarness.ts +366 -0
  1007. package/src/backend/dynamic-table/__tests__/BulkActionsBar.test.tsx +143 -0
  1008. package/src/backend/dynamic-table/__tests__/ColumnQuickFilter.test.tsx +607 -0
  1009. package/src/backend/dynamic-table/__tests__/ConfigureViewFilters.suggestions.test.tsx +177 -0
  1010. package/src/backend/dynamic-table/__tests__/ConfigureViewFormatting.test.tsx +125 -0
  1011. package/src/backend/dynamic-table/__tests__/ConfigureViewFormulas.test.tsx +228 -0
  1012. package/src/backend/dynamic-table/__tests__/ConfigureViewGrouping.aggregations.test.tsx +131 -0
  1013. package/src/backend/dynamic-table/__tests__/ConfigureViewPanel.baseView.test.tsx +162 -0
  1014. package/src/backend/dynamic-table/__tests__/ConfigureViewPanel.focus.test.tsx +54 -0
  1015. package/src/backend/dynamic-table/__tests__/ConfigureViewPanel.sections.test.tsx +142 -0
  1016. package/src/backend/dynamic-table/__tests__/ConfigureViewRollups.test.tsx +228 -0
  1017. package/src/backend/dynamic-table/__tests__/ContextMenu.test.tsx +89 -0
  1018. package/src/backend/dynamic-table/__tests__/DynamicTable.density.test.tsx +299 -0
  1019. package/src/backend/dynamic-table/__tests__/DynamicTable.exportTruncation.test.tsx +145 -0
  1020. package/src/backend/dynamic-table/__tests__/DynamicTable.loadError.test.tsx +173 -0
  1021. package/src/backend/dynamic-table/__tests__/DynamicTable.wiring.test.tsx +357 -0
  1022. package/src/backend/dynamic-table/__tests__/EmptyState.test.tsx +70 -0
  1023. package/src/backend/dynamic-table/__tests__/ExportMenu.test.tsx +82 -0
  1024. package/src/backend/dynamic-table/__tests__/FooterTotalsRow.test.tsx +154 -0
  1025. package/src/backend/dynamic-table/__tests__/GroupSummaryRow.render.test.tsx +131 -0
  1026. package/src/backend/dynamic-table/__tests__/LoadErrorState.test.tsx +74 -0
  1027. package/src/backend/dynamic-table/__tests__/MentionPopup.search.test.tsx +127 -0
  1028. package/src/backend/dynamic-table/__tests__/PerspectiveTabs.test.tsx +170 -0
  1029. package/src/backend/dynamic-table/__tests__/RowHeaderCell.selection.test.tsx +80 -0
  1030. package/src/backend/dynamic-table/__tests__/abbreviationsSubscription.test.ts +171 -0
  1031. package/src/backend/dynamic-table/__tests__/aggregate.spec.test.ts +224 -0
  1032. package/src/backend/dynamic-table/__tests__/aggregateDimension.test.tsx +191 -0
  1033. package/src/backend/dynamic-table/__tests__/anchoredPosition.test.ts +88 -0
  1034. package/src/backend/dynamic-table/__tests__/badgeAutoFit.test.ts +121 -0
  1035. package/src/backend/dynamic-table/__tests__/cellHandlers.save.test.ts +282 -0
  1036. package/src/backend/dynamic-table/__tests__/cellWrites.test.ts +274 -0
  1037. package/src/backend/dynamic-table/__tests__/clipboard.test.ts +230 -0
  1038. package/src/backend/dynamic-table/__tests__/coerceCellValue.test.ts +117 -0
  1039. package/src/backend/dynamic-table/__tests__/colorAdjacency.test.ts +151 -0
  1040. package/src/backend/dynamic-table/__tests__/columnFill.test.ts +66 -0
  1041. package/src/backend/dynamic-table/__tests__/columnHeaderHandlers.sort.test.ts +100 -0
  1042. package/src/backend/dynamic-table/__tests__/columnHeaderReorder.test.tsx +209 -0
  1043. package/src/backend/dynamic-table/__tests__/columnReorderPersistence.test.tsx +127 -0
  1044. package/src/backend/dynamic-table/__tests__/columnVirtualization.render.test.tsx +420 -0
  1045. package/src/backend/dynamic-table/__tests__/columnVisibilityPersistence.test.tsx +136 -0
  1046. package/src/backend/dynamic-table/__tests__/columnWindow.test.ts +643 -0
  1047. package/src/backend/dynamic-table/__tests__/commentAudience.test.tsx +137 -0
  1048. package/src/backend/dynamic-table/__tests__/commentScope.test.tsx +214 -0
  1049. package/src/backend/dynamic-table/__tests__/conditionalFormat.test.ts +175 -0
  1050. package/src/backend/dynamic-table/__tests__/contentRegistry.test.ts +267 -0
  1051. package/src/backend/dynamic-table/__tests__/dateWindows.test.ts +149 -0
  1052. package/src/backend/dynamic-table/__tests__/density.test.ts +204 -0
  1053. package/src/backend/dynamic-table/__tests__/exportTable.test.ts +146 -0
  1054. package/src/backend/dynamic-table/__tests__/fillGeometry.test.ts +201 -0
  1055. package/src/backend/dynamic-table/__tests__/fillPatterns.test.ts +272 -0
  1056. package/src/backend/dynamic-table/__tests__/filterParser.presets.test.ts +64 -0
  1057. package/src/backend/dynamic-table/__tests__/filterParser.relative.test.ts +109 -0
  1058. package/src/backend/dynamic-table/__tests__/filterSuggestions.test.ts +152 -0
  1059. package/src/backend/dynamic-table/__tests__/formatAggregate.test.ts +99 -0
  1060. package/src/backend/dynamic-table/__tests__/formatDateCompact.test.ts +489 -0
  1061. package/src/backend/dynamic-table/__tests__/formatLabelCompact.test.ts +717 -0
  1062. package/src/backend/dynamic-table/__tests__/formula.check.test.ts +257 -0
  1063. package/src/backend/dynamic-table/__tests__/formula.evaluate.test.ts +274 -0
  1064. package/src/backend/dynamic-table/__tests__/formula.functions.test.ts +226 -0
  1065. package/src/backend/dynamic-table/__tests__/formula.noEval.test.ts +92 -0
  1066. package/src/backend/dynamic-table/__tests__/formula.parse.test.ts +233 -0
  1067. package/src/backend/dynamic-table/__tests__/formulaColumns.test.tsx +213 -0
  1068. package/src/backend/dynamic-table/__tests__/handlerIdentity.test.ts +77 -0
  1069. package/src/backend/dynamic-table/__tests__/importMapping.test.ts +174 -0
  1070. package/src/backend/dynamic-table/__tests__/importParse.test.ts +130 -0
  1071. package/src/backend/dynamic-table/__tests__/importServerOnly.test.ts +37 -0
  1072. package/src/backend/dynamic-table/__tests__/layoutPersistence.test.ts +372 -0
  1073. package/src/backend/dynamic-table/__tests__/mouseHandlers.doubleClick.test.ts +155 -0
  1074. package/src/backend/dynamic-table/__tests__/perspectivePersonalization.test.ts +188 -0
  1075. package/src/backend/dynamic-table/__tests__/perspectiveResolver.test.ts +133 -0
  1076. package/src/backend/dynamic-table/__tests__/perspectiveState.defaultGrouping.test.ts +306 -0
  1077. package/src/backend/dynamic-table/__tests__/perspectiveState.defaults.test.ts +107 -0
  1078. package/src/backend/dynamic-table/__tests__/perspectiveTemplates.test.tsx +269 -0
  1079. package/src/backend/dynamic-table/__tests__/perspectiveTransforms.aggregations.test.ts +105 -0
  1080. package/src/backend/dynamic-table/__tests__/perspectiveTransforms.conditionalFormats.test.ts +107 -0
  1081. package/src/backend/dynamic-table/__tests__/perspectiveTransforms.dateFormat.test.ts +61 -0
  1082. package/src/backend/dynamic-table/__tests__/perspectiveTransforms.formulas.test.ts +85 -0
  1083. package/src/backend/dynamic-table/__tests__/perspectiveTransforms.frozenColumns.test.ts +124 -0
  1084. package/src/backend/dynamic-table/__tests__/rollupColumns.test.ts +149 -0
  1085. package/src/backend/dynamic-table/__tests__/sharedCriteria.test.ts +255 -0
  1086. package/src/backend/dynamic-table/__tests__/splitViewTree.test.ts +324 -0
  1087. package/src/backend/dynamic-table/__tests__/store.clipboardMarker.test.ts +143 -0
  1088. package/src/backend/dynamic-table/__tests__/store.fillPreview.test.ts +168 -0
  1089. package/src/backend/dynamic-table/__tests__/store.mutations.test.ts +345 -0
  1090. package/src/backend/dynamic-table/__tests__/store.selection.test.ts +157 -0
  1091. package/src/backend/dynamic-table/__tests__/store.undo.test.ts +225 -0
  1092. package/src/backend/dynamic-table/__tests__/store.valueSource.test.tsx +204 -0
  1093. package/src/backend/dynamic-table/__tests__/useAnnotations.legacyOptions.test.tsx +58 -0
  1094. package/src/backend/dynamic-table/__tests__/useColumnVirtualizer.test.ts +290 -0
  1095. package/src/backend/dynamic-table/__tests__/useDensityPreference.test.ts +435 -0
  1096. package/src/backend/dynamic-table/__tests__/useDynamicTablePage.duplicate.test.tsx +99 -0
  1097. package/src/backend/dynamic-table/__tests__/useDynamicTablePage.export.test.tsx +281 -0
  1098. package/src/backend/dynamic-table/__tests__/useDynamicTablePage.personalization.test.tsx +313 -0
  1099. package/src/backend/dynamic-table/__tests__/useDynamicTablePage.perspectiveRestore.test.tsx +126 -0
  1100. package/src/backend/dynamic-table/__tests__/useGrouping.aggregations.test.ts +158 -0
  1101. package/src/backend/dynamic-table/__tests__/useGrouping.groupKey.test.ts +58 -0
  1102. package/src/backend/dynamic-table/__tests__/useKeyboardNavigation.test.ts +594 -0
  1103. package/src/backend/dynamic-table/__tests__/useStickyOffsets.memo.test.tsx +75 -0
  1104. package/src/backend/dynamic-table/__tests__/useStickyOffsets.test.ts +106 -0
  1105. package/src/backend/dynamic-table/__tests__/widgetPane.test.tsx +338 -0
  1106. package/src/backend/dynamic-table/components/ActiveFilterChips.tsx +109 -0
  1107. package/src/backend/dynamic-table/components/BulkActionsBar.tsx +89 -0
  1108. package/src/backend/dynamic-table/components/Cell.tsx +292 -0
  1109. package/src/backend/dynamic-table/components/CellCommentDialog.tsx +887 -0
  1110. package/src/backend/dynamic-table/components/CellCommentHoverPopup.tsx +94 -0
  1111. package/src/backend/dynamic-table/components/ColumnFilterPopover.tsx +775 -0
  1112. package/src/backend/dynamic-table/components/ColumnHeaderMenu.tsx +189 -0
  1113. package/src/backend/dynamic-table/components/ColumnHeaders.tsx +867 -0
  1114. package/src/backend/dynamic-table/components/CompactLabelCell.tsx +293 -0
  1115. package/src/backend/dynamic-table/components/ConfigureViewFields.tsx +173 -0
  1116. package/src/backend/dynamic-table/components/ConfigureViewFilters.tsx +346 -0
  1117. package/src/backend/dynamic-table/components/ConfigureViewFormatting.tsx +205 -0
  1118. package/src/backend/dynamic-table/components/ConfigureViewFormulas.tsx +423 -0
  1119. package/src/backend/dynamic-table/components/ConfigureViewGrouping.tsx +264 -0
  1120. package/src/backend/dynamic-table/components/ConfigureViewLinkedColumns.tsx +249 -0
  1121. package/src/backend/dynamic-table/components/ConfigureViewPanel.tsx +857 -0
  1122. package/src/backend/dynamic-table/components/ConfigureViewRollups.tsx +364 -0
  1123. package/src/backend/dynamic-table/components/ConfigureViewSorting.tsx +121 -0
  1124. package/src/backend/dynamic-table/components/ContextMenu.tsx +140 -0
  1125. package/src/backend/dynamic-table/components/DateCell.tsx +205 -0
  1126. package/src/backend/dynamic-table/components/Debugger.tsx +340 -0
  1127. package/src/backend/dynamic-table/components/DensityControl.tsx +441 -0
  1128. package/src/backend/dynamic-table/components/EmptyState.tsx +103 -0
  1129. package/src/backend/dynamic-table/components/EntitySearchEditor.tsx +528 -0
  1130. package/src/backend/dynamic-table/components/ExportMenu.tsx +90 -0
  1131. package/src/backend/dynamic-table/components/FilterDatePicker.tsx +194 -0
  1132. package/src/backend/dynamic-table/components/FilterValueInput.tsx +294 -0
  1133. package/src/backend/dynamic-table/components/FooterTotalsRow.tsx +291 -0
  1134. package/src/backend/dynamic-table/components/FullscreenOverlay.tsx +66 -0
  1135. package/src/backend/dynamic-table/components/GroupHeaderRow.tsx +136 -0
  1136. package/src/backend/dynamic-table/components/GroupSummaryRow.tsx +184 -0
  1137. package/src/backend/dynamic-table/components/ImportPanel.tsx +445 -0
  1138. package/src/backend/dynamic-table/components/ImportPreviewGrid.tsx +155 -0
  1139. package/src/backend/dynamic-table/components/LoadErrorState.tsx +70 -0
  1140. package/src/backend/dynamic-table/components/MentionPopup.tsx +224 -0
  1141. package/src/backend/dynamic-table/components/PerspectiveDeleteDialog.tsx +96 -0
  1142. package/src/backend/dynamic-table/components/PerspectivePublishDialog.tsx +152 -0
  1143. package/src/backend/dynamic-table/components/PerspectiveTabMenu.tsx +215 -0
  1144. package/src/backend/dynamic-table/components/PerspectiveTabs.tsx +426 -0
  1145. package/src/backend/dynamic-table/components/PerspectiveTemplateMenu.tsx +135 -0
  1146. package/src/backend/dynamic-table/components/RowHeaderCell.tsx +73 -0
  1147. package/src/backend/dynamic-table/components/SearchBar.tsx +309 -0
  1148. package/src/backend/dynamic-table/components/SelectMenu.tsx +237 -0
  1149. package/src/backend/dynamic-table/components/StaleDataBar.tsx +74 -0
  1150. package/src/backend/dynamic-table/components/TableDeleteDialog.tsx +74 -0
  1151. package/src/backend/dynamic-table/components/TablePagination.tsx +96 -0
  1152. package/src/backend/dynamic-table/components/TableSkeleton.tsx +123 -0
  1153. package/src/backend/dynamic-table/components/ToolbarOverflow.tsx +284 -0
  1154. package/src/backend/dynamic-table/components/ViewModeSwitch.tsx +92 -0
  1155. package/src/backend/dynamic-table/components/VirtualRow.tsx +346 -0
  1156. package/src/backend/dynamic-table/components/editors.tsx +1727 -0
  1157. package/src/backend/dynamic-table/components/renderers.tsx +162 -0
  1158. package/src/backend/dynamic-table/events/events.ts +107 -0
  1159. package/src/backend/dynamic-table/formula/check.ts +359 -0
  1160. package/src/backend/dynamic-table/formula/evaluate.ts +473 -0
  1161. package/src/backend/dynamic-table/formula/functions.ts +737 -0
  1162. package/src/backend/dynamic-table/formula/index.ts +57 -0
  1163. package/src/backend/dynamic-table/formula/parse.ts +414 -0
  1164. package/src/backend/dynamic-table/formula/types.ts +151 -0
  1165. package/src/backend/dynamic-table/handlers/cellWrites.ts +362 -0
  1166. package/src/backend/dynamic-table/handlers/index.ts +789 -0
  1167. package/src/backend/dynamic-table/handlers/perspectiveHandlers.ts +684 -0
  1168. package/src/backend/dynamic-table/hooks/index.ts +888 -0
  1169. package/src/backend/dynamic-table/hooks/useAbbreviations.ts +67 -0
  1170. package/src/backend/dynamic-table/hooks/useAnnotations.ts +215 -0
  1171. package/src/backend/dynamic-table/hooks/useColumnVirtualizer.ts +303 -0
  1172. package/src/backend/dynamic-table/hooks/useColumnWidthPersistence.ts +214 -0
  1173. package/src/backend/dynamic-table/hooks/useDensityPreference.ts +451 -0
  1174. package/src/backend/dynamic-table/hooks/useDynamicTablePage.tsx +1681 -0
  1175. package/src/backend/dynamic-table/hooks/useEscapeLayer.ts +72 -0
  1176. package/src/backend/dynamic-table/hooks/useGrouping.ts +428 -0
  1177. package/src/backend/dynamic-table/hooks/useSuggestionFetch.ts +109 -0
  1178. package/src/backend/dynamic-table/hooks/useTableDateFormat.tsx +19 -0
  1179. package/src/backend/dynamic-table/index.ts +497 -0
  1180. package/src/backend/dynamic-table/registry/ContentRegistryContext.tsx +244 -0
  1181. package/src/backend/dynamic-table/registry/TableRegistryContext.tsx +84 -0
  1182. package/src/backend/dynamic-table/registry/filterAccessibleTables.ts +36 -0
  1183. package/src/backend/dynamic-table/registry/index.ts +38 -0
  1184. package/src/backend/dynamic-table/registry/lazyTable.tsx +80 -0
  1185. package/src/backend/dynamic-table/registry/types.ts +181 -0
  1186. package/src/backend/dynamic-table/registry/widgetCatalog.ts +226 -0
  1187. package/src/backend/dynamic-table/server/aggregate.ts +474 -0
  1188. package/src/backend/dynamic-table/server/dateWindows.ts +201 -0
  1189. package/src/backend/dynamic-table/server/filterParser.ts +132 -0
  1190. package/src/backend/dynamic-table/server/filterSuggestions.ts +150 -0
  1191. package/src/backend/dynamic-table/server/index.ts +47 -0
  1192. package/src/backend/dynamic-table/server/makeDynamicTableRoute.ts +426 -0
  1193. package/src/backend/dynamic-table/split-view/ContentPicker.tsx +251 -0
  1194. package/src/backend/dynamic-table/split-view/EmptySlot.tsx +69 -0
  1195. package/src/backend/dynamic-table/split-view/SplitViewHost.tsx +1336 -0
  1196. package/src/backend/dynamic-table/split-view/WidgetPane.tsx +390 -0
  1197. package/src/backend/dynamic-table/split-view/WorkspaceFilterBar.tsx +728 -0
  1198. package/src/backend/dynamic-table/split-view/index.ts +48 -0
  1199. package/src/backend/dynamic-table/split-view/layoutPersistence.ts +185 -0
  1200. package/src/backend/dynamic-table/split-view/sharedCriteria.ts +196 -0
  1201. package/src/backend/dynamic-table/split-view/types.ts +778 -0
  1202. package/src/backend/dynamic-table/split-view/useSplitViewLayouts.ts +97 -0
  1203. package/src/backend/dynamic-table/store/index.ts +1011 -0
  1204. package/src/backend/dynamic-table/styles/ContextMenu.css +387 -0
  1205. package/src/backend/dynamic-table/styles/DynamicTable.css +3659 -0
  1206. package/src/backend/dynamic-table/styles/DynamicTable.v2.css +2651 -0
  1207. package/src/backend/dynamic-table/styles/SearchBar.css +426 -0
  1208. package/src/backend/dynamic-table/styles/dateCell.css +146 -0
  1209. package/src/backend/dynamic-table/styles/density.css +690 -0
  1210. package/src/backend/dynamic-table/types/density.ts +179 -0
  1211. package/src/backend/dynamic-table/types/filters.ts +156 -0
  1212. package/src/backend/dynamic-table/types/grouping.ts +189 -0
  1213. package/src/backend/dynamic-table/types/import.ts +132 -0
  1214. package/src/backend/dynamic-table/types/index.ts +1198 -0
  1215. package/src/backend/dynamic-table/types/perspective.ts +436 -0
  1216. package/src/backend/dynamic-table/types/rollup.ts +111 -0
  1217. package/src/backend/dynamic-table/utils/abbreviations.ts +556 -0
  1218. package/src/backend/dynamic-table/utils/anchoredPosition.ts +106 -0
  1219. package/src/backend/dynamic-table/utils/badgeAutoFit.ts +108 -0
  1220. package/src/backend/dynamic-table/utils/clipboard.ts +294 -0
  1221. package/src/backend/dynamic-table/utils/coerceCellValue.ts +96 -0
  1222. package/src/backend/dynamic-table/utils/colorAdjacency.ts +87 -0
  1223. package/src/backend/dynamic-table/utils/columnFill.ts +93 -0
  1224. package/src/backend/dynamic-table/utils/columnSpacerStyle.ts +53 -0
  1225. package/src/backend/dynamic-table/utils/columnWindow.ts +668 -0
  1226. package/src/backend/dynamic-table/utils/conditionalFormat.ts +300 -0
  1227. package/src/backend/dynamic-table/utils/exportTable.ts +140 -0
  1228. package/src/backend/dynamic-table/utils/fillGeometry.ts +187 -0
  1229. package/src/backend/dynamic-table/utils/fillPatterns.ts +422 -0
  1230. package/src/backend/dynamic-table/utils/formatAggregate.ts +145 -0
  1231. package/src/backend/dynamic-table/utils/formatDateCompact.ts +879 -0
  1232. package/src/backend/dynamic-table/utils/formatLabelCompact.ts +640 -0
  1233. package/src/backend/dynamic-table/utils/formulaColumns.tsx +237 -0
  1234. package/src/backend/dynamic-table/utils/importMapping.ts +202 -0
  1235. package/src/backend/dynamic-table/utils/importParse.ts +220 -0
  1236. package/src/backend/dynamic-table/utils/lookupColumns.tsx +47 -0
  1237. package/src/backend/dynamic-table/utils/perspectiveTransforms.ts +368 -0
  1238. package/src/backend/dynamic-table/utils/rollupColumns.ts +162 -0
  1239. package/src/backend/dynamic-table/validators/index.ts +40 -0
  1240. package/src/backend/fields/registry.generated.ts +8 -0
  1241. package/src/backend/fields/registry.ts +38 -0
  1242. package/src/backend/filters/AdvancedFilterBuilder.tsx +339 -0
  1243. package/src/backend/forms/ActionsDropdown.tsx +198 -0
  1244. package/src/backend/forms/FormActionButtons.tsx +98 -0
  1245. package/src/backend/forms/FormFooter.tsx +27 -0
  1246. package/src/backend/forms/FormHeader.tsx +204 -0
  1247. package/src/backend/forms/index.ts +4 -0
  1248. package/src/backend/hooks/useAdvancedFilter.ts +77 -0
  1249. package/src/backend/indexes/PartialIndexBanner.tsx +86 -0
  1250. package/src/backend/indexes/store.ts +88 -0
  1251. package/src/backend/injection/ComponentOverrideProvider.tsx +66 -0
  1252. package/src/backend/injection/InjectedField.tsx +196 -0
  1253. package/src/backend/injection/InjectionSpot.tsx +457 -0
  1254. package/src/backend/injection/InjectionWizard.tsx +335 -0
  1255. package/src/backend/injection/PageInjectionBoundary.tsx +31 -0
  1256. package/src/backend/injection/SseEventIndicator.tsx +11 -0
  1257. package/src/backend/injection/StatusBadgeInjectionSpot.tsx +50 -0
  1258. package/src/backend/injection/StatusBadgeRenderer.tsx +100 -0
  1259. package/src/backend/injection/WidgetSharedState.ts +58 -0
  1260. package/src/backend/injection/WizardInjectionSpot.tsx +46 -0
  1261. package/src/backend/injection/eventBridge.ts +158 -0
  1262. package/src/backend/injection/helpers.ts +35 -0
  1263. package/src/backend/injection/mergeMenuItems.ts +80 -0
  1264. package/src/backend/injection/mutationEvents.ts +22 -0
  1265. package/src/backend/injection/resolveInjectedIcon.tsx +30 -0
  1266. package/src/backend/injection/spotIds.ts +64 -0
  1267. package/src/backend/injection/useAppEvent.ts +76 -0
  1268. package/src/backend/injection/useGuardedMutation.ts +101 -0
  1269. package/src/backend/injection/useInjectedMenuItems.ts +67 -0
  1270. package/src/backend/injection/useInjectionDataWidgets.ts +41 -0
  1271. package/src/backend/injection/useOperationProgress.ts +105 -0
  1272. package/src/backend/injection/useRegisteredComponent.tsx +106 -0
  1273. package/src/backend/injection/useWidgetSharedState.ts +28 -0
  1274. package/src/backend/injection/visibility-utils.ts +31 -0
  1275. package/src/backend/injection/widgetRegistry.ts +68 -0
  1276. package/src/backend/inputs/ComboboxInput.tsx +272 -0
  1277. package/src/backend/inputs/DatePicker.tsx +162 -0
  1278. package/src/backend/inputs/DateTimePicker.tsx +199 -0
  1279. package/src/backend/inputs/EventPatternInput.tsx +46 -0
  1280. package/src/backend/inputs/EventSelect.tsx +183 -0
  1281. package/src/backend/inputs/LookupSelect.tsx +253 -0
  1282. package/src/backend/inputs/PhoneNumberField.tsx +166 -0
  1283. package/src/backend/inputs/SwitchableMarkdownInput.tsx +106 -0
  1284. package/src/backend/inputs/TagsInput.tsx +297 -0
  1285. package/src/backend/inputs/TimeInput.tsx +146 -0
  1286. package/src/backend/inputs/TimePicker.tsx +120 -0
  1287. package/src/backend/inputs/__tests__/DatePicker.test.tsx +76 -0
  1288. package/src/backend/inputs/__tests__/DateTimePicker.logic.test.ts +126 -0
  1289. package/src/backend/inputs/__tests__/PhoneNumberField.test.tsx +73 -0
  1290. package/src/backend/inputs/__tests__/TagsInput.test.tsx +78 -0
  1291. package/src/backend/inputs/__tests__/TimeInput.test.tsx +134 -0
  1292. package/src/backend/inputs/__tests__/TimePicker.test.tsx +50 -0
  1293. package/src/backend/inputs/index.ts +11 -0
  1294. package/src/backend/markdown/MarkdownContent.tsx +33 -0
  1295. package/src/backend/markdown/index.ts +2 -0
  1296. package/src/backend/markdown/useMarkdownRemarkPlugins.ts +39 -0
  1297. package/src/backend/messages/MessageComposer.tsx +197 -0
  1298. package/src/backend/messages/MessageObjectDetail.tsx +95 -0
  1299. package/src/backend/messages/MessageObjectPreview.tsx +64 -0
  1300. package/src/backend/messages/MessageObjectRecordPicker.tsx +149 -0
  1301. package/src/backend/messages/MessagePrioritySelector.tsx +78 -0
  1302. package/src/backend/messages/MessagesIcon.tsx +32 -0
  1303. package/src/backend/messages/SendObjectMessageDialog.tsx +74 -0
  1304. package/src/backend/messages/__tests__/MessageComposer.test.tsx +350 -0
  1305. package/src/backend/messages/__tests__/SendObjectMessageDialog.test.tsx +77 -0
  1306. package/src/backend/messages/index.ts +22 -0
  1307. package/src/backend/messages/message-compose-form-groups.tsx +383 -0
  1308. package/src/backend/messages/message-composer.types.ts +82 -0
  1309. package/src/backend/messages/message-priority.ts +53 -0
  1310. package/src/backend/messages/useMessageCompose.ts +666 -0
  1311. package/src/backend/messages/useMessageComposeOperations.ts +259 -0
  1312. package/src/backend/messages/useMessagesPoll.ts +118 -0
  1313. package/src/backend/notifications/NotificationBell.tsx +91 -0
  1314. package/src/backend/notifications/NotificationCountBadge.tsx +14 -0
  1315. package/src/backend/notifications/NotificationDispatcher.ts +213 -0
  1316. package/src/backend/notifications/NotificationItem.tsx +262 -0
  1317. package/src/backend/notifications/NotificationPanel.tsx +225 -0
  1318. package/src/backend/notifications/__tests__/NotificationCountBadge.test.tsx +34 -0
  1319. package/src/backend/notifications/index.ts +20 -0
  1320. package/src/backend/notifications/useNotificationActions.ts +161 -0
  1321. package/src/backend/notifications/useNotificationEffect.ts +17 -0
  1322. package/src/backend/notifications/useNotifications.ts +12 -0
  1323. package/src/backend/notifications/useNotificationsPoll.ts +167 -0
  1324. package/src/backend/notifications/useNotificationsSse.ts +220 -0
  1325. package/src/backend/operations/LastOperationBanner.tsx +85 -0
  1326. package/src/backend/operations/__tests__/LastOperationBanner.test.tsx +101 -0
  1327. package/src/backend/operations/store.ts +230 -0
  1328. package/src/backend/progress/ProgressTopBar.tsx +189 -0
  1329. package/src/backend/progress/useProgress.ts +12 -0
  1330. package/src/backend/progress/useProgressPoll.ts +100 -0
  1331. package/src/backend/progress/useProgressSse.ts +118 -0
  1332. package/src/backend/schedule/ScheduleAgenda.tsx +136 -0
  1333. package/src/backend/schedule/ScheduleGrid.tsx +136 -0
  1334. package/src/backend/schedule/ScheduleToolbar.tsx +178 -0
  1335. package/src/backend/schedule/ScheduleView.tsx +201 -0
  1336. package/src/backend/schedule/index.ts +5 -0
  1337. package/src/backend/schedule/recurrence.ts +99 -0
  1338. package/src/backend/schedule/types.ts +26 -0
  1339. package/src/backend/section-page/SectionNav.tsx +140 -0
  1340. package/src/backend/section-page/SectionPage.tsx +34 -0
  1341. package/src/backend/section-page/index.ts +3 -0
  1342. package/src/backend/section-page/types.ts +30 -0
  1343. package/src/backend/settings/SettingsNavigation.tsx +74 -0
  1344. package/src/backend/settings/SettingsPageWrapper.tsx +78 -0
  1345. package/src/backend/settings/index.ts +3 -0
  1346. package/src/backend/upgrades/UpgradeActionBanner.tsx +128 -0
  1347. package/src/backend/utils/__tests__/api.scoped-headers.test.ts +58 -0
  1348. package/src/backend/utils/__tests__/api.test.ts +148 -0
  1349. package/src/backend/utils/__tests__/apiCall.scoped-request-headers.test.ts +58 -0
  1350. package/src/backend/utils/__tests__/apiCall.test.ts +109 -0
  1351. package/src/backend/utils/__tests__/crud.test.ts +87 -0
  1352. package/src/backend/utils/__tests__/customFieldDefs.test.ts +25 -0
  1353. package/src/backend/utils/__tests__/customFieldValues.test.ts +35 -0
  1354. package/src/backend/utils/__tests__/richTextSanitizer.test.ts +57 -0
  1355. package/src/backend/utils/api.ts +214 -0
  1356. package/src/backend/utils/apiCall.ts +123 -0
  1357. package/src/backend/utils/crud.ts +174 -0
  1358. package/src/backend/utils/customFieldColumns.ts +118 -0
  1359. package/src/backend/utils/customFieldDefs.ts +247 -0
  1360. package/src/backend/utils/customFieldFilters.ts +174 -0
  1361. package/src/backend/utils/customFieldForms.ts +203 -0
  1362. package/src/backend/utils/customFieldRelationDisplay.ts +302 -0
  1363. package/src/backend/utils/customFieldValues.ts +41 -0
  1364. package/src/backend/utils/flash.ts +17 -0
  1365. package/src/backend/utils/nav.ts +402 -0
  1366. package/src/backend/utils/richTextSanitizer.ts +7 -0
  1367. package/src/backend/utils/scopedHeaderStack.ts +49 -0
  1368. package/src/backend/utils/serverErrors.ts +313 -0
  1369. package/src/backend/utils/useAutoDiscoveredFields.ts +138 -0
  1370. package/src/backend/version-history/VersionHistoryAction.tsx +76 -0
  1371. package/src/backend/version-history/VersionHistoryDetail.tsx +87 -0
  1372. package/src/backend/version-history/VersionHistoryPanel.tsx +331 -0
  1373. package/src/backend/version-history/index.ts +9 -0
  1374. package/src/backend/version-history/labels.ts +56 -0
  1375. package/src/backend/version-history/types.ts +27 -0
  1376. package/src/backend/version-history/useAuditPermissions.ts +94 -0
  1377. package/src/backend/version-history/useVersionHistory.ts +173 -0
  1378. package/src/backend/views/NewViewForm.tsx +54 -0
  1379. package/src/backend/views/ShareForm.tsx +62 -0
  1380. package/src/backend/views/ViewChip.tsx +123 -0
  1381. package/src/backend/views/ViewChipMenu.tsx +94 -0
  1382. package/src/backend/views/types.ts +4 -0
  1383. package/src/frontend/AuthFooter.tsx +29 -0
  1384. package/src/frontend/LanguageSwitcher.tsx +66 -0
  1385. package/src/frontend/Layout.tsx +13 -0
  1386. package/src/hooks/useIsMobile.ts +27 -0
  1387. package/src/index.ts +40 -0
  1388. package/src/portal/PortalContext.tsx +180 -0
  1389. package/src/portal/PortalLayoutProvider.tsx +5 -0
  1390. package/src/portal/PortalLayoutShell.tsx +64 -0
  1391. package/src/portal/PortalShell.tsx +370 -0
  1392. package/src/portal/components/PortalCard.tsx +77 -0
  1393. package/src/portal/components/PortalEmptyState.tsx +32 -0
  1394. package/src/portal/components/PortalFeatureCard.tsx +41 -0
  1395. package/src/portal/components/PortalNotificationBell.tsx +81 -0
  1396. package/src/portal/components/PortalNotificationPanel.tsx +230 -0
  1397. package/src/portal/components/PortalPageHeader.tsx +33 -0
  1398. package/src/portal/components/index.ts +6 -0
  1399. package/src/portal/hooks/useCustomerAuth.ts +97 -0
  1400. package/src/portal/hooks/usePortalAppEvent.ts +56 -0
  1401. package/src/portal/hooks/usePortalDashboardWidgets.ts +50 -0
  1402. package/src/portal/hooks/usePortalEventBridge.ts +162 -0
  1403. package/src/portal/hooks/usePortalInjectedMenuItems.ts +130 -0
  1404. package/src/portal/hooks/usePortalNotifications.ts +116 -0
  1405. package/src/portal/hooks/useTenantContext.ts +83 -0
  1406. package/src/primitives/DataLoader.tsx +93 -0
  1407. package/src/primitives/ErrorNotice.tsx +17 -0
  1408. package/src/primitives/Notice.tsx +83 -0
  1409. package/src/primitives/alert.tsx +59 -0
  1410. package/src/primitives/badge.tsx +43 -0
  1411. package/src/primitives/button.tsx +60 -0
  1412. package/src/primitives/calendar.tsx +69 -0
  1413. package/src/primitives/card.tsx +92 -0
  1414. package/src/primitives/checkbox.tsx +34 -0
  1415. package/src/primitives/dialog.tsx +122 -0
  1416. package/src/primitives/form-field.tsx +106 -0
  1417. package/src/primitives/icon-button.tsx +48 -0
  1418. package/src/primitives/input.tsx +26 -0
  1419. package/src/primitives/label.tsx +18 -0
  1420. package/src/primitives/popover.tsx +37 -0
  1421. package/src/primitives/primitives.stories.tsx +223 -0
  1422. package/src/primitives/progress.tsx +28 -0
  1423. package/src/primitives/separator.tsx +7 -0
  1424. package/src/primitives/sheet.tsx +137 -0
  1425. package/src/primitives/spinner.tsx +27 -0
  1426. package/src/primitives/status-badge.tsx +62 -0
  1427. package/src/primitives/switch.tsx +92 -0
  1428. package/src/primitives/table.tsx +27 -0
  1429. package/src/primitives/tabs.tsx +131 -0
  1430. package/src/primitives/textarea.tsx +20 -0
  1431. package/src/primitives/tooltip.tsx +85 -0
  1432. package/src/primitives-v2/AGENTS.md +66 -0
  1433. package/src/primitives-v2/Alert.stories.tsx +56 -0
  1434. package/src/primitives-v2/Alert.tsx +108 -0
  1435. package/src/primitives-v2/Avatar.stories.tsx +60 -0
  1436. package/src/primitives-v2/Avatar.tsx +139 -0
  1437. package/src/primitives-v2/Badge.stories.tsx +80 -0
  1438. package/src/primitives-v2/Badge.tsx +99 -0
  1439. package/src/primitives-v2/Breadcrumb.stories.tsx +61 -0
  1440. package/src/primitives-v2/Breadcrumb.tsx +96 -0
  1441. package/src/primitives-v2/Button.stories.tsx +72 -0
  1442. package/src/primitives-v2/Button.tsx +178 -0
  1443. package/src/primitives-v2/Checkbox.stories.tsx +34 -0
  1444. package/src/primitives-v2/Checkbox.tsx +103 -0
  1445. package/src/primitives-v2/IconButton.stories.tsx +40 -0
  1446. package/src/primitives-v2/IconButton.tsx +40 -0
  1447. package/src/primitives-v2/Input.stories.tsx +60 -0
  1448. package/src/primitives-v2/Input.tsx +113 -0
  1449. package/src/primitives-v2/NavGroup.stories.tsx +50 -0
  1450. package/src/primitives-v2/NavGroup.tsx +76 -0
  1451. package/src/primitives-v2/NavItem.stories.tsx +68 -0
  1452. package/src/primitives-v2/NavItem.tsx +105 -0
  1453. package/src/primitives-v2/SearchInput.stories.tsx +44 -0
  1454. package/src/primitives-v2/SearchInput.tsx +123 -0
  1455. package/src/primitives-v2/Sidebar.stories.tsx +91 -0
  1456. package/src/primitives-v2/Sidebar.tsx +92 -0
  1457. package/src/primitives-v2/Tab.stories.tsx +62 -0
  1458. package/src/primitives-v2/Tab.tsx +148 -0
  1459. package/src/primitives-v2/Tag.stories.tsx +123 -0
  1460. package/src/primitives-v2/Tag.tsx +115 -0
  1461. package/src/primitives-v2/Toggle.stories.tsx +37 -0
  1462. package/src/primitives-v2/Toggle.tsx +93 -0
  1463. package/src/primitives-v2/Topbar.stories.tsx +65 -0
  1464. package/src/primitives-v2/Topbar.tsx +37 -0
  1465. package/src/primitives-v2/index.ts +31 -0
  1466. package/src/primitives-v2/utils.ts +9 -0
  1467. package/src/theme/BrandThemeProvider.tsx +122 -0
  1468. package/src/theme/QueryProvider.tsx +46 -0
  1469. package/src/theme/ThemeProvider.tsx +120 -0
  1470. package/src/theme/ThemeToggle.tsx +88 -0
  1471. package/src/theme/index.ts +4 -0
  1472. package/src/theme/m3.css +622 -0
  1473. package/src/theme/tokens.v2.css +559 -0
  1474. package/src/theme/typography.css +163 -0
  1475. package/src/theme/typography.stories.tsx +157 -0
  1476. package/src/theme/typography.tokens.ts +326 -0
  1477. package/src/types/react-big-calendar.d.ts +16 -0
@@ -0,0 +1,1681 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { useState, useMemo, useRef, useEffect, useCallback } from 'react'
5
+ import { useQuery, useQueryClient } from '@tanstack/react-query'
6
+ import { apiCall } from '../../utils/apiCall'
7
+ import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
8
+ import { flash } from '../../FlashMessages'
9
+ import { dispatch, useEventHandlers } from '../events/events'
10
+ import {
11
+ apiToDynamicTable,
12
+ apiTemplateToDynamicTable,
13
+ dynamicTableToApi,
14
+ } from '../utils/perspectiveTransforms'
15
+ import TableDeleteDialog from '../components/TableDeleteDialog'
16
+ import type {
17
+ ColumnDef,
18
+ FilterRow,
19
+ DynamicTableProps,
20
+ PaginationProps,
21
+ TableUIConfig,
22
+ CellEditSaveEvent,
23
+ CellSaveStartEvent,
24
+ CellSaveSuccessEvent,
25
+ CellSaveErrorEvent,
26
+ NewRowSaveEvent,
27
+ NewRowSaveErrorEvent,
28
+ KeyboardShortcutsConfig,
29
+ OnRowAction,
30
+ LoadLookupSources,
31
+ ExportAllResult,
32
+ } from '../types/index'
33
+ import { TableEvents } from '../types/index'
34
+ import { mergeSharedFilters } from '../split-view/sharedCriteria'
35
+ import { lookupColumnsToParam } from '../utils/lookupColumns'
36
+ import { rollupColumnsToParam } from '../utils/rollupColumns'
37
+ import type { LoadRollupSources, RollupColumnRef } from '../types/rollup'
38
+ import type { AggregationRule, AggregateResult, GroupRule } from '../types/grouping'
39
+ import type {
40
+ PerspectiveConfig,
41
+ PerspectiveSaveEvent,
42
+ PerspectiveSelectEvent,
43
+ PerspectiveRenameEvent,
44
+ PerspectiveDeleteEvent,
45
+ PerspectiveChangeEvent,
46
+ PerspectiveDuplicateEvent,
47
+ PerspectiveSetDefaultEvent,
48
+ PerspectivePublishEvent,
49
+ PerspectiveTemplateCopyEvent,
50
+ PerspectiveTemplate,
51
+ SortRule,
52
+ LookupColumnRef,
53
+ TableViewMode,
54
+ } from '../types/perspective'
55
+ import { BASE_VIEW_PERSPECTIVE_NAME, PERSPECTIVE_NAME_MAX_LENGTH } from '../types/perspective'
56
+ import type {
57
+ PerspectivesIndexResponse,
58
+ PerspectiveDto,
59
+ PerspectiveSaveResponse,
60
+ } from '@open-mercato/shared/modules/perspectives/types'
61
+
62
+ // Stable identity so it can sit in effect dependency arrays without churning.
63
+ const EMPTY_COLUMNS: string[] = []
64
+
65
+ /**
66
+ * The one perspective-write failure a user can actually act on.
67
+ *
68
+ * The upstream table SOFT-deletes views, but its unique index on
69
+ * `(user, tenant, org, table, name)` ignores `deleted_at` — so a name you used
70
+ * on a view you deleted earlier is still taken, forever, and the write comes
71
+ * back as a duplicate-key 500 with no field-level detail. Every generic
72
+ * "Failed to …" message sent the user round the same loop; naming the cause
73
+ * tells them the one thing that will work.
74
+ */
75
+ function perspectiveWriteError(response: { result?: unknown }, fallback: string): string {
76
+ const message = (response.result as { message?: string } | undefined)?.message ?? ''
77
+ return message.includes('duplicate key')
78
+ ? 'A view you deleted earlier still holds this name. Pick a different name.'
79
+ : fallback
80
+ }
81
+
82
+ const LAST_PERSPECTIVE_KEY_PREFIX = 'dt:lastPerspective:'
83
+
84
+ /**
85
+ * How long a burst of single-cell saves is allowed to accumulate before ONE
86
+ * list refetch runs. Long enough to swallow a paste/fill (whose per-cell PUTs
87
+ * land within a few tens of ms of each other) and a human typing across
88
+ * adjacent cells; short enough that a server-derived column updates while the
89
+ * user is still looking at the row they changed.
90
+ */
91
+ const LIST_REFETCH_COALESCE_MS = 400
92
+
93
+ /**
94
+ * Remember which view the user was last on, per table. Session-scoped: it must
95
+ * survive navigating into a record and back (the actual complaint) without
96
+ * outliving the browser session or needing a server round-trip. Storage access
97
+ * is guarded — SSR has no `window`, and Safari private mode throws on write.
98
+ */
99
+ function readLastPerspectiveId(tableId: string | undefined): string | null {
100
+ if (!tableId || typeof window === 'undefined') return null
101
+ try {
102
+ return window.sessionStorage.getItem(`${LAST_PERSPECTIVE_KEY_PREFIX}${tableId}`)
103
+ } catch {
104
+ return null
105
+ }
106
+ }
107
+
108
+ function writeLastPerspectiveId(tableId: string | undefined, id: string | null): void {
109
+ if (!tableId || typeof window === 'undefined') return
110
+ try {
111
+ const key = `${LAST_PERSPECTIVE_KEY_PREFIX}${tableId}`
112
+ if (id) window.sessionStorage.setItem(key, id)
113
+ else window.sessionStorage.removeItem(key)
114
+ } catch {
115
+ /* storage unavailable — last-view memory is best-effort */
116
+ }
117
+ }
118
+
119
+ // ─── Config Types ────────────────────────────
120
+
121
+ export interface DynamicTablePageDeleteConfig<TRow = any> {
122
+ title?: string | ((row: TRow) => string)
123
+ description?: string | ((row: TRow) => string)
124
+ nameColumn?: string
125
+ /** Base URL or function returning full URL for the DELETE request */
126
+ url?: string | ((row: TRow) => string)
127
+ }
128
+
129
+ export interface DynamicTablePageCellEditConfig {
130
+ url?: string | ((payload: CellEditSaveEvent, rowData: any) => string)
131
+ method?: 'PUT' | 'PATCH' | 'POST'
132
+ mapPayload?: (payload: CellEditSaveEvent, rowData: any) => Record<string, unknown>
133
+ }
134
+
135
+ export interface DynamicTableCreateHandlerContext {
136
+ tableRef: React.RefObject<HTMLElement>
137
+ invalidate: () => void
138
+ }
139
+
140
+ export interface DynamicTablePageCreateConfig {
141
+ url?: string
142
+ mapPayload?: (rowData: any) => Record<string, unknown>
143
+ /** Fully custom create flow. Replaces built-in logic. Handler must dispatch NEW_ROW_SAVE_START/SUCCESS/ERROR events. */
144
+ handler?: (payload: NewRowSaveEvent, ctx: DynamicTableCreateHandlerContext) => Promise<void>
145
+ }
146
+
147
+ export interface DynamicTablePageHooks<TRow = any> {
148
+ beforeCellEdit?: (
149
+ payload: CellEditSaveEvent,
150
+ rowData: any
151
+ ) => { url?: string; payload?: Record<string, unknown>; method?: 'PUT' | 'PATCH' | 'POST' } | void
152
+ beforeCreate?: (rowData: any) => Record<string, unknown> | Promise<Record<string, unknown>> | void
153
+ validateCreate?: (rowData: any) => string | null
154
+ afterMutation?: (
155
+ type: 'cellEdit' | 'create' | 'delete',
156
+ context: any
157
+ ) => void | Promise<void>
158
+ beforeDelete?: (row: TRow) => boolean | Promise<boolean>
159
+ }
160
+
161
+ export interface DynamicTablePageConfig<TRow = any> {
162
+ source: string
163
+ columns: ColumnDef[]
164
+ tableName: string
165
+
166
+ perspectives?: string
167
+ /**
168
+ * Entity id for the generic `/api/entities/filter-suggestions` endpoint.
169
+ *
170
+ * Prefer `loadFilterSuggestions` for any list whose columns are denormalised
171
+ * or whose values are skewed: the generic endpoint samples a page of rows and
172
+ * de-duplicates what it sees, so it under-reports common columns and returns
173
+ * `[]` (silently) for anything that isn't a plain column on the entity's own
174
+ * table.
175
+ */
176
+ filterSuggestions?: string
177
+ /**
178
+ * Custom suggestion loader — takes precedence over `filterSuggestions`.
179
+ * Point this at a module route backed by `distinctColumnValues` so the list
180
+ * offers real, scope-constrained values for every column it shows.
181
+ */
182
+ loadFilterSuggestions?: (field: string, query: string) => Promise<string[]>
183
+ /**
184
+ * Prefix for this instance's per-table CLIENT storage keys — the last-used
185
+ * perspective (`sessionStorage`) and column widths (`localStorage`).
186
+ *
187
+ * Absent or `''` → today's keys, byte-for-byte, so no existing user loses a
188
+ * saved width. A host that can mount the same table twice (split view) passes
189
+ * something unique per instance, e.g. `pane:{paneId}:` — without it, both
190
+ * instances derive the same key from `tableId` and overwrite each other.
191
+ *
192
+ * Deliberately NOT applied to density: that stays a user-global preference,
193
+ * so an embedded table never silently gets a different row height from the
194
+ * same table full-page.
195
+ *
196
+ * Spec: .ai/specs/2026-08-04-module-table-registry.md
197
+ */
198
+ storageScope?: string
199
+ /** Loads the table's FK lookup sources. When set, the Configure View drawer
200
+ * shows a "Linked columns" section and the list request carries the active
201
+ * perspective's linked columns as a `lookups` param for server enrichment. */
202
+ loadLookupSources?: LoadLookupSources
203
+ /**
204
+ * Loads the table's one-to-many rollup sources. When set, the Configure View
205
+ * drawer shows a "Summarised columns" section, and this hook serialises the
206
+ * active perspective's rollup columns into a `rollups` param for the list
207
+ * route to aggregate.
208
+ */
209
+ loadRollupSources?: LoadRollupSources
210
+
211
+ defaultSort?: { field: string; direction: 'asc' | 'desc' }
212
+ defaultPageSize?: number
213
+ /** Initial filters to seed on mount (e.g., from URL params). Only read once. */
214
+ initialFilters?: FilterRow[]
215
+ /**
216
+ * Filters a WORKSPACE is driving this table with, already translated into
217
+ * this table's own field names by `mapCriteriaForTable`.
218
+ *
219
+ * Held in a different place from the user's own `filters` on purpose. Merging
220
+ * them into that state would overwrite what the user set in this pane, and
221
+ * turning the workspace toggle back off could then restore nothing — the
222
+ * local filters would be gone. These combine with `filters` ONLY where the
223
+ * list request is built, and a shared rule wins over a local rule on the same
224
+ * field for that request alone (see `mergeSharedFilters`).
225
+ *
226
+ * Absent ⇒ this hook behaves exactly as it did before the workspace existed.
227
+ */
228
+ sharedFilters?: FilterRow[]
229
+ /**
230
+ * The workspace search box's value, while the workspace is driving this pane.
231
+ *
232
+ * PRESENCE IS THE SWITCH, and the distinction matters:
233
+ * `undefined` → no workspace search; the pane's own search box applies.
234
+ * `''` → the workspace bar IS on and its box is empty; the pane's
235
+ * own (now HIDDEN) search must not keep filtering, so this
236
+ * empty needle wins.
237
+ * Any non-empty string replaces the pane's own search for the request. This
238
+ * mirrors the UI: while the bar is on, each pane's search row is hidden, and
239
+ * filtering by a box nobody can see is how a user comes to distrust the grid.
240
+ * The pane's own `search` state is left untouched throughout, so switching the
241
+ * workspace off restores it.
242
+ */
243
+ sharedSearch?: string
244
+ idColumn?: string
245
+
246
+ delete?: boolean | string | DynamicTablePageDeleteConfig<TRow>
247
+ create?: boolean | DynamicTablePageCreateConfig
248
+ cellEdit?: false | DynamicTablePageCellEditConfig
249
+
250
+ queryKey?: string
251
+ /** Extra values appended to the query key for cache invalidation (e.g., scopeVersion) */
252
+ queryKeyDeps?: unknown[]
253
+ extraParams?: Record<string, string> | (() => Record<string, string>)
254
+ mapApiItem?: (item: any) => TRow | null
255
+
256
+ hooks?: DynamicTablePageHooks<TRow>
257
+
258
+ tableProps?: Partial<
259
+ Omit<
260
+ DynamicTableProps,
261
+ 'data' | 'columns' | 'tableRef' | 'pagination' | 'savedPerspectives' | 'activePerspectiveId' | 'tableName'
262
+ >
263
+ >
264
+ }
265
+
266
+ // ─── Return Type ─────────────────────────────
267
+
268
+ export interface DynamicTablePageResult<TRow = any> {
269
+ props: DynamicTableProps
270
+ /** Delete confirmation dialog. Render inline as {table.deleteDialog}. Returns null when no deletion is pending. */
271
+ deleteDialog: React.ReactNode
272
+ /** Trigger the delete dialog for a given row */
273
+ setRowToDelete: (row: TRow | null) => void
274
+ query: ReturnType<typeof useQuery>
275
+ /**
276
+ * Dataset-wide aggregate for the active view's aggregation rules. `null`
277
+ * until it lands, or when the list route has not adopted `?aggregate=`.
278
+ */
279
+ aggregateResult: AggregateResult | null
280
+ aggregateLoading: boolean
281
+ aggregateError: boolean
282
+ /**
283
+ * The LIST request failed (HEDGE-119). Exposed for hosts that render their
284
+ * own body instead of the grid, so they can refuse to draw an "empty"
285
+ * affordance over a request that never landed.
286
+ */
287
+ isError: boolean
288
+ isLoading: boolean
289
+ refresh: () => void
290
+ state: {
291
+ page: number
292
+ limit: number
293
+ search: string
294
+ filters: FilterRow[]
295
+ sortField: string
296
+ sortDir: 'asc' | 'desc'
297
+ /**
298
+ * The active grouping, mirrored up from the table.
299
+ *
300
+ * READ-ONLY, and it is a MIRROR, not the source: `DynamicTable` owns the
301
+ * grouping state and reports it in PERSPECTIVE_CHANGE. A host that wants to
302
+ * label something by the active grouping reads it here; a host that wants
303
+ * to CHANGE it uses the Configure View drawer like everything else.
304
+ */
305
+ grouping: GroupRule[]
306
+ /** How the table is currently drawing its rows. Same mirror contract. */
307
+ viewMode: TableViewMode
308
+ }
309
+ }
310
+
311
+ // ─── Hook ────────────────────────────────────
312
+
313
+ export function useDynamicTablePage<TRow = any>(
314
+ config: DynamicTablePageConfig<TRow>
315
+ ): DynamicTablePageResult<TRow> {
316
+ const {
317
+ source,
318
+ columns,
319
+ tableName,
320
+ perspectives: perspectivesTableId,
321
+ defaultSort,
322
+ defaultPageSize = 50,
323
+ idColumn = 'id',
324
+ storageScope = '',
325
+ hooks,
326
+ } = config
327
+
328
+ const tableRef = useRef<HTMLDivElement>(null)
329
+ const queryClient = useQueryClient()
330
+
331
+ // Identity for this INSTANCE's client-side storage (last-used perspective,
332
+ // column widths) — as opposed to `perspectivesTableId`, which identifies the
333
+ // TABLE and must stay unscoped so every instance loads the same saved views
334
+ // from the server. With an empty scope this is byte-identical to the old key.
335
+ const storageTableId = perspectivesTableId
336
+ ? `${storageScope}${perspectivesTableId}`
337
+ : undefined
338
+
339
+ // Organization scope. Features AND rows are org-scoped, and switching org
340
+ // dispatches a client-side event with NO page reload — so without this in the
341
+ // query key a cached table goes on showing the PREVIOUS organization's rows.
342
+ // That is authorised data in the wrong scope, presented as current, which is
343
+ // how someone invoices the wrong customer.
344
+ //
345
+ // Placed in the DEFAULT key rather than left to each table's `queryKeyDeps`:
346
+ // only 3 of 20 tables opted in, so 17 carried the bug. One place fixes all.
347
+ const organizationScopeVersion = useOrganizationScopeVersion()
348
+
349
+ // ── State ──
350
+
351
+ const [page, setPage] = useState(1)
352
+ const [limit, setLimit] = useState(defaultPageSize)
353
+ const [sortField, setSortField] = useState(defaultSort?.field ?? 'id')
354
+ const [sortDir, setSortDir] = useState<'asc' | 'desc'>(defaultSort?.direction ?? 'asc')
355
+ const [search, setSearch] = useState('')
356
+ const [filters, setFilters] = useState<FilterRow[]>(() => config.initialFilters ?? [])
357
+ // Mirrors of table-owned view state, kept so a HOST can read them (a grid
358
+ // renderer, a heading, an export label). Written only from PERSPECTIVE_CHANGE
359
+ // — never set from here, because a second writer is a second source of truth.
360
+ const [grouping, setGrouping] = useState<GroupRule[]>([])
361
+ const [viewMode, setViewMode] = useState<TableViewMode>('table')
362
+
363
+ const [savedPerspectives, setSavedPerspectives] = useState<PerspectiveConfig[]>([])
364
+ // Views other people published for anyone to COPY. Never applied
365
+ // automatically — see `PerspectiveTemplate`.
366
+ const [sharedTemplates, setSharedTemplates] = useState<PerspectiveTemplate[]>([])
367
+ const [activePerspectiveId, setActivePerspectiveId] = useState<string | null>(null)
368
+ // Mirror of the active perspective's linked (lookup) columns. Synced from the
369
+ // DynamicTable's perspective events so the list request can carry them to the
370
+ // server for enrichment. The grid renders the columns itself from the same
371
+ // perspective state.
372
+ const [lookupColumns, setLookupColumns] = useState<LookupColumnRef[]>([])
373
+ // Mirror of the active perspective's rollup ("summarised") columns, for the
374
+ // same reason as `lookupColumns` — except the cost is higher: each distinct
375
+ // SOURCE is one GROUP BY on the server, so the param carries only what the
376
+ // active view actually asked for, and an empty param leaves the route
377
+ // behaving byte-identically to before rollups existed.
378
+ const [rollupColumns, setRollupColumns] = useState<RollupColumnRef[]>([])
379
+ // Mirror of the active perspective's aggregation rules. Same reason as
380
+ // `lookupColumns`: the grid owns the rules, but the DATASET-wide totals can
381
+ // only be computed on the server, so the request has to carry them.
382
+ const [aggregations, setAggregations] = useState<AggregationRule[]>([])
383
+
384
+ const [pendingDelete, setPendingDelete] = useState<TRow | null>(null)
385
+ const [isDeleting, setIsDeleting] = useState(false)
386
+ // Bulk delete drives a confirmation modal. The promise resolver bridges the
387
+ // modal outcome back to the DynamicTable so it only clears its selection once
388
+ // the rows are actually deleted (and keeps it on cancel).
389
+ const [pendingBulkDelete, setPendingBulkDelete] = useState<string[] | null>(null)
390
+ const bulkResolverRef = useRef<{ resolve: () => void; reject: (reason?: unknown) => void } | null>(null)
391
+
392
+ const queryKeyBase = config.queryKey ?? source
393
+
394
+ // ── List invalidation, COALESCED ──
395
+ //
396
+ // Every successful cell save used to call `invalidateQueries` on the spot, so
397
+ // a three-cell edit produced three PUTs and three IDENTICAL GETs of the same
398
+ // page (ledger row 9.7) — and a paste or a fill, which is one gesture, fires
399
+ // one per written cell. The refetch is not what makes the screen correct: the
400
+ // store writes the row object optimistically and the grid already renders the
401
+ // new value. The refetch exists to pick up SERVER-DERIVED changes (computed
402
+ // columns, a status recalculated by a subscriber), and for that a single
403
+ // trailing refetch after the burst is exactly as correct as N of them.
404
+ //
405
+ // Trailing edge, not leading: the last write in a burst must be included.
406
+ const listInvalidateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
407
+ const cancelPendingListInvalidation = useCallback(() => {
408
+ if (listInvalidateTimerRef.current) {
409
+ clearTimeout(listInvalidateTimerRef.current)
410
+ listInvalidateTimerRef.current = null
411
+ }
412
+ }, [])
413
+ const scheduleListInvalidation = useCallback(() => {
414
+ cancelPendingListInvalidation()
415
+ listInvalidateTimerRef.current = setTimeout(() => {
416
+ listInvalidateTimerRef.current = null
417
+ queryClient.invalidateQueries({ queryKey: [queryKeyBase] })
418
+ }, LIST_REFETCH_COALESCE_MS)
419
+ }, [cancelPendingListInvalidation, queryClient, queryKeyBase])
420
+ /** Row-count changes (create / delete) refetch NOW — and supersede any pending burst. */
421
+ const invalidateListNow = useCallback(() => {
422
+ cancelPendingListInvalidation()
423
+ queryClient.invalidateQueries({ queryKey: [queryKeyBase] })
424
+ }, [cancelPendingListInvalidation, queryClient, queryKeyBase])
425
+ useEffect(() => cancelPendingListInvalidation, [cancelPendingListInvalidation])
426
+
427
+ // Columns the host table hides by default. Needed when rehydrating saved
428
+ // perspectives: one saved without column config must fall back to the table's
429
+ // defaults rather than revealing every hidden column.
430
+ const defaultHiddenColumns = config.tableProps?.defaultHiddenColumns ?? EMPTY_COLUMNS
431
+
432
+ // ── Perspectives ──
433
+
434
+ const { data: perspectivesData, isFetched: perspectivesFetched } = useQuery({
435
+ queryKey: ['perspectives', perspectivesTableId],
436
+ queryFn: async () => {
437
+ const response = await apiCall<PerspectivesIndexResponse>(
438
+ `/api/perspectives/${perspectivesTableId}`
439
+ )
440
+ return response.ok ? response.result : null
441
+ },
442
+ enabled: !!perspectivesTableId,
443
+ })
444
+
445
+ // ── The list query waits for the perspective to be resolved ──
446
+ //
447
+ // Ledger 4.9: a saved view carrying rollup columns rendered every rollup cell
448
+ // as "—" on the FIRST load. The columns come from the perspective and paint
449
+ // immediately; the VALUES come from `?rollups=` on the list request, and that
450
+ // param is derived from the same perspective — which had not been fetched yet
451
+ // when the list query fired. So load one produced a request WITHOUT the param
452
+ // (rollup columns present, all values null), and only the second request —
453
+ // triggered when `setRollupColumns` finally changed `queryParams` — carried
454
+ // it. Two round-trips, and a window in between where the grid confidently
455
+ // shows em-dashes for numbers that exist.
456
+ //
457
+ // Gating the list on the perspective removes the wasted request rather than
458
+ // re-timing it. `setPerspectiveHydrated(true)` is called in the SAME effect
459
+ // that applies the restored filters/sort/lookups/rollups, so React batches
460
+ // them into one re-render and the first request the user ever sees already
461
+ // carries every param the view asked for.
462
+ //
463
+ // Two escape hatches, because a list that never loads is far worse than a
464
+ // wasted request: a table with no `perspectives` id starts hydrated, and the
465
+ // effect below flips the flag as soon as the perspectives request SETTLES,
466
+ // whatever it returned (404, empty, or columns not built yet).
467
+ const [perspectiveHydrated, setPerspectiveHydrated] = useState(!perspectivesTableId)
468
+
469
+ useEffect(() => {
470
+ if (!perspectivesTableId || !perspectivesFetched) return
471
+ if (!perspectivesData?.perspectives || columns.length === 0) setPerspectiveHydrated(true)
472
+ }, [perspectivesTableId, perspectivesFetched, perspectivesData, columns.length])
473
+
474
+ // Guards the one-time initial perspective pick. Kept in a ref (not in the
475
+ // effect deps) because `activePerspectiveId` used to be both read and written
476
+ // here — which re-ran the effect and handed DynamicTable a brand-new
477
+ // `savedPerspectives` array identity on every load.
478
+ const initialPerspectivePickedRef = useRef(false)
479
+
480
+ useEffect(() => {
481
+ if (perspectivesData?.perspectives && columns.length > 0) {
482
+ const allCols = columns.map((c) => c.data)
483
+ const transformed = perspectivesData.perspectives.map((p) =>
484
+ apiToDynamicTable(p, allCols, defaultHiddenColumns)
485
+ )
486
+ setSavedPerspectives(transformed)
487
+ // Shared templates arrive on the SAME response the personal views do —
488
+ // `rolePerspectives` has always been in the payload and was simply ignored.
489
+ setSharedTemplates(
490
+ (perspectivesData.rolePerspectives ?? []).map((rp) =>
491
+ apiTemplateToDynamicTable(rp, allCols, defaultHiddenColumns)
492
+ )
493
+ )
494
+ if (!initialPerspectivePickedRef.current) {
495
+ // Prefer the view the user was last on over the server default, so
496
+ // navigating into a record and back doesn't silently reset the list.
497
+ // With neither, fall back to this user's saved personalization of the
498
+ // base tab, so "Default view" opens the way they last left it.
499
+ const remembered = readLastPerspectiveId(storageTableId)
500
+ const baseViewId = transformed.find((p) => p.isBaseView)?.id ?? null
501
+ const restored = remembered && transformed.some((p) => p.id === remembered)
502
+ ? remembered
503
+ : (perspectivesData.defaultPerspectiveId ?? baseViewId)
504
+ // Close the gate as soon as the views have LOADED, whether or not one was
505
+ // restored. It used to close only inside `if (restored)`, which made the
506
+ // feature chicken-and-egg: with no remembered view and no server default
507
+ // the ref stayed false forever, the persist effect below returned early,
508
+ // and a manual tab click was never written — so the list could only
509
+ // remember a view if it already remembered one. `remembered` is read
510
+ // above, before the gate closes, so nothing is clobbered.
511
+ initialPerspectivePickedRef.current = true
512
+ if (restored) {
513
+ setActivePerspectiveId(restored)
514
+ // Apply the view's QUERY state here, not via the grid's
515
+ // PERSPECTIVE_CHANGE event.
516
+ //
517
+ // The grid applies the view to ITSELF (columns, the sort arrow in the
518
+ // header) and announces it with PERSPECTIVE_CHANGE. On a tab click
519
+ // that reaches us and the list refetches. On first load it does NOT:
520
+ // the grid's dispatch effect and this hook's listener registration
521
+ // race, child passive effects run before the parent's, and the one
522
+ // dispatch that carries the restored view fires before
523
+ // `useEventHandlers` has attached. Proven in the browser — a cold load
524
+ // of a view sorted by Reference # ↓ issued exactly one list request,
525
+ // `sortField=containerNumber` (the table default), while the header
526
+ // already painted the ↓ on Reference #; clicking the same tab then
527
+ // issued `sortField=referenceNumber&sortDir=desc`.
528
+ //
529
+ // Reading the config straight off the perspective we just restored
530
+ // removes the race entirely rather than re-timing it, and it is the
531
+ // same projection `handlePerspectiveSelect` performs for a click, so
532
+ // the two entry points cannot drift.
533
+ const restoredPerspective = transformed.find((p) => p.id === restored)
534
+ if (restoredPerspective) {
535
+ // Only when the view HAS filters: a view without them must not wipe
536
+ // `initialFilters` seeded from the URL (deep links carry a filter).
537
+ if (restoredPerspective.filters.length > 0) setFilters(restoredPerspective.filters)
538
+ setLookupColumns(restoredPerspective.lookupColumns ?? [])
539
+ setRollupColumns(restoredPerspective.rollupColumns ?? [])
540
+ setAggregations(restoredPerspective.aggregations ?? [])
541
+ if (restoredPerspective.sorting.length > 0) {
542
+ setSortField(restoredPerspective.sorting[0].field)
543
+ setSortDir(restoredPerspective.sorting[0].direction)
544
+ }
545
+ }
546
+ }
547
+ // Released in the SAME batch as the setters above, so the very first
548
+ // list request carries the restored view's rollups/lookups/sort/filters
549
+ // rather than being issued once without them and again with (4.9).
550
+ setPerspectiveHydrated(true)
551
+ }
552
+ }
553
+ }, [perspectivesData, columns, defaultHiddenColumns, perspectivesTableId])
554
+
555
+ // Persist every subsequent switch (tab click, save, delete-active) so the
556
+ // next mount restores it. Gated on the initial pick so the mount-time `null`
557
+ // can't wipe the remembered view before it has been read back.
558
+ useEffect(() => {
559
+ if (!initialPerspectivePickedRef.current) return
560
+ writeLastPerspectiveId(storageTableId, activePerspectiveId)
561
+ }, [activePerspectiveId, perspectivesTableId])
562
+
563
+ // ── Workspace-shared criteria ──
564
+ //
565
+ // The ONLY place the workspace's criteria and this pane's own state meet.
566
+ // Both `search`/`filters` above stay exactly what the user set here, so
567
+ // turning the workspace off is a pure removal — nothing to restore, because
568
+ // nothing was overwritten.
569
+ const sharedFilters = config.sharedFilters
570
+ const sharedSearch = config.sharedSearch
571
+ // Identity note: with no shared filters this returns `filters` itself, so the
572
+ // memo below sees the same reference it always did.
573
+ const effectiveFilters = useMemo(
574
+ () => mergeSharedFilters(filters, sharedFilters),
575
+ [filters, sharedFilters],
576
+ )
577
+ const effectiveSearch = sharedSearch !== undefined ? sharedSearch : search
578
+
579
+ // ── Data Query ──
580
+
581
+ const queryParams = useMemo(() => {
582
+ const params = new URLSearchParams()
583
+ params.set('page', String(page))
584
+ params.set('limit', String(limit))
585
+ params.set('pageSize', String(limit))
586
+ params.set('sortField', sortField)
587
+ params.set('sortDir', sortDir)
588
+ if (effectiveSearch) {
589
+ params.set('q', effectiveSearch)
590
+ params.set('search', effectiveSearch)
591
+ }
592
+ if (effectiveFilters.length) params.set('filters', JSON.stringify(effectiveFilters))
593
+ if (lookupColumns.length) params.set('lookups', lookupColumnsToParam(lookupColumns))
594
+ if (rollupColumns.length) params.set('rollups', rollupColumnsToParam(rollupColumns))
595
+
596
+ const extra =
597
+ typeof config.extraParams === 'function' ? config.extraParams() : config.extraParams
598
+ if (extra) {
599
+ Object.entries(extra).forEach(([key, value]) => params.set(key, value))
600
+ }
601
+
602
+ return params.toString()
603
+ }, [page, limit, sortField, sortDir, effectiveSearch, effectiveFilters, lookupColumns, rollupColumns, config.extraParams])
604
+
605
+ // `organizationScopeVersion` leads the deps so EVERY table refetches when the
606
+ // user switches organization, whether or not it opted in via `queryKeyDeps`.
607
+ // The three tables that already pass `scopeVersion` themselves are unaffected
608
+ // — a repeated value in a query key is idempotent.
609
+ const queryKeyDeps = useMemo(
610
+ () => [organizationScopeVersion, ...(config.queryKeyDeps ?? [])],
611
+ [organizationScopeVersion, config.queryKeyDeps],
612
+ )
613
+
614
+ const dataQuery = useQuery({
615
+ queryKey: [queryKeyBase, queryParams, ...queryKeyDeps],
616
+ queryFn: async () => {
617
+ const call = await apiCall<{
618
+ items: any[]
619
+ total: number
620
+ totalPages?: number
621
+ }>(`${source}?${queryParams}`)
622
+ if (!call.ok) throw new Error(`Failed to load ${tableName}`)
623
+ return call.result ?? { items: [], total: 0, totalPages: 1 }
624
+ },
625
+ // See `perspectiveHydrated` above (ledger 4.9). The skeleton stays up for
626
+ // the extra ~50 ms the perspectives request costs; in exchange the first
627
+ // response already carries the view's rollup values instead of a grid full
628
+ // of em-dashes that silently corrects itself a round-trip later.
629
+ enabled: perspectiveHydrated,
630
+ placeholderData: (previousData) => previousData,
631
+ })
632
+
633
+ // ── Dataset-wide aggregate ──
634
+ //
635
+ // A SEPARATE request, deliberately: the rows must never wait on a full-table
636
+ // SUM. The grid shows a page-scoped fold immediately and swaps in the dataset
637
+ // figure when it lands. It reuses `queryParams` (same filters, same search)
638
+ // minus paging, so the total always describes exactly the filtered set the
639
+ // page is a window onto.
640
+ // `field:fn`, or `field:fn:dimension` when the rule partitions its aggregate
641
+ // (per-currency totals). The third segment is optional so every previously
642
+ // published route keeps parsing the specs it already knows.
643
+ const aggregateSpec = useMemo(
644
+ () =>
645
+ aggregations
646
+ .map((r) => (r.dimension ? `${r.field}:${r.fn}:${r.dimension}` : `${r.field}:${r.fn}`))
647
+ .join(','),
648
+ [aggregations],
649
+ )
650
+
651
+ const aggregateParams = useMemo(() => {
652
+ if (!aggregateSpec) return null
653
+ const params = new URLSearchParams(queryParams)
654
+ params.set('aggregate', aggregateSpec)
655
+ // One row is enough to satisfy a route that always paginates; we only read
656
+ // the `aggregate` key off the response.
657
+ params.set('page', '1')
658
+ params.set('limit', '1')
659
+ params.set('pageSize', '1')
660
+ return params.toString()
661
+ }, [queryParams, aggregateSpec])
662
+
663
+ // PROBE ONCE per page. `makeDynamicTableRoute` has no consumers yet, so most
664
+ // list routes simply ignore `?aggregate=` — asking again on every filter
665
+ // keystroke would be one wasted round-trip per keystroke, forever.
666
+ const aggregateUnsupportedRef = useRef(false)
667
+
668
+ const aggregateQuery = useQuery({
669
+ queryKey: [queryKeyBase, 'aggregate', aggregateParams, ...queryKeyDeps],
670
+ enabled: !!aggregateParams && !aggregateUnsupportedRef.current,
671
+ queryFn: async () => {
672
+ const call = await apiCall<{ aggregate?: AggregateResult }>(`${source}?${aggregateParams}`)
673
+ // Degrade, never blank. A route that has not adopted `?aggregate=`
674
+ // (or that rejects the param outright) is not an error condition: the
675
+ // footer already holds a page-scoped fold, and saying "this page only"
676
+ // is truthful. Blanking the totals because a probe failed would be a
677
+ // strictly worse answer than the one we already have.
678
+ if (!call.ok || !call.result?.aggregate) {
679
+ aggregateUnsupportedRef.current = true
680
+ return null
681
+ }
682
+ return call.result.aggregate
683
+ },
684
+ placeholderData: (previousData) => previousData,
685
+ })
686
+
687
+ const tableData = useMemo(() => {
688
+ const items = dataQuery.data?.items ?? []
689
+ if (config.mapApiItem) {
690
+ return items.map(config.mapApiItem).filter(Boolean)
691
+ }
692
+ return items
693
+ }, [dataQuery.data?.items, config.mapApiItem])
694
+
695
+ // ── Whole-table export ──
696
+ // Powers the toolbar Export button: page through `source` collecting every
697
+ // row, ignoring the active perspective (filters + search) and pagination so
698
+ // the export is the whole table. Keeps `extraParams` (tab/category scope) and
699
+ // sort for stable ordering. Mapped through `mapApiItem` like the live table.
700
+ //
701
+ // HEDGE-123 — IT ABORTS, IT DOES NOT SHORTEN.
702
+ //
703
+ // This loop used to end every failure the same way it ended success: `break`,
704
+ // then `return collected`. A page that 500'd, a page that came back `200`
705
+ // with an unparseable body, and a walk that ran into the iteration cap all
706
+ // produced a shorter array and nothing else. The user got a CSV that opened,
707
+ // had headers, had rows, and was missing the rest — with no error anywhere.
708
+ //
709
+ // That is the same server failure as HEDGE-119 wearing different clothes:
710
+ // there a failed fetch rendered as "no invoices", here it writes a short file
711
+ // that reads as "these are all the invoices". The file is the more dangerous
712
+ // of the two, because it looks like an answer and goes into a reconciliation.
713
+ //
714
+ // So: a failed or unparseable page THROWS (the toolbar catches it and says
715
+ // the export failed, and no file is written), and a walk that cannot prove it
716
+ // collected everything returns `complete: false` WITH ITS COUNTS rather than
717
+ // a bare array.
718
+ const exportAllRows = useCallback(async (): Promise<ExportAllResult> => {
719
+ const pageLimit = 100 // upstream list cap
720
+ const collected: any[] = []
721
+ let pageNum = 1
722
+ let expected: number | null = null
723
+
724
+ // Bound the walk by what the SERVER says, not by a constant. The old
725
+ // `guard < 1000` was a silent 100,000-row ceiling: a bigger table exported
726
+ // short for a reason that had nothing to do with failure, and said nothing.
727
+ // Once page 1 reports `totalPages` we know exactly how many iterations are
728
+ // needed; the constant below is only a runaway stop for a server that never
729
+ // reports one, and reaching it is now REPORTED rather than swallowed.
730
+ const RUNAWAY_PAGE_CAP = 10_000
731
+ let maxPages = RUNAWAY_PAGE_CAP
732
+ let incomplete: string | null = null
733
+
734
+ while (pageNum <= maxPages) {
735
+ const params = new URLSearchParams()
736
+ params.set('page', String(pageNum))
737
+ params.set('limit', String(pageLimit))
738
+ params.set('pageSize', String(pageLimit))
739
+ params.set('sortField', sortField)
740
+ params.set('sortDir', sortDir)
741
+ const extra =
742
+ typeof config.extraParams === 'function' ? config.extraParams() : config.extraParams
743
+ if (extra) Object.entries(extra).forEach(([key, value]) => params.set(key, value))
744
+ if (lookupColumns.length) params.set('lookups', lookupColumnsToParam(lookupColumns))
745
+ if (rollupColumns.length) params.set('rollups', rollupColumnsToParam(rollupColumns))
746
+
747
+ const call = await apiCall<{ items: any[]; total: number; totalPages?: number }>(
748
+ `${source}?${params.toString()}`,
749
+ )
750
+
751
+ // The two failures that used to be indistinguishable from "done".
752
+ // `!call.result` is NOT redundant with `!call.ok`: `apiCall` returns
753
+ // `ok: true, result: null` for a 200 whose body will not parse, which is
754
+ // exactly what a route erroring after its status line produces.
755
+ if (!call.ok) {
756
+ throw new Error(
757
+ `Export failed on page ${pageNum} (HTTP ${call.status}) after ${collected.length} row(s)`,
758
+ )
759
+ }
760
+ if (!call.result) {
761
+ throw new Error(
762
+ `Export failed on page ${pageNum}: the server returned no readable body after ${collected.length} row(s)`,
763
+ )
764
+ }
765
+
766
+ const items = call.result.items ?? []
767
+ collected.push(...items)
768
+
769
+ if (typeof call.result.total === 'number') expected = call.result.total
770
+ const total = call.result.total ?? collected.length
771
+ const totalPages = call.result.totalPages ?? Math.ceil(total / pageLimit)
772
+ // Now that the server has declared its own page count, hold the walk to
773
+ // it. `+ 1` of slack absorbs a row appearing mid-walk without turning a
774
+ // benign race into a hard stop.
775
+ if (Number.isFinite(totalPages) && totalPages > 0) {
776
+ maxPages = Math.min(RUNAWAY_PAGE_CAP, totalPages + 1)
777
+ }
778
+
779
+ if (items.length === 0 || pageNum >= totalPages) break
780
+ pageNum++
781
+
782
+ if (pageNum > maxPages) {
783
+ incomplete =
784
+ maxPages >= RUNAWAY_PAGE_CAP
785
+ ? `stopped at the ${RUNAWAY_PAGE_CAP}-page safety limit`
786
+ : `the server declared ${totalPages} page(s) but did not finish within them`
787
+ }
788
+ }
789
+
790
+ const rows = config.mapApiItem
791
+ ? collected.map(config.mapApiItem).filter(Boolean)
792
+ : collected
793
+
794
+ // A count shortfall is reported, never silently accepted — but it is NOT a
795
+ // throw. Rows deleted by someone else while the walk was in flight make
796
+ // `collected < expected` legitimately, and failing an export over a benign
797
+ // concurrent edit would get the button distrusted. The caller decides; what
798
+ // matters is that it is told, with both numbers.
799
+ if (!incomplete && expected !== null && collected.length < expected) {
800
+ incomplete = `fetched ${collected.length} of ${expected} row(s)`
801
+ }
802
+
803
+ return {
804
+ rows,
805
+ expected,
806
+ complete: incomplete === null,
807
+ ...(incomplete ? { reason: incomplete } : {}),
808
+ }
809
+ }, [source, sortField, sortDir, lookupColumns, rollupColumns, config.extraParams, config.mapApiItem])
810
+
811
+ // ── Cell Edit Handler ──
812
+
813
+ const handleCellEditSave = useCallback(
814
+ async (payload: CellEditSaveEvent) => {
815
+ if (config.cellEdit === false) return
816
+
817
+ dispatch(tableRef.current as HTMLElement, TableEvents.CELL_SAVE_START, {
818
+ rowIndex: payload.rowIndex,
819
+ colIndex: payload.colIndex,
820
+ } as CellSaveStartEvent)
821
+
822
+ try {
823
+ const rowData = tableData[payload.rowIndex]
824
+ if (!rowData) throw new Error('Row data not found')
825
+
826
+ const cellEditConfig = typeof config.cellEdit === 'object' ? config.cellEdit : {}
827
+ let hookResult: { url?: string; payload?: Record<string, unknown>; method?: string } | void = undefined
828
+
829
+ if (hooks?.beforeCellEdit) {
830
+ hookResult = hooks.beforeCellEdit(payload, rowData)
831
+ }
832
+
833
+ let apiUrl: string
834
+ if (hookResult?.url) {
835
+ apiUrl = hookResult.url
836
+ } else if (typeof cellEditConfig.url === 'function') {
837
+ apiUrl = cellEditConfig.url(payload, rowData)
838
+ } else if (cellEditConfig.url) {
839
+ apiUrl = `${cellEditConfig.url}/${(rowData as any)[idColumn]}`
840
+ } else {
841
+ apiUrl = `${source}/${(rowData as any)[idColumn]}`
842
+ }
843
+
844
+ let updatePayload: Record<string, unknown>
845
+ if (hookResult?.payload) {
846
+ updatePayload = hookResult.payload
847
+ } else if (cellEditConfig.mapPayload) {
848
+ updatePayload = cellEditConfig.mapPayload(payload, rowData)
849
+ } else {
850
+ updatePayload = { [payload.prop]: payload.newValue }
851
+ }
852
+
853
+ const method = hookResult?.method ?? cellEditConfig.method ?? 'PUT'
854
+
855
+ const response = await apiCall<{ error?: string }>(apiUrl, {
856
+ method,
857
+ headers: { 'Content-Type': 'application/json' },
858
+ body: JSON.stringify(updatePayload),
859
+ })
860
+
861
+ if (response.ok) {
862
+ dispatch(tableRef.current as HTMLElement, TableEvents.CELL_SAVE_SUCCESS, {
863
+ rowIndex: payload.rowIndex,
864
+ colIndex: payload.colIndex,
865
+ } as CellSaveSuccessEvent)
866
+ scheduleListInvalidation()
867
+ if (hooks?.afterMutation) await hooks.afterMutation('cellEdit', { payload, rowData })
868
+ } else {
869
+ const error = response.result?.error || 'Update failed'
870
+ flash(error, 'error')
871
+ dispatch(tableRef.current as HTMLElement, TableEvents.CELL_SAVE_ERROR, {
872
+ rowIndex: payload.rowIndex,
873
+ colIndex: payload.colIndex,
874
+ error,
875
+ } as CellSaveErrorEvent)
876
+ }
877
+ } catch (error) {
878
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
879
+ flash(errorMessage, 'error')
880
+ dispatch(tableRef.current as HTMLElement, TableEvents.CELL_SAVE_ERROR, {
881
+ rowIndex: payload.rowIndex,
882
+ colIndex: payload.colIndex,
883
+ error: errorMessage,
884
+ } as CellSaveErrorEvent)
885
+ }
886
+ },
887
+ [tableData, scheduleListInvalidation, config.cellEdit, hooks, source, idColumn, queryKeyBase]
888
+ )
889
+
890
+ // ── New Row Handler ──
891
+
892
+ const handleNewRowSave = useCallback(
893
+ async (payload: NewRowSaveEvent) => {
894
+ if (!config.create) return
895
+
896
+ const createConfig = typeof config.create === 'object' ? config.create : {}
897
+
898
+ // Custom handler — fully replaces built-in flow
899
+ if (createConfig.handler) {
900
+ await createConfig.handler(payload, {
901
+ tableRef: tableRef as React.RefObject<HTMLElement>,
902
+ invalidate: () => invalidateListNow(),
903
+ })
904
+ return
905
+ }
906
+
907
+ try {
908
+ let rowPayload: Record<string, unknown> = { ...payload.rowData }
909
+
910
+ if (hooks?.validateCreate) {
911
+ const validationError = hooks.validateCreate(payload.rowData)
912
+ if (validationError) {
913
+ flash(validationError, 'error')
914
+ dispatch(tableRef.current as HTMLElement, TableEvents.NEW_ROW_SAVE_ERROR, {
915
+ rowIndex: payload.rowIndex,
916
+ error: validationError,
917
+ } as NewRowSaveErrorEvent)
918
+ return
919
+ }
920
+ }
921
+
922
+ if (hooks?.beforeCreate) {
923
+ const mapped = await hooks.beforeCreate(payload.rowData)
924
+ if (mapped) rowPayload = mapped
925
+ }
926
+
927
+ if (createConfig.mapPayload) {
928
+ rowPayload = createConfig.mapPayload(payload.rowData)
929
+ }
930
+
931
+ const createUrl = createConfig.url ?? source
932
+
933
+ const response = await apiCall<{ id: string; error?: string }>(createUrl, {
934
+ method: 'POST',
935
+ headers: { 'Content-Type': 'application/json' },
936
+ body: JSON.stringify(rowPayload),
937
+ })
938
+
939
+ if (response.ok && response.result) {
940
+ dispatch(tableRef.current as HTMLElement, TableEvents.NEW_ROW_SAVE_SUCCESS, {
941
+ rowIndex: payload.rowIndex,
942
+ savedRowData: { ...payload.rowData, id: response.result.id },
943
+ })
944
+ invalidateListNow()
945
+ if (hooks?.afterMutation)
946
+ await hooks.afterMutation('create', { payload, result: response.result })
947
+ } else {
948
+ const error = response.result?.error || `Failed to create ${tableName}`
949
+ flash(error, 'error')
950
+ dispatch(tableRef.current as HTMLElement, TableEvents.NEW_ROW_SAVE_ERROR, {
951
+ rowIndex: payload.rowIndex,
952
+ error,
953
+ } as NewRowSaveErrorEvent)
954
+ }
955
+ } catch (error) {
956
+ const errorMessage = error instanceof Error ? error.message : `Failed to create ${tableName}`
957
+ flash(errorMessage, 'error')
958
+ dispatch(tableRef.current as HTMLElement, TableEvents.NEW_ROW_SAVE_ERROR, {
959
+ rowIndex: payload.rowIndex,
960
+ error: errorMessage,
961
+ } as NewRowSaveErrorEvent)
962
+ }
963
+ },
964
+ [config.create, hooks, source, tableName, queryClient, queryKeyBase]
965
+ )
966
+
967
+ // ── Delete Handler ──
968
+
969
+ const handleConfirmDelete = useCallback(async () => {
970
+ if (!pendingDelete) return
971
+
972
+ if (hooks?.beforeDelete) {
973
+ const proceed = await hooks.beforeDelete(pendingDelete)
974
+ if (!proceed) return
975
+ }
976
+
977
+ setIsDeleting(true)
978
+ try {
979
+ const deleteId = (pendingDelete as any)[idColumn]
980
+ let deleteUrl: string
981
+ if (typeof config.delete === 'string') {
982
+ deleteUrl = `${config.delete}/${deleteId}`
983
+ } else if (typeof config.delete === 'object' && config.delete.url) {
984
+ const urlConfig = config.delete.url
985
+ deleteUrl = typeof urlConfig === 'function'
986
+ ? urlConfig(pendingDelete)
987
+ : `${urlConfig}/${deleteId}`
988
+ } else {
989
+ deleteUrl = `${source}/${deleteId}`
990
+ }
991
+
992
+ const response = await apiCall<{ error?: string }>(deleteUrl, { method: 'DELETE' })
993
+
994
+ if (response.ok) {
995
+ flash(`${tableName} deleted`, 'success')
996
+ invalidateListNow()
997
+ setPendingDelete(null)
998
+ if (hooks?.afterMutation) await hooks.afterMutation('delete', { row: pendingDelete })
999
+ } else {
1000
+ flash(response.result?.error || `Failed to delete ${tableName}`, 'error')
1001
+ }
1002
+ } catch (error) {
1003
+ flash(error instanceof Error ? error.message : `Failed to delete ${tableName}`, 'error')
1004
+ } finally {
1005
+ setIsDeleting(false)
1006
+ }
1007
+ }, [pendingDelete, hooks, idColumn, config.delete, source, tableName, queryClient, queryKeyBase])
1008
+
1009
+ // ── Bulk Delete Handler ──
1010
+ // Drives the DynamicTable grouped-actions bar. Deletes each selected row via
1011
+ // the same endpoint convention as the single-row delete, then refreshes the
1012
+ // list. Failures are tallied so a partial success still surfaces clearly.
1013
+ const buildDeleteUrl = useCallback(
1014
+ (id: string) => {
1015
+ if (typeof config.delete === 'string') return `${config.delete}/${id}`
1016
+ if (config.delete && typeof config.delete === 'object' && config.delete.url) {
1017
+ const urlConfig = config.delete.url
1018
+ return typeof urlConfig === 'function'
1019
+ ? urlConfig({ [idColumn]: id } as TRow)
1020
+ : `${urlConfig}/${id}`
1021
+ }
1022
+ return `${source}/${id}`
1023
+ },
1024
+ [config.delete, idColumn, source]
1025
+ )
1026
+
1027
+ // onBulkDelete prop for DynamicTable: open the confirmation modal and return a
1028
+ // promise that resolves once the rows are deleted, or rejects if cancelled.
1029
+ const requestBulkDelete = useCallback(
1030
+ (ids: string[]) =>
1031
+ new Promise<void>((resolve, reject) => {
1032
+ if (!ids.length) {
1033
+ resolve()
1034
+ return
1035
+ }
1036
+ bulkResolverRef.current = { resolve, reject }
1037
+ setPendingBulkDelete(ids)
1038
+ }),
1039
+ []
1040
+ )
1041
+
1042
+ const handleConfirmBulkDelete = useCallback(async () => {
1043
+ const ids = pendingBulkDelete
1044
+ if (!ids || !ids.length) return
1045
+ setIsDeleting(true)
1046
+ try {
1047
+ let deleted = 0
1048
+ let failed = 0
1049
+ for (const id of ids) {
1050
+ try {
1051
+ const response = await apiCall<{ error?: string }>(buildDeleteUrl(id), { method: 'DELETE' })
1052
+ if (response.ok) deleted += 1
1053
+ else failed += 1
1054
+ } catch {
1055
+ failed += 1
1056
+ }
1057
+ }
1058
+ if (deleted > 0) flash(`${deleted} ${tableName} deleted`, 'success')
1059
+ if (failed > 0) flash(`Failed to delete ${failed} ${tableName}`, 'error')
1060
+ invalidateListNow()
1061
+ if (hooks?.afterMutation) await hooks.afterMutation('delete', { ids })
1062
+ bulkResolverRef.current?.resolve()
1063
+ } finally {
1064
+ bulkResolverRef.current = null
1065
+ setPendingBulkDelete(null)
1066
+ setIsDeleting(false)
1067
+ }
1068
+ }, [pendingBulkDelete, buildDeleteUrl, tableName, queryClient, queryKeyBase, hooks])
1069
+
1070
+ const cancelBulkDelete = useCallback(() => {
1071
+ bulkResolverRef.current?.reject(new Error('cancelled'))
1072
+ bulkResolverRef.current = null
1073
+ setPendingBulkDelete(null)
1074
+ }, [])
1075
+
1076
+ // ── Perspective Handlers ──
1077
+
1078
+ const handlePerspectiveSave = useCallback(
1079
+ async (payload: PerspectiveSaveEvent) => {
1080
+ if (!perspectivesTableId) return
1081
+ const settings = dynamicTableToApi(payload.perspective)
1082
+ // Resolve the row to update BY ID FIRST, name only as a fallback.
1083
+ //
1084
+ // Name-only matching is what made "rename in the Configure View drawer"
1085
+ // create a SECOND view: the panel minted a fresh client-side id, the name
1086
+ // had just changed, nothing matched, and the upsert inserted. An id that
1087
+ // is already in `savedPerspectives` is a server uuid by construction, so
1088
+ // it is safe to send as `perspectiveId`; a client-minted id (a brand-new
1089
+ // view) matches nothing and correctly falls through to insert.
1090
+ const existingPerspective =
1091
+ savedPerspectives.find((p) => p.id === payload.perspective.id) ??
1092
+ savedPerspectives.find((p) => p.name === payload.perspective.name)
1093
+ const response = await apiCall<PerspectiveSaveResponse>(`/api/perspectives/${perspectivesTableId}`, {
1094
+ method: 'POST',
1095
+ headers: { 'Content-Type': 'application/json' },
1096
+ body: JSON.stringify({
1097
+ // The upstream validator reads `perspectiveId`, not `id` — an `id` key
1098
+ // is stripped by zod and the save silently falls back to an upsert by
1099
+ // NAME. Harmless here (same name ⇒ same row) but fatal for rename,
1100
+ // which changes the name and so used to CLONE the view.
1101
+ perspectiveId: existingPerspective?.id,
1102
+ name: payload.perspective.name,
1103
+ settings,
1104
+ }),
1105
+ })
1106
+ if (response.ok) {
1107
+ // `silent` is set for saves the user did not explicitly ask for — the
1108
+ // column order written back after a header drag. The write and the
1109
+ // refetch still happen; only the confirmation toast is dropped.
1110
+ if (!payload.silent) {
1111
+ flash(
1112
+ payload.perspective.isBaseView ? 'Default view saved' : 'Perspective saved',
1113
+ 'success',
1114
+ )
1115
+ }
1116
+ queryClient.invalidateQueries({ queryKey: ['perspectives', perspectivesTableId] })
1117
+ const savedId = response.result?.perspective?.id
1118
+ if (savedId) setActivePerspectiveId(savedId)
1119
+ } else {
1120
+ // See `perspectiveWriteError` — the deleted-name collision is the one
1121
+ // failure here a user can act on, and every perspective write can hit it.
1122
+ flash(perspectiveWriteError(response, 'Failed to save perspective'), 'error')
1123
+ }
1124
+ },
1125
+ [perspectivesTableId, savedPerspectives, queryClient]
1126
+ )
1127
+
1128
+ const handlePerspectiveSelect = useCallback(
1129
+ (payload: PerspectiveSelectEvent) => {
1130
+ setActivePerspectiveId(payload.id)
1131
+ if (payload.config) {
1132
+ setFilters(payload.config.filters)
1133
+ setLookupColumns(payload.config.lookupColumns ?? [])
1134
+ setRollupColumns(payload.config.rollupColumns ?? [])
1135
+ setAggregations(payload.config.aggregations ?? [])
1136
+ if (payload.config.sorting.length > 0) {
1137
+ setSortField(payload.config.sorting[0].field)
1138
+ setSortDir(payload.config.sorting[0].direction)
1139
+ }
1140
+ setPage(1)
1141
+ } else {
1142
+ setFilters([])
1143
+ setLookupColumns([])
1144
+ setRollupColumns([])
1145
+ setAggregations([])
1146
+ setSortField(defaultSort?.field ?? 'id')
1147
+ setSortDir(defaultSort?.direction ?? 'asc')
1148
+ setPage(1)
1149
+ }
1150
+ },
1151
+ [defaultSort]
1152
+ )
1153
+
1154
+ const handlePerspectiveRename = useCallback(
1155
+ async (payload: PerspectiveRenameEvent) => {
1156
+ if (!perspectivesTableId) return
1157
+ const perspective = savedPerspectives.find((p) => p.id === payload.id)
1158
+ if (perspective) {
1159
+ // Refuse locally rather than round-trip to a 400 whose body says
1160
+ // nothing. The rename input caps typing at the same limit, so this only
1161
+ // fires for a name assembled in code (a "(copy)" suffix on an already
1162
+ // long name) — and when it does, it names the actual problem instead of
1163
+ // "Failed to rename perspective".
1164
+ if (payload.newName.length > PERSPECTIVE_NAME_MAX_LENGTH) {
1165
+ flash(
1166
+ `A view name can be at most ${PERSPECTIVE_NAME_MAX_LENGTH} characters. This one is ${payload.newName.length}.`,
1167
+ 'error',
1168
+ )
1169
+ return
1170
+ }
1171
+ const settings = dynamicTableToApi(perspective)
1172
+ const response = await apiCall(`/api/perspectives/${perspectivesTableId}`, {
1173
+ method: 'POST',
1174
+ headers: { 'Content-Type': 'application/json' },
1175
+ // `perspectiveId`, not `id` — see handlePerspectiveSave. With `id` the
1176
+ // rename created a SECOND view under the new name and left the old one.
1177
+ body: JSON.stringify({ perspectiveId: payload.id, name: payload.newName, settings }),
1178
+ })
1179
+ if (response.ok) {
1180
+ flash('Perspective renamed', 'success')
1181
+ queryClient.invalidateQueries({ queryKey: ['perspectives', perspectivesTableId] })
1182
+ } else {
1183
+ flash(perspectiveWriteError(response, 'Failed to rename perspective'), 'error')
1184
+ }
1185
+ }
1186
+ },
1187
+ [perspectivesTableId, savedPerspectives, queryClient]
1188
+ )
1189
+
1190
+ /**
1191
+ * A6 — duplicate. A brand-new row (no `perspectiveId`) carrying the source's
1192
+ * settings under a new name, and explicitly NOT the default: a copy must never
1193
+ * hijack which view opens tomorrow.
1194
+ *
1195
+ * `publication` is dropped for the same reason, and it is not cosmetic. It
1196
+ * rides the settings blob as `_published`, so duplicating a view that had been
1197
+ * shared produced a SECOND view that reported itself as a published template —
1198
+ * its `⋯` menu said "Update shared template…" and the tab claimed a shelf
1199
+ * entry that does not exist, because duplicate never calls `applyToRoles` and
1200
+ * so writes no role rows at all. Every view is PRIVATE by default
1201
+ * (personalization model, 2026-08-03); publishing is an explicit act on the
1202
+ * copy, not something inherited from what it was copied from.
1203
+ *
1204
+ * `isBaseView` goes too: the base row owns the reserved `__base__` name and
1205
+ * there can only be one per user, so a copy carrying the flag would render as
1206
+ * a second "Default view".
1207
+ */
1208
+ const handlePerspectiveDuplicate = useCallback(
1209
+ async (payload: PerspectiveDuplicateEvent) => {
1210
+ if (!perspectivesTableId) return
1211
+ const settings = dynamicTableToApi({
1212
+ ...payload.perspective,
1213
+ name: payload.newName,
1214
+ isDefault: false,
1215
+ isBaseView: false,
1216
+ publication: undefined,
1217
+ })
1218
+ const response = await apiCall<PerspectiveSaveResponse>(`/api/perspectives/${perspectivesTableId}`, {
1219
+ method: 'POST',
1220
+ headers: { 'Content-Type': 'application/json' },
1221
+ body: JSON.stringify({ name: payload.newName, settings, isDefault: false }),
1222
+ })
1223
+ if (response.ok) {
1224
+ flash('View duplicated', 'success')
1225
+ queryClient.invalidateQueries({ queryKey: ['perspectives', perspectivesTableId] })
1226
+ const savedId = response.result?.perspective?.id
1227
+ if (savedId) setActivePerspectiveId(savedId)
1228
+ } else {
1229
+ flash(perspectiveWriteError(response, 'Failed to duplicate view'), 'error')
1230
+ }
1231
+ },
1232
+ [perspectivesTableId, queryClient]
1233
+ )
1234
+
1235
+ /**
1236
+ * A4 — make a view this USER's default. `isDefault` is a first-class column
1237
+ * on the upstream entity scoped by `userId`; the service demotes the caller's
1238
+ * other views in one `nativeUpdate` and touches nobody else's rows.
1239
+ */
1240
+ const handlePerspectiveSetDefault = useCallback(
1241
+ async (payload: PerspectiveSetDefaultEvent) => {
1242
+ if (!perspectivesTableId) return
1243
+ const perspective = savedPerspectives.find((p) => p.id === payload.id)
1244
+ if (!perspective) return
1245
+ const settings = dynamicTableToApi(perspective)
1246
+ const response = await apiCall(`/api/perspectives/${perspectivesTableId}`, {
1247
+ method: 'POST',
1248
+ headers: { 'Content-Type': 'application/json' },
1249
+ body: JSON.stringify({
1250
+ perspectiveId: payload.id,
1251
+ name: perspective.name,
1252
+ settings,
1253
+ isDefault: payload.isDefault,
1254
+ }),
1255
+ })
1256
+ if (response.ok) {
1257
+ flash('Default view updated', 'success')
1258
+ queryClient.invalidateQueries({ queryKey: ['perspectives', perspectivesTableId] })
1259
+ } else {
1260
+ flash('Failed to set the default view', 'error')
1261
+ }
1262
+ },
1263
+ [perspectivesTableId, savedPerspectives, queryClient]
1264
+ )
1265
+
1266
+ /**
1267
+ * PUBLISH — make one of MY views copyable by colleagues.
1268
+ *
1269
+ * One POST does both halves, which is why it is one request and not two:
1270
+ * `applyToRoles` writes the role-scoped template rows, and the same call
1271
+ * re-saves the personal row with `_published` so the tab menu can say
1272
+ * "Update shared template" next time. `setRoleDefault` is pinned to `false`
1273
+ * — a template must never become anybody's default view. Publishing changes
1274
+ * nobody's screen; it only puts the view on the shelf.
1275
+ *
1276
+ * The template necessarily carries the view's own name: the upstream service
1277
+ * uses one `name` for both the personal row and the role rows, so allowing a
1278
+ * different template name here would silently RENAME the publisher's view.
1279
+ */
1280
+ const handlePerspectivePublish = useCallback(
1281
+ async (payload: PerspectivePublishEvent) => {
1282
+ if (!perspectivesTableId) return
1283
+ const perspective = savedPerspectives.find((p) => p.id === payload.id)
1284
+ if (!perspective || !payload.roleIds.length) return
1285
+ const settings = dynamicTableToApi({
1286
+ ...perspective,
1287
+ publication: { roleIds: payload.roleIds, publishedAt: new Date().toISOString() },
1288
+ })
1289
+ const response = await apiCall(`/api/perspectives/${perspectivesTableId}`, {
1290
+ method: 'POST',
1291
+ headers: { 'Content-Type': 'application/json' },
1292
+ body: JSON.stringify({
1293
+ perspectiveId: payload.id,
1294
+ name: perspective.name,
1295
+ settings,
1296
+ applyToRoles: payload.roleIds,
1297
+ setRoleDefault: false,
1298
+ }),
1299
+ })
1300
+ if (response.ok) {
1301
+ flash('View shared as a template', 'success')
1302
+ queryClient.invalidateQueries({ queryKey: ['perspectives', perspectivesTableId] })
1303
+ } else {
1304
+ flash('Failed to share the view', 'error')
1305
+ }
1306
+ },
1307
+ [perspectivesTableId, savedPerspectives, queryClient],
1308
+ )
1309
+
1310
+ /**
1311
+ * COPY A TEMPLATE — the only way a shared view reaches your screen.
1312
+ *
1313
+ * Writes a brand-new PERSONAL row (no `perspectiveId`), never the default,
1314
+ * carrying `_origin` = which template and which version it came from. That
1315
+ * record is what lets a later pass ask "the shared template changed — update
1316
+ * yours?" instead of the two silently diverging.
1317
+ */
1318
+ const handlePerspectiveTemplateCopy = useCallback(
1319
+ async (payload: PerspectiveTemplateCopyEvent) => {
1320
+ if (!perspectivesTableId) return
1321
+ const { template } = payload
1322
+ const settings = dynamicTableToApi({
1323
+ ...template,
1324
+ name: payload.newName,
1325
+ isDefault: false,
1326
+ isBaseView: false,
1327
+ publication: undefined,
1328
+ origin: {
1329
+ templateId: template.id,
1330
+ version: template.version,
1331
+ name: template.name,
1332
+ copiedAt: new Date().toISOString(),
1333
+ },
1334
+ })
1335
+ const response = await apiCall<PerspectiveSaveResponse>(`/api/perspectives/${perspectivesTableId}`, {
1336
+ method: 'POST',
1337
+ headers: { 'Content-Type': 'application/json' },
1338
+ body: JSON.stringify({ name: payload.newName, settings, isDefault: false }),
1339
+ })
1340
+ if (response.ok) {
1341
+ flash('Template copied to your views', 'success')
1342
+ queryClient.invalidateQueries({ queryKey: ['perspectives', perspectivesTableId] })
1343
+ const savedId = response.result?.perspective?.id
1344
+ if (savedId) setActivePerspectiveId(savedId)
1345
+ } else {
1346
+ flash(perspectiveWriteError(response, 'Failed to copy the template'), 'error')
1347
+ }
1348
+ },
1349
+ [perspectivesTableId, queryClient],
1350
+ )
1351
+
1352
+ const handlePerspectiveDelete = useCallback(
1353
+ async (payload: PerspectiveDeleteEvent) => {
1354
+ if (!perspectivesTableId) return
1355
+ const url = payload.hardDelete
1356
+ ? `/api/perspectives/${perspectivesTableId}/${payload.id}?hardDelete=true`
1357
+ : `/api/perspectives/${perspectivesTableId}/${payload.id}`
1358
+ const response = await apiCall(url, { method: 'DELETE' })
1359
+ if (response.ok) {
1360
+ flash('Perspective deleted', 'success')
1361
+ queryClient.invalidateQueries({ queryKey: ['perspectives', perspectivesTableId] })
1362
+ if (activePerspectiveId === payload.id) {
1363
+ setActivePerspectiveId(null)
1364
+ setFilters([])
1365
+ setSortField(defaultSort?.field ?? 'id')
1366
+ setSortDir(defaultSort?.direction ?? 'asc')
1367
+ }
1368
+ } else {
1369
+ flash('Failed to delete perspective', 'error')
1370
+ }
1371
+ },
1372
+ [perspectivesTableId, activePerspectiveId, queryClient, defaultSort]
1373
+ )
1374
+
1375
+ const handlePerspectiveChange = useCallback(
1376
+ (payload: PerspectiveChangeEvent) => {
1377
+ if (payload.config.lookupColumns) {
1378
+ setLookupColumns(payload.config.lookupColumns)
1379
+ setPage(1)
1380
+ }
1381
+ // Rollups do NOT reset the page — they add a column to the rows already
1382
+ // on screen, they do not change which rows those are.
1383
+ if (payload.config.rollupColumns) {
1384
+ setRollupColumns(payload.config.rollupColumns)
1385
+ }
1386
+ // Aggregations do NOT reset the page — they change what the footer says,
1387
+ // not which rows are shown.
1388
+ if (payload.config.aggregations) {
1389
+ setAggregations(payload.config.aggregations)
1390
+ }
1391
+ // Grouping and view mode are MIRRORED, never acted on: both are folds
1392
+ // over the rows already fetched, so neither changes the request and
1393
+ // neither may reset the page. Resetting on a grouping change would throw
1394
+ // the user back to page 1 for a purely visual regroup.
1395
+ if (payload.config.grouping) {
1396
+ setGrouping(payload.config.grouping)
1397
+ }
1398
+ if (payload.config.viewMode) {
1399
+ setViewMode(payload.config.viewMode)
1400
+ }
1401
+ if (payload.config.sorting) {
1402
+ if (payload.config.sorting.length > 0) {
1403
+ const firstSort = payload.config.sorting[0]
1404
+ setSortField(firstSort.field)
1405
+ setSortDir(firstSort.direction)
1406
+ } else {
1407
+ setSortField(defaultSort?.field ?? 'id')
1408
+ setSortDir(defaultSort?.direction ?? 'asc')
1409
+ }
1410
+ setPage(1)
1411
+ }
1412
+ },
1413
+ [defaultSort]
1414
+ )
1415
+
1416
+ // ── Register Event Handlers ──
1417
+
1418
+ const eventHandlers = useMemo(() => {
1419
+ const handlers: Record<string, any> = {
1420
+ [TableEvents.COLUMN_SORT]: (payload: { columnName: string; direction: 'asc' | 'desc' | null }) => {
1421
+ setSortField(payload.columnName)
1422
+ setSortDir(payload.direction || 'asc')
1423
+ setPage(1)
1424
+ },
1425
+ [TableEvents.SEARCH]: (payload: { query: string }) => {
1426
+ setSearch(payload.query)
1427
+ setPage(1)
1428
+ },
1429
+ [TableEvents.FILTER_CHANGE]: (payload: { filters: FilterRow[] }) => {
1430
+ setFilters(payload.filters)
1431
+ setPage(1)
1432
+ },
1433
+ }
1434
+
1435
+ if (config.cellEdit !== false) {
1436
+ handlers[TableEvents.CELL_EDIT_SAVE] = handleCellEditSave
1437
+ }
1438
+
1439
+ if (config.create) {
1440
+ handlers[TableEvents.NEW_ROW_SAVE] = handleNewRowSave
1441
+ }
1442
+
1443
+ if (perspectivesTableId) {
1444
+ handlers[TableEvents.PERSPECTIVE_SAVE] = handlePerspectiveSave
1445
+ handlers[TableEvents.PERSPECTIVE_SELECT] = handlePerspectiveSelect
1446
+ handlers[TableEvents.PERSPECTIVE_RENAME] = handlePerspectiveRename
1447
+ handlers[TableEvents.PERSPECTIVE_DELETE] = handlePerspectiveDelete
1448
+ handlers[TableEvents.PERSPECTIVE_CHANGE] = handlePerspectiveChange
1449
+ handlers[TableEvents.PERSPECTIVE_DUPLICATE] = handlePerspectiveDuplicate
1450
+ handlers[TableEvents.PERSPECTIVE_SET_DEFAULT] = handlePerspectiveSetDefault
1451
+ handlers[TableEvents.PERSPECTIVE_PUBLISH] = handlePerspectivePublish
1452
+ handlers[TableEvents.PERSPECTIVE_TEMPLATE_COPY] = handlePerspectiveTemplateCopy
1453
+ }
1454
+
1455
+ return handlers
1456
+ }, [
1457
+ config.cellEdit,
1458
+ config.create,
1459
+ perspectivesTableId,
1460
+ handleCellEditSave,
1461
+ handleNewRowSave,
1462
+ handlePerspectiveSave,
1463
+ handlePerspectiveSelect,
1464
+ handlePerspectiveRename,
1465
+ handlePerspectiveDelete,
1466
+ handlePerspectiveChange,
1467
+ handlePerspectiveDuplicate,
1468
+ handlePerspectiveSetDefault,
1469
+ handlePerspectivePublish,
1470
+ handlePerspectiveTemplateCopy,
1471
+ ])
1472
+
1473
+ useEventHandlers(eventHandlers, tableRef as React.RefObject<HTMLElement>)
1474
+
1475
+ // ── Filter Suggestions ──
1476
+
1477
+ const customLoadFilterSuggestions = config.loadFilterSuggestions
1478
+ const loadFilterSuggestions = useMemo(() => {
1479
+ // An explicit loader wins: it is the only way a list can suggest values for
1480
+ // columns that aren't plain fields on its own entity table.
1481
+ if (customLoadFilterSuggestions) return customLoadFilterSuggestions
1482
+ if (!config.filterSuggestions) return undefined
1483
+ const entityType = config.filterSuggestions
1484
+
1485
+ return async (field: string, query: string): Promise<string[]> => {
1486
+ try {
1487
+ const params = new URLSearchParams({ entityId: entityType, field, query: query || '' })
1488
+ const result = await apiCall<{ items: string[] }>(
1489
+ `/api/entities/filter-suggestions?${params.toString()}`,
1490
+ { credentials: 'include' }
1491
+ )
1492
+ if (!result.ok || !result.result) return []
1493
+ return result.result.items ?? []
1494
+ } catch {
1495
+ return []
1496
+ }
1497
+ }
1498
+ }, [config.filterSuggestions, customLoadFilterSuggestions])
1499
+
1500
+ // ── Build DynamicTable Props ──
1501
+
1502
+ const pagination: PaginationProps = useMemo(
1503
+ () => ({
1504
+ currentPage: page,
1505
+ totalPages: Math.ceil((dataQuery.data?.total || 0) / limit),
1506
+ limit,
1507
+ total: dataQuery.data?.total ?? 0,
1508
+ limitOptions: [25, 50, 100],
1509
+ onPageChange: setPage,
1510
+ onLimitChange: (l: number) => {
1511
+ setLimit(l)
1512
+ setPage(1)
1513
+ },
1514
+ }),
1515
+ [page, limit, dataQuery.data?.total]
1516
+ )
1517
+
1518
+ const dynamicTableProps: DynamicTableProps = useMemo(() => {
1519
+ const baseProps: DynamicTableProps = {
1520
+ tableRef,
1521
+ data: tableData,
1522
+ columns,
1523
+ tableName,
1524
+ // Stable identity for per-user column-width persistence. Reuse the
1525
+ // perspectives table id when present, else fall back to the entity source.
1526
+ tableId: storageTableId ?? `${storageScope}${source}`,
1527
+ idColumnName: idColumn,
1528
+ colHeaders: true,
1529
+ rowHeaders: true,
1530
+ pagination,
1531
+ // Read-only, for ONE thing: the in-grid finder's scope label says
1532
+ // "…in 100 loaded rows (filtered by \"abc\")". Without it, a finder count
1533
+ // and a SearchBar filter can be conflated while both are active.
1534
+ // The EFFECTIVE needle, so the label names what actually narrowed the
1535
+ // rows — a workspace search when one is driving this pane, this pane's
1536
+ // own box otherwise (identical when no workspace is involved).
1537
+ searchQuery: effectiveSearch,
1538
+ onExportAll: exportAllRows,
1539
+ aggregateResult: aggregateQuery.data ?? null,
1540
+ aggregateLoading: aggregateQuery.isFetching,
1541
+ aggregateError: aggregateQuery.isError,
1542
+ // HEDGE-119 — carry the list query's FAILURE to the grid.
1543
+ //
1544
+ // `tableData` above collapses a rejected query to `[]` (there is no
1545
+ // `dataQuery.data` to read items off), so without this flag the grid
1546
+ // cannot tell "the server refused" from "the server said zero rows" and
1547
+ // paints `EmptyState` for both. That is how a 500 on the invoice queue
1548
+ // reached an accountant as the words "Nothing here yet".
1549
+ //
1550
+ // `placeholderData` keeps the PREVIOUS page's rows visible while a
1551
+ // refetch fails, so this is deliberately not `|| isError` on the whole
1552
+ // grid: rows we still hold are still shown, and the error state only
1553
+ // takes over the zero-row region, which is the case that lies.
1554
+ loadError: dataQuery.isError,
1555
+ onRetryLoad: () => { void dataQuery.refetch() },
1556
+ ...(perspectivesTableId
1557
+ ? {
1558
+ savedPerspectives,
1559
+ activePerspectiveId,
1560
+ // A4/A6 — which view opens by default FOR THIS USER. Comes straight
1561
+ // off the perspectives index (`isDefault` is scoped by `userId`), so
1562
+ // the star on the tab is a per-user fact, never an org-wide one.
1563
+ defaultPerspectiveId: perspectivesData?.defaultPerspectiveId ?? null,
1564
+ // Personal views + shared templates. `canPublishTemplates` is the
1565
+ // server's own answer (`perspectives.role_defaults`), not a guess:
1566
+ // a user without the feature never sees the publish affordance, and
1567
+ // the endpoint would 403 them anyway.
1568
+ sharedTemplates,
1569
+ canPublishTemplates: perspectivesData?.canApplyToRoles ?? false,
1570
+ publishableRoles: perspectivesData?.roles ?? [],
1571
+ }
1572
+ : {}),
1573
+ ...(loadFilterSuggestions ? { loadFilterSuggestions } : {}),
1574
+ ...(config.loadLookupSources ? { loadLookupSources: config.loadLookupSources } : {}),
1575
+ ...(config.loadRollupSources ? { loadRollupSources: config.loadRollupSources } : {}),
1576
+ ...(config.delete ? { onBulkDelete: requestBulkDelete } : {}),
1577
+ ...config.tableProps,
1578
+ // LAST, so it cannot be overridden: while a workspace search is driving
1579
+ // this pane, that pane's own search box is ignored (see `sharedSearch`).
1580
+ // Leaving it on screen would let a user type into a box that does
1581
+ // nothing — and the density argument for the bar only holds if the rows
1582
+ // it replaces actually go away. Absent `sharedSearch`, this spreads
1583
+ // nothing and `config.tableProps` still has the final word.
1584
+ ...(sharedSearch !== undefined ? { hideSearch: true } : {}),
1585
+ }
1586
+
1587
+ return baseProps
1588
+ }, [
1589
+ tableData,
1590
+ columns,
1591
+ tableName,
1592
+ idColumn,
1593
+ pagination,
1594
+ effectiveSearch,
1595
+ exportAllRows,
1596
+ aggregateQuery.data,
1597
+ aggregateQuery.isFetching,
1598
+ aggregateQuery.isError,
1599
+ dataQuery.isError,
1600
+ dataQuery.refetch,
1601
+ perspectivesTableId,
1602
+ source,
1603
+ savedPerspectives,
1604
+ activePerspectiveId,
1605
+ perspectivesData?.defaultPerspectiveId,
1606
+ perspectivesData?.canApplyToRoles,
1607
+ perspectivesData?.roles,
1608
+ sharedTemplates,
1609
+ loadFilterSuggestions,
1610
+ config.loadLookupSources,
1611
+ config.loadRollupSources,
1612
+ config.delete,
1613
+ requestBulkDelete,
1614
+ config.tableProps,
1615
+ sharedSearch,
1616
+ ])
1617
+
1618
+ // ── Delete Dialog Props ──
1619
+
1620
+ const deleteConfig = config.delete
1621
+ const dialogConfig = useMemo(() => {
1622
+ if (!deleteConfig) return null
1623
+ if (typeof deleteConfig === 'object' && typeof deleteConfig !== 'boolean') {
1624
+ return deleteConfig as DynamicTablePageDeleteConfig
1625
+ }
1626
+ return {} as DynamicTablePageDeleteConfig
1627
+ }, [deleteConfig])
1628
+
1629
+ const cancelDelete = useCallback(() => setPendingDelete(null), [])
1630
+
1631
+ // Return JSX element (not a component) so the Dialog's element type is always
1632
+ // TableDeleteDialog — React keeps it mounted across state changes, preserving
1633
+ // animation state. Using a component (function) would cause unmount/remount on
1634
+ // every dep change since the function identity changes.
1635
+ const deleteDialog = dialogConfig ? (
1636
+ <>
1637
+ <TableDeleteDialog
1638
+ row={pendingDelete}
1639
+ isDeleting={isDeleting}
1640
+ onConfirm={handleConfirmDelete}
1641
+ onCancel={cancelDelete}
1642
+ restoreFocusRef={tableRef}
1643
+ title={dialogConfig.title}
1644
+ description={dialogConfig.description}
1645
+ nameColumn={dialogConfig.nameColumn}
1646
+ />
1647
+ <TableDeleteDialog
1648
+ row={pendingBulkDelete ? ({ count: pendingBulkDelete.length } as TRow) : null}
1649
+ isDeleting={isDeleting}
1650
+ onConfirm={handleConfirmBulkDelete}
1651
+ onCancel={cancelBulkDelete}
1652
+ restoreFocusRef={tableRef}
1653
+ title={(row: any) => `Delete ${row?.count ?? ''} ${tableName}`.replace(/\s+/g, ' ').trim()}
1654
+ description={(row: any) =>
1655
+ `Are you sure you want to delete ${row?.count ?? 'these'} selected items? This action cannot be undone.`}
1656
+ />
1657
+ </>
1658
+ ) : null
1659
+
1660
+ return {
1661
+ props: dynamicTableProps,
1662
+ deleteDialog,
1663
+ setRowToDelete: setPendingDelete,
1664
+ query: dataQuery,
1665
+ aggregateResult: aggregateQuery.data ?? null,
1666
+ aggregateLoading: aggregateQuery.isFetching,
1667
+ aggregateError: aggregateQuery.isError,
1668
+ // HEDGE-119 — hosts that render their own body (split views, card modes)
1669
+ // need the same signal the grid now gets, so they can refuse to draw an
1670
+ // "empty" affordance over a request that never landed.
1671
+ isError: dataQuery.isError,
1672
+ // `!perspectiveHydrated` counts as loading. TanStack reports `isLoading`
1673
+ // false while a query is DISABLED (it is pending but not fetching), so
1674
+ // without this the host would drop its skeleton and paint an empty grid for
1675
+ // the one frame the list is waiting on the perspective.
1676
+ isLoading: (!perspectiveHydrated || dataQuery.isLoading) && !dataQuery.data,
1677
+ refresh: () => invalidateListNow(),
1678
+ state: { page, limit, search, filters, sortField, sortDir, grouping, viewMode },
1679
+ }
1680
+ }
1681
+