@jmruthers/pace-core 0.6.5 → 0.6.7

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 (473) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +5 -403
  3. package/audit-tool/00-dependencies.cjs +394 -0
  4. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  5. package/audit-tool/audits/02-project-structure.cjs +255 -0
  6. package/audit-tool/audits/03-architecture.cjs +196 -0
  7. package/audit-tool/audits/04-code-quality.cjs +149 -0
  8. package/audit-tool/audits/05-styling.cjs +224 -0
  9. package/audit-tool/audits/06-security-rbac.cjs +544 -0
  10. package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
  11. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  12. package/audit-tool/audits/09-operations.cjs +208 -0
  13. package/audit-tool/index.cjs +291 -0
  14. package/audit-tool/utils/code-utils.cjs +218 -0
  15. package/audit-tool/utils/file-utils.cjs +230 -0
  16. package/audit-tool/utils/report-utils.cjs +241 -0
  17. package/core-usage-manifest.json +93 -0
  18. package/cursor-rules/00-standards-overview.mdc +156 -0
  19. package/cursor-rules/01-pace-core-compliance.mdc +586 -0
  20. package/cursor-rules/02-project-structure.mdc +42 -4
  21. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
  22. package/cursor-rules/04-code-quality.mdc +419 -0
  23. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
  24. package/cursor-rules/06-security-rbac.mdc +518 -0
  25. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  26. package/cursor-rules/08-testing-documentation.mdc +324 -0
  27. package/cursor-rules/09-operations.mdc +365 -0
  28. package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
  29. package/dist/DataTable-7PMH7XN7.js +15 -0
  30. package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  31. package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
  32. package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
  33. package/dist/api-Y4MQWOFW.js +4 -0
  34. package/dist/audit-MYQXYZFU.js +3 -0
  35. package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
  36. package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
  37. package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
  38. package/dist/chunk-6F3IILHI.js +62 -0
  39. package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
  40. package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
  41. package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
  42. package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
  43. package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
  44. package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
  45. package/dist/chunk-C7NSAPTL.js +1 -0
  46. package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
  47. package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
  48. package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
  49. package/dist/chunk-GHYHJTYV.js +994 -0
  50. package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
  51. package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
  52. package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
  53. package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
  54. package/dist/chunk-MBADTM7L.js +64 -0
  55. package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
  56. package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
  57. package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
  58. package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
  59. package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
  60. package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
  61. package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
  62. package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
  63. package/dist/components.d.ts +7 -5
  64. package/dist/components.js +46 -257
  65. package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
  66. package/dist/eslint-rules/index.cjs +35 -0
  67. package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
  68. package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
  69. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  70. package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
  71. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
  72. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  73. package/dist/eslint-rules/utils/helpers.cjs +42 -0
  74. package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
  75. package/dist/hooks.d.ts +6 -6
  76. package/dist/hooks.js +62 -172
  77. package/dist/icons/index.d.ts +1 -0
  78. package/dist/icons/index.js +1 -0
  79. package/dist/index.d.ts +12 -11
  80. package/dist/index.js +67 -660
  81. package/dist/providers.d.ts +2 -2
  82. package/dist/providers.js +8 -35
  83. package/dist/rbac/eslint-rules.d.ts +46 -44
  84. package/dist/rbac/eslint-rules.js +7 -4
  85. package/dist/rbac/index.d.ts +109 -586
  86. package/dist/rbac/index.js +14 -207
  87. package/dist/styles/index.js +2 -12
  88. package/dist/theming/runtime.d.ts +14 -1
  89. package/dist/theming/runtime.js +3 -19
  90. package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
  91. package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
  92. package/dist/types-t9H8qKRw.d.ts +55 -0
  93. package/dist/types.d.ts +1 -1
  94. package/dist/types.js +7 -94
  95. package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
  96. package/dist/utils.d.ts +24 -117
  97. package/dist/utils.js +54 -392
  98. package/docs/README.md +17 -7
  99. package/docs/api/README.md +4 -402
  100. package/docs/api/modules.md +301 -871
  101. package/docs/api-reference/components.md +21 -21
  102. package/docs/api-reference/deprecated.md +31 -6
  103. package/docs/api-reference/hooks.md +80 -80
  104. package/docs/api-reference/rpc-functions.md +78 -3
  105. package/docs/api-reference/types.md +1 -1
  106. package/docs/api-reference/utilities.md +1 -1
  107. package/docs/architecture/README.md +1 -1
  108. package/docs/core-concepts/events.md +3 -3
  109. package/docs/core-concepts/organisations.md +6 -6
  110. package/docs/core-concepts/permissions.md +6 -6
  111. package/docs/documentation-index.md +12 -18
  112. package/docs/getting-started/cursor-rules.md +3 -23
  113. package/docs/getting-started/dependencies.md +650 -0
  114. package/docs/getting-started/documentation-index.md +1 -1
  115. package/docs/getting-started/examples/README.md +4 -4
  116. package/docs/getting-started/examples/full-featured-app.md +1 -1
  117. package/docs/getting-started/faq.md +2 -2
  118. package/docs/getting-started/installation-guide.md +20 -7
  119. package/docs/getting-started/quick-reference.md +4 -4
  120. package/docs/getting-started/quick-start.md +23 -12
  121. package/docs/implementation-guides/authentication.md +15 -15
  122. package/docs/implementation-guides/component-styling.md +1 -1
  123. package/docs/implementation-guides/data-tables.md +126 -33
  124. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  125. package/docs/implementation-guides/dynamic-colors.md +3 -3
  126. package/docs/implementation-guides/file-upload-storage.md +2 -2
  127. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  128. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  129. package/docs/implementation-guides/large-datasets.md +3 -2
  130. package/docs/implementation-guides/organisation-security.md +2 -2
  131. package/docs/implementation-guides/performance.md +2 -2
  132. package/docs/implementation-guides/permission-enforcement.md +5 -1
  133. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  134. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  135. package/docs/rbac/MIGRATION_GUIDE.md +819 -0
  136. package/docs/rbac/RBAC_CONTRACT.md +724 -0
  137. package/docs/rbac/README.md +17 -8
  138. package/docs/rbac/advanced-patterns.md +6 -6
  139. package/docs/rbac/api-reference.md +20 -20
  140. package/docs/rbac/edge-functions-guide.md +376 -0
  141. package/docs/rbac/event-based-apps.md +3 -3
  142. package/docs/rbac/examples.md +41 -41
  143. package/docs/rbac/getting-started.md +37 -37
  144. package/docs/rbac/performance.md +1 -1
  145. package/docs/rbac/quick-start.md +52 -52
  146. package/docs/rbac/secure-client-protection.md +1 -35
  147. package/docs/rbac/troubleshooting.md +1 -1
  148. package/docs/security/README.md +5 -5
  149. package/docs/standards/0-standards-overview.md +220 -0
  150. package/docs/standards/1-pace-core-compliance-standards.md +986 -0
  151. package/docs/standards/2-project-structure-standards.md +949 -0
  152. package/docs/standards/3-architecture-standards.md +606 -0
  153. package/docs/standards/4-code-quality-standards.md +728 -0
  154. package/docs/standards/5-styling-standards.md +348 -0
  155. package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
  156. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  157. package/docs/standards/8-testing-documentation-standards.md +401 -0
  158. package/docs/standards/9-operations-standards.md +1102 -0
  159. package/docs/standards/README.md +185 -57
  160. package/docs/troubleshooting/README.md +4 -4
  161. package/docs/troubleshooting/common-issues.md +2 -2
  162. package/docs/troubleshooting/debugging.md +9 -9
  163. package/docs/troubleshooting/migration.md +4 -4
  164. package/docs/troubleshooting/organisation-context-setup.md +42 -19
  165. package/eslint-config-pace-core.cjs +33 -6
  166. package/package.json +35 -23
  167. package/scripts/install-cursor-rules.cjs +25 -6
  168. package/scripts/install-eslint-config.cjs +284 -0
  169. package/src/__tests__/fixtures/supabase.ts +1 -1
  170. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
  171. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
  172. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
  173. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  174. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
  175. package/src/__tests__/helpers/component-test-utils.tsx +1 -1
  176. package/src/__tests__/helpers/supabaseMock.ts +2 -2
  177. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  178. package/src/__tests__/public-recipe-view.test.ts +38 -9
  179. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  180. package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
  181. package/src/__tests__/templates/component.test.template.tsx +18 -15
  182. package/src/components/Button/Button.tsx +5 -1
  183. package/src/components/Calendar/Calendar.tsx +201 -47
  184. package/src/components/ContextSelector/ContextSelector.tsx +106 -119
  185. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  186. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
  187. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
  188. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  189. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  190. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  191. package/src/components/DataTable/components/DataTableCore.tsx +186 -13
  192. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  193. package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
  194. package/src/components/DataTable/components/EditFields.tsx +23 -3
  195. package/src/components/DataTable/components/EditableRow.tsx +12 -9
  196. package/src/components/DataTable/components/EmptyState.tsx +10 -9
  197. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  198. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  199. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  200. package/src/components/DataTable/components/RowComponent.tsx +12 -0
  201. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  202. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  203. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  204. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  205. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  206. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  207. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
  208. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
  209. package/src/components/DataTable/components/index.ts +2 -1
  210. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
  211. package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
  212. package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
  213. package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
  214. package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
  215. package/src/components/DataTable/types.ts +5 -18
  216. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  217. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
  218. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  219. package/src/components/DateTimeField/DateTimeField.tsx +10 -9
  220. package/src/components/Dialog/Dialog.test.tsx +128 -104
  221. package/src/components/Dialog/Dialog.tsx +742 -24
  222. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  223. package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
  224. package/src/components/FileDisplay/FileDisplay.tsx +23 -17
  225. package/src/components/FileUpload/FileUpload.test.tsx +52 -14
  226. package/src/components/FileUpload/FileUpload.tsx +112 -130
  227. package/src/components/Form/Form.test.tsx +6 -8
  228. package/src/components/Form/Form.tsx +365 -4
  229. package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
  230. package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
  231. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
  232. package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
  233. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
  234. package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
  235. package/src/components/Progress/Progress.tsx +2 -4
  236. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  237. package/src/components/Select/Select.tsx +109 -98
  238. package/src/components/Select/types.ts +4 -1
  239. package/src/components/UserMenu/UserMenu.tsx +9 -6
  240. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  241. package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
  242. package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
  243. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
  244. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
  245. package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
  246. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
  247. package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
  248. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
  249. package/src/hooks/public/usePublicEvent.ts +67 -195
  250. package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
  251. package/src/hooks/public/usePublicEventLogo.ts +24 -14
  252. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  253. package/src/hooks/public/usePublicRouteParams.ts +5 -5
  254. package/src/hooks/useAppConfig.ts +28 -26
  255. package/src/hooks/useEventTheme.test.ts +217 -239
  256. package/src/hooks/useEventTheme.ts +16 -28
  257. package/src/hooks/useFileDisplay.ts +2 -2
  258. package/src/hooks/useOrganisationPermissions.ts +5 -7
  259. package/src/hooks/useQueryCache.ts +0 -1
  260. package/src/hooks/useSessionDraft.ts +380 -0
  261. package/src/hooks/useSessionRestoration.ts +3 -1
  262. package/src/icons/index.ts +27 -0
  263. package/src/index.ts +5 -0
  264. package/src/providers/OrganisationProvider.tsx +23 -14
  265. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  266. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  267. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  268. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  269. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  270. package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
  271. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  272. package/src/providers/services/EventServiceProvider.tsx +1 -24
  273. package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
  274. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  275. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
  276. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
  277. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
  278. package/src/rbac/adapters.tsx +7 -295
  279. package/src/rbac/api.test.ts +44 -56
  280. package/src/rbac/api.ts +10 -17
  281. package/src/rbac/cache-invalidation.ts +0 -1
  282. package/src/rbac/compliance/index.ts +10 -0
  283. package/src/rbac/compliance/pattern-detector.ts +553 -0
  284. package/src/rbac/compliance/runtime-compliance.ts +22 -0
  285. package/src/rbac/components/AccessDenied.tsx +150 -0
  286. package/src/rbac/components/NavigationGuard.tsx +12 -20
  287. package/src/rbac/components/PagePermissionGuard.tsx +4 -24
  288. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
  289. package/src/rbac/components/index.ts +3 -41
  290. package/src/rbac/eslint-rules.js +1 -1
  291. package/src/rbac/hooks/index.ts +0 -3
  292. package/src/rbac/hooks/permissions/index.ts +0 -3
  293. package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
  294. package/src/rbac/hooks/usePermissions.ts +0 -3
  295. package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
  296. package/src/rbac/hooks/useResolvedScope.ts +58 -140
  297. package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
  298. package/src/rbac/hooks/useResourcePermissions.ts +139 -48
  299. package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
  300. package/src/rbac/hooks/useRoleManagement.ts +147 -19
  301. package/src/rbac/hooks/useSecureSupabase.ts +4 -8
  302. package/src/rbac/index.ts +7 -9
  303. package/src/rbac/utils/contextValidator.ts +9 -7
  304. package/src/services/AuthService.ts +130 -18
  305. package/src/services/EventService.ts +4 -97
  306. package/src/services/InactivityService.ts +16 -0
  307. package/src/services/OrganisationService.ts +7 -44
  308. package/src/services/__tests__/OrganisationService.test.ts +26 -8
  309. package/src/services/base/BaseService.ts +0 -3
  310. package/src/styles/core.css +7 -0
  311. package/src/theming/__tests__/parseEventColours.test.ts +9 -3
  312. package/src/theming/parseEventColours.ts +22 -10
  313. package/src/types/database.generated.ts +4733 -3809
  314. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  315. package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
  316. package/src/utils/context/organisationContext.test.ts +13 -28
  317. package/src/utils/context/organisationContext.ts +21 -52
  318. package/src/utils/dynamic/dynamicUtils.ts +1 -1
  319. package/src/utils/file-reference/index.ts +39 -15
  320. package/src/utils/formatting/formatDateTime.test.ts +3 -2
  321. package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
  322. package/src/utils/index.ts +4 -1
  323. package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
  324. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
  325. package/src/utils/persistence/keyDerivation.ts +304 -0
  326. package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
  327. package/src/utils/security/secureStorage.ts +5 -5
  328. package/src/utils/storage/README.md +1 -1
  329. package/src/utils/storage/helpers.ts +3 -3
  330. package/src/utils/supabase/createBaseClient.ts +147 -0
  331. package/src/utils/timezone/timezone.test.ts +1 -2
  332. package/src/utils/timezone/timezone.ts +1 -1
  333. package/src/utils/validation/csrf.ts +4 -4
  334. package/cursor-rules/00-pace-core-compliance.mdc +0 -331
  335. package/cursor-rules/01-standards-compliance.mdc +0 -244
  336. package/cursor-rules/04-testing-standards.mdc +0 -268
  337. package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
  338. package/cursor-rules/06-code-quality.mdc +0 -309
  339. package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
  340. package/cursor-rules/CHANGELOG.md +0 -119
  341. package/cursor-rules/README.md +0 -192
  342. package/dist/DataTable-AOVNCPTX.js +0 -175
  343. package/dist/DataTable-AOVNCPTX.js.map +0 -1
  344. package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
  345. package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
  346. package/dist/api-O6HTBX5Y.js +0 -52
  347. package/dist/api-O6HTBX5Y.js.map +0 -1
  348. package/dist/audit-V53FV5AG.js +0 -17
  349. package/dist/audit-V53FV5AG.js.map +0 -1
  350. package/dist/chunk-5DRSZLL2.js.map +0 -1
  351. package/dist/chunk-63FOKYGO.js.map +0 -1
  352. package/dist/chunk-6COVEUS7.js.map +0 -1
  353. package/dist/chunk-AFVQODI2.js +0 -263
  354. package/dist/chunk-AFVQODI2.js.map +0 -1
  355. package/dist/chunk-DGUM43GV.js.map +0 -1
  356. package/dist/chunk-E66EQZE6.js.map +0 -1
  357. package/dist/chunk-EFN2EIMK.js.map +0 -1
  358. package/dist/chunk-FFQEQTNW.js.map +0 -1
  359. package/dist/chunk-FMUCXFII.js.map +0 -1
  360. package/dist/chunk-G37KK66H.js.map +0 -1
  361. package/dist/chunk-G7QEZTYQ.js +0 -2053
  362. package/dist/chunk-G7QEZTYQ.js.map +0 -1
  363. package/dist/chunk-HU2C6SSC.js.map +0 -1
  364. package/dist/chunk-IHB5DR3H.js.map +0 -1
  365. package/dist/chunk-IVOFDYWT.js.map +0 -1
  366. package/dist/chunk-J36DSWQK.js.map +0 -1
  367. package/dist/chunk-JGRYX5UX.js.map +0 -1
  368. package/dist/chunk-KQCRWDSA.js +0 -1
  369. package/dist/chunk-KQCRWDSA.js.map +0 -1
  370. package/dist/chunk-L4OXEN46.js.map +0 -1
  371. package/dist/chunk-LMC26NLJ.js +0 -84
  372. package/dist/chunk-LMC26NLJ.js.map +0 -1
  373. package/dist/chunk-M43Y4SSO.js.map +0 -1
  374. package/dist/chunk-M7MPQISP.js.map +0 -1
  375. package/dist/chunk-NTM7ZSB6.js.map +0 -1
  376. package/dist/chunk-PWLANIRT.js.map +0 -1
  377. package/dist/chunk-QXHPKYJV.js.map +0 -1
  378. package/dist/chunk-RGAWHO7N.js.map +0 -1
  379. package/dist/chunk-UPPMRMYG.js.map +0 -1
  380. package/dist/chunk-VBXEHIUJ.js.map +0 -1
  381. package/dist/chunk-ZSAAAMVR.js.map +0 -1
  382. package/dist/components.js.map +0 -1
  383. package/dist/contextValidator-5OGXSPKS.js +0 -9
  384. package/dist/contextValidator-5OGXSPKS.js.map +0 -1
  385. package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
  386. package/dist/hooks.js.map +0 -1
  387. package/dist/index.js.map +0 -1
  388. package/dist/providers.js.map +0 -1
  389. package/dist/rbac/eslint-rules.js.map +0 -1
  390. package/dist/rbac/index.js.map +0 -1
  391. package/dist/styles/index.js.map +0 -1
  392. package/dist/theming/runtime.js.map +0 -1
  393. package/dist/types.js.map +0 -1
  394. package/dist/utils.js.map +0 -1
  395. package/docs/best-practices/README.md +0 -472
  396. package/docs/best-practices/accessibility.md +0 -601
  397. package/docs/best-practices/common-patterns.md +0 -516
  398. package/docs/best-practices/deployment.md +0 -1103
  399. package/docs/best-practices/performance.md +0 -1328
  400. package/docs/best-practices/security.md +0 -940
  401. package/docs/best-practices/testing.md +0 -1034
  402. package/docs/rbac/compliance/compliance-guide.md +0 -544
  403. package/docs/standards/01-architecture-standard.md +0 -44
  404. package/docs/standards/02-api-and-rpc-standard.md +0 -39
  405. package/docs/standards/03-component-standard.md +0 -32
  406. package/docs/standards/04-code-style-standard.md +0 -32
  407. package/docs/standards/05-security-standard.md +0 -44
  408. package/docs/standards/06-testing-and-docs-standard.md +0 -29
  409. package/docs/standards/pace-core-compliance.md +0 -432
  410. package/scripts/audit/core/checks/accessibility.cjs +0 -197
  411. package/scripts/audit/core/checks/api-usage.cjs +0 -191
  412. package/scripts/audit/core/checks/bundle.cjs +0 -142
  413. package/scripts/audit/core/checks/compliance.cjs +0 -2706
  414. package/scripts/audit/core/checks/config.cjs +0 -54
  415. package/scripts/audit/core/checks/coverage.cjs +0 -84
  416. package/scripts/audit/core/checks/dependencies.cjs +0 -994
  417. package/scripts/audit/core/checks/documentation.cjs +0 -268
  418. package/scripts/audit/core/checks/environment.cjs +0 -116
  419. package/scripts/audit/core/checks/error-handling.cjs +0 -340
  420. package/scripts/audit/core/checks/forms.cjs +0 -172
  421. package/scripts/audit/core/checks/heuristics.cjs +0 -68
  422. package/scripts/audit/core/checks/hooks.cjs +0 -334
  423. package/scripts/audit/core/checks/imports.cjs +0 -244
  424. package/scripts/audit/core/checks/performance.cjs +0 -325
  425. package/scripts/audit/core/checks/routes.cjs +0 -117
  426. package/scripts/audit/core/checks/state.cjs +0 -130
  427. package/scripts/audit/core/checks/structure.cjs +0 -65
  428. package/scripts/audit/core/checks/style.cjs +0 -584
  429. package/scripts/audit/core/checks/testing.cjs +0 -122
  430. package/scripts/audit/core/checks/typescript.cjs +0 -61
  431. package/scripts/audit/core/scanner.cjs +0 -199
  432. package/scripts/audit/core/utils.cjs +0 -137
  433. package/scripts/audit/index.cjs +0 -223
  434. package/scripts/audit/reporters/console.cjs +0 -151
  435. package/scripts/audit/reporters/json.cjs +0 -54
  436. package/scripts/audit/reporters/markdown.cjs +0 -124
  437. package/scripts/audit-consuming-app.cjs +0 -86
  438. package/src/components/DataTable/components/DataTableBody.tsx +0 -454
  439. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  440. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  441. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  442. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  443. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  444. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  445. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  446. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  447. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  448. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  449. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  450. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  451. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  452. package/src/components/DataTable/utils/debugTools.ts +0 -514
  453. package/src/eslint-rules/pace-core-compliance.js +0 -638
  454. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
  455. package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
  456. package/src/rbac/components/NavigationProvider.test.tsx +0 -481
  457. package/src/rbac/components/NavigationProvider.tsx +0 -345
  458. package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
  459. package/src/rbac/components/PagePermissionProvider.tsx +0 -279
  460. package/src/rbac/components/PermissionEnforcer.tsx +0 -312
  461. package/src/rbac/components/RoleBasedRouter.tsx +0 -440
  462. package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
  463. package/src/rbac/components/SecureDataProvider.tsx +0 -339
  464. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
  465. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
  466. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
  467. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
  468. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
  469. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
  470. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
  471. package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
  472. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
  473. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
