@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
@@ -392,9 +392,11 @@ describe('DataTable Accessibility', () => {
392
392
  );
393
393
 
394
394
  // When loading, the table might not be rendered, so check for loading state
395
- const loadingElement = screen.getByText('Loading...');
396
- expect(loadingElement).toBeInTheDocument();
397
- expect(loadingElement).toHaveAttribute('aria-live', 'polite');
395
+ // The spinner has role="status" which provides the aria-live region
396
+ const spinner = screen.getByRole('status');
397
+ expect(spinner).toBeInTheDocument();
398
+ // Check that the visible loading text is present
399
+ expect(screen.getByText('Loading...', { selector: 'strong' })).toBeInTheDocument();
398
400
  });
399
401
 
400
402
  it('should not have aria-busy when not loading', async () => {
@@ -682,7 +684,11 @@ describe('DataTable Accessibility', () => {
682
684
  const results = await axe(container, {
683
685
  rules: {
684
686
  // Column visibility button has icon-only design - acceptable pattern
685
- 'button-name': { enabled: false }
687
+ 'button-name': { enabled: false },
688
+ // EmptyState uses Alert which renders as <aside> with role="status" - acceptable pattern for status messages
689
+ 'aria-allowed-role': { enabled: false },
690
+ // EmptyState uses h5 for title - acceptable when used in status messages
691
+ 'heading-order': { enabled: false }
686
692
  }
687
693
  });
688
694
  expect(results).toHaveNoViolations();
@@ -14,20 +14,20 @@ import '@testing-library/jest-dom';
14
14
  import React from 'react';
15
15
 
16
16
  // Mock icon components
17
- const MockEditIcon = ({ className }: { className?: string }) => <div className={className} data-testid="edit-icon">Edit</div>;
18
- const MockDeleteIcon = ({ className }: { className?: string }) => <div className={className} data-testid="delete-icon">Delete</div>;
17
+ const MockEditIcon = ({ className }: { className?: string }) => <span className={className} data-testid="edit-icon">Edit</span>;
18
+ const MockDeleteIcon = ({ className }: { className?: string }) => <span className={className} data-testid="delete-icon">Delete</span>;
19
19
 
20
20
  /**
21
21
  * Common mock implementations for DataTable components
22
22
  */
23
23
  export const mockComponents = {
24
- DataTableContext: vi.fn(({ children }: any) => <div data-testid="data-table-context">{children}</div>),
25
- DataTableToolbar: vi.fn(() => <div data-testid="data-table-toolbar">Toolbar</div>),
26
- DataTableBody: vi.fn(() => <div data-testid="data-table-body">Body</div>),
27
- DataTableModals: vi.fn(() => <div data-testid="data-table-modals">Modals</div>),
28
- PaginationControls: vi.fn(() => <div data-testid="pagination-controls">Pagination</div>),
29
- LoadingState: vi.fn(() => <div data-testid="loading-state">Loading</div>),
30
- DataTableErrorBoundary: vi.fn(({ children }: any) => <div data-testid="error-boundary">{children}</div>),
24
+ DataTableContext: vi.fn(({ children }: any) => <section data-testid="data-table-context">{children}</section>),
25
+ DataTableToolbar: vi.fn(() => <nav data-testid="data-table-toolbar">Toolbar</nav>),
26
+ UnifiedTableBody: vi.fn(() => <main data-testid="unified-table-body">Body</main>),
27
+ DataTableModals: vi.fn(() => <section data-testid="data-table-modals">Modals</section>),
28
+ PaginationControls: vi.fn(() => <nav data-testid="pagination-controls">Pagination</nav>),
29
+ LoadingState: vi.fn(() => <section data-testid="loading-state">Loading</section>),
30
+ DataTableErrorBoundary: vi.fn(({ children }: any) => <section data-testid="error-boundary">{children}</section>),
31
31
  };
32
32
 
33
33
  /**
@@ -1,10 +1,9 @@
1
1
  import React from 'react';
2
2
  import { Input } from '../../Input/Input';
3
3
  import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../../Select/Select';
4
- import { Button } from '../../Button/Button';
5
- import { X, Filter } from 'lucide-react';
6
4
  import type { Column } from '@tanstack/react-table';
7
5
  import { getColumnHeaderText } from '../utils/columnUtils';
6
+ import { Filter } from 'lucide-react';
8
7
 
9
8
  /**
10
9
  * Props for the ColumnFilter component.
@@ -50,80 +49,70 @@ export function ColumnFilter({
50
49
 
51
50
  const hasFilter = columnFilterValue !== undefined && columnFilterValue !== '';
52
51
 
53
- // Get the default placeholder using column header text
52
+ // Get the default placeholder using column header text (for Input components)
54
53
  const defaultPlaceholder = `Filter ${getColumnHeaderText(column)}...`;
55
-
56
- const renderFilterInput = () => {
57
- switch (filterType) {
58
- case 'select':
59
- return (
60
- <Select
61
- value={columnFilterValue as string || ''}
62
- onValueChange={handleFilterChange}
63
- >
64
- <SelectTrigger className="h-8">
65
- <SelectValue placeholder={placeholder || defaultPlaceholder} />
66
- </SelectTrigger>
67
- <SelectContent>
68
- <SelectItem value="">All</SelectItem>
69
- {options.map((option) => (
70
- <SelectItem key={option.value} value={option.value}>
71
- {option.label}
72
- </SelectItem>
73
- ))}
74
- </SelectContent>
75
- </Select>
76
- );
77
-
78
- case 'number':
79
- // Always hide spinner arrows for number filter inputs (cleaner UX)
80
- return (
81
- <Input
82
- type="number"
83
- value={columnFilterValue as string || ''}
84
- onChange={(e) => handleFilterChange(e.target.value ? Number(e.target.value) : undefined)}
85
- placeholder={placeholder || defaultPlaceholder}
86
- className="h-8 datatable-number-no-spinners"
87
- />
88
- );
89
-
90
- case 'date':
91
- return (
92
- <Input
93
- type="date"
94
- value={columnFilterValue as string || ''}
95
- onChange={(e) => handleFilterChange(e.target.value || undefined)}
96
- placeholder={placeholder || defaultPlaceholder}
97
- className="h-8"
98
- />
99
- );
100
-
101
- default: // text
102
- return (
103
- <Input
104
- value={columnFilterValue as string || ''}
105
- onChange={(e) => handleFilterChange(e.target.value || undefined)}
106
- placeholder={placeholder || defaultPlaceholder}
107
- className="h-8"
108
- />
109
- );
110
- }
111
- };
54
+ // For Select components, use just the column name (icon will replace "Filter" text)
55
+ const selectColumnName = `${getColumnHeaderText(column)}...`;
112
56
 
113
57
  return (
114
- <div className="relative flex items-center gap-1">
115
- {renderFilterInput()}
116
- {hasFilter && (
117
- <Button
118
- variant="ghost"
119
- onClick={clearFilter}
120
- >
121
- <X className="size-3" />
122
- </Button>
123
- )}
124
- {hasFilter && (
125
- <div className="absolute -top-1 -right-1 h-2 w-2 bg-main-500 rounded-full" />
126
- )}
127
- </div>
58
+ (() => {
59
+ switch (filterType) {
60
+ case 'select':
61
+ return (
62
+ <Select
63
+ value={columnFilterValue as string || ''}
64
+ onValueChange={handleFilterChange}
65
+ >
66
+ <SelectTrigger className="h-8">
67
+ <SelectValue>
68
+ <Filter className="size-4 inline-block mr-2"/>
69
+ <span className="truncate">{selectColumnName}</span>
70
+ </SelectValue>
71
+ </SelectTrigger>
72
+ <SelectContent>
73
+ <SelectItem value="">All</SelectItem>
74
+ {options.map((option) => (
75
+ <SelectItem key={option.value} value={option.value}>
76
+ {option.label}
77
+ </SelectItem>
78
+ ))}
79
+ </SelectContent>
80
+ </Select>
81
+ );
82
+
83
+ case 'number':
84
+ // Always hide spinner arrows for number filter inputs (cleaner UX)
85
+ return (
86
+ <Input
87
+ type="number"
88
+ value={columnFilterValue as string || ''}
89
+ onChange={(e) => handleFilterChange(e.target.value ? Number(e.target.value) : undefined)}
90
+ placeholder={placeholder || defaultPlaceholder}
91
+ className="h-8 datatable-number-no-spinners"
92
+ />
93
+ );
94
+
95
+ case 'date':
96
+ return (
97
+ <Input
98
+ type="date"
99
+ value={columnFilterValue as string || ''}
100
+ onChange={(e) => handleFilterChange(e.target.value || undefined)}
101
+ placeholder={placeholder || defaultPlaceholder}
102
+ className="h-8"
103
+ />
104
+ );
105
+
106
+ default: // text
107
+ return (
108
+ <Input
109
+ value={columnFilterValue as string || ''}
110
+ onChange={(e) => handleFilterChange(e.target.value || undefined)}
111
+ placeholder={placeholder || defaultPlaceholder}
112
+ className="h-8"
113
+ />
114
+ );
115
+ }
116
+ })()
128
117
  );
129
118
  }
@@ -5,9 +5,11 @@ import {
5
5
  Select,
6
6
  SelectContent,
7
7
  SelectItem,
8
+ SelectGroup,
8
9
  SelectSeparator,
9
10
  SelectTrigger,
10
11
  } from '../../Select/Select';
12
+ import { Label } from '../../Label/Label';
11
13
  import { Checkbox } from '../../Checkbox/Checkbox';
12
14
  import { Settings2, Eye, EyeOff } from 'lucide-react';
13
15
 
@@ -37,7 +39,7 @@ export function ColumnVisibilityDropdown<TData>({
37
39
  );
38
40
 
39
41
  return (
40
- <Select className="w-52">
42
+ <Select className="w-52" showCheckmark={false}>
41
43
  <SelectTrigger asChild>
42
44
  <Button
43
45
  variant="outline"
@@ -47,40 +49,40 @@ export function ColumnVisibilityDropdown<TData>({
47
49
  </Button>
48
50
  </SelectTrigger>
49
51
  <SelectContent>
50
- <div className="p-2 space-y-1">
51
- <div className="flex gap-1 mb-2">
52
- <Button
53
- variant="ghost"
54
- size="sm"
55
- className="h-7 px-2 text-xs"
56
- onClick={() => {
57
- toggleableColumns.forEach(column => {
58
- if (!column.getIsVisible()) {
59
- onColumnVisibilityChange(column.id, true);
60
- }
61
- });
62
- }}
63
- >
64
- <Eye className="size-3 mr-1" />
65
- Show All
66
- </Button>
67
- <Button
68
- variant="ghost"
69
- size="sm"
70
- className="h-7 px-2 text-xs"
71
- onClick={() => {
72
- toggleableColumns.forEach(column => {
73
- if (column.getIsVisible()) {
74
- onColumnVisibilityChange(column.id, false);
75
- }
76
- });
77
- }}
78
- >
79
- <EyeOff className="size-3 mr-1" />
80
- Hide All
81
- </Button>
82
- </div>
83
- <SelectSeparator />
52
+ <SelectGroup className="flex flex-row gap-1 p-2">
53
+ <Button
54
+ variant="ghost"
55
+ size="sm"
56
+ className="h-7 px-2 text-xs flex-1"
57
+ onClick={() => {
58
+ toggleableColumns.forEach(column => {
59
+ if (!column.getIsVisible()) {
60
+ onColumnVisibilityChange(column.id, true);
61
+ }
62
+ });
63
+ }}
64
+ >
65
+ <Eye className="size-3 mr-1" />
66
+ Show All
67
+ </Button>
68
+ <Button
69
+ variant="ghost"
70
+ size="sm"
71
+ className="h-7 px-2 text-xs flex-1"
72
+ onClick={() => {
73
+ toggleableColumns.forEach(column => {
74
+ if (column.getIsVisible()) {
75
+ onColumnVisibilityChange(column.id, false);
76
+ }
77
+ });
78
+ }}
79
+ >
80
+ <EyeOff className="size-3 mr-1" />
81
+ Hide All
82
+ </Button>
83
+ </SelectGroup>
84
+ <SelectSeparator />
85
+ <SelectGroup>
84
86
  {toggleableColumns.map((column) => (
85
87
  <SelectItem
86
88
  key={column.id}
@@ -88,24 +90,24 @@ export function ColumnVisibilityDropdown<TData>({
88
90
  className="flex items-center space-x-2 cursor-pointer px-3 py-2 text-sm hover:bg-sec-50"
89
91
  onClick={(e) => e.preventDefault()}
90
92
  >
93
+ <Label htmlFor={column.id}
94
+ // className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer"
95
+ >
91
96
  <Checkbox
97
+ className="mr-2 align-middle"
92
98
  id={column.id}
93
99
  checked={column.getIsVisible()}
94
100
  onCheckedChange={(checked) =>
95
101
  onColumnVisibilityChange(column.id, !!checked)
96
102
  }
97
103
  />
98
- <label
99
- htmlFor={column.id}
100
- className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer"
101
- >
102
104
  {typeof column.columnDef?.header === 'string'
103
105
  ? column.columnDef.header
104
106
  : column.id}
105
- </label>
107
+ </Label>
106
108
  </SelectItem>
107
109
  ))}
108
- </div>
110
+ </SelectGroup>
109
111
  </SelectContent>
110
112
  </Select>
111
113
  );
@@ -8,8 +8,9 @@
8
8
  * This is the main component that consumers will use.
9
9
  */
10
10
 
11
- import React, { useMemo, useCallback, useEffect, useRef } from 'react';
11
+ import React, { useMemo, useCallback, useEffect, useLayoutEffect, useRef, useState, startTransition, useDeferredValue } from 'react';
12
12
  import { useReactTable } from '@tanstack/react-table';
13
+ import { useLocation } from 'react-router-dom';
13
14
  import type {
14
15
  SortingState,
15
16
  } from '@tanstack/react-table';
@@ -182,6 +183,81 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
182
183
 
183
184
  const logger = createLogger('DataTableCore');
184
185
 
186
+ // Track deletion state to batch updates and prevent flashing
187
+ const [isDeleting, setIsDeleting] = useState(false);
188
+ const deletionTimeoutRef = useRef<NodeJS.Timeout | null>(null);
189
+ const pendingDeletionsRef = useRef<Set<string>>(new Set());
190
+ const dataSnapshotRef = useRef<TData[]>(data);
191
+ const dataChangeCountRef = useRef(0);
192
+ const previousDataLengthRef = useRef(data.length);
193
+
194
+ // CRITICAL: Use useLayoutEffect to capture previous length synchronously
195
+ // This runs before paint, so we can detect changes before React commits
196
+ useLayoutEffect(() => {
197
+ const currentLength = data.length;
198
+ const previousLength = previousDataLengthRef.current;
199
+
200
+ // Check if length decreased - this is the key detection
201
+ const lengthDecreased = currentLength < previousLength;
202
+
203
+ if (lengthDecreased && !isDeleting) {
204
+ // Data length decreased - start deletion batching
205
+ const snapshotLength = dataSnapshotRef.current.length;
206
+
207
+ // Data decrease detected (logging removed for performance)
208
+
209
+ // If snapshot is larger, it's valid (has pre-deletion state)
210
+ // Otherwise, update it to preserve current state
211
+ if (snapshotLength <= currentLength) {
212
+ dataSnapshotRef.current = [...data];
213
+ }
214
+
215
+ setIsDeleting(true);
216
+
217
+ // Set timeout to reset after batching
218
+ if (deletionTimeoutRef.current) {
219
+ clearTimeout(deletionTimeoutRef.current);
220
+ }
221
+ deletionTimeoutRef.current = setTimeout(() => {
222
+ setIsDeleting(false);
223
+ dataSnapshotRef.current = data;
224
+ previousDataLengthRef.current = data.length;
225
+ // Batching complete (logging removed for performance)
226
+ }, 150);
227
+
228
+ // Update previous length AFTER setting up batching
229
+ previousDataLengthRef.current = currentLength;
230
+ } else if (!lengthDecreased && !isDeleting) {
231
+ // No deletion - update snapshot and previous length normally
232
+ dataSnapshotRef.current = data;
233
+ previousDataLengthRef.current = currentLength;
234
+ } else if (isDeleting) {
235
+ // Already deleting - just update previous length for next comparison
236
+ previousDataLengthRef.current = currentLength;
237
+ }
238
+ }, [data, isDeleting]);
239
+
240
+ // Keep data snapshot stable during deletions to prevent flashing
241
+ // Logging effect - runs after layout to show what happened
242
+ useEffect(() => {
243
+ dataChangeCountRef.current += 1;
244
+
245
+ const previousLength = previousDataLengthRef.current;
246
+ const currentLength = data.length;
247
+ const snapshotLength = dataSnapshotRef.current.length;
248
+ const lengthDecreased = currentLength < previousLength;
249
+
250
+ // Data prop changed (logging removed for performance)
251
+ }, [data, isDeleting]);
252
+
253
+ // Use snapshot data during deletions to prevent flashing
254
+ // CRITICAL: If we're deleting and data length decreased, use snapshot to prevent flashing
255
+ const dataLengthChanged = data.length !== dataSnapshotRef.current.length;
256
+ const isDataDecreasing = data.length < dataSnapshotRef.current.length;
257
+ const effectiveData = (isDeleting && isDataDecreasing) ? dataSnapshotRef.current : data;
258
+
259
+ // Effective data tracking (logging removed for performance)
260
+
185
261
  // ============================================================================
186
262
  // ALL HOOKS MUST BE CALLED IN THE SAME ORDER EVERY RENDER
187
263
  // ============================================================================
@@ -241,15 +317,38 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
241
317
  return {};
242
318
  }, [secureFeatures.columnVisibility, savedColumnVisibility]);
243
319
 
320
+ // Get location for route-based key derivation
321
+ let location: { pathname: string } | null = null;
322
+ try {
323
+ // useLocation may not be available if React Router is not set up
324
+ const routerLocation = useLocation();
325
+ location = routerLocation;
326
+ } catch {
327
+ // Router not available, location will be null
328
+ location = null;
329
+ }
330
+
331
+ // Extract column field names for sensitive field filtering
332
+ const columnFieldNames = useMemo(() => {
333
+ return columns
334
+ .map((col) => col.accessorKey || col.id)
335
+ .filter((key): key is string => Boolean(key));
336
+ }, [columns]);
337
+
244
338
  // Use the centralized state management hook for ALL table state
245
339
  // Note: 'actions' prop parameter is shadowed by destructuring, so we rename to stateActions
246
- const { state, actions: stateActions } = useDataTableState<TData>({
340
+ const { state, actions: stateActions, clearDraft } = useDataTableState<TData>({
247
341
  initialPageSize,
248
342
  columnIds: effectiveColumnOrder,
249
343
  initialRowSelection: selection || {},
250
344
  onRowSelectionChange,
251
345
  defaultSorting: defaultSorting || [],
252
- defaultGrouping: defaultGrouping || []
346
+ defaultGrouping: defaultGrouping || [],
347
+ // Persistence props
348
+ rbacPageId: rbac.pageId,
349
+ title,
350
+ location,
351
+ columnFieldNames,
253
352
  });
254
353
 
255
354
  // Apply saved visibility to state if available
@@ -344,7 +443,7 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
344
443
  hierarchicalState,
345
444
  hierarchicalValidation,
346
445
  } = useDataTableDataPipeline<TData>({
347
- data,
446
+ data: effectiveData,
348
447
  features: secureFeatures,
349
448
  hierarchical,
350
449
  sorting: state.sorting,
@@ -357,6 +456,34 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
357
456
  logger.error('Hierarchical data validation failed:', hierarchicalValidation.errors);
358
457
  }
359
458
  }, [hierarchicalValidation, logger]);
459
+
460
+ // Final table data tracking (logging removed for performance)
461
+
462
+ // Cleanup deletion timeout on unmount
463
+ useEffect(() => {
464
+ return () => {
465
+ if (deletionTimeoutRef.current) {
466
+ clearTimeout(deletionTimeoutRef.current);
467
+ }
468
+ pendingDeletionsRef.current.clear();
469
+ };
470
+ }, []);
471
+
472
+ // Update data snapshot when data changes and we're not deleting
473
+ useEffect(() => {
474
+ if (!isDeleting && data !== dataSnapshotRef.current) {
475
+ dataSnapshotRef.current = data;
476
+ }
477
+ }, [data, isDeleting]);
478
+
479
+ // Cleanup deletion timeout on unmount
480
+ useEffect(() => {
481
+ return () => {
482
+ if (deletionTimeoutRef.current) {
483
+ clearTimeout(deletionTimeoutRef.current);
484
+ }
485
+ };
486
+ }, []);
360
487
 
361
488
  const {
362
489
  columnOrder: savedColumnOrder,
@@ -563,19 +690,24 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
563
690
  // RBAC VALIDATION AND SECURE CONFIGURATION - ALWAYS call these hooks
564
691
  // ============================================================================
565
692
 
693
+ // Wrap handlers - persistence is handled automatically via useDataTableState
694
+ // The state actions (clearCreationData, clearEditing) will automatically update persisted state
695
+ const wrappedOnCreateRow = onCreateRow;
696
+ const wrappedOnEditRow = onEditRow;
697
+
566
698
  // MANDATORY: Handlers are automatically secured
567
699
  const secureHandlers = useMemo(() => {
568
700
  const handlers = {
569
- onEditRow: permissions.canUpdate.can ? onEditRow : undefined,
701
+ onEditRow: permissions.canUpdate.can ? wrappedOnEditRow : undefined,
570
702
  onDeleteRow: permissions.canDelete.can ? onDeleteRow : undefined,
571
- onCreateRow: permissions.canCreate.can ? onCreateRow : undefined,
703
+ onCreateRow: permissions.canCreate.can ? wrappedOnCreateRow : undefined,
572
704
  onImport: permissions.canImport.can ? onImport : undefined,
573
705
  onExport: permissions.canExport.can ? onExport : undefined,
574
706
  onDeleteSelected: permissions.canDelete.can ? onDeleteSelected : undefined,
575
707
  };
576
708
 
577
709
  return handlers;
578
- }, [permissions.canUpdate.can, permissions.canDelete.can, permissions.canCreate.can, permissions.canImport.can, permissions.canExport.can, onEditRow, onDeleteRow, onCreateRow, onImport, onExport, onDeleteSelected, secureFeatures.creation]);
710
+ }, [permissions.canUpdate.can, permissions.canDelete.can, permissions.canCreate.can, permissions.canImport.can, permissions.canExport.can, wrappedOnEditRow, onDeleteRow, wrappedOnCreateRow, onImport, onExport, onDeleteSelected, secureFeatures.creation]);
579
711
 
580
712
  // MANDATORY: Process actions with RBAC checks
581
713
  const effectiveActions = useMemo(() => {
@@ -617,19 +749,60 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
617
749
  });
618
750
  return;
619
751
  }
