@jmruthers/pace-core 0.5.193 → 0.6.2

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 (577) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +7 -1
  3. package/cursor-rules/00-pace-core-compliance.mdc +299 -0
  4. package/cursor-rules/01-standards-compliance.mdc +244 -0
  5. package/cursor-rules/02-project-structure.mdc +200 -0
  6. package/cursor-rules/03-solid-principles.mdc +222 -0
  7. package/cursor-rules/04-testing-standards.mdc +268 -0
  8. package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
  9. package/cursor-rules/06-code-quality.mdc +309 -0
  10. package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
  11. package/cursor-rules/08-markup-quality.mdc +452 -0
  12. package/cursor-rules/CHANGELOG.md +119 -0
  13. package/cursor-rules/README.md +192 -0
  14. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  15. package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
  16. package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
  17. package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
  18. package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  19. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  20. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  21. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  22. package/dist/chunk-24UVZUZG.js.map +1 -0
  23. package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
  24. package/dist/chunk-2UOI2FG5.js.map +1 -0
  25. package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
  26. package/dist/chunk-3XC4CPTD.js.map +1 -0
  27. package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
  28. package/dist/chunk-6J4GEEJR.js.map +1 -0
  29. package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
  30. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  31. package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
  32. package/dist/chunk-EHMR7VYL.js.map +1 -0
  33. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  34. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  35. package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
  36. package/dist/chunk-FFQEQTNW.js.map +1 -0
  37. package/dist/chunk-FMUCXFII.js +76 -0
  38. package/dist/chunk-FMUCXFII.js.map +1 -0
  39. package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
  40. package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
  41. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  42. package/dist/chunk-L4OXEN46.js.map +1 -0
  43. package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
  44. package/dist/chunk-M43Y4SSO.js.map +1 -0
  45. package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
  46. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  47. package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
  48. package/dist/chunk-NECFR5MM.js.map +1 -0
  49. package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
  50. package/dist/chunk-SFZUDBL5.js.map +1 -0
  51. package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
  52. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  53. package/dist/components.d.ts +6 -6
  54. package/dist/components.js +15 -12
  55. package/dist/components.js.map +1 -1
  56. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  57. package/dist/hooks.d.ts +59 -126
  58. package/dist/hooks.js +19 -28
  59. package/dist/hooks.js.map +1 -1
  60. package/dist/index.d.ts +63 -16
  61. package/dist/index.js +23 -24
  62. package/dist/index.js.map +1 -1
  63. package/dist/providers.d.ts +21 -3
  64. package/dist/providers.js +2 -2
  65. package/dist/rbac/index.d.ts +146 -115
  66. package/dist/rbac/index.js +8 -11
  67. package/dist/theming/runtime.d.ts +1 -13
  68. package/dist/theming/runtime.js +1 -1
  69. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  70. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  71. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  72. package/dist/types.d.ts +2 -2
  73. package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
  74. package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
  75. package/dist/utils.d.ts +4 -5
  76. package/dist/utils.js +15 -15
  77. package/dist/utils.js.map +1 -1
  78. package/docs/api/README.md +7 -1
  79. package/docs/api/classes/ColumnFactory.md +8 -8
  80. package/docs/api/classes/InvalidScopeError.md +4 -4
  81. package/docs/api/classes/Logger.md +1 -1
  82. package/docs/api/classes/MissingUserContextError.md +4 -4
  83. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  84. package/docs/api/classes/PermissionDeniedError.md +4 -4
  85. package/docs/api/classes/RBACAuditManager.md +1 -1
  86. package/docs/api/classes/RBACCache.md +1 -1
  87. package/docs/api/classes/RBACEngine.md +1 -1
  88. package/docs/api/classes/RBACError.md +4 -4
  89. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  90. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  91. package/docs/api/classes/StorageUtils.md +1 -1
  92. package/docs/api/enums/FileCategory.md +1 -1
  93. package/docs/api/enums/LogLevel.md +1 -1
  94. package/docs/api/enums/RBACErrorCode.md +1 -1
  95. package/docs/api/enums/RPCFunction.md +1 -1
  96. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  97. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  98. package/docs/api/interfaces/AggregateConfig.md +4 -4
  99. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  100. package/docs/api/interfaces/AvatarProps.md +1 -1
  101. package/docs/api/interfaces/BadgeProps.md +9 -2
  102. package/docs/api/interfaces/ButtonProps.md +7 -4
  103. package/docs/api/interfaces/CalendarProps.md +8 -5
  104. package/docs/api/interfaces/CardProps.md +8 -5
  105. package/docs/api/interfaces/ColorPalette.md +1 -1
  106. package/docs/api/interfaces/ColorShade.md +1 -1
  107. package/docs/api/interfaces/ComplianceResult.md +1 -1
  108. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  109. package/docs/api/interfaces/DataRecord.md +1 -1
  110. package/docs/api/interfaces/DataTableAction.md +24 -21
  111. package/docs/api/interfaces/DataTableColumn.md +31 -31
  112. package/docs/api/interfaces/DataTableProps.md +1 -1
  113. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  114. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  115. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  116. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  117. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  118. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  119. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  120. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  121. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  122. package/docs/api/interfaces/ExportColumn.md +1 -1
  123. package/docs/api/interfaces/ExportOptions.md +8 -8
  124. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  125. package/docs/api/interfaces/FileMetadata.md +1 -1
  126. package/docs/api/interfaces/FileReference.md +1 -1
  127. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  128. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  129. package/docs/api/interfaces/FileUploadProps.md +26 -23
  130. package/docs/api/interfaces/FooterProps.md +10 -8
  131. package/docs/api/interfaces/FormFieldProps.md +10 -10
  132. package/docs/api/interfaces/FormProps.md +1 -1
  133. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  134. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  135. package/docs/api/interfaces/InputProps.md +7 -4
  136. package/docs/api/interfaces/LabelProps.md +1 -1
  137. package/docs/api/interfaces/LoggerConfig.md +1 -1
  138. package/docs/api/interfaces/LoginFormProps.md +14 -11
  139. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  140. package/docs/api/interfaces/NavigationContextType.md +1 -1
  141. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  142. package/docs/api/interfaces/NavigationItem.md +11 -11
  143. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  144. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  145. package/docs/api/interfaces/Organisation.md +1 -1
  146. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  147. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  148. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  149. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  150. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  151. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  152. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  153. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  154. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  155. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  156. package/docs/api/interfaces/PaletteData.md +1 -1
  157. package/docs/api/interfaces/ParsedAddress.md +1 -1
  158. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  159. package/docs/api/interfaces/ProgressProps.md +1 -1
  160. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  161. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  162. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  163. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  164. package/docs/api/interfaces/QuickFix.md +1 -1
  165. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  166. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  167. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  168. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  169. package/docs/api/interfaces/RBACConfig.md +1 -1
  170. package/docs/api/interfaces/RBACContext.md +1 -1
  171. package/docs/api/interfaces/RBACLogger.md +1 -1
  172. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  173. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  174. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  175. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  176. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  177. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  178. package/docs/api/interfaces/RBACResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  181. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  182. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  183. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  184. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  185. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  186. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  187. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  188. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  189. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  190. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  191. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  192. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  193. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  194. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  195. package/docs/api/interfaces/RouteConfig.md +1 -1
  196. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  197. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  198. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  199. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  200. package/docs/api/interfaces/SetupIssue.md +1 -1
  201. package/docs/api/interfaces/StorageConfig.md +1 -1
  202. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  203. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  204. package/docs/api/interfaces/StorageListOptions.md +1 -1
  205. package/docs/api/interfaces/StorageListResult.md +1 -1
  206. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  207. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  208. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  209. package/docs/api/interfaces/StyleImport.md +1 -1
  210. package/docs/api/interfaces/SwitchProps.md +1 -1
  211. package/docs/api/interfaces/TabsContentProps.md +1 -1
  212. package/docs/api/interfaces/TabsListProps.md +1 -1
  213. package/docs/api/interfaces/TabsProps.md +1 -1
  214. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  215. package/docs/api/interfaces/TextareaProps.md +1 -1
  216. package/docs/api/interfaces/ToastActionElement.md +4 -1
  217. package/docs/api/interfaces/ToastProps.md +1 -1
  218. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  219. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  220. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  221. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  222. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  223. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  224. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  225. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  226. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  227. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  228. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  229. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  230. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  231. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  232. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  233. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  234. package/docs/api/interfaces/UserEventAccess.md +14 -11
  235. package/docs/api/interfaces/UserMenuProps.md +8 -6
  236. package/docs/api/interfaces/UserProfile.md +1 -1
  237. package/docs/api/modules.md +575 -634
  238. package/docs/architecture/database-schema-requirements.md +161 -0
  239. package/docs/core-concepts/rbac-system.md +3 -3
  240. package/docs/documentation-index.md +2 -4
  241. package/docs/getting-started/cursor-rules.md +263 -0
  242. package/docs/getting-started/installation-guide.md +6 -1
  243. package/docs/getting-started/quick-start.md +6 -1
  244. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  245. package/docs/migration/MIGRATION_GUIDE.md +6 -28
  246. package/docs/migration/README.md +52 -6
  247. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  248. package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
  249. package/docs/migration/database-changes-december-2025.md +3 -3
  250. package/docs/rbac/event-based-apps.md +1 -1
  251. package/docs/rbac/getting-started.md +1 -1
  252. package/docs/rbac/quick-start.md +1 -1
  253. package/docs/standards/README.md +40 -0
  254. package/docs/troubleshooting/migration.md +4 -4
  255. package/examples/PublicPages/PublicEventPage.tsx +1 -1
  256. package/package.json +12 -6
  257. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  258. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  259. package/scripts/audit/core/checks/bundle.cjs +142 -0
  260. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +737 -691
  261. package/scripts/audit/core/checks/config.cjs +54 -0
  262. package/scripts/audit/core/checks/coverage.cjs +84 -0
  263. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  264. package/scripts/audit/core/checks/documentation.cjs +203 -0
  265. package/scripts/audit/core/checks/environment.cjs +128 -0
  266. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  267. package/scripts/audit/core/checks/forms.cjs +172 -0
  268. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  269. package/scripts/audit/core/checks/hooks.cjs +334 -0
  270. package/scripts/audit/core/checks/imports.cjs +244 -0
  271. package/scripts/audit/core/checks/performance.cjs +325 -0
  272. package/scripts/audit/core/checks/routes.cjs +117 -0
  273. package/scripts/audit/core/checks/state.cjs +130 -0
  274. package/scripts/audit/core/checks/structure.cjs +65 -0
  275. package/scripts/audit/core/checks/style.cjs +584 -0
  276. package/scripts/audit/core/checks/testing.cjs +122 -0
  277. package/scripts/audit/core/checks/typescript.cjs +61 -0
  278. package/scripts/audit/core/scanner.cjs +199 -0
  279. package/scripts/audit/core/utils.cjs +137 -0
  280. package/scripts/audit/index.cjs +223 -0
  281. package/scripts/audit/reporters/console.cjs +151 -0
  282. package/scripts/audit/reporters/json.cjs +54 -0
  283. package/scripts/audit/reporters/markdown.cjs +124 -0
  284. package/scripts/audit-consuming-app.cjs +86 -0
  285. package/scripts/build-docs/build-decision.js +240 -0
  286. package/scripts/build-docs/cache-utils.js +105 -0
  287. package/scripts/build-docs/content-normalization.js +150 -0
  288. package/scripts/build-docs/file-utils.js +105 -0
  289. package/scripts/build-docs/git-utils.js +86 -0
  290. package/scripts/build-docs/hash-utils.js +116 -0
  291. package/scripts/build-docs/typedoc-runner.js +220 -0
  292. package/scripts/build-docs-incremental.js +77 -913
  293. package/scripts/install-cursor-rules.cjs +236 -0
  294. package/scripts/utils/command-runner.js +16 -11
  295. package/scripts/validate-formats.js +61 -56
  296. package/scripts/validate-master.js +74 -69
  297. package/scripts/validate-pre-publish.js +70 -65
  298. package/src/__tests__/helpers/test-providers.tsx +1 -1
  299. package/src/__tests__/helpers/test-utils.tsx +1 -1
  300. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  301. package/src/components/Alert/Alert.test.tsx +12 -18
  302. package/src/components/Alert/Alert.tsx +5 -7
  303. package/src/components/Avatar/Avatar.test.tsx +4 -4
  304. package/src/components/Badge/Badge.tsx +16 -4
  305. package/src/components/Button/Button.tsx +27 -4
  306. package/src/components/Calendar/Calendar.tsx +9 -3
  307. package/src/components/Card/Card.tsx +4 -0
  308. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  309. package/src/components/Checkbox/Checkbox.tsx +2 -2
  310. package/src/components/DataTable/DataTable.test.tsx +57 -93
  311. package/src/components/DataTable/DataTable.tsx +40 -6
  312. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  313. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
  314. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
  315. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  316. package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
  317. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  318. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  319. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  320. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  321. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  322. package/src/components/DataTable/components/DataTableCore.tsx +200 -561
  323. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  324. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  325. package/src/components/DataTable/components/DataTableModals.tsx +9 -1
  326. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  327. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  328. package/src/components/DataTable/components/EditFields.tsx +307 -0
  329. package/src/components/DataTable/components/EditableRow.tsx +9 -1
  330. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  331. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  332. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  333. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  334. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  335. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  336. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  337. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  338. package/src/components/DataTable/components/UnifiedTableBody.tsx +62 -852
  339. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  340. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  341. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
  342. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
  343. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
  344. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
  345. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
  346. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
  347. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
  348. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  349. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  350. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  351. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  352. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  353. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  354. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  355. package/src/components/DataTable/hooks/useColumnReordering.ts +14 -2
  356. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  357. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  358. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  359. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  360. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  361. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
  362. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  363. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  364. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  365. package/src/components/DataTable/styles.ts +6 -6
  366. package/src/components/DataTable/types.ts +6 -10
  367. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  368. package/src/components/DataTable/utils/debugTools.ts +18 -113
  369. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  370. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  371. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  372. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  373. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  374. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
  375. package/src/components/Dialog/Dialog.tsx +8 -7
  376. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  377. package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
  378. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  379. package/src/components/ErrorBoundary/index.ts +27 -2
  380. package/src/components/EventSelector/EventSelector.tsx +4 -1
  381. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
  382. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  383. package/src/components/FileUpload/FileUpload.tsx +22 -2
  384. package/src/components/Footer/Footer.test.tsx +16 -16
  385. package/src/components/Footer/Footer.tsx +15 -12
  386. package/src/components/Form/Form.test.tsx +36 -15
  387. package/src/components/Form/Form.tsx +31 -26
  388. package/src/components/Header/Header.tsx +22 -11
  389. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
  390. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
  391. package/src/components/Input/Input.test.tsx +2 -2
  392. package/src/components/Input/Input.tsx +36 -34
  393. package/src/components/Label/Label.tsx +1 -1
  394. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  395. package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
  396. package/src/components/LoginForm/LoginForm.test.tsx +42 -42
  397. package/src/components/LoginForm/LoginForm.tsx +12 -8
  398. package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
  399. package/src/components/NavigationMenu/types.ts +56 -0
  400. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  401. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  402. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
  403. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
  404. package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
  405. package/src/components/PaceAppLayout/README.md +1 -1
  406. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  407. package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
  408. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
  409. package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
  410. package/src/components/Progress/Progress.tsx +1 -1
  411. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  412. package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
  413. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  414. package/src/components/Select/Select.tsx +95 -438
  415. package/src/components/Select/context.ts +23 -0
  416. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  417. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  418. package/src/components/Select/hooks/useSelectState.ts +104 -0
  419. package/src/components/Select/index.ts +9 -1
  420. package/src/components/Select/types.ts +123 -0
  421. package/src/components/Select/utils/text.ts +26 -0
  422. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +5 -6
  423. package/src/components/Switch/Switch.tsx +4 -4
  424. package/src/components/Table/Table.tsx +1 -1
  425. package/src/components/Tabs/Tabs.tsx +1 -1
  426. package/src/components/Textarea/Textarea.tsx +27 -29
  427. package/src/components/Toast/Toast.tsx +5 -1
  428. package/src/components/Tooltip/Tooltip.tsx +3 -3
  429. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  430. package/src/components/UserMenu/UserMenu.tsx +22 -19
  431. package/src/components/index.ts +2 -2
  432. package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
  433. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  434. package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
  435. package/src/hooks/index.ts +1 -2
  436. package/src/hooks/public/usePublicEvent.ts +5 -1
  437. package/src/hooks/public/usePublicEventLogo.ts +5 -1
  438. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  439. package/src/hooks/public/usePublicRouteParams.ts +5 -1
  440. package/src/hooks/services/useAuth.ts +32 -0
  441. package/src/hooks/services/useCurrentEvent.ts +6 -0
  442. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  443. package/src/hooks/useDataTableState.ts +8 -18
  444. package/src/hooks/useDebounce.ts +9 -0
  445. package/src/hooks/useEventTheme.ts +6 -0
  446. package/src/hooks/useFileDisplay.ts +4 -0
  447. package/src/hooks/useFileReference.ts +25 -7
  448. package/src/hooks/useFileUrl.ts +11 -1
  449. package/src/hooks/useFocusManagement.ts +16 -2
  450. package/src/hooks/useFocusTrap.ts +7 -4
  451. package/src/hooks/useFormDialog.ts +8 -7
  452. package/src/hooks/useInactivityTracker.ts +4 -1
  453. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  454. package/src/hooks/useOrganisationPermissions.ts +4 -0
  455. package/src/hooks/useOrganisationSecurity.ts +4 -0
  456. package/src/hooks/usePerformanceMonitor.ts +4 -0
  457. package/src/hooks/usePermissionCache.ts +8 -1
  458. package/src/hooks/useQueryCache.ts +12 -1
  459. package/src/hooks/useSessionRestoration.ts +4 -0
  460. package/src/hooks/useStorage.ts +4 -0
  461. package/src/hooks/useToast.ts +3 -3
  462. package/src/index.ts +2 -1
  463. package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
  464. package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
  465. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
  466. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  467. package/src/providers/services/EventServiceProvider.tsx +18 -0
  468. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  469. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  470. package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
  471. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
  472. package/src/rbac/README.md +1 -1
  473. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
  474. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  475. package/src/rbac/adapters.tsx +14 -5
  476. package/src/rbac/api.ts +100 -67
  477. package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
  478. package/src/rbac/components/NavigationGuard.tsx +1 -1
  479. package/src/rbac/components/NavigationProvider.tsx +5 -2
  480. package/src/rbac/components/PagePermissionGuard.tsx +158 -18
  481. package/src/rbac/components/PagePermissionProvider.tsx +1 -1
  482. package/src/rbac/components/PermissionEnforcer.tsx +1 -1
  483. package/src/rbac/components/RoleBasedRouter.tsx +6 -2
  484. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  485. package/src/rbac/components/SecureDataProvider.tsx +21 -6
  486. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  487. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  488. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  489. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  490. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  491. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  492. package/src/rbac/engine.ts +38 -14
  493. package/src/rbac/hooks/permissions/index.ts +7 -0
  494. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  495. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  496. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  497. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  498. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  499. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  500. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  501. package/src/rbac/hooks/useCan.test.ts +71 -64
  502. package/src/rbac/hooks/usePermissions.ts +14 -995
  503. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  504. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  505. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  506. package/src/rbac/permissions.ts +0 -30
  507. package/src/rbac/secureClient.ts +212 -61
  508. package/src/rbac/types.ts +8 -0
  509. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  510. package/src/theming/parseEventColours.ts +5 -19
  511. package/src/types/vitest-globals.d.ts +51 -26
  512. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  513. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  514. package/src/utils/__tests__/index.unit.test.ts +2 -2
  515. package/src/utils/audit/audit.ts +0 -3
  516. package/src/utils/core/cn.ts +1 -1
  517. package/src/utils/file-reference/index.ts +53 -1
  518. package/src/utils/formatting/formatting.ts +8 -18
  519. package/src/utils/index.ts +0 -1
  520. package/src/utils/security/secureDataAccess.test.ts +31 -20
  521. package/src/utils/security/secureDataAccess.ts +4 -3
  522. package/dist/chunk-6C4YBBJM.js +0 -628
  523. package/dist/chunk-6C4YBBJM.js.map +0 -1
  524. package/dist/chunk-7D4SUZUM.js 2.map +0 -1
  525. package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
  526. package/dist/chunk-7EQTDTTJ.js.map +0 -1
  527. package/dist/chunk-7FLMSG37.js 2.map +0 -1
  528. package/dist/chunk-7FLMSG37.js.map +0 -1
  529. package/dist/chunk-BC4IJKSL.js.map +0 -1
  530. package/dist/chunk-E3SPN4VZ.js +0 -12917
  531. package/dist/chunk-E3SPN4VZ.js.map +0 -1
  532. package/dist/chunk-E66EQZE6 5.js +0 -37
  533. package/dist/chunk-E66EQZE6.js 2.map +0 -1
  534. package/dist/chunk-HWIIPPNI.js.map +0 -1
  535. package/dist/chunk-I7PSE6JW 5.js +0 -191
  536. package/dist/chunk-I7PSE6JW.js 2.map +0 -1
  537. package/dist/chunk-I7PSE6JW.js.map +0 -1
  538. package/dist/chunk-IIELH4DL.js.map +0 -1
  539. package/dist/chunk-KNC55RTG.js 5.map +0 -1
  540. package/dist/chunk-KNC55RTG.js.map +0 -1
  541. package/dist/chunk-KQCRWDSA.js 5.map +0 -1
  542. package/dist/chunk-LFNCN2SP.js +0 -412
  543. package/dist/chunk-LFNCN2SP.js 2.map +0 -1
  544. package/dist/chunk-LFNCN2SP.js.map +0 -1
  545. package/dist/chunk-LMC26NLJ 2.js +0 -84
  546. package/dist/chunk-NOAYCWCX.js +0 -4993
  547. package/dist/chunk-NOAYCWCX.js.map +0 -1
  548. package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
  549. package/dist/chunk-QWWZ5CAQ.js.map +0 -1
  550. package/dist/chunk-QXHPKYJV 3.js +0 -113
  551. package/dist/chunk-R77UEZ4E.js +0 -68
  552. package/dist/chunk-R77UEZ4E.js.map +0 -1
  553. package/dist/chunk-SQGMNID3.js.map +0 -1
  554. package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
  555. package/dist/chunk-XNXXZ43G.js.map +0 -1
  556. package/dist/chunk-ZSAAAMVR 6.js +0 -25
  557. package/dist/components.js 5.map +0 -1
  558. package/dist/styles/index 2.js +0 -12
  559. package/dist/styles/index.js 5.map +0 -1
  560. package/dist/theming/runtime 5.js +0 -19
  561. package/dist/theming/runtime.js 5.map +0 -1
  562. package/docs/api/classes/ErrorBoundary.md +0 -144
  563. package/docs/migration/quick-migration-guide.md +0 -356
  564. package/docs/migration/service-architecture.md +0 -281
  565. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  566. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  567. package/src/hooks/useSecureDataAccess.ts +0 -666
  568. /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  569. /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  570. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  571. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  572. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  573. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  574. /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
  575. /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
  576. /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
  577. /package/examples/{rbac → RBAC}/index.ts +0 -0