@@ -10,6 +10,10 @@ import { renderHook, act } from '@testing-library/react';
10
10
  import { useDataTableState, dataTableReducer } from '../useDataTableState';
11
11
  import type { DataRecord, SortingState, ColumnFiltersState, VisibilityState, GroupingState, ExpandedState, PaginationState } from '../../types';
12
12
 
13
+ // Following testing standards: use timeout parameter to prevent hanging
14
+ // See: packages/core/docs/standards/04-testing-standards.md
15
+ const TEST_TIMEOUT = 10000; // 10 seconds per test
16
+
13
17
  interface TestData extends DataRecord {
14
18
  id: string;
15
19
  name: string;
@@ -51,7 +55,7 @@ describe('[unit] dataTableReducer', () => {
51
55
 
52
56
  expect(result.sorting).toEqual(newSorting);
53
57
  expect(result).toEqual({ ...initialState, sorting: newSorting });
54
- });
58
+ }, TEST_TIMEOUT);
55
59
 
56
60
  it('handles SET_COLUMN_FILTERS action', () => {
57
61
  const newFilters: ColumnFiltersState = [{ id: 'name', value: 'John' }];
@@ -60,7 +64,7 @@ describe('[unit] dataTableReducer', () => {
60
64
  const result = dataTableReducer(initialState, action);
61
65
 
62
66
  expect(result.columnFilters).toEqual(newFilters);
63
- });
67
+ }, TEST_TIMEOUT);
64
68
 