752
+
753
+ // Get row ID for tracking - use current data, not effectiveData
754
+ const rowIndex = data.findIndex(r => r === row);
755
+ const rowId = resolvedGetRowId(row, rowIndex >= 0 ? rowIndex : 0);
756
+
757
+ // Mark deletion as in progress and track this deletion
758
+ // CRITICAL: Set isDeleting and snapshot BEFORE calling the handler
759
+ // This prevents the parent's data update from causing a flash
760
+ if (!isDeleting) {
761
+ // Snapshot current data BEFORE deletion
762
+ dataSnapshotRef.current = [...data]; // Create a copy to prevent reference issues
763
+ setIsDeleting(true);
764
+ }
765
+ pendingDeletionsRef.current.add(rowId);
766
+
767
+ // Clear any existing timeout
768
+ if (deletionTimeoutRef.current) {
769
+ clearTimeout(deletionTimeoutRef.current);
770
+ }
771
+
620
772
  try {
773
+ // Call the delete handler - this may update the parent's data prop
774
+ const deleteStartTime = Date.now();
621
775
  const result = secureHandlers.onDeleteRow!(row) as any;
622
776
  // Handle async operations
623
777
  if (result !== undefined && result !== null && typeof result === 'object' && typeof result.then === 'function') {
624
778
  await result;
625
779
  }
626
- toast({
627
- title: "Delete Successful",
628
- description: "Row deleted successfully",
629
- variant: "default"
630
- });
780
+ // Remove from pending deletions
781
+ pendingDeletionsRef.current.delete(rowId);
782
+
783
+ // Reset deletion state after a delay to allow batching
784
+ // This prevents the table from refreshing after each individual deletion
785
+ deletionTimeoutRef.current = setTimeout(() => {
786
+ // Only reset if all deletions are complete
787
+ if (pendingDeletionsRef.current.size === 0) {
788
+ setIsDeleting(false);
789
+ // Update snapshot to latest data
790
+ dataSnapshotRef.current = data;
791
+ toast({
792
+ title: "Delete Successful",
793
+ description: "Row deleted successfully",
794
+ variant: "default"
795
+ });
796
+ }
797
+ }, 150); // Delay to batch rapid deletions
798
+
631
799
  } catch (error) {
632
800
  logger.error('Delete error:', error);
801
+ pendingDeletionsRef.current.delete(rowId);
802
+ if (pendingDeletionsRef.current.size === 0) {
803
+ setIsDeleting(false);
804
+ dataSnapshotRef.current = data;
805
+ }
633
806
  toast({
634
807
  title: "Delete Failed",
635
808
  description: error instanceof Error ? error.message : 'Failed to delete row',
@@ -645,7 +818,7 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
645
818
  }
646
819
 
647
820
  return result;
648
- }, [actions, secureFeatures, permissions, secureHandlers, resolvedGetRowId, stateActions, data]);
821
+ }, [actions, secureFeatures, permissions, secureHandlers, resolvedGetRowId, stateActions, effectiveData, isDeleting, pendingDeletionsRef, deletionTimeoutRef, dataSnapshotRef]);
649
822
 
650
823
  // Normalize columnOrder for useTableColumns: ensure 'select' is always first
651
824
  const normalizedColumnOrderForColumns = useMemo(() => {