@@ -8,27 +8,15 @@
8
8
  * This is the main component that consumers will use.
9
9
  */
10
10
 
11
- import React, { useMemo, useCallback, useEffect, useState, useRef } from 'react';
12
- import { useReactTable, flexRender } from '@tanstack/react-table';
11
+ import React, { useMemo, useCallback, useEffect, useRef } from 'react';
12
+ import { useReactTable } from '@tanstack/react-table';
13
13
  import type {
14
14
  SortingState,
15
- ColumnFiltersState,
16
- VisibilityState,
17
- GroupingState,
18
- ExpandedState,
19
- PaginationState,
20
15
  } from '@tanstack/react-table';
21
- import { Edit, Trash, ChevronUp, ChevronDown, ChevronsUpDown } from 'lucide-react';
22
- import { cn } from '../../../utils/core/cn';
23
- import { Checkbox } from '../../Checkbox/Checkbox';
24
- import { Button } from '../../Button/Button';
25
- import { getTableClasses, getMainContainerClasses } from '../styles';
16
+ import { Edit, Trash } from 'lucide-react';
26
17
  import { useDataTablePerformance } from '../../../hooks/useDataTablePerformance';
27
- import { DataTableToolbar } from './DataTableToolbar';
28
- import { UnifiedTableBody } from './UnifiedTableBody';
29
- import { PaginationControls, EnhancedPaginationControls } from './PaginationControls';
30
18
  import { LoadingState } from './LoadingState';