65
69
  it('handles SET_COLUMN_VISIBILITY action', () => {
66
70
  const newVisibility: VisibilityState = { name: false, email: true };
@@ -69,7 +73,7 @@ describe('[unit] dataTableReducer', () => {
69
73
  const result = dataTableReducer(initialState, action);
70
74
 
71
75
  expect(result.columnVisibility).toEqual(newVisibility);
72
- });
76
+ }, TEST_TIMEOUT);
73
77
 
74
78
  it('handles SET_GROUPING action', () => {
75
79
  const newGrouping: GroupingState = ['name'];
@@ -78,7 +82,7 @@ describe('[unit] dataTableReducer', () => {
78
82
  const result = dataTableReducer(initialState, action);
79
83
 
80
84
  expect(result.grouping).toEqual(newGrouping);
81
- });
85
+ }, TEST_TIMEOUT);
82
86
 
83
87
  it('handles SET_EXPANDED action', () => {
84
88
  const newExpanded: ExpandedState = { '1': true };
@@ -87,7 +91,7 @@ describe('[unit] dataTableReducer', () => {
87
91
  const result = dataTableReducer(initialState, action);
88
92
 
89
93
  expect(result.expanded).toEqual(newExpanded);
90
- });
94
+ }, TEST_TIMEOUT);
91
95
 