31
- import { DataTableModals } from './DataTableModals';
19
+ import { DataTableLayout } from './DataTableLayout';
32
20
  import { DataTableErrorBoundary } from './DataTableErrorBoundary';
33
21
  import { useColumnOrderPersistence } from '../hooks/useColumnOrderPersistence';
34
22
  import { useColumnVisibilityPersistence } from '../hooks/useColumnVisibilityPersistence';
@@ -41,20 +29,19 @@ import { useDataTableConfiguration } from '../hooks/useDataTableConfiguration';
41
29
  import type { TableStateSnapshot } from '../hooks/useTableHandlers';
42
30
  import { ColumnFactory } from '../core/ColumnFactory';
43
31
  import { AccessDeniedPage } from './AccessDeniedPage';
44
- import { useCan, useResolvedScope } from '../../../rbac/hooks';
45
32
  // NOTE: All toast() calls in this component use the default timeout (5 seconds).
46
33
  // Do NOT set duration or timeout properties - let the toast system use its default.
47
34
  import { toast } from '../../../hooks/useToast';
48
- import { exportToCSV, exportToCSVWithTableRows } from '../utils/exportUtils';
49
- import type { ExportOptions } from '../types';
50
35
  import { useUnifiedAuth } from '../../../providers/services/UnifiedAuthProvider';
51
- import { Scope } from '../../../rbac/types';
52
36
  import { useDataTablePermissions } from '../hooks/useDataTablePermissions';
53
37
  import { useTableColumns } from '../hooks/useTableColumns';
54
- import { initializeLiveRegion, announceSortChange } from '../utils/a11yUtils';
38
+ import { initializeLiveRegion } from '../utils/a11yUtils';
55
39
  import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation';
56
40
  import { getRowIdSafe } from '../utils/rowUtils';
57
41
  import { createLogger } from '../../../utils/core/logger';
42
+ import { usePermissionTracking } from './hooks/usePermissionTracking';
43
+ import { useImportModalFocus } from './hooks/useImportModalFocus';
44
+ import { toCellValueRecord } from './cellValueUtils';
58
45
 
59
46
  import { normalizeDataTableFeatures } from '../types';
60
47
  import type {
@@ -69,50 +56,23 @@ import type {
69
56
  DataTableFeatureConfig,
70
57
  NormalizedDataTableFeatureConfig,
71
58
  DataTableColumn,
72
- SimpleColumn,
73
59
  AggregateConfig,
74
60
  DataTableAction,
75
61
  HierarchicalConfig,
76
62
  DataTableRBACConfig,
77
- CellValue
78
63
  } from '../types';
79
64
  import type { ImportModalConfig } from './ImportModal';
80
65
 
81
- const isCellValue = (value: unknown): value is CellValue => {
82
- if (value === null || value === undefined) {
83
- return true;
84
- }
85
-
86
- if (value instanceof Date) {
87
- return true;
88
- }
89
-
90
- const valueType = typeof value;
91
- return valueType === 'string' || valueType === 'number' || valueType === 'boolean';
92
- };
93
-
94
- const toCellValueRecord = <TData extends DataRecord>(row: TData): Record<string, CellValue> => {
95
- if (typeof row !== 'object' || row === null) {
96
- return {};
97
- }
98
-
99
- return Object.entries(row).reduce<Record<string, CellValue>>((accumulator, [key, entryValue]) => {
100
- if (isCellValue(entryValue)) {
101
- accumulator[key] = entryValue;
102
- } else if (entryValue && typeof entryValue === 'object' && 'toString' in entryValue) {
103
- accumulator[key] = String(entryValue) as CellValue;
104
- } else {
105
- accumulator[key] = entryValue as CellValue;
106
- }
107
-
108
- return accumulator;
109
- }, {});
110
- };
111
-
112
66
  // ============================================================================
113
67
  // CORE COMPONENT PROPS
114
68
  // ============================================================================
115
69
 
70
+ /**
71
+ * Core DataTable component props.
72
+ * This is the internal component that handles all DataTable functionality.
73
+ *
74
+ * @template TData - The type of data records in the table
75
+ */
116
76
  export interface DataTableCoreProps<TData extends DataRecord> {
117
77
  // Core data
118
78
  data: TData[];
@@ -159,7 +119,7 @@ export interface DataTableCoreProps<TData extends DataRecord> {
159
119
  // Utilities
160
120
  getRowId?: GetRowId<TData>;
161
121
  isLoading?: boolean;
162
- emptyState?: EmptyStateConfig | React.ReactElement;
122
+ emptyState?: EmptyStateConfig | React.ReactElement<any>;
163
123
  aggregates?: AggregateConfig[];
164
124
  importModalConfig?: ImportModalConfig;
165
125
  actions?: DataTableAction<TData>[];
@@ -220,7 +180,7 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
220
180
  onLayoutChange,
221
181
  } = props;
222
182
 
223
- const logger = React.useMemo(() => createLogger('DataTableCore'), []);
183
+ const logger = createLogger('DataTableCore');
224
184
 
225
185
  // ============================================================================
226
186
  // ALL HOOKS MUST BE CALLED IN THE SAME ORDER EVERY RENDER
@@ -236,6 +196,16 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
236
196
 
237
197
  // MANDATORY: Get permissions and secure features
238
198
  const { permissions, secureFeatures, effectivePageId } = useDataTablePermissions(rbac, requestedFeatures);
199
+
200
+ const {
201
+ permissionElapsed,
202
+ shouldAllowRenderAfterTimeout,
203
+ isPermissionLoading,
204
+ } = usePermissionTracking({
205
+ permissions,
206
+ effectivePageId,
207
+ logger,
208
+ });
239
209
 
240
210
 
241
211
  // ============================================================================
@@ -362,43 +332,7 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
362
332
  }
363
333
  );