92
96
  it('handles SET_PAGINATION action', () => {
93
97
  const newPagination: PaginationState = { pageIndex: 2, pageSize: 25 };
@@ -96,7 +100,7 @@ describe('[unit] dataTableReducer', () => {
96
100
  const result = dataTableReducer(initialState, action);
97
101
 
98
102
  expect(result.pagination).toEqual(newPagination);
99
- });
103
+ }, TEST_TIMEOUT);
100
104
 
101
105
  it('handles SET_COLUMN_ORDER action', () => {
102
106
  const newOrder = ['name', 'id', 'email'];
@@ -105,7 +109,7 @@ describe('[unit] dataTableReducer', () => {
105
109
  const result = dataTableReducer(initialState, action);
106
110
 
107
111
  expect(result.columnOrder).toEqual(newOrder);
108
- });
112
+ }, TEST_TIMEOUT);
109
113
 
110
114
  it('handles SET_ROW_SELECTION action', () => {
111
115
  const newSelection = { '1': true, '2': false };
@@ -114,7 +118,7 @@ describe('[unit] dataTableReducer', () => {
114
118
  const result = dataTableReducer(initialState, action);
115
119
 
116
120
  expect(result.rowSelection).toEqual(newSelection);
117
- });
121
+ }, TEST_TIMEOUT);
118
122
 