364
334
 
365
- const lastFocusedElementRef = useRef<HTMLElement | null>(null);
366
- const wasImportModalOpenRef = useRef(false);
367
-
368
- // Store focus when modals open, restore when they close
369
- useEffect(() => {
370
- if (state.showImportModal) {
371
- wasImportModalOpenRef.current = true;
372
- keyboardNavigation.storeFocus();
373
- if (document.activeElement instanceof HTMLElement) {
374
- lastFocusedElementRef.current = document.activeElement;
375
- }
376
- }
377
- }, [state.showImportModal, keyboardNavigation]);
378
-
379
- useEffect(() => {
380
- if (!state.showImportModal) {
381
- if (!wasImportModalOpenRef.current) {
382
- return;
383
- }
384
- wasImportModalOpenRef.current = false;
385
- // Restore focus after modal closes
386
- setTimeout(() => {
387
- const storedElement = lastFocusedElementRef.current;
388
- lastFocusedElementRef.current = null;
389
-
390
- const elementToRestore = storedElement?.isConnected
391
- ? storedElement
392
- : document.querySelector<HTMLElement>('[data-restore-target="datatable-import-button"]');
393
-
394
- if (elementToRestore && typeof elementToRestore.focus === 'function') {
395
- elementToRestore.focus();
396
- } else {
397
- keyboardNavigation.restoreFocus();
398
- }
399
- }, 100); // Small delay to ensure modal is fully closed
400
- }
401
- }, [state.showImportModal, keyboardNavigation]);
335
+ const { lastFocusedElementRef } = useImportModalFocus(state.showImportModal, keyboardNavigation);
402
336
 
403
337
  // ============================================================================
404
338
  // HIERARCHICAL DATA VALIDATION AND PROCESSING - ALWAYS call these hooks
@@ -540,7 +474,13 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
540
474
  finalDataCount
541
475
  ]);
542
476
 
543
- const isLoading = externalIsLoading || performanceLoading;
477
+ // React 19 fix: Use useMemo to ensure isLoading updates when props change
478
+ // This prevents the component from getting stuck in loading state when externalIsLoading
479
+ // changes from true to false in React 19 with automatic memoization
480
+ const isLoading = useMemo(
481
+ () => externalIsLoading || performanceLoading,
482
+ [externalIsLoading, performanceLoading]
483
+ );
544
484
 
545
485
  // ============================================================================
546
486
  // DATA PROCESSING - ALWAYS call these hooks
@@ -773,15 +713,31 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
773
713
  onLayoutChange,
774
714
  });
775
715
 
716
+ // PERFORMANCE FIX: If permissions still loading after timeout, filter data to empty array
717
+ // This allows table structure to render but keeps data hidden until permissions confirm
718
+ // SECURITY: Data remains protected - only table structure (headers) will show
719
+ const safeTableData = useMemo(() => {
720
+ if (permissions.canRead.isLoading && shouldAllowRenderAfterTimeout) {
721
+ // Permissions still loading after timeout - return empty array to hide data
722
+ return [] as TData[];
723
+ }
724
+ if (!permissions.canRead.can) {
725
+ // Permissions denied - return empty array
726
+ return [] as TData[];
727
+ }
728
+ // Permissions confirmed - return actual data
729
+ return finalTableData as TData[];
730
+ }, [finalTableData, permissions.canRead.isLoading, permissions.canRead.can, shouldAllowRenderAfterTimeout]);
731
+
776
732
  const tableConfig = useDataTableConfiguration({
777
- data: finalTableData as TData[],
733
+ data: safeTableData,
778
734
  columns: enhancedColumns,
779
735
  stateSnapshot: tableStateSnapshot,
780
736
  handlers: tableHandlers,
781
737
  features: secureFeatures,
782
738
  getRowId: resolvedGetRowId,
783
739
  finalPaginationMode,
784
- finalDataCount,
740
+ finalDataCount: safeTableData.length > 0 ? finalDataCount : 0,
785
741
  pageSize: effectivePageSize,
786
742
  hasServerSideConfig: !!serverSide,
787
743
  });
@@ -793,498 +749,172 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
793
749
  // RBAC VALIDATION AND EARLY RETURNS - AFTER ALL HOOKS
794
750
  // ============================================================================
795
751
 
752
+ // DIAGNOSTIC: Log render state for debugging
753
+ const renderDiagnostics = {
754
+ hasUser: !!user,
755
+ userId: user?.id,
756
+ permissionLoading: permissions.canRead.isLoading,
757
+ permissionCan: permissions.canRead.can,
758
+ permissionError: permissions.canRead.error,
759
+ effectivePageId,
760
+ externalIsLoading,
761
+ performanceLoading,
762
+ computedIsLoading: isLoading,
763
+ dataLength: data.length,
764
+ finalTableDataLength: finalTableData.length,
765
+ columnsLength: columns.length,
766
+ tableRowsCount: table?.getRowModel().rows.length || 0,
767
+ };
768
+
769
+ // Log diagnostics in development mode
770
+ if (process.env.NODE_ENV === 'development') {
771
+ logger.debug('DataTable render diagnostics:', renderDiagnostics);
772
+ }
773
+
796
774
  // MANDATORY: Every DataTable must have a user
797
775
  if (!user) {
776
+ logger.error('DataTable render blocked: No user', renderDiagnostics);
798
777
  throw new Error('DataTable requires authenticated user for RBAC');
799
778
  }
800
779
 
780
+ // PERFORMANCE FIX: Allow rendering after timeout to prevent infinite blocking
781
+ // After 3 seconds, allow table to render but keep data hidden until permissions confirm
782
+ // This provides better UX while maintaining security (data remains protected)
783
+ // Note: permissionElapsed and shouldAllowRenderAfterTimeout are calculated above for useMemo
801
784
  // Wait for permission check to complete before making access decisions
802
- if (permissions.canRead.isLoading) {
785
+ // BUT: After 3s timeout, allow table structure to render (data will remain hidden)
786
+ if (isPermissionLoading) {
787
+ // Enhanced diagnostics for hanging permission checks
788
+ if (permissionElapsed > 10000) {
789
+ logger.error('DataTableCore', 'DataTable: Permission check hanging (>10s)', {
790
+ ...renderDiagnostics,
791
+ permissionState: {
792
+ can: permissions.canRead.can,
793
+ isLoading: permissions.canRead.isLoading,
794
+ error: permissions.canRead.error?.message,
795
+ },
796
+ elapsedMs: permissionElapsed,
797
+ diagnostic: 'Permission check has been loading for over 10 seconds. This likely indicates a hanging database query or network issue. Check browser network tab for pending requests to Supabase.',
798
+ recommendation: 'Check: 1) Browser network tab for pending requests, 2) Supabase connection, 3) Database query performance, 4) Super admin check completion',
799
+ });
800
+ }
801
+
802
+ if (process.env.NODE_ENV === 'development') {
803
+ logger.debug('DataTable render blocked: Permissions loading', {
804
+ ...renderDiagnostics,
805
+ permissionState: permissions.canRead,
806
+ elapsedMs: permissionElapsed,
807
+ });
808
+ }
803
809
  return <LoadingComponent />;
804
810
  }
811
+
812
+ // If timeout reached but permissions still loading, log warning and proceed with caution
813
+ if (permissions.canRead.isLoading && shouldAllowRenderAfterTimeout) {
814
+ logger.warn('DataTable: Rendering after timeout - permissions still loading. Data will remain hidden until confirmed.', {
815
+ pageId: effectivePageId,
816
+ elapsedMs: permissionElapsed,
817
+ permissionState: permissions.canRead,
818
+ });
819
+ // Continue to render check below - we'll show empty state until permissions confirm
820
+ }
805
821
 
806
- // MANDATORY: No data access without read permission (only check after loading completes)
807
- if (!permissions.canRead.can) {
822
+ // MANDATORY: No data access without read permission
823
+ // SECURITY: If permissions are still loading after timeout, allow table structure but hide data
824
+ // If permissions are confirmed as denied, show access denied page
825
+ if (!permissions.canRead.isLoading && !permissions.canRead.can) {
808
826
  logger.warn('Access denied - no read permission:', {
809
827
  canRead: permissions.canRead,
810
828
  pageId: effectivePageId,
811
829
  isLoading: permissions.canRead.isLoading,
830
+ diagnostics: renderDiagnostics,
812
831
  });
813
832
  return <AccessDeniedPage resource={effectivePageId || 'unknown-page'} operation="read" />;
814
833
  }
834
+
835
+ // If permissions still loading after timeout, proceed to render but data will be empty/hidden
836
+ // The table structure will render, but rows will be empty until permissions confirm
837
+ if (permissions.canRead.isLoading && shouldAllowRenderAfterTimeout) {
838
+ // Log that we're proceeding with timeout
839
+ logger.debug('DataTable: Proceeding to render after timeout - permissions still loading', {
840
+ pageId: effectivePageId,
841
+ elapsedMs: permissionElapsed,
842
+ });
843
+ // Continue to render - data will be empty until permissions confirm
844
+ }
815
845
 
816
846
  // ============================================================================
817
847
  // RENDER
818
848
  // ============================================================================
819
849
 
820
850
  if (isLoading) {
851
+ if (process.env.NODE_ENV === 'development') {
852
+ logger.debug('DataTable render blocked: External isLoading', {
853
+ ...renderDiagnostics,
854
+ isLoadingSource: {
855
+ externalIsLoading,
856
+ performanceLoading,
857
+ computed: isLoading,
858
+ },
859
+ });
860
+ }
821
861
  return <LoadingComponent />;
822
862
  }
823
863
 
824
- const PaginationComponent = enhancedPagination || finalPaginationMode !== 'client'
825
- ? EnhancedPaginationControls
826
- : PaginationControls;
827
-
828
- // Calculate column counts for colgroup
829
- const visibleColumns = table?.getVisibleFlatColumns() || [];
830
- const dataColumns = visibleColumns.filter(col =>
831
- col.id !== 'select' && col.id !== 'actions'
832
- ).length;
833
- const hasSelectColumn = visibleColumns.some(col => col.id === 'select');
834
- const hasActionsColumn = visibleColumns.some(col => col.id === 'actions');
864
+ // DIAGNOSTIC: Log successful render path
865
+ if (process.env.NODE_ENV === 'development') {
866
+ logger.debug('DataTable proceeding to render:', {
867
+ ...renderDiagnostics,
868
+ willRender: true,
869
+ tableState: {
870
+ rowCount: table?.getRowModel().rows.length || 0,
871
+ columnCount: table?.getVisibleFlatColumns().length || 0,
872
+ paginationMode: finalPaginationMode,
873
+ },
874
+ });
875
+ }
835
876
 
836
877
  return (
837
- <>
838
- {/* Table with semantic HTML structure */}
839
- <table
840
- className={getTableClasses({
841
- isFixed: false, // Use auto table-layout so columns size based on content
842
- variant,
843
- className: cn('border-collapse relative w-full', className)
844
- })}
845
- aria-label={title}
846
- aria-describedby={description ? 'table-description' : undefined}
847
- aria-busy={isLoading ? 'true' : 'false'}
848
- >
849
- {/* Caption with title, description, and toolbar */}
850
- <caption className="text-left pb-2">
851
- {(title || description) && (
852
- <>
853
- {title && <h2 >{title}</h2>}
854
- {description && <p id="table-description">{description}</p>}
855
- </>
856
- )}
857
- <>
858
- <DataTableToolbar
859
- features={secureFeatures}
860
- globalFilter={searchQuery}
861
- onGlobalFilterChange={handleSearch}
862
- columns={columns}
863
- grouping={state.grouping}
864
- onGroupByChange={(columnId) => {
865
- stateActions.setGrouping(columnId ? [columnId] : []);
866
- }}
867
- tableColumns={table?.getAllColumns() || []}
868
- onColumnVisibilityChange={(columnId: string, visible: boolean) => {
869
- // Update visibility for specific column
870
- stateActions.setColumnVisibility({ ...state.columnVisibility, [columnId]: visible });
871
- }}
872
- onCreateRow={secureFeatures.creation && secureHandlers.onCreateRow ? () => stateActions.setCreating(true) : undefined}
873
- onImportClick={() => {
874
- if (document.activeElement instanceof HTMLElement) {
875
- lastFocusedElementRef.current = document.activeElement;
876
- }
877
- stateActions.setImportModal(true);
878
- }}
879
- onExport={async () => {
880
- try {
881
- // Prepare export options with all available data
882
- // Get the table rows (which have getValue() that properly evaluates accessorFn)
883
- const tableRows = table.getFilteredRowModel().rows;
884
-
885
- // Get only visible columns by checking the actual table columns
886
- // This approach is more reliable because it uses the table's actual column registry
887
- const tableColumns = table.getAllColumns();
888
- const visibleTableColumns = tableColumns.filter(col => {
889
- // Exclude system columns (selection, actions) and only include data columns
890
- const isSystemColumn = col.id === 'select' || col.id === 'actions';
891
- return !isSystemColumn && col.getIsVisible();
892
- });
893
-
894
- // Map table columns to visible columns
895
- const visibleColumns: DataTableColumn<TData>[] = [];
896
-
897
- // Store mapping of column IDs to table column instances for getValue() calls
898
- const columnIdToTableColumn = new Map<string, typeof visibleTableColumns[0]>();
899
-
900
- visibleTableColumns.forEach(tableCol => {
901
- // Find the original column definition that matches this table column
902
- const originalCol = columns.find(col => {
903
- const colId = col.id || col.accessorKey;
904
- return colId && String(colId) === tableCol.id;
905
- });
906
-
907
- if (!originalCol) return;
908
-
909
- // Store the table column for getValue() calls
910
- columnIdToTableColumn.set(tableCol.id, tableCol);
911
-
912
- // Add the display column (what's shown in the table)
913
- visibleColumns.push(originalCol);
914
- });
915
-
916
- // Generate filename with timestamp
917
- const timestamp = new Date().toISOString().split('T')[0];
918
- const filename = title ? `${title.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_${timestamp}.csv` : `data_export_${timestamp}.csv`;
919
-
920
- // Create export options
921
- const exportOptions: ExportOptions<TData> = {
922
- tableRows,
923
- allColumns: columns,
924
- visibleColumns,
925
- columnIdToTableColumn,
926
- data,
927
- filename,
928
- table
929
- };
930
-
931
- // If custom handler provided, call it with options
932
- if (secureHandlers.onExport) {
933
- await secureHandlers.onExport(exportOptions);
934
- return;
935
- }
936
-
937
- // Default export: exports exactly what's shown in the table
938
- // Convert visible columns to ExportColumn format
939
- const exportColumns: Array<{
940
- header?: string;
941
- id?: string;
942
- accessorKey?: string;
943
- accessorFn?: (row: any) => any;
944
- editAccessorKey?: string;
945
- isIdColumn?: boolean;
946
- }> = exportOptions.visibleColumns.map(col => {
947
- const colId = col.id || col.accessorKey;
948
- const hasAccessorFn = 'accessorFn' in col && (col as any).accessorFn;
949
-
950
- return {
951
- ...col,
952
- header: typeof col.header === 'string'
953
- ? col.header
954
- : col.accessorKey || colId || 'Column',
955
- id: colId ? String(colId) : undefined,
956
- accessorFn: hasAccessorFn ? (col as any).accessorFn : undefined,
957
- };
958
- });
959
-
960
- // Export using table rows with getValue() for proper accessorFn evaluation
961
- // This ensures we get the same values that are displayed in the table
962
- await exportToCSVWithTableRows(
963
- exportOptions.tableRows,
964
- exportColumns,
965
- exportOptions.columnIdToTableColumn,
966
- exportOptions.filename
967
- );
968
-
969
- // Show success toast notification
970
- // NOTE: Toast notifications use default timeout (5 seconds) - do not set duration property
971
- toast({
972
- title: "Export Successful",
973
- description: `Data exported to ${exportOptions.filename}`,
974
- variant: "default"
975
- });
976
- } catch (error) {
977
- logger.error('Failed to export data:', error);
978
-
979
- // Show error toast notification to user
980
- const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
981
- toast({
982
- title: "Export Failed",
983
- description: `Failed to export data: ${errorMessage}`,
984
- variant: "destructive"
985
- });
986
- }
987
- }}
988
- rowSelection={rowSelection}
989
- onDeleteSelected={secureHandlers.onDeleteSelected ? async (selectedRows: Record<string, boolean>) => {
990
- const selectedCount = Object.values(selectedRows).filter(Boolean).length;
991
- if (selectedCount === 0) {
992
- toast({
993
- title: "No Selection",
994
- description: "Please select at least one row to delete",
995
- variant: "default"
996
- });
997
- return;
998
- }
999
- try {
1000
- const result = secureHandlers.onDeleteSelected!(selectedRows) as any;
1001
- // Handle async operations
1002
- if (result !== undefined && result !== null && typeof result === 'object' && typeof result.then === 'function') {
1003
- await result;
1004
- }
1005
- toast({
1006
- title: "Delete Successful",
1007
- description: `Successfully deleted ${selectedCount} ${selectedCount === 1 ? 'row' : 'rows'}`,
1008
- variant: "default"
1009
- });
1010
- } catch (error) {
1011
- logger.error('Bulk delete error:', error);
1012
- toast({
1013
- title: "Delete Failed",
1014
- description: error instanceof Error ? error.message : 'Failed to delete selected rows',
1015
- variant: "destructive"
1016
- });
1017
- }
1018
- } : undefined}
1019
- onToggleFilterRow={() => stateActions.setFilterRow(!state.showFilterRow)}
1020
- showFilterRow={state.showFilterRow}
1021
- rbac={rbac}
1022
- permissions={permissions}
1023
- />
1024
- </>
1025
- </caption>
1026
-
1027
- {/* Column groups */}
1028
- <colgroup>
1029
- {hasSelectColumn && <col span={1} data-col-type="select"/>}
1030
- <col span={dataColumns} data-col-type="data" />
1031
- {hasActionsColumn && <col span={1} data-col-type="actions"/>}
1032
- </colgroup>
1033
-
1034
- {/* Table header */}
1035
- <thead>
1036
- {table?.getHeaderGroups().map((headerGroup) => {
1037
- // Filter visible headers once to determine first and last
1038
- const visibleHeaders = headerGroup.headers.filter(header => {
1039
- return typeof header.column.getIsVisible === 'function'
1040
- ? header.column.getIsVisible()
1041
- : true;
1042
- });
1043
-
1044
- return (
1045
- <tr key={headerGroup.id}>
1046
- {visibleHeaders.map((header, index) => {
1047
- const isFirst = index === 0;
1048
- const isLast = index === visibleHeaders.length - 1;
1049
- const isSortable = header.column.getCanSort();
1050
- const ariaSort = isSortable
1051
- ? (header.column.getIsSorted() === 'asc'
1052
- ? 'ascending'
1053
- : header.column.getIsSorted() === 'desc'
1054
- ? 'descending'
1055
- : 'none')
1056
- : undefined;
1057
- const isRightAligned = header.column.columnDef.meta?.align === 'right';
1058
-
1059
- // Create custom sort handler with accessibility announcement
1060
- const handleSortClick = (event: React.MouseEvent) => {
1061
- const originalHandler = header.column.getToggleSortingHandler();
1062
- if (originalHandler) {
1063
- originalHandler(event);
1064
- }
1065
-
1066
- // Announce the sort change
1067
- const columnName = typeof header.column.columnDef.header === 'string'
1068
- ? header.column.columnDef.header
1069
- : 'column';
1070
- const currentSort = header.column.getIsSorted();
1071
- const newSort = currentSort === 'asc' ? 'desc' : currentSort === 'desc' ? null : 'asc';
1072
- announceSortChange(columnName, newSort);
1073
- };
1074
-
1075
- // Get keyboard navigation handlers for this header
1076
- const headerKeyboardHandlers = keyboardNavigation.getHeaderKeyboardHandlers(
1077
- header.index,
1078
- () => {
1079
- // Sort handler for keyboard navigation
1080
- const originalHandler = header.column.getToggleSortingHandler();
1081
- if (originalHandler) {
1082
- originalHandler({} as any);
1083
- }
1084
-
1085
- // Announce the sort change
1086
- const columnName = typeof header.column.columnDef.header === 'string'
1087
- ? header.column.columnDef.header
1088
- : 'column';
1089
- const currentSort = header.column.getIsSorted();
1090
- const newSort = currentSort === 'asc' ? 'desc' : currentSort === 'desc' ? null : 'asc';
1091
- announceSortChange(columnName, newSort);
1092
- }
1093
- );
1094
-
1095
- return (
1096
- <th
1097
- key={header.id}
1098
- className={cn(
1099
- 'px-3 py-2 bg-main-200',
1100
- isRightAligned ? 'text-right' : 'text-left',
1101
- isFirst && 'rounded-l-md',
1102
- isLast && 'rounded-r-md'
1103
- )}
1104
- scope="col"
1105
- role="columnheader"
1106
- {...(isSortable ? { 'aria-sort': ariaSort } : {})}
1107
- {...(isSortable ? headerKeyboardHandlers : {})}
1108
- >
1109
- {header.isPlaceholder ? null : (
1110
- isSortable ? (
1111
- <Button
1112
- variant="ghost"
1113
- className={`h-auto p-0 font-bold hover:bg-transparent ${isRightAligned ? 'justify-end' : 'justify-start'}`}
1114
- onClick={handleSortClick}
1115
- {...headerKeyboardHandlers}
1116
- aria-label={`Sort by ${typeof header.column.columnDef.header === 'string' ? header.column.columnDef.header : 'column'}`}
1117
- tabIndex={0}
1118
- >
1119
- {typeof header.column.columnDef.header === 'function'
1120
- ? header.column.columnDef.header(header.getContext())
1121
- : header.column.columnDef.header}
1122
- {header.column.getIsSorted() === 'asc' ? (
1123
- <ChevronUp className="size-4" />
1124
- ) : header.column.getIsSorted() === 'desc' ? (
1125
- <ChevronDown className="size-4" />
1126
- ) : (
1127
- <ChevronsUpDown className="size-4" />
1128
- )}
1129
- </Button>
1130
- ) : (
1131
- typeof header.column.columnDef.header === 'function'
1132
- ? header.column.columnDef.header(header.getContext())
1133
- : header.column.columnDef.header
1134
- )
1135
- )}
1136
- </th>
1137
- );
1138
- })}
1139
- </tr>
1140
- );
1141
- })}
1142
- </thead>
1143
-
1144
- {/* Table body */}
1145
- <UnifiedTableBody
1146
- table={table}
1147
- isCreating={isCreating}
1148
- creationData={creationData}
1149
- onCreationDataChange={stateActions.setCreationData}
1150
- onSaveCreation={() => {
1151
- if (onCreateRow) {
1152
- onCreateRow(creationData as Partial<TData>);
1153
- stateActions.clearCreationData();
1154
- stateActions.setCreating(false);
1155
- }
1156
- }}
1157
- onCancelCreation={() => {
1158
- stateActions.clearCreationData();
1159
- stateActions.setCreating(false);
1160
- }}
1161
- editingRowId={editingRowId}
1162
- editingData={editingData}
1163
- onEditingDataChange={(data) => {
1164
- // Update the editing data in the centralized state
1165
- if (editingRowId) {
1166
- stateActions.setEditingRow(editingRowId, data);
1167
- }
1168
- }}
1169
- onSaveEditing={() => {
1170
- if (onEditRow && editingRowId) {
1171
- // Find the original row data
1172
- const originalRow = data.find(row => {
1173
- try {
1174
- const rowId = resolvedGetRowId(row, 0);
1175
- return rowId === editingRowId;
1176
- } catch {
1177
- return false;
1178
- }
1179
- });
1180
- if (originalRow) {
1181
- onEditRow(originalRow, editingData as Partial<TData>);
1182
- }
1183
- }
1184
- stateActions.clearEditing();
1185
- }}
1186
- onCancelEditing={() => {
1187
- stateActions.clearEditing();
1188
- }}
1189
- grouping={state.grouping}
1190
- aggregates={aggregates}
1191
- getRowId={resolvedGetRowId}
1192
- emptyState={React.isValidElement(emptyState) ? undefined : emptyState as any}
1193
- isFiltered={searchQuery !== '' || state.columnFilters.length > 0}
1194
- onClearFilters={() => {
1195
- // Clear both search query states to keep them in sync
1196
- stateActions.setSearchQuery('');
1197
- setSearchQuery('');
1198
- stateActions.setColumnFilters([]);
1199
- }}
1200
- enableFiltering={secureFeatures.filtering}
1201
- showFilterRow={showFilterRow}
1202
- dataLength={finalTableData?.length || 0}
1203
- virtualHeight={virtualHeight}
1204
- forceVirtualization={false}
1205
- hierarchical={secureFeatures.hierarchical && hierarchical?.enabled && hierarchicalState ? {
1206
- ...hierarchical,
1207
- state: hierarchicalState,
1208
- expandAll: hierarchicalState.expandAll,
1209
- collapseAll: hierarchicalState.collapseAll,
1210
- isAllExpanded: hierarchicalState.getExpandedIds().length > 0 &&
1211
- hierarchicalState.getExpandedIds().length === (finalTableData as any[]).filter(row => row.isParent).length,
1212
- hasAnyChildren: (finalTableData as any[]).some(row => row.isParent),
1213
- } : undefined}
1214
- actions={effectiveActions}
1215
- rbac={rbac}
1216
- permissions={permissions}
1217
- />
1218
-
1219
- {/* Table footer with pagination */}
1220
- {secureFeatures.pagination && (
1221
- <tfoot>
1222
- <tr>
1223
- <td colSpan={visibleColumns.length}>
1224
- <PaginationComponent
1225
- table={table}
1226
- pageSizeOptions={finalPageSizeOptions}
1227
- paginationMode={finalPaginationMode}
1228
- totalCount={finalDataCount}
1229
- isLoading={isLoading}
1230
- />
1231
- </td>
1232
- </tr>
1233
- </tfoot>
1234
- )}
1235
-
1236
- </table>
1237
-
1238
- {/* Modal Dialogs */}
1239
- <DataTableModals
1240
- showImportModal={state.showImportModal}
1241
- onCloseImportModal={() => stateActions.setImportModal(false)}
1242
- onImport={async (data: TData[]) => {
1243
- if (onImport) {
1244
- try {
1245
- const result = onImport(data);
1246
- if (result && typeof result.then === 'function') {
1247
- await result;
1248
- }
1249
-
1250
- // Show success toast
1251
- // NOTE: Toast notifications use default timeout (5 seconds) - do not set duration property
1252
- toast({
1253
- title: "Import Successful",
1254
- description: `Successfully imported ${data.length} ${data.length === 1 ? 'row' : 'rows'}`,
1255
- variant: "default"
1256
- });
1257
- } catch (error) {
1258
- logger.error('Import error:', error);
1259
- toast({
1260
- title: "Import Failed",
1261
- description: error instanceof Error ? error.message : 'Failed to import data',
1262
- variant: "destructive"
1263
- });
1264
- // Don't close modal on error so user can see the error
1265
- return;
1266
- }
1267
- } else {
1268
- logger.error('onImport handler not provided');
1269
- toast({
1270
- title: "Import Not Configured",
1271
- description: "Import functionality requires an onImport handler to be provided.",
1272
- variant: "destructive"
1273
- });
1274
- // Don't close modal so user can see the error
1275
- return;
1276
- }
1277
- stateActions.setImportModal(false);
1278
- }}
1279
- importModalConfig={importModalConfig}
1280
- columns={columns.map(col => ({
1281
- id: col.id,
1282
- accessorKey: col.accessorKey,
1283
- header: typeof col.header === 'string' ? col.header : undefined,
1284
- editAccessorKey: col.editAccessorKey,
1285
- }))}
1286
- />
1287
- </>
878
+ <DataTableLayout
879
+ table={table}
880
+ title={title}
881
+ description={description}
882
+ variant={variant}
883
+ className={className}
884
+ columns={columns}
885
+ secureFeatures={secureFeatures}
886
+ enhancedPagination={enhancedPagination}
887
+ searchQuery={searchQuery}
888
+ onSearch={handleSearch}
889
+ state={state}
890
+ stateActions={stateActions}
891
+ rowSelection={rowSelection}
892
+ onCreateRow={secureHandlers.onCreateRow}
893
+ onEditRow={secureHandlers.onEditRow}
894
+ onImport={secureHandlers.onImport}
895
+ onExport={secureHandlers.onExport}
896
+ onDeleteSelected={secureHandlers.onDeleteSelected}
897
+ rbac={rbac}
898
+ permissions={permissions}
899
+ effectiveActions={effectiveActions}
900
+ finalPageSizeOptions={finalPageSizeOptions}
901
+ finalPaginationMode={finalPaginationMode}
902
+ finalDataCount={finalDataCount}
903
+ isLoading={isLoading}
904
+ finalTableData={finalTableData}
905
+ aggregates={aggregates}
906
+ resolvedGetRowId={resolvedGetRowId}
907
+ data={data}
908
+ emptyState={emptyState}
909
+ virtualHeight={virtualHeight}
910
+ hierarchical={hierarchical}
911
+ hierarchicalState={hierarchicalState}
912
+ logger={logger}
913
+ secureHandlers={secureHandlers}
914
+ importModalConfig={importModalConfig}
915
+ keyboardNavigation={keyboardNavigation}
916
+ lastFocusedElementRef={lastFocusedElementRef}
917
+ />
1288
918
  );
1289
919
  }
1290
920
 
@@ -1292,6 +922,15 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
1292
922
  // MAIN COMPONENT
1293
923
  // ============================================================================
1294
924
 
925
+ /**
926
+ * Core DataTable component implementation.
927
+ * This is the internal component that handles all DataTable functionality including
928
+ * state management, RBAC, performance optimizations, and feature rendering.
929
+ *
930
+ * @template TData - The type of data records in the table
931
+ * @param props - DataTable configuration
932
+ * @returns The rendered DataTable with error boundary
933
+ */
1295
934
  export function DataTableCore<TData extends DataRecord>(props: DataTableCoreProps<TData>) {
1296
935
  return (
1297
936
  <DataTableErrorBoundary>