119
123
  it('handles TOGGLE_ROW_SELECTION action', () => {
120
124
  const initialStateWithSelection = { ...initialState, rowSelection: { '1': true } };
@@ -123,7 +127,7 @@ describe('[unit] dataTableReducer', () => {
123
127
  const result = dataTableReducer(initialStateWithSelection, action);
124
128
 
125
129
  expect(result.rowSelection).toEqual({ '1': true, '2': true });
126
- });
130
+ }, TEST_TIMEOUT);
127
131
 
128
132
  it('handles CLEAR_ROW_SELECTION action', () => {
129
133
  const initialStateWithSelection = { ...initialState, rowSelection: { '1': true, '2': true } };
@@ -132,7 +136,7 @@ describe('[unit] dataTableReducer', () => {
132
136
  const result = dataTableReducer(initialStateWithSelection, action);
133
137
 
134
138
  expect(result.rowSelection).toEqual({});
135
- });
139
+ }, TEST_TIMEOUT);
136
140
 
137
141
  it('handles SET_CREATING action', () => {
138
142
  const action = { type: 'SET_CREATING' as const, payload: true };
@@ -140,7 +144,7 @@ describe('[unit] dataTableReducer', () => {
140
144
  const result = dataTableReducer(initialState, action);
141
145
 
142
146
  expect(result.isCreating).toBe(true);
143
- });
147
+ }, TEST_TIMEOUT);
144
148
 
145
149
  it('handles SET_CREATION_DATA action', () => {
146
150
  const creationData = { name: 'New User', email: 'new@example.com' };
@@ -149,7 +153,7 @@ describe('[unit] dataTableReducer', () => {
149
153
  const result = dataTableReducer(initialState, action);
150
154
 
151
155
  expect(result.creationData).toEqual(creationData);
152
- });
156
+ }, TEST_TIMEOUT);
153
157
 
154
158
  it('handles CLEAR_CREATION_DATA action', () => {
155
159
  const initialStateWithCreationData = { ...initialState, creationData: { name: 'Test' } };
@@ -158,7 +162,7 @@ describe('[unit] dataTableReducer', () => {
158
162
  const result = dataTableReducer(initialStateWithCreationData, action);
159
163
 
160
164
  expect(result.creationData).toEqual({});
161
- });
165
+ }, TEST_TIMEOUT);
162
166
 
163
167
  it('handles SET_EDITING_ROW action', () => {
164
168
  const editingData = { rowId: '1', data: { name: 'Updated Name' } };
@@ -168,7 +172,7 @@ describe('[unit] dataTableReducer', () => {
168
172
 
169
173
  expect(result.editingRowId).toBe('1');
170
174
  expect(result.editingData).toEqual({ name: 'Updated Name' });
171
- });
175
+ }, TEST_TIMEOUT);
172
176
 
173
177
  it('handles CLEAR_EDITING action', () => {
174
178
  const initialStateWithEditing = {
@@ -182,7 +186,7 @@ describe('[unit] dataTableReducer', () => {
182
186
 
183
187
  expect(result.editingRowId).toBe(null);
184
188
  expect(result.editingData).toEqual({});
185
- });
189
+ }, TEST_TIMEOUT);
186
190
 
187
191
  it('handles SET_IMPORT_MODAL action', () => {
188
192
  const action = { type: 'SET_IMPORT_MODAL' as const, payload: true };
@@ -190,7 +194,7 @@ describe('[unit] dataTableReducer', () => {
190
194
  const result = dataTableReducer(initialState, action);
191
195
 
192
196
  expect(result.showImportModal).toBe(true);
193
- });
197
+ }, TEST_TIMEOUT);
194
198
 
195
199
  it('handles SET_FILTER_ROW action', () => {
196
200
  const action = { type: 'SET_FILTER_ROW' as const, payload: true };
@@ -198,7 +202,7 @@ describe('[unit] dataTableReducer', () => {
198
202
  const result = dataTableReducer(initialState, action);
199
203
 
200
204
  expect(result.showFilterRow).toBe(true);
201
- });
205
+ }, TEST_TIMEOUT);
202
206
 
203
207
  it('handles SET_SEARCH_QUERY action', () => {
204
208
  const action = { type: 'SET_SEARCH_QUERY' as const, payload: 'search term' };
@@ -206,7 +210,7 @@ describe('[unit] dataTableReducer', () => {
206
210
  const result = dataTableReducer(initialState, action);
207
211
 
208
212
  expect(result.searchQuery).toBe('search term');
209
- });
213
+ }, TEST_TIMEOUT);
210
214
 
211
215
  it('handles RESET_STATE action', () => {
212
216
  const modifiedState = {
@@ -229,7 +233,7 @@ describe('[unit] dataTableReducer', () => {
229
233
  expect(result.rowSelection).toEqual({});
230
234
  expect(result.isCreating).toBe(false);
231
235
  expect(result.searchQuery).toBe('');
232
- });
236
+ }, TEST_TIMEOUT);
233
237
 
234
238
  it('handles INITIALIZE_STATE action', () => {
235
239
  const partialState = {
@@ -244,7 +248,7 @@ describe('[unit] dataTableReducer', () => {
244
248
  expect(result.sorting).toEqual(partialState.sorting);
245
249
  expect(result.searchQuery).toBe(partialState.searchQuery);
246
250
  expect(result.columnFilters).toEqual(initialState.columnFilters); // Should remain unchanged
247
- });
251
+ }, TEST_TIMEOUT);
248
252
 
249
253
  it('returns same state for unknown action', () => {
250
254
  const unknownAction = { type: 'UNKNOWN_ACTION' as any, payload: null };
@@ -252,7 +256,7 @@ describe('[unit] dataTableReducer', () => {
252
256
  const result = dataTableReducer(initialState, unknownAction);
253
257
 
254
258
  expect(result).toBe(initialState);
255
- });
259
+ }, TEST_TIMEOUT);
256
260
  });
257
261
 
258
262
  describe('[unit] useDataTableState', () => {
@@ -274,7 +278,7 @@ describe('[unit] useDataTableState', () => {
274
278
  expect(result.current.state.showImportModal).toBe(false);
275
279
  expect(result.current.state.showFilterRow).toBe(false);
276
280
  expect(result.current.state.searchQuery).toBe('');
277
- });
281
+ }, TEST_TIMEOUT);
278
282
 
279
283
  it('initializes with custom options', () => {
280
284
  const options = {
@@ -288,7 +292,7 @@ describe('[unit] useDataTableState', () => {
288
292
  expect(result.current.state.pagination.pageSize).toBe(25);
289
293
  expect(result.current.state.columnOrder).toEqual(['id', 'name', 'email']);
290
294
  expect(result.current.state.rowSelection).toEqual({ '1': true });
291
- });
295
+ }, TEST_TIMEOUT);
292
296
 
293
297
  it('provides action creators', () => {
294
298
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -312,7 +316,7 @@ describe('[unit] useDataTableState', () => {
312
316
  expect(typeof result.current.actions.setFilterRow).toBe('function');
313
317
  expect(typeof result.current.actions.setSearchQuery).toBe('function');
314
318
  expect(typeof result.current.actions.resetState).toBe('function');
315
- });
319
+ }, TEST_TIMEOUT);
316
320
 
317
321
  it('updates state when actions are called', () => {
318
322
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -322,7 +326,7 @@ describe('[unit] useDataTableState', () => {
322
326
  });
323
327
 
324
328
  expect(result.current.state.sorting).toEqual([{ id: 'name', desc: false }]);
325
- });
329
+ }, TEST_TIMEOUT);
326
330
 
327
331
  it('updates column filters', () => {
328
332
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -332,7 +336,7 @@ describe('[unit] useDataTableState', () => {
332
336
  });
333
337
 
334
338
  expect(result.current.state.columnFilters).toEqual([{ id: 'name', value: 'John' }]);
335
- });
339
+ }, TEST_TIMEOUT);
336
340
 
337
341
  it('updates column visibility', () => {
338
342
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -342,7 +346,7 @@ describe('[unit] useDataTableState', () => {
342
346
  });
343
347
 
344
348
  expect(result.current.state.columnVisibility).toEqual({ name: false, email: true });
345
- });
349
+ }, TEST_TIMEOUT);
346
350
 
347
351
  it('updates grouping', () => {
348
352
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -352,7 +356,7 @@ describe('[unit] useDataTableState', () => {
352
356
  });
353
357
 
354
358
  expect(result.current.state.grouping).toEqual(['name']);
355
- });
359
+ }, TEST_TIMEOUT);
356
360
 
357
361
  it('updates expanded state', () => {
358
362
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -362,7 +366,7 @@ describe('[unit] useDataTableState', () => {
362
366
  });
363
367
 
364
368
  expect(result.current.state.expanded).toEqual({ '1': true });
365
- });
369
+ }, TEST_TIMEOUT);
366
370
 
367
371
  it('updates pagination', () => {
368
372
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -372,7 +376,7 @@ describe('[unit] useDataTableState', () => {
372
376
  });
373
377
 
374
378
  expect(result.current.state.pagination).toEqual({ pageIndex: 2, pageSize: 25 });
375
- });
379
+ }, TEST_TIMEOUT);
376
380
 
377
381
  it('updates column order', () => {
378
382
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -382,7 +386,7 @@ describe('[unit] useDataTableState', () => {
382
386
  });
383
387
 
384
388
  expect(result.current.state.columnOrder).toEqual(['name', 'id', 'email']);
385
- });
389
+ }, TEST_TIMEOUT);
386
390
 
387
391
  it('updates row selection', () => {
388
392
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -392,7 +396,7 @@ describe('[unit] useDataTableState', () => {
392
396
  });
393
397
 
394
398
  expect(result.current.state.rowSelection).toEqual({ '1': true, '2': false });
395
- });
399
+ }, TEST_TIMEOUT);
396
400
 
397
401
  it('toggles row selection', () => {
398
402
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -408,7 +412,7 @@ describe('[unit] useDataTableState', () => {
408
412
  });
409
413
 
410
414
  expect(result.current.state.rowSelection).toEqual({ '1': false });
411
- });
415
+ }, TEST_TIMEOUT);
412
416
 
413
417
  it('clears row selection', () => {
414
418
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -424,7 +428,7 @@ describe('[unit] useDataTableState', () => {
424
428
  });
425
429
 
426
430
  expect(result.current.state.rowSelection).toEqual({});
427
- });
431
+ }, TEST_TIMEOUT);
428
432
 
429
433
  it('updates creating state', () => {
430
434
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -434,7 +438,7 @@ describe('[unit] useDataTableState', () => {
434
438
  });
435
439
 
436
440
  expect(result.current.state.isCreating).toBe(true);
437
- });
441
+ }, TEST_TIMEOUT);
438
442
 
439
443
  it('updates creation data', () => {
440
444
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -445,7 +449,7 @@ describe('[unit] useDataTableState', () => {
445
449
  });
446
450
 
447
451
  expect(result.current.state.creationData).toEqual(creationData);
448
- });
452
+ }, TEST_TIMEOUT);
449
453
 
450
454
  it('clears creation data', () => {
451
455
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -461,7 +465,7 @@ describe('[unit] useDataTableState', () => {
461
465
  });
462
466
 
463
467
  expect(result.current.state.creationData).toEqual({});
464
- });
468
+ }, TEST_TIMEOUT);
465
469
 
466
470
  it('updates editing row', () => {
467
471
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -472,7 +476,7 @@ describe('[unit] useDataTableState', () => {
472
476
 
473
477
  expect(result.current.state.editingRowId).toBe('1');
474
478
  expect(result.current.state.editingData).toEqual({ name: 'Updated Name' });
475
- });
479
+ }, TEST_TIMEOUT);
476
480
 
477
481
  it('clears editing state', () => {
478
482
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -489,7 +493,7 @@ describe('[unit] useDataTableState', () => {
489
493
 
490
494
  expect(result.current.state.editingRowId).toBe(null);
491
495
  expect(result.current.state.editingData).toEqual({});
492
- });
496
+ }, TEST_TIMEOUT);
493
497
 
494
498
  it('updates import modal state', () => {
495
499
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -499,7 +503,7 @@ describe('[unit] useDataTableState', () => {
499
503
  });
500
504
 
501
505
  expect(result.current.state.showImportModal).toBe(true);
502
- });
506
+ }, TEST_TIMEOUT);
503
507
 
504
508
  it('updates filter row state', () => {
505
509
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -509,7 +513,7 @@ describe('[unit] useDataTableState', () => {
509
513
  });
510
514
 
511
515
  expect(result.current.state.showFilterRow).toBe(true);
512
- });
516
+ }, TEST_TIMEOUT);
513
517
 
514
518
  it('updates search query', () => {
515
519
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -519,7 +523,7 @@ describe('[unit] useDataTableState', () => {
519
523
  });
520
524
 
521
525
  expect(result.current.state.searchQuery).toBe('search term');
522
- });
526
+ }, TEST_TIMEOUT);
523
527
 
524
528
  it('resets state to initial values', () => {
525
529
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -546,7 +550,7 @@ describe('[unit] useDataTableState', () => {
546
550
  expect(result.current.state.columnFilters).toEqual([]);
547
551
  expect(result.current.state.rowSelection).toEqual({});
548
552
  expect(result.current.state.searchQuery).toBe('');
549
- });
553
+ }, TEST_TIMEOUT);
550
554
 
551
555
  it('calls onRowSelectionChange callback when provided', () => {
552
556
  const onRowSelectionChange = vi.fn();
@@ -559,20 +563,20 @@ describe('[unit] useDataTableState', () => {
559
563
  });
560
564
 
561
565
  expect(onRowSelectionChange).toHaveBeenCalledWith({ '1': true });
562
- });
566
+ }, TEST_TIMEOUT);
563
567
 
564
568
  it('provides computed values', () => {
565
569
  const { result } = renderHook(() => useDataTableState<TestData>());
566
570
 
567
571
  expect(result.current.computed).toBeDefined();
568
572
  expect(typeof result.current.computed).toBe('object');
569
- });
573
+ }, TEST_TIMEOUT);
570
574
 
571
575
  it('provides clearFilters function', () => {
572
576
  const { result } = renderHook(() => useDataTableState<TestData>());
573
577
 
574
578
  expect(typeof result.current.clearFilters).toBe('function');
575
- });
579
+ }, TEST_TIMEOUT);
576
580
 
577
581
  it('maintains state consistency across multiple actions', () => {
578
582
  const { result } = renderHook(() => useDataTableState<TestData>());
@@ -588,5 +592,5 @@ describe('[unit] useDataTableState', () => {
588
592
  expect(result.current.state.columnFilters).toEqual([{ id: 'name', value: 'John' }]);
589
593
  expect(result.current.state.pagination).toEqual({ pageIndex: 1, pageSize: 25 });
590
594
  expect(result.current.state.rowSelection).toEqual({ '1': true, '2': false });
591
- });
595
+ }, TEST_TIMEOUT);
592
596
  });
@@ -100,14 +100,7 @@ export function useDataTablePermissions<TData extends DataRecord>(
100
100
  }, 5000);
101
101
 
102
102
  try {
103
- logger.debug('useDataTablePermissions', 'Starting super admin check', { userId: user?.id });
104
103
  const superAdminStatus = await isSuperAdmin(user.id);
105
- const elapsed = Date.now() - startTime;
106
- logger.debug('useDataTablePermissions', 'Super admin check completed', {
107
- userId: user?.id,
108
- isSuperAdmin: superAdminStatus,
109
- elapsedMs: elapsed,
110
- });
111
104
  setIsSuperAdminUser(superAdminStatus);
112
105
  } catch (error) {
113
106
  const elapsed = Date.now() - startTime;
@@ -159,7 +152,8 @@ export function useDataTablePermissions<TData extends DataRecord>(
159
152
  const { resolvedScope: rawResolvedScope, isLoading: scopeLoading } = useResolvedScope({
160
153
  supabase,
161
154
  selectedOrganisationId: selectedOrganisation?.id || null,
162
- selectedEventId: selectedEvent?.event_id || null
155
+ selectedEventId: selectedEvent?.event_id || null,
156
+ selectedEventOrganisationId: selectedEvent?.organisation_id || null
163
157
  });
164
158
 
165
159
  /**
@@ -277,24 +271,33 @@ export function useDataTablePermissions<TData extends DataRecord>(
277
271
  // to maintain consistent API with useCan return type
278
272
  // React 19 fix: Read isLoading directly from result objects to ensure we get the latest state
279
273
  // instead of relying on memoized object references which may not update properly in React 19
274
+ // CRITICAL FIX: When isSuperAdminUser is true, immediately grant all permissions without waiting
275
+ // for useCan results. This ensures super admins can use DataTable features immediately.
280
276
  const permissions = useMemo(() => {
281
277
  // Helper to create a permission result that bypasses for super admins
282
278
  // If super admin check is still loading, we show loading state to prevent premature "Access Denied"
283
279
  // Once super admin check completes, if user is super admin, all permissions are true
284
280
  // Otherwise, use the normal permission check results
285
- const createSuperAdminAwarePermission = (result: ReturnType<typeof useCan>) => ({
286
- // If super admin check completed and user is super admin, grant all permissions
287
- // and mark loading as complete for DataTable gating purposes.
288
- // This is not a "hack": super admins *semantically* bypass permission checks, so the
289
- // table must not remain blocked behind background permission queries.
290
- can: isSuperAdminUser === true ? true : result.can,
291
- isLoading:
292
- isSuperAdminUser === true
293
- ? false
294
- : (isSuperAdminUser === null && isCheckingSuperAdmin) || result.isLoading,
295
- error: isSuperAdminUser === true ? null : result.error,
296
- refetch: result.refetch,
297
- });
281
+ const createSuperAdminAwarePermission = (result: ReturnType<typeof useCan>) => {
282
+ // CRITICAL: If super admin is confirmed, immediately grant permission
283
+ // Don't wait for useCan results - super admins bypass all checks
284
+ if (isSuperAdminUser === true) {
285
+ return {
286
+ can: true,
287
+ isLoading: false,
288
+ error: null,
289
+ refetch: result.refetch,
290
+ };
291
+ }
292
+
293
+ // For non-super-admins or while checking, use normal permission results
294
+ return {
295
+ can: result.can,
296
+ isLoading: (isSuperAdminUser === null && isCheckingSuperAdmin) || result.isLoading,
297
+ error: result.error,
298
+ refetch: result.refetch,
299
+ };
300
+ };
298
301
 
299
302
  return {
300
303
  canRead: createSuperAdminAwarePermission(canReadResult),
@@ -8,7 +8,7 @@
8
8
  * This replaces the scattered useState calls with a single, predictable state management system.
9
9
  */
10
10
 
11
- import { useReducer, useCallback, useMemo } from 'react';
11
+ import { useReducer, useCallback, useMemo, useEffect } from 'react';
12
12
  import type {
13
13
  SortingState,
14
14
  ColumnFiltersState,
@@ -18,6 +18,10 @@ import type {
18
18
  PaginationState
19
19
  } from '@tanstack/react-table';
20
20
  import type { DataRecord, CellValue, RowId } from '../types';
21
+ import { useSessionDraft } from '../../../hooks/useSessionDraft';
22
+ import { deriveDataTableKey } from '../../../utils/persistence/keyDerivation';
23
+ import { filterSensitiveFields } from '../../../utils/persistence/sensitiveFieldDetection';
24
+ import { useUnifiedAuth } from '../../../providers/services/UnifiedAuthProvider';
21
25
 
22
26
  // Re-export types for external use
23
27
  export type { DataRecord, CellValue, RowId };
@@ -265,6 +269,11 @@ export interface UseDataTableStateOptions<TData extends DataRecord> {
265
269
  onRowSelectionChange?: (selection: Record<string, boolean>) => void;
266
270
  defaultSorting?: SortingState;
267
271
  defaultGrouping?: GroupingState;
272
+ // Persistence options
273
+ rbacPageId?: string;
274
+ title?: string;
275
+ location?: { pathname: string } | null;
276
+ columnFieldNames?: string[]; // Field names for sensitive field filtering
268
277
  }
269
278
 
270
279
  export function useDataTableState<TData extends DataRecord>({
@@ -273,18 +282,78 @@ export function useDataTableState<TData extends DataRecord>({
273
282
  initialRowSelection = {},
274
283
  onRowSelectionChange,
275
284
  defaultSorting,
276
- defaultGrouping
285
+ defaultGrouping,
286
+ rbacPageId,
287
+ title,
288
+ location,
289
+ columnFieldNames = [],
277
290
  }: UseDataTableStateOptions<TData> = {}) {
278
291
 
279
- const initialState = useMemo(() =>
292
+ // Call all hooks unconditionally at the top level
293
+ // Hooks must be called in the same order on every render
294
+ // Get user ID for scoping persistence (prevents data leakage between users)
295
+ // If provider is missing, it will throw - errors should be handled by error boundaries
296
+ const auth = useUnifiedAuth();
297
+ const userId = auth.user?.id || null;
298
+
299
+ const persistenceKey = useMemo(() => {
300
+ return deriveDataTableKey(
301
+ {
302
+ rbacPageId,
303
+ title,
304
+ columnIds,
305
+ },
306
+ location,
307
+ userId
308
+ );
309
+ }, [rbacPageId, title, columnIds, location, userId]);
310
+
311
+ // Create initial state
312
+ const baseInitialState = useMemo(() =>
280
313
  createInitialState<TData>(initialPageSize, columnIds, defaultSorting, defaultGrouping),
281
314
  [initialPageSize, columnIds, defaultSorting, defaultGrouping]
282
315
  );
283
316
 
284
- const [state, dispatch] = useReducer(dataTableReducer<TData>, {
285
- ...initialState,
286
- rowSelection: initialRowSelection
287
- });
317
+ // Define state subset to persist (excluding sensitive fields from editing/creation data)
318
+ type PersistedState = Omit<DataTableState<TData>, 'editingData' | 'creationData'> & {
319
+ editingData: Partial<Record<string, CellValue>>;
320
+ creationData: Partial<Record<string, CellValue>>;
321
+ };
322
+
323
+ // Use session draft for persistence
324
+ const { state: persistedState, setState: setPersistedState, clearDraft } = useSessionDraft<PersistedState>(
325
+ persistenceKey || 'datatable:no-key',
326
+ {
327
+ ...baseInitialState,
328
+ rowSelection: initialRowSelection,
329
+ editingData: {},
330
+ creationData: {},
331
+ } as PersistedState,
332
+ {
333
+ enabled: Boolean(persistenceKey),
334
+ debounceMs: 300,
335
+ }
336
+ );
337
+
338
+ // Merge persisted state with initial state (persisted takes precedence)
339
+ const initialState = useMemo(() => {
340
+ if (!persistenceKey || !persistedState) {
341
+ return {
342
+ ...baseInitialState,
343
+ rowSelection: initialRowSelection,
344
+ };
345
+ }
346
+
347
+ // Merge persisted state with initial state
348
+ return {
349
+ ...baseInitialState,
350
+ ...persistedState,
351
+ // Ensure rowSelection is merged properly
352
+ rowSelection: persistedState.rowSelection || initialRowSelection,
353
+ };
354
+ }, [baseInitialState, persistedState, initialRowSelection, persistenceKey]);
355
+
356
+ const [state, dispatch] = useReducer(dataTableReducer<TData>, initialState);
288
357
 
289
358
  // ============================================================================
290
359
  // ACTION CREATORS
@@ -384,10 +453,57 @@ export function useDataTableState<TData extends DataRecord>({
384
453
  dispatch({ type: 'SET_SEARCH_QUERY', payload: '' });
385
454
  }, []);
386
455
 
456
+ // Persist state changes (debounced)
457
+ useEffect(() => {
458
+ if (!persistenceKey) {
459
+ return;
460
+ }
461
+
462
+ // Filter sensitive fields from editingData and creationData
463
+ const filteredEditingData = columnFieldNames.length > 0
464
+ ? filterSensitiveFields(state.editingData, columnFieldNames)
465
+ : state.editingData;
466
+
467
+ const filteredCreationData = columnFieldNames.length > 0
468
+ ? filterSensitiveFields(state.creationData, columnFieldNames)
469
+ : state.creationData;
470
+
471
+ // Persist state subset
472
+ const stateToPersist: PersistedState = {
473
+ sorting: state.sorting,
474
+ columnFilters: state.columnFilters,
475
+ columnVisibility: state.columnVisibility,
476
+ grouping: state.grouping,
477
+ expanded: state.expanded,
478
+ pagination: state.pagination,
479
+ columnOrder: state.columnOrder,
480
+ rowSelection: state.rowSelection,
481
+ isCreating: state.isCreating,
482
+ creationData: filteredCreationData,
483
+ editingRowId: state.editingRowId,
484
+ editingData: filteredEditingData,
485
+ showImportModal: state.showImportModal,
486
+ showFilterRow: state.showFilterRow,
487
+ searchQuery: state.searchQuery,
488
+ };
489
+
490
+ setPersistedState(stateToPersist);
491
+ }, [
492
+ state,
493
+ persistenceKey,
494
+ columnFieldNames,
495
+ setPersistedState,
496
+ ]);
497
+
498
+ // Enhanced actions - no need to modify, persistence happens automatically via useEffect
499
+ const enhancedActions = actions;
500
+
387
501
  return {
388
502
  state,
389
- actions,
503
+ actions: enhancedActions,
390
504
  computed,
391
- clearFilters
505
+ clearFilters,
506
+ // Expose clearDraft for external use (e.g., on successful submit)
507
+ clearDraft: persistenceKey ? clearDraft : undefined,
392
508
  };
393
509
  }