@jmruthers/pace-core 0.6.9 → 0.6.11

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 (1182) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/audit-tool/00-dependencies.cjs +46 -13
  3. package/audit-tool/audits/01-pace-core-compliance.cjs +96 -21
  4. package/audit-tool/audits/02-project-structure.cjs +74 -2
  5. package/audit-tool/audits/03-architecture.cjs +220 -20
  6. package/audit-tool/audits/04-code-quality.cjs +95 -3
  7. package/audit-tool/audits/05-styling.cjs +19 -7
  8. package/audit-tool/audits/06-security-rbac.cjs +214 -25
  9. package/audit-tool/audits/07-api-tech-stack.cjs +31 -15
  10. package/audit-tool/audits/08-testing-documentation.cjs +11 -3
  11. package/audit-tool/audits/09-operations.cjs +19 -7
  12. package/audit-tool/index.cjs +22 -11
  13. package/audit-tool/utils/report-utils.cjs +4 -0
  14. package/cursor-rules/01-pace-core-compliance.mdc +1 -0
  15. package/cursor-rules/02-project-structure.mdc +3 -26
  16. package/cursor-rules/03-architecture.mdc +3 -1
  17. package/cursor-rules/04-code-quality.mdc +1 -0
  18. package/cursor-rules/05-styling.mdc +120 -8
  19. package/cursor-rules/06-security-rbac.mdc +126 -2
  20. package/cursor-rules/07-api-tech-stack.mdc +1 -0
  21. package/cursor-rules/08-testing-documentation.mdc +1 -0
  22. package/cursor-rules/09-operations.mdc +1 -0
  23. package/dist/DataTable-EFYP2QLE.js +16 -0
  24. package/dist/InactivityServiceProvider-BbxwwDz1.d.ts +308 -0
  25. package/dist/UnifiedAuthProvider-Bkt_tzdS.d.ts +183 -0
  26. package/dist/api-BZR2CYXL.js +5 -0
  27. package/dist/api-result-USV1Czr-.d.ts +51 -0
  28. package/dist/assets/app-icons/admin_favicon.svg +462 -0
  29. package/dist/assets/app-icons/base_favicon.svg +85 -0
  30. package/dist/assets/app-icons/cake_favicon.svg +68 -0
  31. package/dist/assets/app-icons/core_favicon.svg +256 -0
  32. package/dist/assets/app-icons/gear_favicon.svg +91 -0
  33. package/dist/assets/app-icons/medi_favicon.svg +92 -0
  34. package/dist/assets/app-icons/mint_favicon.svg +83 -0
  35. package/dist/assets/app-icons/pace_favicon.svg +49 -0
  36. package/dist/assets/app-icons/pump_favicon.svg +68 -0
  37. package/dist/assets/app-icons/seed_favicon.svg +91 -0
  38. package/dist/assets/app-icons/team_favicon.svg +67 -0
  39. package/dist/assets/app-icons/trac_favicon.svg +112 -0
  40. package/dist/assets/app-icons/trip_favicon.svg +102 -0
  41. package/dist/audit-HI2DHUVU.js +4 -0
  42. package/dist/auth-JvdRVaud.d.ts +49 -0
  43. package/dist/chunk-2DL2WSOE.js +327 -0
  44. package/dist/chunk-2OEVOGGR.js +9598 -0
  45. package/dist/chunk-44CNXN4P.js +15 -0
  46. package/dist/chunk-4R3T5ENU.js +2943 -0
  47. package/dist/chunk-7A6IMHH2.js +2321 -0
  48. package/dist/chunk-BTHN5MKC.js +121 -0
  49. package/dist/chunk-CU2BU2MQ.js +2 -0
  50. package/dist/chunk-D6BMFMQZ.js +200 -0
  51. package/dist/chunk-DDMPHZ3D.js +58 -0
  52. package/dist/chunk-ENLXB7GP.js +721 -0
  53. package/dist/chunk-J2KQK6DG.js +2159 -0
  54. package/dist/chunk-KJXRL3XE.js +6434 -0
  55. package/dist/chunk-L5LFKKLJ.js +61 -0
  56. package/dist/chunk-PCSHBLPB.js +811 -0
  57. package/dist/chunk-QRYSEPHB.js +429 -0
  58. package/dist/chunk-RMLY6KB5.js +187 -0
  59. package/dist/chunk-SACF5YSM.js +31 -0
  60. package/dist/chunk-UZNAFKGW.js +125 -0
  61. package/dist/chunk-V7FTM2LU.js +1080 -0
  62. package/dist/chunk-WY6Y7KC3.js +264 -0
  63. package/dist/chunk-XOJME5T7.js +407 -0
  64. package/dist/chunk-XPFVT3GN.js +492 -0
  65. package/dist/chunk-YFTFFJIV.js +529 -0
  66. package/dist/chunk-YYTWKVHO.js +1334 -0
  67. package/dist/components.d.ts +12 -89
  68. package/dist/components.js +23 -55
  69. package/dist/database.generated-qkdoiVrJ.d.ts +9441 -0
  70. package/dist/eslint-rules/index.cjs +3 -0
  71. package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
  72. package/dist/eslint-rules/rules/05-styling.cjs +507 -0
  73. package/dist/eslint-rules/rules/06-security-rbac.cjs +84 -0
  74. package/dist/event-BfCox3N2.d.ts +265 -0
  75. package/dist/file-reference-DU1hcawx.d.ts +164 -0
  76. package/dist/functions-DH45k8ec.d.ts +208 -0
  77. package/dist/hooks.d.ts +28 -14
  78. package/dist/hooks.js +90 -56
  79. package/dist/icons/index.d.ts +1 -0
  80. package/dist/icons/index.js +1 -0
  81. package/dist/index.d.ts +392 -155
  82. package/dist/index.js +337 -347
  83. package/dist/pagination-BW1mqywp.d.ts +201 -0
  84. package/dist/papaparseLoader-WG2UXQ22.js +7 -0
  85. package/dist/providers.d.ts +29 -14
  86. package/dist/providers.js +7 -5
  87. package/dist/rbac/eslint-rules.js +2 -2
  88. package/dist/rbac/index.d.ts +180 -351
  89. package/dist/rbac/index.js +13 -11
  90. package/dist/theming/runtime.d.ts +28 -5
  91. package/dist/theming/runtime.js +2 -2
  92. package/dist/timezone-BTWWXKVY.d.ts +696 -0
  93. package/dist/types-BE2sEHKd.d.ts +55 -0
  94. package/dist/types-CvOPXWWZ.d.ts +111 -0
  95. package/dist/types-Dr8sNhER.d.ts +50 -0
  96. package/dist/types.d.ts +20 -13
  97. package/dist/types.js +1 -0
  98. package/dist/usePublicPageContext-B91dGYW1.d.ts +4367 -0
  99. package/dist/usePublicRouteParams-BgV6VhMi.d.ts +946 -0
  100. package/dist/utils.d.ts +338 -156
  101. package/dist/utils.js +78 -60
  102. package/dist/validation-g5n0hDkh.d.ts +177 -0
  103. package/docs/api/modules.md +1226 -1094
  104. package/docs/api-reference/components.md +5 -5
  105. package/docs/api-reference/rpc-functions.md +12 -3
  106. package/docs/core-concepts/rbac-system.md +8 -0
  107. package/docs/getting-started/cursor-rules.md +17 -20
  108. package/docs/getting-started/dependencies.md +1 -1
  109. package/docs/getting-started/setup.md +235 -0
  110. package/docs/implementation-guides/authentication.md +27 -0
  111. package/docs/implementation-guides/data-tables.md +365 -10
  112. package/docs/migration/ApiResult-migration.md +25 -0
  113. package/docs/rbac/RBAC_CONTRACT.md +0 -12
  114. package/docs/rbac/api-reference.md +33 -31
  115. package/docs/standards/0-standards-overview.md +50 -15
  116. package/docs/standards/1-pace-core-compliance-standards.md +62 -57
  117. package/docs/standards/2-project-structure-standards.md +45 -90
  118. package/docs/standards/3-architecture-standards.md +41 -1
  119. package/docs/standards/4-code-quality-standards.md +26 -6
  120. package/docs/standards/5-styling-standards.md +35 -1
  121. package/docs/standards/6-security-rbac-standards.md +288 -7
  122. package/docs/standards/7-api-tech-stack-standards.md +116 -17
  123. package/docs/standards/8-testing-documentation-standards.md +31 -0
  124. package/docs/standards/9-operations-standards.md +19 -0
  125. package/docs/standards/README.md +20 -201
  126. package/docs/testing/README.md +10 -0
  127. package/docs/testing/test-setup-for-consumers.md +916 -0
  128. package/docs/troubleshooting/common-issues.md +17 -1
  129. package/docs/troubleshooting/organisation-context-setup.md +8 -0
  130. package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
  131. package/eslint-config-pace-core.cjs +24 -0
  132. package/package.json +14 -20
  133. package/scripts/build-docs.js +180 -0
  134. package/scripts/setup.cjs +536 -0
  135. package/scripts/validate.cjs +480 -0
  136. package/src/__mocks__/lucide-react.ts +0 -2
  137. package/src/__tests__/helpers/component-test-utils.test.tsx +260 -0
  138. package/src/__tests__/helpers/optimized-test-setup.test.ts +224 -0
  139. package/src/__tests__/helpers/supabaseMock.test.ts +273 -0
  140. package/src/__tests__/helpers/test-providers.test.tsx +99 -0
  141. package/src/__tests__/helpers/test-providers.tsx +37 -39
  142. package/src/__tests__/helpers/test-utils.test.tsx +447 -0
  143. package/src/__tests__/helpers/timer-utils.test.ts +371 -0
  144. package/src/assets/app-icons/admin_favicon.svg +462 -0
  145. package/src/assets/app-icons/base_favicon.svg +85 -0
  146. package/src/assets/app-icons/cake_favicon.svg +68 -0
  147. package/src/assets/app-icons/core_favicon.svg +256 -0
  148. package/src/assets/app-icons/gear_favicon.svg +91 -0
  149. package/src/assets/app-icons/index.test.ts +304 -0
  150. package/src/assets/app-icons/index.ts +83 -0
  151. package/src/assets/app-icons/medi_favicon.svg +92 -0
  152. package/src/assets/app-icons/mint_favicon.svg +83 -0
  153. package/src/assets/app-icons/pace_favicon.svg +49 -0
  154. package/src/assets/app-icons/pump_favicon.svg +68 -0
  155. package/src/assets/app-icons/seed_favicon.svg +91 -0
  156. package/src/assets/app-icons/team_favicon.svg +67 -0
  157. package/src/assets/app-icons/trac_favicon.svg +112 -0
  158. package/src/assets/app-icons/trip_favicon.svg +102 -0
  159. package/src/components/AddressField/AddressField.test.tsx +379 -4
  160. package/src/components/AddressField/AddressField.tsx +239 -213
  161. package/src/components/AddressField/types.ts +2 -2
  162. package/src/components/Alert/Alert.test.tsx +35 -25
  163. package/src/components/Alert/Alert.tsx +8 -8
  164. package/src/components/AppSwitcher/AppSwitcher.test.tsx +1250 -0
  165. package/src/components/AppSwitcher/AppSwitcher.tsx +315 -0
  166. package/src/components/Avatar/Avatar.test.tsx +11 -1
  167. package/src/components/Avatar/Avatar.tsx +3 -2
  168. package/src/components/Badge/Badge.test.tsx +11 -1
  169. package/src/components/Button/Button.test.tsx +13 -3
  170. package/src/components/Button/Button.tsx +1 -1
  171. package/src/components/Calendar/Calendar.test.tsx +523 -131
  172. package/src/components/Calendar/Calendar.tsx +107 -488
  173. package/src/components/Card/Card.test.tsx +384 -258
  174. package/src/components/Card/Card.tsx +19 -10
  175. package/src/components/Checkbox/Checkbox.test.tsx +58 -174
  176. package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
  177. package/src/components/ContextSelector/ContextSelector.test.tsx +360 -0
  178. package/src/components/ContextSelector/ContextSelector.tsx +66 -280
  179. package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
  180. package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
  181. package/src/components/DataTable/AUDIT_REPORT.md +59 -44
  182. package/src/components/DataTable/DataTable.comprehensive.test.tsx +759 -0
  183. package/src/components/DataTable/DataTable.default-state.test.tsx +524 -0
  184. package/src/components/DataTable/DataTable.export.test.tsx +705 -0
  185. package/src/components/DataTable/DataTable.grouping-aggregation.test.tsx +658 -0
  186. package/src/components/DataTable/DataTable.hooks.test.tsx +192 -0
  187. package/src/components/DataTable/DataTable.select-label-display.test.tsx +485 -0
  188. package/src/components/DataTable/DataTable.test.tsx +787 -416
  189. package/src/components/DataTable/DataTable.tsx +14 -14
  190. package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
  191. package/src/components/DataTable/DataTableCore.test-setup.ts +221 -0
  192. package/src/components/DataTable/DataTableCore.test.tsx +970 -0
  193. package/src/components/DataTable/README.md +155 -0
  194. package/src/components/DataTable/TESTING.md +101 -0
  195. package/src/components/DataTable/a11y.basic.test.tsx +788 -0
  196. package/src/components/DataTable/components/DataTableCore.tsx +126 -894
  197. package/src/components/DataTable/components/GroupingDropdown.test.tsx +621 -0
  198. package/src/components/DataTable/components/GroupingDropdown.tsx +2 -3
  199. package/src/components/DataTable/components/ImportModal.tsx +82 -408
  200. package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
  201. package/src/components/DataTable/context/DataTableContext.test.tsx +328 -0
  202. package/src/components/DataTable/context/DataTableContext.tsx +13 -13
  203. package/src/components/DataTable/core/ColumnFactory.test.ts +403 -0
  204. package/src/components/DataTable/core/ColumnFactory.ts +3 -3
  205. package/src/components/DataTable/hooks/useColumnOrderPersistence.test.ts +516 -0
  206. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
  207. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.test.ts +256 -0
  208. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
  209. package/src/components/DataTable/hooks/useDataTableConfiguration.test.ts +297 -0
  210. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +15 -3
  211. package/src/components/DataTable/hooks/useDataTableDataPipeline.test.ts +270 -0
  212. package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
  213. package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
  214. package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
  215. package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
  216. package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
  217. package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
  218. package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
  219. package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
  220. package/src/components/DataTable/hooks/useDataTablePermissions.test.ts +280 -0
  221. package/src/components/DataTable/hooks/useDataTablePermissions.ts +81 -260
  222. package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
  223. package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
  224. package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
  225. package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
  226. package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
  227. package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
  228. package/src/components/DataTable/hooks/useDataTableState.test.ts +733 -0
  229. package/src/components/DataTable/hooks/useDataTableState.ts +161 -114
  230. package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
  231. package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
  232. package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
  233. package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
  234. package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
  235. package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
  236. package/src/components/DataTable/hooks/useEffectiveColumnOrder.test.ts +183 -0
  237. package/src/components/DataTable/hooks/useHierarchicalState.test.ts +294 -0
  238. package/src/components/DataTable/hooks/useImportModalFocus.test.ts +184 -0
  239. package/src/components/DataTable/hooks/useImportModalFocus.ts +53 -0
  240. package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
  241. package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
  242. package/src/components/DataTable/hooks/useKeyboardNavigation.test.ts +787 -0
  243. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +311 -271
  244. package/src/components/DataTable/hooks/usePermissionTracking.test.ts +381 -0
  245. package/src/components/DataTable/hooks/usePermissionTracking.ts +122 -0
  246. package/src/components/DataTable/hooks/useServerSideDataEffect.test.ts +258 -0
  247. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +27 -4
  248. package/src/components/DataTable/hooks/useTableColumns.test.ts +499 -0
  249. package/src/components/DataTable/hooks/useTableColumns.ts +15 -39
  250. package/src/components/DataTable/hooks/useTableHandlers.test.ts +461 -0
  251. package/src/components/DataTable/hooks/useTableHandlers.ts +13 -22
  252. package/src/components/DataTable/index.ts +28 -5
  253. package/src/components/DataTable/keyboard.test.tsx +734 -0
  254. package/src/components/DataTable/mocks/MockRBACProvider.tsx +66 -0
  255. package/src/components/DataTable/pagination.modes.test.tsx +728 -0
  256. package/src/components/DataTable/ssr.strict-mode.test.tsx +319 -0
  257. package/src/components/DataTable/styles.test.ts +379 -0
  258. package/src/components/DataTable/styles.ts +0 -1
  259. package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
  260. package/src/components/DataTable/test-utils/dataFactories.ts +103 -0
  261. package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
  262. package/src/components/DataTable/test-utils/sharedTestUtils.ts +419 -0
  263. package/src/components/DataTable/test-utils.ts +94 -0
  264. package/src/components/DataTable/types/actions.ts +71 -0
  265. package/src/components/DataTable/types/base.ts +39 -0
  266. package/src/components/DataTable/types/columns.ts +125 -0
  267. package/src/components/DataTable/types/export.ts +32 -0
  268. package/src/components/DataTable/types/features.ts +81 -0
  269. package/src/components/DataTable/types/hierarchical.ts +44 -0
  270. package/src/components/DataTable/types/index.ts +43 -0
  271. package/src/components/DataTable/types/pagination.ts +85 -0
  272. package/src/components/DataTable/types/performance.ts +47 -0
  273. package/src/components/DataTable/types/props.ts +62 -0
  274. package/src/components/DataTable/types/rbac.ts +45 -0
  275. package/src/components/DataTable/ui/layout/DataTableCore.test.tsx +1194 -0
  276. package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
  277. package/src/components/DataTable/ui/layout/DataTableErrorBoundary.test.tsx +438 -0
  278. package/src/components/DataTable/ui/layout/DataTableErrorBoundary.tsx +225 -0
  279. package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
  280. package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
  281. package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
  282. package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
  283. package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
  284. package/src/components/DataTable/ui/modals/DataTableModals.tsx +341 -0
  285. package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
  286. package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
  287. package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
  288. package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
  289. package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
  290. package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
  291. package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
  292. package/src/components/DataTable/ui/shared/AccessDeniedPage.test.tsx +245 -0
  293. package/src/components/DataTable/ui/shared/AccessDeniedPage.tsx +159 -0
  294. package/src/components/DataTable/ui/shared/ActionButtons.test.tsx +921 -0
  295. package/src/components/DataTable/ui/shared/ActionButtons.tsx +195 -0
  296. package/src/components/DataTable/ui/shared/ColumnFilter.test.tsx +497 -0
  297. package/src/components/DataTable/ui/shared/ColumnFilter.tsx +113 -0
  298. package/src/components/DataTable/ui/shared/PaginationControls.test.tsx +451 -0
  299. package/src/components/DataTable/ui/shared/PaginationControls.tsx +291 -0
  300. package/src/components/DataTable/ui/shared/SortIndicator.test.tsx +135 -0
  301. package/src/components/DataTable/ui/shared/SortIndicator.tsx +50 -0
  302. package/src/components/DataTable/ui/table/EditFields.test.tsx +526 -0
  303. package/src/components/DataTable/ui/table/EditFields.tsx +355 -0
  304. package/src/components/DataTable/ui/table/EditableRow.test.tsx +1003 -0
  305. package/src/components/DataTable/ui/table/EditableRow.tsx +444 -0
  306. package/src/components/DataTable/ui/table/EmptyState.test.tsx +360 -0
  307. package/src/components/DataTable/ui/table/EmptyState.tsx +74 -0
  308. package/src/components/DataTable/ui/table/FilterRow.test.tsx +416 -0
  309. package/src/components/DataTable/ui/table/FilterRow.tsx +148 -0
  310. package/src/components/DataTable/ui/table/LoadingState.test.tsx +77 -0
  311. package/src/components/DataTable/ui/table/LoadingState.tsx +17 -0
  312. package/src/components/DataTable/ui/table/RowComponent.test.tsx +1024 -0
  313. package/src/components/DataTable/ui/table/RowComponent.tsx +429 -0
  314. package/src/components/DataTable/ui/table/UnifiedTableBody.test.tsx +1273 -0
  315. package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
  316. package/src/components/DataTable/ui/table/cellValueUtils.test.ts +453 -0
  317. package/src/components/DataTable/ui/table/cellValueUtils.ts +40 -0
  318. package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.test.tsx +551 -0
  319. package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.tsx +160 -0
  320. package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.test.tsx +751 -0
  321. package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.tsx +114 -0
  322. package/src/components/DataTable/ui/toolbar/DataTableToolbar.test.tsx +629 -0
  323. package/src/components/DataTable/ui/toolbar/DataTableToolbar.tsx +271 -0
  324. package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
  325. package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
  326. package/src/components/DataTable/utils/a11yUtils.test.ts +548 -0
  327. package/src/components/DataTable/utils/a11yUtils.ts +1 -1
  328. package/src/components/DataTable/utils/aggregationUtils.test.ts +288 -0
  329. package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
  330. package/src/components/DataTable/utils/columnUtils.test.ts +94 -0
  331. package/src/components/DataTable/utils/csvParse.test.ts +74 -0
  332. package/src/components/DataTable/utils/csvParse.ts +65 -0
  333. package/src/components/DataTable/utils/errorHandling.test.ts +209 -0
  334. package/src/components/DataTable/utils/errorHandling.ts +3 -1
  335. package/src/components/DataTable/utils/exportUtils.test.ts +954 -0
  336. package/src/components/DataTable/utils/exportUtils.ts +1 -1
  337. package/src/components/DataTable/utils/flexibleImport.test.ts +573 -0
  338. package/src/components/DataTable/utils/flexibleImport.ts +3 -186
  339. package/src/components/DataTable/utils/hierarchicalSorting.test.ts +235 -0
  340. package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
  341. package/src/components/DataTable/utils/hierarchicalUtils.test.ts +586 -0
  342. package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
  343. package/src/components/DataTable/utils/importDateParser.ts +114 -0
  344. package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
  345. package/src/components/DataTable/utils/importValueParser.ts +91 -0
  346. package/src/components/DataTable/utils/paginationUtils.test.ts +593 -0
  347. package/src/components/DataTable/utils/paginationUtils.ts +7 -4
  348. package/src/components/DataTable/utils/performanceUtils.test.ts +470 -0
  349. package/src/components/DataTable/utils/performanceUtils.ts +1 -1
  350. package/src/components/DataTable/utils/rowUtils.test.ts +235 -0
  351. package/src/components/DataTable/utils/selectFieldUtils.test.ts +271 -0
  352. package/src/components/DataTable/utils/selectFieldUtils.ts +97 -67
  353. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +18 -25
  354. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
  355. package/src/components/DateTimeField/DateTimeField.test.tsx +3 -16
  356. package/src/components/DateTimeField/DateTimeField.tsx +1 -1
  357. package/src/components/Dialog/Dialog.test-utils.ts +49 -0
  358. package/src/components/Dialog/Dialog.test.tsx +2865 -458
  359. package/src/components/Dialog/Dialog.tsx +183 -986
  360. package/src/components/Dialog/dialogLock.test.ts +238 -0
  361. package/src/components/Dialog/dialogLock.ts +98 -0
  362. package/src/components/Dialog/index.ts +2 -0
  363. package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
  364. package/src/components/Dialog/useDialogDimensions.ts +140 -0
  365. package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
  366. package/src/components/Dialog/useDialogLifecycle.ts +135 -0
  367. package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
  368. package/src/components/Dialog/useDialogPersistence.ts +357 -0
  369. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +2 -62
  370. package/src/components/ErrorBoundary/ErrorBoundaryContext.context.ts +17 -0
  371. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +2 -45
  372. package/src/components/ErrorBoundary/ErrorBoundaryContext.types.ts +41 -0
  373. package/src/components/ErrorBoundary/index.ts +3 -4
  374. package/src/components/ErrorBoundary/useErrorBoundaryContext.ts +20 -0
  375. package/src/components/FileDisplay/FileDisplay.test.tsx +479 -247
  376. package/src/components/FileDisplay/FileDisplay.tsx +29 -659
  377. package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
  378. package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
  379. package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
  380. package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
  381. package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
  382. package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
  383. package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
  384. package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
  385. package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
  386. package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
  387. package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
  388. package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
  389. package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
  390. package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
  391. package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
  392. package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
  393. package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
  394. package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
  395. package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
  396. package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
  397. package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
  398. package/src/components/FileDisplay/fallbackUtils.ts +44 -0
  399. package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
  400. package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
  401. package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
  402. package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
  403. package/src/components/FileDisplay/index.tsx +1 -1
  404. package/src/components/FileDisplay/useFileDisplay.test.ts +538 -0
  405. package/src/components/FileDisplay/useFileDisplay.ts +515 -0
  406. package/src/components/FileDisplay/useFileDisplay.unit.test.ts +1438 -0
  407. package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
  408. package/src/components/FileDisplay/usePublicFileDisplay.test.ts +729 -0
  409. package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
  410. package/src/components/FileUpload/FileUpload.test.tsx +69 -27
  411. package/src/components/FileUpload/FileUpload.tsx +112 -527
  412. package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
  413. package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
  414. package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
  415. package/src/components/FileUpload/index.tsx +1 -1
  416. package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
  417. package/src/components/FileUpload/useFileUploadManager.ts +454 -0
  418. package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
  419. package/src/components/FileUpload/useResolvedAppId.ts +77 -0
  420. package/src/components/Footer/Footer.test.tsx +15 -382
  421. package/src/components/Footer/Footer.tsx +8 -125
  422. package/src/components/Form/Form.test.tsx +425 -88
  423. package/src/components/Form/Form.tsx +91 -299
  424. package/src/components/Form/useFormPersistence.ts +257 -0
  425. package/src/components/Header/Header.test.tsx +653 -163
  426. package/src/components/Header/Header.tsx +62 -44
  427. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
  428. package/src/components/Input/Input.test.tsx +34 -120
  429. package/src/components/Input/Input.tsx +1 -1
  430. package/src/components/Label/Label.test.tsx +46 -45
  431. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +8 -11
  432. package/src/components/LoginForm/LoginForm.test.tsx +0 -1
  433. package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
  434. package/src/components/NavigationMenu/NavigationMenu.test.tsx +2422 -102
  435. package/src/components/NavigationMenu/NavigationMenu.tsx +62 -362
  436. package/src/components/NavigationMenu/index.ts +6 -1
  437. package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
  438. package/src/components/NavigationMenu/useNavigationFiltering.test.ts +1949 -0
  439. package/src/components/NavigationMenu/useNavigationFiltering.ts +199 -308
  440. package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
  441. package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1322 -0
  442. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +50 -49
  443. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
  444. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +103 -85
  445. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +774 -44
  446. package/src/components/PaceAppLayout/PaceAppLayout.tsx +282 -764
  447. package/src/components/PaceAppLayout/README.md +0 -9
  448. package/src/components/PaceAppLayout/test-setup.tsx +15 -9
  449. package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
  450. package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
  451. package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
  452. package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
  453. package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
  454. package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
  455. package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
  456. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +782 -20
  457. package/src/components/PaceLoginPage/PaceLoginPage.tsx +33 -125
  458. package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
  459. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
  460. package/src/components/Progress/Progress.test.tsx +127 -1
  461. package/src/components/Progress/Progress.tsx +1 -2
  462. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
  463. package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -217
  464. package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
  465. package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
  466. package/src/components/PublicLayout/PublicLayout.test.tsx +1640 -38
  467. package/src/components/PublicLayout/PublicPageContext.ts +28 -0
  468. package/src/components/PublicLayout/PublicPageLayout.tsx +134 -75
  469. package/src/components/PublicLayout/PublicPageProvider.tsx +7 -42
  470. package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
  471. package/src/components/Select/Select.test.tsx +45 -8
  472. package/src/components/Select/Select.tsx +57 -40
  473. package/src/components/Select/context.test.tsx +56 -0
  474. package/src/components/Select/text.test.tsx +104 -0
  475. package/src/components/Select/text.ts +26 -0
  476. package/src/components/Select/types.ts +3 -0
  477. package/src/components/Select/useSelectEvents.test.ts +279 -0
  478. package/src/components/Select/useSelectEvents.ts +87 -0
  479. package/src/components/Select/useSelectSearch.test.tsx +295 -0
  480. package/src/components/Select/useSelectSearch.ts +91 -0
  481. package/src/components/Select/useSelectState.test.ts +268 -0
  482. package/src/components/Select/useSelectState.ts +104 -0
  483. package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +28 -112
  484. package/src/components/Switch/Switch.test.tsx +57 -153
  485. package/src/components/Table/Table.test.tsx +395 -317
  486. package/src/components/Tabs/Tabs.test.tsx +270 -0
  487. package/src/components/Tabs/Tabs.tsx +4 -4
  488. package/src/components/Textarea/Textarea.test.tsx +11 -38
  489. package/src/components/Toast/Toast.test.tsx +425 -496
  490. package/src/components/Tooltip/Tooltip.test.tsx +4 -21
  491. package/src/components/UserMenu/UserMenu.test.tsx +1 -21
  492. package/src/components/UserMenu/UserMenu.tsx +0 -1
  493. package/src/components/index.test.ts +346 -0
  494. package/src/components/index.ts +12 -1
  495. package/src/constants/performance.test.ts +91 -0
  496. package/src/hooks/ServiceHooks.test.tsx +725 -0
  497. package/src/hooks/hooks.integration.test.tsx +608 -0
  498. package/src/hooks/index.ts +18 -3
  499. package/src/hooks/index.unit.test.ts +220 -0
  500. package/src/hooks/public/usePublicEvent.test.ts +304 -0
  501. package/src/hooks/public/usePublicEvent.ts +11 -11
  502. package/src/hooks/public/usePublicEventLogo.test.ts +655 -120
  503. package/src/hooks/public/usePublicEventLogo.ts +2 -2
  504. package/src/hooks/public/usePublicRouteParams.test.ts +595 -0
  505. package/src/hooks/public/usePublicRouteParams.ts +2 -2
  506. package/src/hooks/services/useAuth.ts +9 -7
  507. package/src/hooks/services/useAuthService.ts +1 -1
  508. package/src/hooks/services/useEventService.ts +1 -1
  509. package/src/hooks/useAccessibleApps.test.ts +400 -0
  510. package/src/hooks/useAccessibleApps.ts +264 -0
  511. package/src/hooks/useAddressAutocomplete.test.ts +170 -47
  512. package/src/hooks/useAddressAutocomplete.ts +109 -81
  513. package/src/hooks/useApiFetch.unit.test.ts +111 -0
  514. package/src/hooks/useAppConfig.ts +13 -3
  515. package/src/hooks/useAppConfig.unit.test.ts +712 -0
  516. package/src/hooks/useComponentPerformance.unit.test.tsx +314 -0
  517. package/src/hooks/useDataTablePerformance.ts +111 -130
  518. package/src/hooks/useDataTablePerformance.unit.test.ts +720 -0
  519. package/src/hooks/useDataTableState.test.ts +170 -0
  520. package/src/hooks/useDataTableState.ts +5 -5
  521. package/src/hooks/useDebounce.unit.test.ts +157 -0
  522. package/src/hooks/useEventTheme.test.ts +70 -18
  523. package/src/hooks/useEventTheme.ts +50 -22
  524. package/src/hooks/useEvents.ts +49 -2
  525. package/src/hooks/useEvents.unit.test.ts +227 -0
  526. package/src/hooks/useFileReference.test.ts +388 -107
  527. package/src/hooks/useFileReference.ts +184 -179
  528. package/src/hooks/useFileUrl.ts +1 -1
  529. package/src/hooks/useFileUrl.unit.test.ts +686 -0
  530. package/src/hooks/useFileUrlCache.test.ts +319 -0
  531. package/src/hooks/useFileUrlCache.ts +5 -2
  532. package/src/hooks/useFocusManagement.unit.test.ts +604 -0
  533. package/src/hooks/useFocusTrap.unit.test.tsx +613 -0
  534. package/src/hooks/useFormDialog.test.ts +307 -0
  535. package/src/hooks/useFormDialog.ts +2 -2
  536. package/src/hooks/useInactivityTracker.ts +141 -134
  537. package/src/hooks/useInactivityTracker.unit.test.ts +446 -0
  538. package/src/hooks/useIsMobile.unit.test.ts +317 -0
  539. package/src/hooks/useIsPrint.ts +62 -0
  540. package/src/hooks/useIsPrint.unit.test.ts +545 -0
  541. package/src/hooks/useKeyboardShortcuts.unit.test.ts +907 -0
  542. package/src/hooks/useOrganisationPermissions.test.ts +1 -2
  543. package/src/hooks/useOrganisationPermissions.ts +1 -4
  544. package/src/hooks/useOrganisationPermissions.unit.test.tsx +293 -0
  545. package/src/hooks/useOrganisationSecurity.test.ts +4 -33
  546. package/src/hooks/useOrganisationSecurity.ts +192 -203
  547. package/src/hooks/useOrganisationSecurity.unit.test.tsx +959 -0
  548. package/src/hooks/useOrganisations.ts +1 -1
  549. package/src/hooks/useOrganisations.unit.test.ts +369 -0
  550. package/src/hooks/usePerformanceMonitor.ts +1 -1
  551. package/src/hooks/usePerformanceMonitor.unit.test.ts +693 -0
  552. package/src/hooks/usePermissionCache.test.ts +298 -329
  553. package/src/hooks/usePermissionCache.ts +277 -276
  554. package/src/hooks/usePreventTabReload.test.ts +307 -0
  555. package/src/hooks/usePublicEvent.simple.test.ts +794 -0
  556. package/src/hooks/usePublicEvent.test.ts +670 -0
  557. package/src/hooks/usePublicEvent.unit.test.ts +638 -0
  558. package/src/hooks/usePublicFileDisplay.test.ts +948 -0
  559. package/src/hooks/usePublicRouteParams.unit.test.ts +442 -0
  560. package/src/hooks/useQueryCache.test.ts +391 -0
  561. package/src/hooks/useQueryCache.ts +7 -9
  562. package/src/hooks/useRBAC.unit.test.ts +253 -0
  563. package/src/hooks/useSessionDraft.test.ts +556 -0
  564. package/src/hooks/useSessionDraft.ts +14 -11
  565. package/src/hooks/useSessionRestoration.ts +1 -1
  566. package/src/hooks/useSessionRestoration.unit.test.tsx +381 -0
  567. package/src/hooks/useStorage.ts +94 -54
  568. package/src/hooks/useStorage.unit.test.ts +684 -0
  569. package/src/hooks/useToast.test.ts +413 -0
  570. package/src/hooks/useToast.ts +2 -2
  571. package/src/hooks/useToast.unit.test.tsx +481 -0
  572. package/src/hooks/useZodForm.ts +3 -3
  573. package/src/hooks/useZodForm.unit.test.tsx +191 -0
  574. package/src/icons/index.test.ts +133 -0
  575. package/src/icons/index.ts +3 -1
  576. package/src/index.test.ts +528 -0
  577. package/src/index.ts +56 -9
  578. package/src/providers/AuthProvider.test.tsx +218 -0
  579. package/src/providers/EventProvider.test.tsx +487 -0
  580. package/src/providers/InactivityProvider.test-helper.tsx +40 -0
  581. package/src/providers/InactivityProvider.test.tsx +421 -0
  582. package/src/providers/ProviderLifecycle.test.tsx +308 -0
  583. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
  584. package/src/providers/UnifiedAuthProvider.test.tsx +503 -0
  585. package/src/providers/index.test.ts +138 -0
  586. package/src/providers/services/AuthServiceContext.ts +27 -0
  587. package/src/providers/services/AuthServiceProvider.integration.test.tsx +229 -0
  588. package/src/providers/services/AuthServiceProvider.test.tsx +638 -0
  589. package/src/providers/services/AuthServiceProvider.tsx +81 -20
  590. package/src/providers/services/EventServiceContext.ts +25 -0
  591. package/src/providers/services/EventServiceProvider.test.tsx +839 -0
  592. package/src/providers/services/EventServiceProvider.tsx +11 -20
  593. package/src/providers/services/InactivityServiceContext.ts +25 -0
  594. package/src/providers/services/InactivityServiceProvider.test.tsx +662 -0
  595. package/src/providers/services/InactivityServiceProvider.tsx +7 -17
  596. package/src/providers/services/OrganisationServiceContext.ts +25 -0
  597. package/src/providers/services/OrganisationServiceProvider.test.tsx +440 -0
  598. package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
  599. package/src/providers/services/UnifiedAuthContext.ts +102 -0
  600. package/src/providers/services/UnifiedAuthProvider.advanced.test.tsx +434 -0
  601. package/src/providers/services/UnifiedAuthProvider.appId.test.tsx +408 -0
  602. package/src/providers/services/UnifiedAuthProvider.integration.test.tsx +304 -0
  603. package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
  604. package/src/providers/services/UnifiedAuthProvider.tsx +147 -497
  605. package/src/providers/services/contexts.test.tsx +281 -0
  606. package/src/providers/services/useUnifiedAuth.test.tsx +251 -0
  607. package/src/providers/services/useUnifiedAuth.ts +29 -0
  608. package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
  609. package/src/providers/useInactivity.test-helper.ts +27 -0
  610. package/src/rbac/README.md +5 -5
  611. package/src/rbac/adapters.comprehensive.test.tsx +429 -0
  612. package/src/rbac/adapters.test.tsx +654 -0
  613. package/src/rbac/adapters.tsx +53 -38
  614. package/src/rbac/api.test.ts +986 -259
  615. package/src/rbac/api.ts +260 -216
  616. package/src/rbac/audit-batched.test.ts +550 -0
  617. package/src/rbac/audit-batched.ts +5 -4
  618. package/src/rbac/audit.test.ts +225 -28
  619. package/src/rbac/audit.ts +26 -18
  620. package/src/rbac/auth-rbac-security.integration.test.tsx +300 -0
  621. package/src/rbac/auth-rbac.e2e.test.tsx +510 -0
  622. package/src/rbac/cache-invalidation.test.ts +715 -0
  623. package/src/rbac/cache-invalidation.ts +18 -15
  624. package/src/rbac/cache.test.ts +123 -63
  625. package/src/rbac/cache.ts +3 -4
  626. package/src/rbac/components/AccessDenied.test.tsx +324 -0
  627. package/src/rbac/components/AccessDenied.tsx +20 -18
  628. package/src/rbac/components/NavigationGuard.test.tsx +1148 -0
  629. package/src/rbac/components/NavigationGuard.tsx +10 -8
  630. package/src/rbac/components/PagePermissionGuard.guard.test.tsx +236 -0
  631. package/src/rbac/components/PagePermissionGuard.performance.test.tsx +252 -0
  632. package/src/rbac/components/PagePermissionGuard.race-condition.test.tsx +243 -0
  633. package/src/rbac/components/PagePermissionGuard.test.tsx +1430 -0
  634. package/src/rbac/components/PagePermissionGuard.tsx +188 -381
  635. package/src/rbac/components/PagePermissionGuard.verification.test.tsx +185 -0
  636. package/src/rbac/config.test.ts +131 -48
  637. package/src/rbac/config.ts +69 -26
  638. package/src/rbac/docs/event-based-apps.md +26 -13
  639. package/src/rbac/engine.comprehensive.test.ts +808 -0
  640. package/src/rbac/engine.test.ts +974 -130
  641. package/src/rbac/engine.ts +53 -13
  642. package/src/rbac/errors.test.ts +99 -87
  643. package/src/rbac/errors.ts +89 -55
  644. package/src/rbac/eslint-rules.js +2 -2
  645. package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
  646. package/src/rbac/hooks/permissions/useAccessLevel.test.ts +622 -0
  647. package/src/rbac/hooks/permissions/useAccessLevel.ts +23 -14
  648. package/src/rbac/hooks/permissions/useCan.test.ts +798 -0
  649. package/src/rbac/hooks/permissions/useCan.ts +173 -253
  650. package/src/rbac/hooks/permissions/useMultiplePermissions.test.ts +843 -0
  651. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +63 -10
  652. package/src/rbac/hooks/permissions/usePermissions.test.ts +543 -0
  653. package/src/rbac/hooks/permissions/usePermissions.ts +50 -78
  654. package/src/rbac/hooks/useCan.test.ts +348 -32
  655. package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
  656. package/src/rbac/hooks/usePageGuardScope.ts +117 -0
  657. package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
  658. package/src/rbac/hooks/usePermissions.integration.test.ts +427 -0
  659. package/src/rbac/hooks/usePermissions.stability.test.ts +268 -0
  660. package/src/rbac/hooks/usePermissions.test.ts +459 -33
  661. package/src/rbac/hooks/usePermissions.ts +5 -7
  662. package/src/rbac/hooks/useRBAC.test.ts +1784 -21
  663. package/src/rbac/hooks/useRBAC.ts +148 -88
  664. package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
  665. package/src/rbac/hooks/useResolvedScope.ts +4 -1
  666. package/src/rbac/hooks/useResourcePermissions.test.ts +561 -24
  667. package/src/rbac/hooks/useResourcePermissions.ts +76 -140
  668. package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
  669. package/src/rbac/hooks/useRoleManagement.test.ts +634 -61
  670. package/src/rbac/hooks/useRoleManagement.ts +158 -586
  671. package/src/rbac/hooks/useSecureSupabase.test.ts +1179 -0
  672. package/src/rbac/hooks/useSecureSupabase.ts +21 -14
  673. package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
  674. package/src/rbac/index.test.ts +107 -0
  675. package/src/rbac/index.ts +32 -32
  676. package/src/rbac/performance.test.ts +451 -0
  677. package/src/rbac/permissions.test.ts +149 -68
  678. package/src/rbac/permissions.ts +0 -3
  679. package/src/rbac/rbac-core.test.tsx +276 -0
  680. package/src/rbac/rbac-engine-core-logic.test.ts +387 -0
  681. package/src/rbac/rbac-engine-simplified.test.ts +252 -0
  682. package/src/rbac/rbac-functions.test.ts +703 -0
  683. package/src/rbac/rbac-integration.test.ts +523 -0
  684. package/src/rbac/rbac-role-isolation.test.ts +456 -0
  685. package/src/rbac/request-deduplication.test.ts +352 -0
  686. package/src/rbac/request-deduplication.ts +5 -4
  687. package/src/rbac/scenarios.user-role.test.tsx +271 -0
  688. package/src/rbac/secureClient.test.ts +499 -115
  689. package/src/rbac/secureClient.ts +54 -28
  690. package/src/rbac/security.test.ts +448 -44
  691. package/src/rbac/security.ts +7 -6
  692. package/src/rbac/types/roleManagement.ts +66 -0
  693. package/src/rbac/types.test.ts +236 -0
  694. package/src/rbac/types.ts +7 -5
  695. package/src/rbac/utils/clientSecurity.test.ts +192 -0
  696. package/src/rbac/utils/clientSecurity.ts +6 -4
  697. package/src/rbac/utils/contextValidator.test.ts +126 -0
  698. package/src/rbac/utils/contextValidator.ts +6 -3
  699. package/src/rbac/utils/deep-equal.test.ts +76 -0
  700. package/src/rbac/utils/eventContext.test.ts +401 -0
  701. package/src/rbac/utils/eventContext.ts +38 -34
  702. package/src/rbac/utils/fetchPermissionMap.ts +13 -0
  703. package/src/rbac/utils/permissionMapHelpers.ts +34 -0
  704. package/src/rbac/utils/roleManagementRpc.ts +303 -0
  705. package/src/services/AuthService.edge-cases.test.ts +746 -0
  706. package/src/services/AuthService.restoreSession.test.ts +59 -0
  707. package/src/services/AuthService.test.ts +1362 -0
  708. package/src/services/AuthService.ts +197 -216
  709. package/src/services/BaseService.edge-cases.test.ts +506 -0
  710. package/src/services/BaseService.test.ts +363 -0
  711. package/src/services/EventService.edge-cases.test.ts +636 -0
  712. package/src/services/EventService.eventColours.test.ts +64 -0
  713. package/src/services/EventService.test.ts +1250 -0
  714. package/src/services/EventService.ts +244 -315
  715. package/src/services/InactivityService.edge-cases.test.ts +492 -0
  716. package/src/services/InactivityService.lifecycle.test.ts +406 -0
  717. package/src/services/InactivityService.test.ts +829 -0
  718. package/src/services/InactivityService.ts +172 -213
  719. package/src/services/OrganisationService.edge-cases.test.ts +633 -0
  720. package/src/services/OrganisationService.pagination.test.ts +409 -0
  721. package/src/services/OrganisationService.test.ts +1579 -0
  722. package/src/services/OrganisationService.ts +186 -257
  723. package/src/services/base/BaseService.test.ts +214 -0
  724. package/src/services/interfaces/IAuthService.test.ts +184 -0
  725. package/src/services/interfaces/IAuthService.ts +10 -9
  726. package/src/services/interfaces/IEventService.test.ts +176 -0
  727. package/src/services/interfaces/IInactivityService.test.ts +183 -0
  728. package/src/services/interfaces/IOrganisationService.test.ts +207 -0
  729. package/src/services/interfaces/IOrganisationService.ts +0 -1
  730. package/src/styles/core.css +244 -12
  731. package/src/theming/parseEventColours.test.ts +321 -0
  732. package/src/theming/parseEventColours.ts +18 -9
  733. package/src/theming/runtime.test.ts +495 -0
  734. package/src/theming/runtime.ts +72 -7
  735. package/src/types/api-result.ts +53 -0
  736. package/src/types/auth.ts +0 -1
  737. package/src/types/core.test.ts +397 -0
  738. package/src/types/database-generated.test.ts +78 -0
  739. package/src/types/database.generated.ts +45 -10
  740. package/src/types/event.ts +39 -19
  741. package/src/types/file-reference.test.ts +351 -0
  742. package/src/types/file-reference.ts +37 -12
  743. package/src/types/guards.test.ts +246 -0
  744. package/src/types/index.test.ts +265 -0
  745. package/src/types/index.ts +3 -0
  746. package/src/types/organisation.roles.test.ts +55 -0
  747. package/src/types/organisation.test.ts +1105 -0
  748. package/src/types/organisation.ts +15 -15
  749. package/src/types/rpc-responses.ts +33 -0
  750. package/src/types/supabase.ts +14 -6
  751. package/src/types/theme.test.ts +830 -0
  752. package/src/types/type-validation.test.ts +526 -0
  753. package/src/types/validation.test.ts +729 -0
  754. package/src/types/vitest-globals.d.ts +1 -1
  755. package/src/utils/app/appConfig.test.ts +235 -0
  756. package/src/utils/app/appIdResolver.test.ts +252 -57
  757. package/src/utils/app/appIdResolver.ts +31 -20
  758. package/src/utils/app/appNameResolver.test.ts +18 -10
  759. package/src/utils/app/appNameResolver.ts +11 -9
  760. package/src/utils/app/appPortMap.test.ts +125 -0
  761. package/src/utils/app/appPortMap.ts +51 -0
  762. package/src/utils/app/buildAppUrl.test.ts +273 -0
  763. package/src/utils/app/buildAppUrl.ts +114 -0
  764. package/src/utils/appConfig.unit.test.ts +55 -0
  765. package/src/utils/audit/audit.test.ts +354 -39
  766. package/src/utils/audit.unit.test.ts +69 -0
  767. package/src/utils/auth-utils.unit.test.ts +69 -0
  768. package/src/utils/bundleAnalysis.unit.test.ts +326 -0
  769. package/src/utils/cn.unit.test.ts +34 -0
  770. package/src/utils/context/organisationContext.test.ts +115 -95
  771. package/src/utils/context/organisationContext.ts +32 -43
  772. package/src/utils/context/sessionTracking.test.ts +354 -0
  773. package/src/utils/core/cn.test.ts +66 -0
  774. package/src/utils/core/debugLogger.test.ts +113 -0
  775. package/src/utils/core/debugLogger.ts +15 -8
  776. package/src/utils/core/logger.test.ts +217 -0
  777. package/src/utils/core/logger.ts +20 -16
  778. package/src/utils/core/mergeRefs.ts +24 -0
  779. package/src/utils/debugLogger.test.ts +417 -0
  780. package/src/utils/device/deviceFingerprint.test.ts +8 -5
  781. package/src/utils/device/deviceFingerprint.ts +3 -3
  782. package/src/utils/deviceFingerprint.unit.test.ts +818 -0
  783. package/src/utils/dynamic/createLazyComponent.tsx +46 -0
  784. package/src/utils/dynamic/dynamicUtils.test.ts +185 -0
  785. package/src/utils/dynamic/dynamicUtils.ts +6 -6
  786. package/src/utils/dynamic/lazyLoad.test.tsx +156 -0
  787. package/src/utils/dynamic/lazyLoad.tsx +8 -36
  788. package/src/utils/dynamic/papaparseLoader.ts +7 -0
  789. package/src/utils/dynamicUtils.unit.test.ts +331 -0
  790. package/src/utils/file-reference/file-reference.test.ts +1238 -0
  791. package/src/utils/file-reference/index.ts +330 -348
  792. package/src/utils/formatDate.unit.test.ts +109 -0
  793. package/src/utils/formatting/formatDate.test.ts +22 -148
  794. package/src/utils/formatting/formatDateTime.test.ts +41 -119
  795. package/src/utils/formatting/formatDateTimeTimezone.test.ts +41 -85
  796. package/src/utils/formatting/formatNumber.test.ts +259 -0
  797. package/src/utils/formatting/formatTime.test.ts +36 -128
  798. package/src/utils/formatting/formatting.ts +1 -1
  799. package/src/utils/formatting.unit.test.ts +99 -0
  800. package/src/utils/google-places/googlePlacesUtils.test.ts +127 -36
  801. package/src/utils/google-places/googlePlacesUtils.ts +67 -86
  802. package/src/utils/google-places/loadGoogleMapsScript.test.ts +68 -8
  803. package/src/utils/google-places/loadGoogleMapsScript.ts +140 -118
  804. package/src/utils/index.ts +52 -11
  805. package/src/utils/index.unit.test.ts +251 -0
  806. package/src/utils/lazyLoad.unit.test.tsx +319 -0
  807. package/src/utils/location/location.test.ts +19 -116
  808. package/src/utils/logger.unit.test.ts +398 -0
  809. package/src/utils/organisationContext.unit.test.ts +180 -0
  810. package/src/utils/performance/bundleAnalysis.test.ts +148 -0
  811. package/src/utils/performance/bundleAnalysis.ts +16 -22
  812. package/src/utils/performance/performanceBenchmark.test.ts +251 -0
  813. package/src/utils/performance/performanceBenchmark.ts +12 -4
  814. package/src/utils/performance/performanceBudgets.test.ts +241 -0
  815. package/src/utils/performance/performanceBudgets.ts +9 -6
  816. package/src/utils/performanceBenchmark.test.ts +174 -0
  817. package/src/utils/performanceBudgets.unit.test.ts +288 -0
  818. package/src/utils/permissionTypes.unit.test.ts +250 -0
  819. package/src/utils/permissionUtils.unit.test.ts +362 -0
  820. package/src/utils/permissions/permissionTypes.test.ts +149 -0
  821. package/src/utils/permissions/permissionUtils.test.ts +20 -42
  822. package/src/utils/persistence/keyDerivation.test.ts +306 -0
  823. package/src/utils/persistence/sensitiveFieldDetection.test.ts +271 -0
  824. package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
  825. package/src/utils/request-deduplication.test.ts +349 -0
  826. package/src/utils/request-deduplication.ts +6 -4
  827. package/src/utils/sanitization.unit.test.ts +346 -0
  828. package/src/utils/schemaUtils.unit.test.ts +441 -0
  829. package/src/utils/secureDataAccess.unit.test.ts +334 -0
  830. package/src/utils/secureErrors.unit.test.ts +390 -0
  831. package/src/utils/secureStorage.unit.test.ts +289 -0
  832. package/src/utils/security/auth-utils.ts +38 -27
  833. package/src/utils/security/secureDataAccess.test.ts +22 -191
  834. package/src/utils/security/secureDataAccess.ts +241 -281
  835. package/src/utils/security/secureErrors.test.ts +163 -0
  836. package/src/utils/security/secureStorage.test.ts +156 -0
  837. package/src/utils/security/secureStorage.ts +1 -1
  838. package/src/utils/security/security.test.ts +212 -0
  839. package/src/utils/security/security.ts +15 -18
  840. package/src/utils/security/securityMonitor.test.ts +90 -0
  841. package/src/utils/security/securityMonitor.ts +1 -1
  842. package/src/utils/security.unit.test.ts +155 -0
  843. package/src/utils/securityMonitor.unit.test.ts +276 -0
  844. package/src/utils/sessionTracking.unit.test.ts +218 -0
  845. package/src/utils/storage/config.unit.test.ts +239 -0
  846. package/src/utils/storage/helpers.test.ts +769 -456
  847. package/src/utils/storage/helpers.ts +174 -253
  848. package/src/utils/storage/index.unit.test.ts +68 -0
  849. package/src/utils/storage/storageUtils.ts +32 -0
  850. package/src/utils/storage/types.ts +9 -2
  851. package/src/utils/supabase/createBaseClient.test.ts +201 -0
  852. package/src/utils/supabase/createBaseClient.ts +2 -1
  853. package/src/utils/timezone/timezone.test.ts +26 -44
  854. package/src/utils/timezone.test.ts +345 -0
  855. package/src/utils/validation/common.test.ts +115 -0
  856. package/src/utils/validation/csrf.test.ts +198 -0
  857. package/src/utils/validation/csrf.ts +42 -41
  858. package/src/utils/validation/htmlSanitization.ts +27 -31
  859. package/src/utils/validation/htmlSanitization.unit.test.ts +618 -0
  860. package/src/utils/validation/passwordSchema.test.ts +164 -0
  861. package/src/utils/validation/schema.test.ts +127 -0
  862. package/src/utils/validation/schema.ts +6 -3
  863. package/src/utils/validation/sqlInjectionProtection.test.ts +165 -0
  864. package/src/utils/validation/sqlInjectionProtection.ts +2 -2
  865. package/src/utils/validation/user.test.ts +173 -0
  866. package/src/utils/validation/validation.test.ts +197 -0
  867. package/src/utils/validation/validationUtils.test.ts +294 -0
  868. package/src/utils/validation.unit.test.ts +307 -0
  869. package/src/utils/validationUtils.unit.test.ts +558 -0
  870. package/src/vite-env.d.ts +6 -0
  871. package/dist/AuthService-DmfO5rGS.d.ts +0 -524
  872. package/dist/DataTable-DRUIgtUH.d.ts +0 -166
  873. package/dist/DataTable-SOAFXIWY.js +0 -15
  874. package/dist/PublicPageProvider-CIGSujI2.d.ts +0 -4147
  875. package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
  876. package/dist/UnifiedAuthProvider-CKvHP1MK.d.ts +0 -139
  877. package/dist/api-7P7DI652.js +0 -4
  878. package/dist/audit-MYQXYZFU.js +0 -3
  879. package/dist/auth-BZOJqrdd.d.ts +0 -49
  880. package/dist/chunk-4DDCYDQ3.js +0 -544
  881. package/dist/chunk-5HNSDQWH.js +0 -5046
  882. package/dist/chunk-5W2A3DRC.js +0 -164
  883. package/dist/chunk-6GLLNA6U.js +0 -31
  884. package/dist/chunk-7ILTDCL2.js +0 -80
  885. package/dist/chunk-A3W6LW53.js +0 -70
  886. package/dist/chunk-AHU7G2R5.js +0 -423
  887. package/dist/chunk-C7ZQ5O4C.js +0 -481
  888. package/dist/chunk-EF2UGZWY.js +0 -611
  889. package/dist/chunk-FEJLJNWA.js +0 -181
  890. package/dist/chunk-FYHN4DD5.js +0 -415
  891. package/dist/chunk-GS5672WG.js +0 -2003
  892. package/dist/chunk-HF6O3O37.js +0 -187
  893. package/dist/chunk-J2U36LHD.js +0 -8517
  894. package/dist/chunk-LX6U42O3.js +0 -2177
  895. package/dist/chunk-MPBLMWVR.js +0 -2161
  896. package/dist/chunk-OJ4SKRSV.js +0 -105
  897. package/dist/chunk-S6ZQKDY6.js +0 -62
  898. package/dist/chunk-S7DKJPLT.js +0 -699
  899. package/dist/chunk-T5CVK4R3.js +0 -2816
  900. package/dist/chunk-TTRFSOKR.js +0 -121
  901. package/dist/chunk-Z2FNRKF3.js +0 -994
  902. package/dist/database.generated-DT8JTZiP.d.ts +0 -9406
  903. package/dist/event-CW5YB_2p.d.ts +0 -239
  904. package/dist/file-reference-BavO2eQj.d.ts +0 -148
  905. package/dist/functions-lBy5L2ry.d.ts +0 -208
  906. package/dist/timezone-0AyangqX.d.ts +0 -697
  907. package/dist/types-BeoeWV5I.d.ts +0 -110
  908. package/dist/types-DXstZpNI.d.ts +0 -614
  909. package/dist/types-t9H8qKRw.d.ts +0 -55
  910. package/dist/usePublicRouteParams-DQLrDqDb.d.ts +0 -876
  911. package/dist/useToast-AyaT-x7p.d.ts +0 -68
  912. package/dist/validation-643vUDZW.d.ts +0 -177
  913. package/scripts/build-docs-incremental.js +0 -179
  914. package/scripts/eslint-audit.cjs +0 -123
  915. package/scripts/generate-docs.js +0 -157
  916. package/scripts/install-cursor-rules.cjs +0 -255
  917. package/scripts/install-eslint-config.cjs +0 -349
  918. package/scripts/setup-build-cache.js +0 -73
  919. package/scripts/validate-pre-publish.js +0 -145
  920. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +0 -260
  921. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +0 -224
  922. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +0 -273
  923. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +0 -99
  924. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -448
  925. package/src/__tests__/helpers/__tests__/timer-utils.test.ts +0 -371
  926. package/src/__tests__/hooks/usePermissions.test.ts +0 -268
  927. package/src/__tests__/integration/UserProfile.test.tsx +0 -124
  928. package/src/__tests__/public-recipe-view.test.ts +0 -228
  929. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +0 -220
  930. package/src/__tests__/rls-policies.test.ts +0 -471
  931. package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +0 -759
  932. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +0 -524
  933. package/src/components/DataTable/__tests__/DataTable.export.test.tsx +0 -705
  934. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +0 -658
  935. package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +0 -192
  936. package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +0 -483
  937. package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
  938. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +0 -220
  939. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +0 -1474
  940. package/src/components/DataTable/__tests__/README.md +0 -145
  941. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +0 -788
  942. package/src/components/DataTable/__tests__/keyboard.test.tsx +0 -756
  943. package/src/components/DataTable/__tests__/mocks/MockRBACProvider.tsx +0 -66
  944. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +0 -730
  945. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -325
  946. package/src/components/DataTable/__tests__/styles.test.ts +0 -382
  947. package/src/components/DataTable/__tests__/test-utils/dataFactories.ts +0 -103
  948. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +0 -380
  949. package/src/components/DataTable/__tests__/test-utils.ts +0 -94
  950. package/src/components/DataTable/components/AccessDeniedPage.tsx +0 -159
  951. package/src/components/DataTable/components/ActionButtons.tsx +0 -190
  952. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +0 -160
  953. package/src/components/DataTable/components/ColumnFilter.tsx +0 -118
  954. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +0 -114
  955. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +0 -225
  956. package/src/components/DataTable/components/DataTableLayout.tsx +0 -573
  957. package/src/components/DataTable/components/DataTableModals.tsx +0 -245
  958. package/src/components/DataTable/components/DataTableToolbar.tsx +0 -271
  959. package/src/components/DataTable/components/EditFields.tsx +0 -327
  960. package/src/components/DataTable/components/EditableRow.tsx +0 -462
  961. package/src/components/DataTable/components/EmptyState.tsx +0 -79
  962. package/src/components/DataTable/components/FilterRow.tsx +0 -141
  963. package/src/components/DataTable/components/LoadingState.tsx +0 -17
  964. package/src/components/DataTable/components/PaginationControls.tsx +0 -289
  965. package/src/components/DataTable/components/RowComponent.tsx +0 -403
  966. package/src/components/DataTable/components/SortIndicator.tsx +0 -50
  967. package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -355
  968. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +0 -657
  969. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +0 -913
  970. package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +0 -572
  971. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +0 -612
  972. package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +0 -708
  973. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +0 -479
  974. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -475
  975. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +0 -157
  976. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +0 -1061
  977. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +0 -437
  978. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +0 -474
  979. package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +0 -617
  980. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -1093
  981. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +0 -139
  982. package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +0 -519
  983. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +0 -1004
  984. package/src/components/DataTable/components/cellValueUtils.ts +0 -40
  985. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +0 -53
  986. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -122
  987. package/src/components/DataTable/components/index.ts +0 -16
  988. package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +0 -342
  989. package/src/components/DataTable/core/ActionManager.ts +0 -235
  990. package/src/components/DataTable/core/ColumnManager.ts +0 -205
  991. package/src/components/DataTable/core/DataManager.ts +0 -188
  992. package/src/components/DataTable/core/LocalDataAdapter.ts +0 -274
  993. package/src/components/DataTable/core/PluginRegistry.ts +0 -229
  994. package/src/components/DataTable/core/StateManager.ts +0 -312
  995. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -123
  996. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +0 -305
  997. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -84
  998. package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -115
  999. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -100
  1000. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -120
  1001. package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -104
  1002. package/src/components/DataTable/core/index.ts +0 -1
  1003. package/src/components/DataTable/core/interfaces.ts +0 -338
  1004. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +0 -521
  1005. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +0 -167
  1006. package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +0 -124
  1007. package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +0 -117
  1008. package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +0 -102
  1009. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +0 -596
  1010. package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +0 -53
  1011. package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +0 -214
  1012. package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +0 -448
  1013. package/src/components/DataTable/hooks/index.ts +0 -13
  1014. package/src/components/DataTable/types.ts +0 -761
  1015. package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +0 -612
  1016. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +0 -94
  1017. package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +0 -266
  1018. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +0 -954
  1019. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +0 -573
  1020. package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +0 -247
  1021. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +0 -570
  1022. package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +0 -470
  1023. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +0 -251
  1024. package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +0 -207
  1025. package/src/components/DataTable/utils/index.ts +0 -10
  1026. package/src/components/PublicLayout/index.ts +0 -32
  1027. package/src/components/Select/hooks/useSelectEvents.ts +0 -87
  1028. package/src/components/Select/hooks/useSelectSearch.ts +0 -91
  1029. package/src/components/Select/hooks/useSelectState.ts +0 -104
  1030. package/src/components/Select/utils/text.ts +0 -26
  1031. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -615
  1032. package/src/hooks/__tests__/hooks.integration.test.tsx +0 -607
  1033. package/src/hooks/__tests__/index.unit.test.ts +0 -220
  1034. package/src/hooks/__tests__/useApiFetch.unit.test.ts +0 -111
  1035. package/src/hooks/__tests__/useAppConfig.unit.test.ts +0 -347
  1036. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +0 -144
  1037. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +0 -776
  1038. package/src/hooks/__tests__/useDataTableState.test.ts +0 -76
  1039. package/src/hooks/__tests__/useDebounce.unit.test.ts +0 -82
  1040. package/src/hooks/__tests__/useEvents.unit.test.ts +0 -252
  1041. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +0 -1112
  1042. package/src/hooks/__tests__/useFileUrl.unit.test.ts +0 -916
  1043. package/src/hooks/__tests__/useFileUrlCache.test.ts +0 -129
  1044. package/src/hooks/__tests__/useFocusManagement.unit.test.ts +0 -230
  1045. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +0 -828
  1046. package/src/hooks/__tests__/useFormDialog.test.ts +0 -478
  1047. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +0 -446
  1048. package/src/hooks/__tests__/useIsMobile.unit.test.ts +0 -317
  1049. package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +0 -910
  1050. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -294
  1051. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +0 -961
  1052. package/src/hooks/__tests__/useOrganisations.unit.test.ts +0 -369
  1053. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +0 -694
  1054. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
  1055. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
  1056. package/src/hooks/__tests__/usePreventTabReload.test.ts +0 -88
  1057. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -785
  1058. package/src/hooks/__tests__/usePublicEvent.test.ts +0 -678
  1059. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -630
  1060. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +0 -951
  1061. package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +0 -443
  1062. package/src/hooks/__tests__/useQueryCache.test.ts +0 -144
  1063. package/src/hooks/__tests__/useRBAC.unit.test.ts +0 -236
  1064. package/src/hooks/__tests__/useSessionDraft.test.ts +0 -163
  1065. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +0 -390
  1066. package/src/hooks/__tests__/useStorage.unit.test.ts +0 -751
  1067. package/src/hooks/__tests__/useToast.unit.test.tsx +0 -481
  1068. package/src/hooks/__tests__/useZodForm.unit.test.tsx +0 -37
  1069. package/src/hooks/public/index.ts +0 -36
  1070. package/src/hooks/public/usePublicFileDisplay.ts +0 -504
  1071. package/src/hooks/useFileDisplay.ts +0 -715
  1072. package/src/providers/OrganisationProvider.tsx +0 -92
  1073. package/src/providers/__tests__/AuthProvider.test.tsx +0 -287
  1074. package/src/providers/__tests__/EventProvider.test.tsx +0 -551
  1075. package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
  1076. package/src/providers/__tests__/InactivityProvider.test.tsx +0 -572
  1077. package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -617
  1078. package/src/providers/__tests__/ProviderLifecycle.test.tsx +0 -424
  1079. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +0 -596
  1080. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +0 -263
  1081. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +0 -294
  1082. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +0 -434
  1083. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +0 -313
  1084. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +0 -486
  1085. package/src/rbac/__tests__/cache-invalidation.test.ts +0 -399
  1086. package/src/rbac/__tests__/engine.comprehensive.test.ts +0 -813
  1087. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
  1088. package/src/rbac/__tests__/rbac-core.test.tsx +0 -276
  1089. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -392
  1090. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +0 -258
  1091. package/src/rbac/__tests__/rbac-functions.test.ts +0 -647
  1092. package/src/rbac/__tests__/rbac-integration.test.ts +0 -524
  1093. package/src/rbac/__tests__/rbac-role-isolation.test.ts +0 -456
  1094. package/src/rbac/__tests__/scenarios.user-role.test.tsx +0 -282
  1095. package/src/rbac/audit-enhanced.ts +0 -384
  1096. package/src/rbac/compliance/database-validator.ts +0 -165
  1097. package/src/rbac/compliance/index.ts +0 -48
  1098. package/src/rbac/compliance/pattern-detector.ts +0 -553
  1099. package/src/rbac/compliance/quick-fix-suggestions.ts +0 -209
  1100. package/src/rbac/compliance/runtime-compliance.ts +0 -99
  1101. package/src/rbac/compliance/setup-validator.ts +0 -131
  1102. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +0 -975
  1103. package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +0 -248
  1104. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +0 -242
  1105. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +0 -1107
  1106. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +0 -184
  1107. package/src/rbac/components/index.ts +0 -26
  1108. package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -432
  1109. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +0 -579
  1110. package/src/rbac/hooks/index.ts +0 -34
  1111. package/src/rbac/hooks/permissions/index.ts +0 -4
  1112. package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
  1113. package/src/rbac/utils/__tests__/contextValidator.test.ts +0 -128
  1114. package/src/rbac/utils/__tests__/deep-equal.test.ts +0 -53
  1115. package/src/rbac/utils/__tests__/eventContext.test.ts +0 -433
  1116. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
  1117. package/src/services/__tests__/AuthService.restoreSession.test.ts +0 -39
  1118. package/src/services/__tests__/AuthService.test.ts +0 -1332
  1119. package/src/services/__tests__/BaseService.test.ts +0 -314
  1120. package/src/services/__tests__/EventService.eventColours.test.ts +0 -76
  1121. package/src/services/__tests__/EventService.test.ts +0 -1025
  1122. package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -411
  1123. package/src/services/__tests__/InactivityService.test.ts +0 -654
  1124. package/src/services/__tests__/OrganisationService.pagination.test.ts +0 -409
  1125. package/src/services/__tests__/OrganisationService.test.ts +0 -1176
  1126. package/src/theming/__tests__/parseEventColours.test.ts +0 -321
  1127. package/src/theming/__tests__/runtime.test.ts +0 -569
  1128. package/src/types/__tests__/file-reference.test.ts +0 -447
  1129. package/src/types/__tests__/guards.test.ts +0 -246
  1130. package/src/types/__tests__/organisation.roles.test.ts +0 -55
  1131. package/src/types/__tests__/organisation.test.ts +0 -1133
  1132. package/src/types/__tests__/theme.test.ts +0 -830
  1133. package/src/types/__tests__/type-validation.test.ts +0 -526
  1134. package/src/types/__tests__/validation.test.ts +0 -731
  1135. package/src/utils/__tests__/appConfig.unit.test.ts +0 -55
  1136. package/src/utils/__tests__/audit.unit.test.ts +0 -69
  1137. package/src/utils/__tests__/auth-utils.unit.test.ts +0 -70
  1138. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +0 -339
  1139. package/src/utils/__tests__/cn.unit.test.ts +0 -34
  1140. package/src/utils/__tests__/debugLogger.test.ts +0 -417
  1141. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +0 -818
  1142. package/src/utils/__tests__/dynamicUtils.unit.test.ts +0 -318
  1143. package/src/utils/__tests__/formatDate.unit.test.ts +0 -109
  1144. package/src/utils/__tests__/formatting.unit.test.ts +0 -99
  1145. package/src/utils/__tests__/index.unit.test.ts +0 -251
  1146. package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -321
  1147. package/src/utils/__tests__/logger.unit.test.ts +0 -398
  1148. package/src/utils/__tests__/organisationContext.unit.test.ts +0 -191
  1149. package/src/utils/__tests__/performanceBenchmark.test.ts +0 -175
  1150. package/src/utils/__tests__/performanceBudgets.unit.test.ts +0 -253
  1151. package/src/utils/__tests__/permissionTypes.unit.test.ts +0 -250
  1152. package/src/utils/__tests__/permissionUtils.unit.test.ts +0 -362
  1153. package/src/utils/__tests__/sanitization.unit.test.ts +0 -346
  1154. package/src/utils/__tests__/schemaUtils.unit.test.ts +0 -441
  1155. package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -335
  1156. package/src/utils/__tests__/secureErrors.unit.test.ts +0 -390
  1157. package/src/utils/__tests__/secureStorage.unit.test.ts +0 -289
  1158. package/src/utils/__tests__/security.unit.test.ts +0 -149
  1159. package/src/utils/__tests__/securityMonitor.unit.test.ts +0 -276
  1160. package/src/utils/__tests__/sessionTracking.unit.test.ts +0 -218
  1161. package/src/utils/__tests__/timezone.test.ts +0 -345
  1162. package/src/utils/__tests__/validation.unit.test.ts +0 -308
  1163. package/src/utils/__tests__/validationUtils.unit.test.ts +0 -555
  1164. package/src/utils/app/appNameResolver.simple.test.ts +0 -212
  1165. package/src/utils/file-reference/__tests__/file-reference.test.ts +0 -875
  1166. package/src/utils/google-places/index.ts +0 -26
  1167. package/src/utils/location/index.ts +0 -16
  1168. package/src/utils/persistence/__tests__/keyDerivation.test.ts +0 -135
  1169. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +0 -123
  1170. package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
  1171. package/src/utils/storage/__tests__/index.unit.test.ts +0 -16
  1172. package/src/utils/storage/index.ts +0 -67
  1173. package/src/utils/timezone/index.ts +0 -17
  1174. package/src/utils/validation/__tests__/csrf.test.ts +0 -105
  1175. package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +0 -598
  1176. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +0 -92
  1177. package/src/utils/validation/__tests__/validationUtils.test.ts +0 -72
  1178. package/src/utils/validation/index.ts +0 -73
  1179. /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
  1180. /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
  1181. /package/src/providers/{__tests__/README.md → README.md} +0 -0
  1182. /package/src/types/{__tests__/README.md → README.md} +0 -0
@@ -97,113 +97,184 @@
97
97
  * - Tailwind CSS - Styling
98
98
  */
99
99
 
100
- import React, { useState, useEffect, useMemo, useCallback } from 'react';
100
+ import React, { useEffect } from 'react';
101
101
  import { Outlet, useNavigate, useLocation } from 'react-router-dom';
102
+ import type { User } from '@supabase/supabase-js';
102
103
  import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
103
104
  import { useOrganisations } from '../../hooks/useOrganisations';
104
- import { useEvents } from '../../hooks/useEvents';
105
+ import { useOptionalEvents } from '../../hooks/useEvents';
105
106
  import { useEventTheme } from '../../hooks/useEventTheme';
106
- import type { Event } from '../../types/event';
107
- import { useCan, useResolvedScope, useRBAC } from '../../rbac/hooks';
108
- import { createScopeFromEvent } from '../../rbac/utils/eventContext';
109
- import { getCurrentAppName } from '../../utils/app/appNameResolver';
110
- import { isSuperAdmin as checkSuperAdminApi } from '../../rbac/api';
107
+ import { useRBAC } from '../../rbac/hooks/useRBAC';
111
108
  import { logger } from '../../utils/core/logger';
112
- import type { Permission, Scope } from '../../rbac/types';
113
- import { EventContextRequiredError, OrganisationContextRequiredError } from '../../rbac/types';
114
- import { AccessDenied } from '../../rbac/components/AccessDenied';
115
-
116
- // Stable empty objects to prevent infinite loops
117
- const EMPTY_PAGE_ID_MAPPING = {};
118
- const EMPTY_ROUTE_PERMISSIONS = {};
119
- import { Button } from '../Button';
109
+ import { useSuperAdminFallback } from './useSuperAdminFallback';
110
+ import { useFilteredNavItems } from './useFilteredNavItems';
111
+ import { useRoleBasedRouteAccess } from './useRoleBasedRouteAccess';
112
+ import { usePaceAppLayoutConfig } from './usePaceAppLayoutConfig';
113
+ import { usePaceAppLayoutPermissions } from './usePaceAppLayoutPermissions';
114
+ import { usePaceAppLayoutGate } from './usePaceAppLayoutGate';
115
+ import { usePaceAppLayoutScope } from './usePaceAppLayoutScope';
120
116
  import { Footer } from '../Footer';
121
117
  import { Header } from '../Header';
122
118
  import type { NavigationItem } from '../NavigationMenu/NavigationMenu';
123
- import type { PasswordChangeFormError } from '../PasswordChange/PasswordChangeForm';
124
- import { LoadingSpinner } from '../LoadingSpinner';
125
- // Define Operation type locally since old RBAC types are removed
119
+ import { setupPrintDateTime } from '../../theming/runtime';
120
+
126
121
  type Operation = 'read' | 'create' | 'update' | 'delete' | 'manage';
122
+ const EMPTY_PAGE_ID_MAPPING: Record<string, string> = {};
123
+ const EMPTY_ROUTE_PERMISSIONS: Record<string, Operation> = {};
124
+
125
+ /** Route config item for role-based routing. */
126
+ export interface PaceAppLayoutRouteConfigItem {
127
+ path: string;
128
+ component: React.ComponentType;
129
+ permissions: string[];
130
+ roles?: string[];
131
+ accessLevel?: string;
132
+ pageId?: string;
133
+ strictMode?: boolean;
134
+ meta?: {
135
+ title?: string;
136
+ description?: string;
137
+ requiresAuth?: boolean;
138
+ hidden?: boolean;
139
+ };
140
+ }
127
141
 
128
- /**
129
- * Props for the PaceAppLayout component.
130
- * Configures the application layout including navigation, header, and footer.
131
- */
132
- export interface PaceAppLayoutProps {
133
- /** The name of the application to be displayed in the header. */
142
+ /** Optional grouped permission options. When provided, overrides flat permission props. */
143
+ export interface PaceAppLayoutPermissionConfig {
144
+ enforcePermissions?: boolean;
145
+ defaultPermission?: Operation;
146
+ routePermissions?: Record<string, Operation>;
147
+ pageIdMapping?: Record<string, string>;
148
+ permissionFallback?: React.ReactNode;
149
+ strictMode?: boolean;
150
+ enforcePagePermissions?: boolean;
151
+ pagePermissionFallback?: React.ReactNode;
152
+ auditLog?: boolean;
153
+ onPageAccessDenied?: (pageName: string, operation: string) => void;
154
+ onStrictModeViolation?: (pageName: string, operation: string) => void;
155
+ }
156
+
157
+ /** Optional grouped routing options. When provided, overrides flat routing props. */
158
+ export interface PaceAppLayoutRoutingConfig {
159
+ roleBasedRouting?: boolean;
160
+ routeConfig?: PaceAppLayoutRouteConfigItem[];
161
+ fallbackRoute?: string;
162
+ onRouteAccessDenied?: (route: string, reason: string) => void;
163
+ onRouteStrictModeViolation?: (route: string, reason: string) => void;
164
+ }
165
+
166
+ /** Layout props for PaceAppLayout (ISP). */
167
+ export interface PaceAppLayoutPropsLayout {
134
168
  appName: string;
135
- /** Optional navigation items for the header menu. If not provided, uses default navigation. */
136
169
  navItems?: NavigationItem[];
137
- /** Show/hide unified context selector (shows all accessible orgs and events) - default: true */
138
170
  showContextSelector?: boolean;
139
- /** Show organisations in context selector - default: true */
140
171
  showOrganisations?: boolean;
141
- /** Show events in context selector - default: true */
142
172
  showEvents?: boolean;
143
- /** Custom actions to display in the header (between event selector and user menu) */
144
173
  headerActions?: React.ReactNode;
145
- /** Custom logo component (overrides default logo) */
146
- customLogo?: React.ReactNode;
147
- /** URL to navigate to when logo is clicked (defaults to '/dashboard') */
148
174
  logoHref?: string;
149
- /** Custom user menu component (overrides default user menu) */
150
175
  customUserMenu?: React.ReactNode;
151
- /** Custom className for the header */
152
176
  headerClassName?: string;
153
- /** Show/hide user menu */
154
177
  showUserMenu?: boolean;
155
- /** Enable layout-level permission enforcement */
178
+ }
179
+
180
+ /** Permission props for PaceAppLayout (ISP). */
181
+ export interface PaceAppLayoutPropsPermission {
182
+ permissionConfig?: PaceAppLayoutPermissionConfig;
156
183
  enforcePermissions?: boolean;
157
- /** Default permission to check for all routes (when enforcePermissions is true) */
158
184
  defaultPermission?: Operation;
159
- /** Route-specific permissions mapping */
160
185
  routePermissions?: Record<string, Operation>;
161
- /** Fallback component to show when user lacks permission */
162
186
  permissionFallback?: React.ReactNode;
163
- /** Custom permission page ID mapping */
164
187
  pageIdMapping?: Record<string, string>;
165
-
166
- // NEW: Phase 1 - Enhanced Security Features
167
- /** Enable strict mode to prevent bypassing permission checks (default: true) */
168
188
  strictMode?: boolean;
169
- /** Enable page-level permission enforcement (default: false) */
170
189
  enforcePagePermissions?: boolean;
171
- /** Default page permission fallback component */
172
190
  pagePermissionFallback?: React.ReactNode;
173
- /** Enable audit logging for all permission checks (default: true) */
174
191
  auditLog?: boolean;
175
- /** Callback when page access is denied */
176
192
  onPageAccessDenied?: (pageName: string, operation: string) => void;
177
- /** Callback when strict mode violation occurs */
178
193
  onStrictModeViolation?: (pageName: string, operation: string) => void;
179
-
180
- // NEW: Phase 2 - Enhanced Routing Features
181
- /** Enable role-based routing (default: false) */
194
+ }
195
+
196
+ /** Routing props for PaceAppLayout (ISP). */
197
+ export interface PaceAppLayoutPropsRouting {
198
+ routingConfig?: PaceAppLayoutRoutingConfig;
182
199
  roleBasedRouting?: boolean;
183
- /** Route configuration for role-based routing */
184
- routeConfig?: Array<{
185
- path: string;
186
- component: React.ComponentType;
187
- permissions: string[];
188
- roles?: string[];
189
- accessLevel?: string;
190
- pageId?: string;
191
- strictMode?: boolean;
192
- meta?: {
193
- title?: string;
194
- description?: string;
195
- requiresAuth?: boolean;
196
- hidden?: boolean;
197
- };
198
- }>;
199
- /** Fallback route for unauthorized access */
200
+ routeConfig?: PaceAppLayoutRouteConfigItem[];
200
201
  fallbackRoute?: string;
201
- /** Callback when route access is denied */
202
202
  onRouteAccessDenied?: (route: string, reason: string) => void;
203
- /** Callback when route strict mode violation occurs */
204
203
  onRouteStrictModeViolation?: (route: string, reason: string) => void;
205
204
  }
206
205
 
206
+ /**
207
+ * Props for the PaceAppLayout component (ISP: composed from focused interfaces).
208
+ * Permission and routing options can be passed either as flat props or via permissionConfig / routingConfig.
209
+ */
210
+ export type PaceAppLayoutProps = PaceAppLayoutPropsLayout
211
+ & PaceAppLayoutPropsPermission
212
+ & PaceAppLayoutPropsRouting;
213
+
214
+ /** Renders the main layout structure (Header + main + Footer). Extracted to keep PaceAppLayout under max-lines. */
215
+ function PaceAppLayoutContent({
216
+ appName,
217
+ logoHref,
218
+ filteredMenuItems,
219
+ headerActions,
220
+ customUserMenu,
221
+ user,
222
+ onSignOut,
223
+ onChangePassword,
224
+ headerClassName,
225
+ showContextSelector,
226
+ showOrganisations,
227
+ showEvents,
228
+ showUserMenu,
229
+ navigate,
230
+ }: {
231
+ appName: string;
232
+ logoHref: string;
233
+ filteredMenuItems: NavigationItem[];
234
+ headerActions?: React.ReactNode;
235
+ customUserMenu?: React.ReactNode;
236
+ user: User | null | undefined;
237
+ onSignOut: () => Promise<void>;
238
+ onChangePassword: (newPassword: string, confirmPassword: string) => Promise<{ error?: { message: string; code?: string } }>;
239
+ headerClassName?: string;
240
+ showContextSelector: boolean;
241
+ showOrganisations: boolean;
242
+ showEvents: boolean;
243
+ showUserMenu: boolean;
244
+ navigate: (to: string) => void;
245
+ }) {
246
+ return (
247
+ <>
248
+ <Header
249
+ logoUrl={`/${appName.toLowerCase()}_logo_wide.svg`}
250
+ logoAlt={`${appName} Logo`}
251
+ logoHref={logoHref}
252
+ navItems={filteredMenuItems}
253
+ actions={headerActions}
254
+ userMenu={customUserMenu}
255
+ user={user}
256
+ onSignOut={onSignOut}
257
+ onChangePassword={onChangePassword}
258
+ currentPath={window.location.pathname}
259
+ onNavigate={(item) => {
260
+ if (item.href) {
261
+ navigate(item.href);
262
+ }
263
+ }}
264
+ showContextSelector={showContextSelector}
265
+ showOrganisations={showOrganisations}
266
+ showEvents={showEvents}
267
+ showUserMenu={showUserMenu}
268
+ className={headerClassName || "sticky top-0 z-header w-full"}
269
+ />
270
+ <main className="px-4 w-full max-w-app-content-max mx-auto py-8">
271
+ <Outlet />
272
+ </main>
273
+ <Footer />
274
+ </>
275
+ );
276
+ }
277
+
207
278
  /**
208
279
  * A consistent layout component for all PACE suite applications that provides a standard
209
280
  * structure with header, main content area, and footer.
@@ -352,747 +423,194 @@ export interface PaceAppLayoutProps {
352
423
  *
353
424
  * @since 0.1.0
354
425
  */
355
- export function PaceAppLayout({
356
- appName,
357
- navItems,
426
+ export function PaceAppLayout({
427
+ appName,
428
+ navItems,
358
429
  showContextSelector = true,
359
430
  showOrganisations = true,
360
431
  showEvents = true,
361
432
  headerActions,
362
- customLogo,
363
433
  logoHref = '/dashboard',
364
434
  customUserMenu,
365
435
  headerClassName,
366
436
  showUserMenu = true,
367
- enforcePermissions = false,
368
- defaultPermission = 'read',
369
- routePermissions = EMPTY_ROUTE_PERMISSIONS,
370
- permissionFallback,
371
- pageIdMapping = EMPTY_PAGE_ID_MAPPING,
372
- // NEW: Phase 1 - Enhanced Security Features
373
- strictMode = true,
374
- enforcePagePermissions = false,
375
- pagePermissionFallback,
376
- auditLog = true,
377
- onPageAccessDenied,
378
- onStrictModeViolation,
379
- // NEW: Phase 2 - Enhanced Routing Features
380
- roleBasedRouting = false,
381
- routeConfig = [],
382
- fallbackRoute = '/unauthorized',
383
- onRouteAccessDenied,
384
- onRouteStrictModeViolation
437
+ permissionConfig,
438
+ enforcePermissions: enforcePermissionsFlat = false,
439
+ defaultPermission: defaultPermissionFlat = 'read',
440
+ routePermissions: routePermissionsFlat = EMPTY_ROUTE_PERMISSIONS,
441
+ permissionFallback: permissionFallbackFlat,
442
+ pageIdMapping: pageIdMappingFlat = EMPTY_PAGE_ID_MAPPING,
443
+ strictMode: strictModeFlat = true,
444
+ enforcePagePermissions: enforcePagePermissionsFlat = false,
445
+ pagePermissionFallback: pagePermissionFallbackFlat,
446
+ auditLog: _auditLogFlat = true,
447
+ onPageAccessDenied: onPageAccessDeniedFlat,
448
+ onStrictModeViolation: onStrictModeViolationFlat,
449
+ routingConfig,
450
+ roleBasedRouting: roleBasedRoutingFlat = false,
451
+ routeConfig: routeConfigFlat = [],
452
+ fallbackRoute: fallbackRouteFlat = '/unauthorized',
453
+ onRouteAccessDenied: onRouteAccessDeniedFlat,
454
+ onRouteStrictModeViolation: onRouteStrictModeViolationFlat,
385
455
  }: PaceAppLayoutProps) {
386
- const { user, signOut, updatePassword, supabase, appId: contextAppId, selectedOrganisationId } = useUnifiedAuth(); // Get appId from context (resolved on login)
387
- const {
388
- selectedOrganisation,
389
- isContextReady,
390
- hasValidOrganisationContext,
391
- ensureOrganisationContext,
392
- isLoading: organisationLoading
456
+ const config = usePaceAppLayoutConfig({
457
+ permissionConfig,
458
+ enforcePermissions: enforcePermissionsFlat,
459
+ defaultPermission: defaultPermissionFlat,
460
+ routePermissions: routePermissionsFlat,
461
+ permissionFallback: permissionFallbackFlat,
462
+ pageIdMapping: pageIdMappingFlat,
463
+ strictMode: strictModeFlat,
464
+ enforcePagePermissions: enforcePagePermissionsFlat,
465
+ pagePermissionFallback: pagePermissionFallbackFlat,
466
+ onPageAccessDenied: onPageAccessDeniedFlat,
467
+ onStrictModeViolation: onStrictModeViolationFlat,
468
+ routingConfig,
469
+ roleBasedRouting: roleBasedRoutingFlat,
470
+ routeConfig: routeConfigFlat,
471
+ fallbackRoute: fallbackRouteFlat,
472
+ onRouteAccessDenied: onRouteAccessDeniedFlat,
473
+ onRouteStrictModeViolation: onRouteStrictModeViolationFlat,
474
+ });
475
+
476
+ const { user, signOut, updatePassword, supabase, appId: contextAppId, selectedOrganisationId } = useUnifiedAuth();
477
+ const {
478
+ selectedOrganisation,
479
+ isLoading: organisationLoading,
393
480
  } = useOrganisations();
394
- // Use useRBAC to get super admin status - it's more reliable than async check
395
- // Note: isSuperAdmin might be false initially while loading, but that's OK - we'll allow rendering
396
- // if organisation loading completes or if we're a super admin
397
481
  const { isSuperAdmin: isSuperAdminFromRBAC, isLoading: rbacLoading } = useRBAC();
482
+ const { isSuperAdminDirect, isCheckingSuperAdminDirect } = useSuperAdminFallback(user?.id, isSuperAdminFromRBAC);
398
483
 
399
- // Also check super admin status directly as a fallback (for ADMIN/PORTAL apps)
400
- // This allows super admins to proceed even if RBAC hasn't loaded yet
401
- const [isSuperAdminDirect, setIsSuperAdminDirect] = useState<boolean>(false);
402
- const [isCheckingSuperAdminDirect, setIsCheckingSuperAdminDirect] = useState<boolean>(false);
403
-
484
+ // Update --print-date-time when user prints (e.g. Ctrl+P) so printed pages show current date/time
404
485
  useEffect(() => {
405
- const checkSuperAdminDirect = async () => {
406
- if (!user?.id) {
407
- setIsSuperAdminDirect(false);
408
- setIsCheckingSuperAdminDirect(false);
409
- return;
410
- }
486
+ const teardown = setupPrintDateTime();
487
+ return teardown;
488
+ }, []);
411
489
 
412
- // Only skip if RBAC already confirmed super admin
413
- if (isSuperAdminFromRBAC) {
414
- setIsCheckingSuperAdminDirect(false);
415
- return;
416
- }
417
-
418
- setIsCheckingSuperAdminDirect(true);
419
- try {
420
- const superAdminStatus = await checkSuperAdminApi(user.id);
421
- setIsSuperAdminDirect(superAdminStatus);
422
- } catch (error) {
423
- logger.error('PaceAppLayout', 'Error checking super admin status directly', { userId: user?.id, error });
424
- setIsSuperAdminDirect(false);
425
- } finally {
426
- setIsCheckingSuperAdminDirect(false);
427
- }
428
- };
429
-
430
- checkSuperAdminDirect();
431
- }, [user?.id, isSuperAdminFromRBAC]);
432
-
433
- // Use direct check if RBAC hasn't loaded yet, otherwise use RBAC result
434
490
  const isSuperAdmin = isSuperAdminFromRBAC || isSuperAdminDirect;
435
491
  const navigate = useNavigate();
436
492
  const location = useLocation();
437
-
438
- // Apply event theme colors automatically
493
+
439
494
  useEventTheme();
440
-
441
- // Get selected event (optional)
442
- let selectedEvent: Event | null = null;
443
- try {
444
- const eventsContext = useEvents();
445
- selectedEvent = eventsContext.selectedEvent;
446
- } catch (error) {
447
- // Event provider not available - continue without event context
448
- }
449
-
450
- // Resolve scope for permission checking
451
- const { resolvedScope, isLoading: scopeLoading } = useResolvedScope({
452
- supabase: supabase || null,
453
- selectedOrganisationId: selectedOrganisation?.id || null,
454
- selectedEventId: selectedEvent?.event_id || null,
455
- selectedEventOrganisationId: selectedEvent?.organisation_id || null
495
+ const { selectedEvent } = useOptionalEvents();
496
+
497
+ const { scope, resolvedAppId, scopeLoading, baseMenuItems } = usePaceAppLayoutScope({
498
+ supabase,
499
+ selectedOrganisation,
500
+ selectedEvent,
501
+ contextAppId,
502
+ navItems,
456
503
  });
457
504
 
458
- // Use appId from context (resolved immediately on login) or fallback to resolvedScope
459
- // This ensures appId is available immediately without waiting for additional resolution
460
- const resolvedAppId = contextAppId || resolvedScope?.appId;
461
-
462
- // Build scope from resolved values
463
- // Preserve appId from resolvedScope or fallback to resolvedAppId
464
- // CRITICAL: Always create a new scope object from primitive values to ensure stable reference
465
- // This prevents useCan from re-checking permissions when resolvedScope changes reference but values are the same
466
- const scopeOrgId = resolvedScope?.organisationId || selectedOrganisation?.id || '';
467
- const scopeEventId = resolvedScope?.eventId || selectedEvent?.event_id || undefined;
468
- const scopeAppId = resolvedScope?.appId || resolvedAppId || undefined;
469
-
470
- const scope = useMemo<Scope>(() => {
471
- const newScope: Scope = {};
472
- if (scopeOrgId) {
473
- newScope.organisationId = scopeOrgId;
474
- }
475
- if (scopeEventId) {
476
- newScope.eventId = scopeEventId;
477
- }
478
- if (scopeAppId) {
479
- newScope.appId = scopeAppId;
480
- }
481
- return newScope;
482
- }, [scopeOrgId, scopeEventId, scopeAppId]);
483
-
484
- // Default navigation items if none provided
485
- const defaultNavItems: NavigationItem[] = useMemo(() => [
486
- { id: 'home', label: 'Home', href: '/', icon: 'Home' },
487
- { id: 'dashboard', label: 'Dashboard', href: '/dashboard', icon: 'LayoutDashboard' },
488
- { id: 'settings', label: 'Settings', href: '/settings', icon: 'Settings' },
489
- { id: 'ui-showcase', label: 'UI Showcase', href: '/ui-showcase', icon: 'Component' },
490
- { id: 'data-table-showcase', label: 'DataTable Showcase', href: '/data-table-showcase', icon: 'Table' },
491
- ], []);
492
-
493
- // Use provided navItems or fall back to default
494
- const baseMenuItems = useMemo(() => navItems || defaultNavItems, [navItems]);
495
-
496
- // Get current route permission requirements
497
- const currentRoutePermission = useMemo(() => {
498
- const currentPath = location.pathname;
499
- return routePermissions[currentPath] || defaultPermission;
500
- }, [location.pathname, routePermissions, defaultPermission]);
501
-
502
- // Get current page ID for permission checking
503
- // Extract base page name (first path segment) instead of full route path
504
- // Example: /organisation/scouts-victoria -> "organisation"
505
- const currentPageId = useMemo(() => {
506
- const currentPath = location.pathname;
507
- // Use pageIdMapping if provided (takes precedence)
508
- if (pageIdMapping[currentPath]) {
509
- return pageIdMapping[currentPath];
510
- }
511
- // Extract first path segment (base page name)
512
- const pathSegments = currentPath.slice(1).split('/').filter(Boolean);
513
- // Only return 'home' if there's actually a path segment, otherwise return empty string
514
- // This prevents checking permissions for a non-existent "home" page when the index route is used
515
- return pathSegments[0] || '';
516
- }, [location.pathname, pageIdMapping]);
517
-
518
- // Build permission string in format: operation:page.pageId
519
- const currentPermission = useMemo<Permission>(() => {
520
- // If enforcePermissions is false, don't check any permission (return empty string)
521
- // If currentPageId is empty (index route with no path segments), don't check permissions
522
- if (!enforcePermissions || !currentPageId) {
523
- return '' as Permission;
524
- }
525
- const permissionString = `${currentRoutePermission}:page.${currentPageId}`;
526
- return permissionString as Permission;
527
- }, [enforcePermissions, currentRoutePermission, currentPageId]);
528
-
529
- // Check super admin status before permission enforcement
530
- // Removed duplicate super admin check - using useRBAC hook instead
531
- // The useRBAC hook provides isSuperAdmin which is more reliable
532
-
533
- // Use useCan hook for permission checking (standardized approach)
534
- // Note: The database function already handles super admin bypass, but we check here
535
- // as an additional safety layer to prevent unnecessary permission checks
536
- // Pass appName to useCan so it can be passed to isPermitted for PORTAL/ADMIN special case
537
- // Only check permissions if enforcePermissions is true and we have a valid permission string
538
- const shouldCheckPermission = enforcePermissions && !!currentPermission && !!currentPageId;
539
- // Pass super admin status to avoid duplicate check - use null if still checking, false/true if known
540
- const superAdminStatus = isSuperAdminFromRBAC ? true : (isCheckingSuperAdminDirect ? null : isSuperAdminDirect);
541
- const { can: canFromHook, isLoading: isCheckingPermission, error: permissionError } = useCan(
542
- user?.id || '',
505
+ const permissions = usePaceAppLayoutPermissions({
506
+ config,
507
+ user,
543
508
  scope,
544
- shouldCheckPermission ? currentPermission : ('' as Permission),
545
- shouldCheckPermission ? currentPageId : '',
546
- true, // useCache
547
- superAdminStatus, // Pass super admin status to avoid duplicate check
548
- appName // Pass appName for PORTAL/ADMIN special case
549
- );
550
-
551
- // Permission enforcement state - super admin bypasses all checks
552
- // This ensures super admins never see permission errors even if useCan hasn't completed
553
- // Use combined super admin check (RBAC + direct check)
554
- const can = isSuperAdmin ? true : canFromHook;
555
- const hasPermission = enforcePermissions ? can : true;
556
-
557
- // Check if the permission error is a context error (missing event/org context)
558
- // Context errors should allow the page to render so it can show helpful messaging
559
- // Real permission denials should still show "Access Denied"
560
- const isContextError = useMemo(() => {
561
- if (!permissionError) {
562
- return false;
563
- }
564
- return (
565
- permissionError instanceof EventContextRequiredError ||
566
- permissionError instanceof OrganisationContextRequiredError ||
567
- permissionError.name === 'EventContextRequiredError' ||
568
- permissionError.name === 'OrganisationContextRequiredError'
569
- );
570
- }, [permissionError]);
571
-
572
- // Handle permission check results with audit logging and callbacks
573
- useEffect(() => {
574
- if (!enforcePermissions) {
575
- return;
576
- }
577
-
578
- // Only proceed when permission check is complete (not loading)
579
- // Super admin status is checked via useRBAC hook (isSuperAdminFromRBAC)
580
- // If RBAC is still loading, allow rendering to proceed (optimistic for super admins)
581
- if (isCheckingPermission) {
582
- return;
583
- }
584
-
585
- // NEW: Phase 1 - Enhanced Security Features
586
- // Handle strict mode violations - skip for super admins
587
- if (strictMode && !isSuperAdmin && !can) {
588
- logger.error('PaceAppLayout', 'STRICT MODE VIOLATION: User attempted to access protected page without permission', {
589
- pageName: currentPageId,
590
- operation: currentRoutePermission,
591
- userId: user?.id,
592
- isSuperAdmin: isSuperAdmin,
593
- timestamp: new Date().toISOString()
594
- });
595
-
596
- if (onStrictModeViolation) {
597
- onStrictModeViolation(currentPageId, currentRoutePermission);
598
- }
599
- }
600
-
601
- // Handle page access denied callback - skip for super admins
602
- if (!isSuperAdmin && !can && onPageAccessDenied) {
603
- onPageAccessDenied(currentPageId, currentRoutePermission);
604
- }
605
- }, [enforcePermissions, can, isCheckingPermission, isSuperAdmin, currentPageId, currentRoutePermission, user?.id, strictMode, auditLog, onPageAccessDenied, onStrictModeViolation]);
606
-
607
- // Filter navigation items based on permissions
608
- // Permission filtering is always enabled - users only see navigation items they have permission to access
609
- const [filteredMenuItems, setFilteredMenuItems] = useState<NavigationItem[]>(baseMenuItems);
610
-
611
- useEffect(() => {
612
- let isMounted = true;
613
-
614
- const filterItems = async () => {
615
- // Wait for organisation context to be ready before filtering
616
- // This prevents blocking navigation while context is loading
617
- if (!user?.id) {
618
- // User not loaded yet - show all items until context is ready
619
- if (isMounted) {
620
- setFilteredMenuItems(baseMenuItems);
621
- }
622
- return;
623
- }
624
-
625
- // Use the scope from context (includes selectedOrganisation and selectedEvent)
626
- // This is critical for event-based apps where event context comes from EventService, not user metadata
627
- const currentScope = scope;
628
-
629
- // Wait for scope to be fully resolved (including appId) before filtering
630
- // This is important because getPermissionMap requires appId to fetch pages
631
- // Check both resolvedScope.appId and resolvedAppId (fallback)
632
- // appId is now resolved immediately on login in UnifiedAuthProvider, so it should be available
633
- const hasAppId = currentScope.appId || resolvedAppId;
634
-
635
- // OPTIMIZATION: Show items optimistically while permissions load
636
- // This makes the menu appear immediately, then we filter when permissions are ready
637
- const hasOrganisationContext = currentScope.organisationId;
638
- const hasUser = !!user?.id;
639
-
640
- // Show navigation optimistically while waiting for context selection.
641
- // Only hide navigation if user is not loaded.
642
- // This prevents navigation from disappearing after initial render while waiting for context.
643
- if (!hasUser) {
644
- // User not loaded yet - show all items until user is ready
645
- if (isMounted) {
646
- setFilteredMenuItems(baseMenuItems);
647
- }
648
- return;
649
- }
650
-
651
- // If no organisation context yet, show items optimistically
652
- // This allows users to see navigation while they select a context
653
- // Only proceed with permission filtering once context is selected
654
- if (!hasOrganisationContext) {
655
- if (isMounted) {
656
- // Show items optimistically when org selector is enabled, otherwise show all items
657
- // This prevents navigation from disappearing while waiting for organisation selection
658
- setFilteredMenuItems(baseMenuItems);
659
- }
660
- return;
661
- }
662
-
663
- // For super admins, show all items (they bypass permission checks)
664
- // Gracefully handle RBAC not being initialized (e.g., in tests)
665
- try {
666
- const { isSuperAdmin: checkSuperAdminDynamic } = await import('../../rbac/api');
667
- const isSuper = await checkSuperAdminDynamic(user.id);
668
-
669
- if (isSuper) {
670
- // Super admins see all navigation items
671
- if (isMounted) {
672
- setFilteredMenuItems(baseMenuItems);
673
- }
674
- return;
675
- }
676
- } catch (error) {
677
- // If RBAC is not initialized (e.g., in tests), continue with normal filtering
678
- // This prevents errors from breaking navigation when RBAC isn't available
679
- if (error && typeof error === 'object' && 'code' in error && error.code === 'RBAC_NOT_INITIALIZED') {
680
- // RBAC not available - proceed with normal filtering without super admin check
681
- // In this case, we'll filter items normally based on permissions
682
- } else {
683
- // Re-throw unexpected errors
684
- throw error;
685
- }
686
- }
687
-
688
- // Organisation context is ready - now filter items based on permissions
689
- // OPTIMIZATION: Use batch permission map instead of individual checks to avoid rate limits
690
- // This makes 1 call instead of N calls (where N = number of navigation items)
691
- // OPTIMIZATION: Proceed even if appId is still resolving - permission checks work with organisationId
692
- try {
693
- const { getPermissionMap } = await import('../../rbac/api');
694
-
695
- // Build scope for permission check - use appId if available, but don't wait for it
696
- // Permission checks will work with resource permissions even without appId
697
- const permissionScope: Scope = {
698
- organisationId: currentScope.organisationId,
699
- eventId: currentScope.eventId,
700
- appId: currentScope.appId || resolvedAppId || undefined // Use appId if available, but proceed without it
701
- };
702
-
703
- const permissionMap = await getPermissionMap({
704
- userId: user.id,
705
- scope: permissionScope,
706
- });
707
-
708
- // Filter items using the permission map and scope type
709
- // First, get scope types for all pages (batch if possible, or individual)
710
- const { getPageScopeType } = await import('../../rbac/api');
711
- const effectiveAppId = currentScope.appId || resolvedAppId;
712
- const effectiveAppName = appName;
713
-
714
- // Determine current context type for scope filtering
715
- const hasEventContext = !!currentScope.eventId;
716
- const hasOrgContext = !!currentScope.organisationId;
717
-
718
- // Filter items using both permission map and scope type
719
- const filtered = await Promise.all(
720
- baseMenuItems.map(async (item) => {
721
- if (!item.href) return { item, hasAccess: true, matchesScope: true, scopeCheckError: false };
722
-
723
- // Extract page ID from href: remove leading slash, fallback to 'dashboard' for root
724
- // This matches database page names in rbac_app_pages
725
- const pageId = pageIdMapping[item.href] || (item.href === '/' ? 'dashboard' : item.href.slice(1)) || 'dashboard';
726
- const permission = routePermissions[item.href] || defaultPermission;
727
- const fullPermission: Permission = permission.includes(':')
728
- ? (permission as Permission)
729
- : (pageId ? `${permission}:page.${pageId}` : permission) as Permission;
730
-
731
- // Check permission map (super admin check already handled in getPermissionMap)
732
- const hasAccess = permissionMap['*'] === true || permissionMap[fullPermission] === true;
733
-
734
- // Check scope type compatibility
735
- let matchesScope = true;
736
- let scopeCheckError = false;
737
- if (effectiveAppId || effectiveAppName) {
738
- try {
739
- const pageScopeType = await getPageScopeType(
740
- pageId,
741
- effectiveAppId,
742
- effectiveAppName
743
- );
744
-
745
- // Filter based on current context:
746
- // - If event is selected: show only 'event' or 'both' pages
747
- // - If only org is selected: show only 'organisation' or 'both' pages
748
- if (hasEventContext) {
749
- // Event context: show 'event' or 'both' pages only
750
- matchesScope = pageScopeType === 'event' || pageScopeType === 'both';
751
- } else if (hasOrgContext) {
752
- // Organisation context only: show 'organisation' or 'both' pages only
753
- matchesScope = pageScopeType === 'organisation' || pageScopeType === 'both';
754
- } else {
755
- // No context: show all pages (shouldn't happen, but safe fallback)
756
- matchesScope = true;
757
- }
758
- } catch (error) {
759
- // If we can't get scope type, allow the page (graceful degradation)
760
- // This prevents navigation from disappearing if there's a database issue
761
- scopeCheckError = true;
762
- logger.warn('PaceAppLayout', 'Failed to get page scope type for navigation filtering', {
763
- pageId,
764
- href: item.href,
765
- contextType: hasEventContext ? 'event' : (hasOrgContext ? 'organisation' : 'none'),
766
- error: error instanceof Error ? error.message : String(error),
767
- note: 'Allowing page to prevent navigation from disappearing - this may indicate missing scope_type in database'
768
- });
769
- matchesScope = true; // Default to allowing if we can't check
770
- }
771
- } else {
772
- // No appId/appName available - can't check scope, allow the page
773
- // This happens during initial load before app context is resolved
774
- matchesScope = true;
775
- }
776
-
777
- return { item, hasAccess, matchesScope, scopeCheckError };
778
- })
779
- );
780
-
781
- if (!isMounted) return;
782
-
783
- const accessibleItems = filtered
784
- .filter(({ hasAccess, matchesScope }) => hasAccess && matchesScope)
785
- .map(({ item }) => item);
786
-
787
- // If all items were filtered out, check if it's due to scope filtering
788
- // This can happen if:
789
- // 1. All pages are scoped for the other context type (expected behavior)
790
- // 2. Scope type lookup failed for all pages (should fallback to showing items with permissions)
791
- if (accessibleItems.length === 0 && filtered.length > 0) {
792
- const itemsWithPermissions = filtered.filter(({ hasAccess }) => hasAccess);
793
- const itemsFilteredByScope = filtered.filter(({ hasAccess, matchesScope }) => hasAccess && !matchesScope);
794
- const itemsWithScopeErrors = filtered.filter(({ hasAccess, scopeCheckError }) => hasAccess && scopeCheckError);
795
-
796
- // If scope checking failed for ALL items with permissions, fall back to showing them
797
- // This prevents navigation from disappearing due to database/API issues
798
- if (itemsWithPermissions.length > 0 &&
799
- itemsWithScopeErrors.length === itemsWithPermissions.length) {
800
- logger.warn('PaceAppLayout', 'Scope checking failed for all items - falling back to permission-only filtering', {
801
- contextType: hasEventContext ? 'event' : (hasOrgContext ? 'organisation' : 'none'),
802
- totalItems: baseMenuItems.length,
803
- itemsWithPermissions: itemsWithPermissions.length,
804
- scopeCheckErrorCount: itemsWithScopeErrors.length,
805
- note: 'Showing items based on permissions only - scope filtering disabled due to errors. This may indicate database issues or missing scope_type values.'
806
- });
807
-
808
- // Fall back to showing items that have permissions (ignore scope check)
809
- const fallbackItems = filtered
810
- .filter(({ hasAccess }) => hasAccess)
811
- .map(({ item }) => item);
812
-
813
- if (isMounted && fallbackItems.length > 0) {
814
- setFilteredMenuItems(fallbackItems);
815
- return;
816
- }
817
- } else if (itemsWithPermissions.length > 0 && itemsFilteredByScope.length === itemsWithPermissions.length) {
818
- // All items were filtered by scope (not errors) - this is expected if all pages are scoped for other context
819
- logger.info('PaceAppLayout', 'All navigation items filtered out by scope type', {
820
- contextType: hasEventContext ? 'event' : (hasOrgContext ? 'organisation' : 'none'),
821
- totalItems: baseMenuItems.length,
822
- itemsWithPermissions: itemsWithPermissions.length,
823
- itemsFilteredByScope: itemsFilteredByScope.length,
824
- note: 'All pages appear to be scoped for a different context type - this is expected behavior. Navigation will be empty until user selects the appropriate context.'
825
- });
826
- }
827
- }
828
-
829
- // SECURITY: Never show all items if permission check fails - this would be a security risk
830
- // If all items are filtered out, it means the user doesn't have permission to see any navigation
831
- // OR all pages are scoped for a different context type
832
- // This is the correct behavior - better to show nothing than show unauthorized items
833
- setFilteredMenuItems(accessibleItems);
834
- } catch (error) {
835
- // On error, fall back to showing all items (graceful degradation)
836
- // This prevents navigation from being empty if permission checks fail
837
- logger.error('PaceAppLayout', 'Failed to load permission map for navigation filtering', { userId: user?.id, error });
838
- if (isMounted) {
839
- setFilteredMenuItems(baseMenuItems);
840
- }
841
- }
842
- };
843
-
844
- filterItems();
509
+ appName,
510
+ isSuperAdminFromRBAC,
511
+ isSuperAdminDirect,
512
+ isCheckingSuperAdminDirect,
513
+ isSuperAdmin,
514
+ });
845
515
 
846
- return () => {
847
- isMounted = false;
848
- };
849
- }, [baseMenuItems, pageIdMapping, routePermissions, defaultPermission, can, user?.id, scope, scopeLoading, contextAppId, resolvedScope?.appId, selectedOrganisation?.id, selectedEvent?.event_id, appName]);
516
+ const filteredMenuItems = useFilteredNavItems({
517
+ baseMenuItems,
518
+ user,
519
+ scope,
520
+ routePermissions: config.routePermissions,
521
+ defaultPermission: config.defaultPermission,
522
+ pageIdMapping: config.pageIdMapping,
523
+ appName,
524
+ resolvedAppId,
525
+ scopeLoading,
526
+ contextAppId,
527
+ resolvedScopeAppId: resolvedAppId,
528
+ selectedOrganisationId: selectedOrganisation?.id,
529
+ selectedEventId: selectedEvent?.event_id ?? null,
530
+ });
850
531
 
851
- // NEW: Phase 2 - Enhanced Routing Features
852
- // Check route access for role-based routing
853
- useEffect(() => {
854
- if (!roleBasedRouting || routeConfig.length === 0) return;
855
-
856
- let isMounted = true;
857
-
858
- const checkRouteAccess = async () => {
859
- const currentPath = location.pathname;
860
- const currentRoute = routeConfig.find(route => route.path === currentPath);
861
-
862
- if (!currentRoute) {
863
- // Route not found in configuration
864
- if (strictMode) {
865
- logger.error('PaceAppLayout', 'STRICT MODE VIOLATION: Route not found in configuration', {
866
- route: currentPath,
867
- userId: user?.id,
868
- timestamp: new Date().toISOString()
869
- });
870
-
871
- if (onRouteStrictModeViolation) {
872
- onRouteStrictModeViolation(currentPath, 'Route not found in configuration');
873
- }
874
- }
875
- return;
876
- }
877
-
878
- // Check permissions using useCan hook result
879
- let hasAccess = true; // Default to true if no permission requirements
880
-
881
- // Check page permissions
882
- if (currentRoute.pageId && currentRoute.permissions && currentRoute.permissions.length > 0) {
883
- // Use the permission check result from useCan hook
884
- // For now, we'll use a simple check - in future we might need useMultiplePermissions here
885
- try {
886
- const { isPermittedCached } = await import('../../rbac/api');
887
- const hasPagePermission = await isPermittedCached({
888
- userId: user?.id || '',
889
- scope,
890
- permission: currentRoute.permissions[0] as Permission,
891
- pageId: currentRoute.pageId,
892
- });
893
- if (!isMounted) return;
894
- hasAccess = hasPagePermission;
895
- } catch (error) {
896
- logger.error('PaceAppLayout', 'Failed to check page permission', { route: currentPath, pageId: currentRoute.pageId, error });
897
- if (!isMounted) return;
898
- hasAccess = false;
899
- }
900
- }
901
-
902
- // If permission check passed or not required, check roles
903
- if (hasAccess && currentRoute.roles && currentRoute.roles.length > 0 && user?.id) {
904
- const { useUnifiedAuth } = await import('../../providers/services/UnifiedAuthProvider');
905
- // Note: We're already in the component with authContext via useUnifiedAuth at top
906
- // For this feature to work properly, we need the auth context
907
- // This is a limitation of the current implementation
908
- hasAccess = true; // Will be properly implemented when auth context is available in this effect
909
- }
910
-
911
- if (!isMounted) return;
912
-
913
- if (!hasAccess) {
914
- // Handle route access denied
915
- if (onRouteAccessDenied) {
916
- onRouteAccessDenied(currentPath, 'Insufficient permissions');
917
- }
918
-
919
- if (strictMode) {
920
- logger.error('PaceAppLayout', 'STRICT MODE VIOLATION: User attempted to access protected route without permission', {
921
- route: currentPath,
922
- userId: user?.id,
923
- permissions: currentRoute.permissions,
924
- roles: currentRoute.roles,
925
- accessLevel: currentRoute.accessLevel,
926
- timestamp: new Date().toISOString()
927
- });
928
-
929
- if (onRouteStrictModeViolation) {
930
- onRouteStrictModeViolation(currentPath, 'Insufficient permissions');
931
- }
932
- }
933
-
934
- // Redirect to fallback route
935
- navigate(fallbackRoute, { replace: true });
936
- return;
937
- }
938
- };
939
-
940
- checkRouteAccess();
941
-
942
- return () => {
943
- isMounted = false;
944
- };
945
- }, [roleBasedRouting, routeConfig, location.pathname, strictMode, user?.id, fallbackRoute, scope, navigate, auditLog, onRouteAccessDenied, onRouteStrictModeViolation]);
532
+ useRoleBasedRouteAccess({
533
+ roleBasedRouting: config.roleBasedRouting,
534
+ routeConfig: config.routeConfig,
535
+ currentPath: location.pathname,
536
+ user,
537
+ scope,
538
+ strictMode: config.strictMode,
539
+ fallbackRoute: config.fallbackRoute,
540
+ navigate,
541
+ onRouteAccessDenied: config.onRouteAccessDenied,
542
+ onRouteStrictModeViolation: config.onRouteStrictModeViolation,
543
+ });
946
544
 
947
545
  const handleSignOut = async () => {
948
546
  try {
949
547
  await signOut();
950
548
  } catch (error) {
951
- logger.error('PaceAppLayout', 'Failed to sign out', { error: error instanceof Error ? error.message : String(error) });
549
+ logger.error('PaceAppLayout', 'Failed to sign out', {
550
+ error: error instanceof Error ? error.message : String(error),
551
+ });
952
552
  }
953
553
  };
954
554
 
955
- const handleChangePassword = async (newPassword: string, confirmPassword: string) => {
555
+ const handleChangePassword = async (newPassword: string, _confirmPassword: string) => {
956
556
  try {
957
- // The form component in UserMenu already checks for matching passwords
958
557
  const result = await updatePassword(newPassword);
959
- if (result?.error) {
960
- // The form will display the error message
558
+ if (!result.ok) {
961
559
  logger.error('PaceAppLayout', 'Failed to change password', { error: result.error.message });
962
- // Convert AuthError to PasswordChangeFormError
963
- return {
964
- error: {
965
- message: result.error.message,
966
- code: result.error.name || 'PASSWORD_UPDATE_ERROR'
967
- }
968
- };
560
+ return { error: { message: result.error.message, code: result.error.code || 'PASSWORD_UPDATE_ERROR' } };
969
561
  }
970
- // The form will handle closing the modal on success
971
562
  return {};
972
563
  } catch (error) {
973
- logger.error('PaceAppLayout', 'Failed to change password', { error: error instanceof Error ? error.message : String(error) });
564
+ logger.error('PaceAppLayout', 'Failed to change password', {
565
+ error: error instanceof Error ? error.message : String(error),
566
+ });
974
567
  return {
975
568
  error: {
976
569
  message: error instanceof Error ? error.message : 'An unexpected error occurred',
977
- code: 'PASSWORD_UPDATE_ERROR'
978
- }
570
+ code: 'PASSWORD_UPDATE_ERROR',
571
+ },
979
572
  };
980
573
  }
981
574
  };
982
575
 
983
- // CRITICAL: Wait for organisation context to be ready before proceeding
984
- // The OrganisationService automatically selects an organisation when loading
985
- // We just need to wait for isContextReady to be true
986
- // No need to call ensureOrganisationContext() here - it will throw if no org is selected
987
- // and the service handles selection automatically during loadUserOrganisations()
988
-
989
- // Show loading state while organisation context is being set
990
- // This is critical - we must wait for organisation context before allowing any data access
991
- // BUT: Allow rendering to proceed if loading is complete, even if user has no organisations (valid state for profile pages)
992
- // Only block if we're actively loading - once loading completes (success or error), allow rendering
993
- // EXCEPTION: Super admins can proceed even during organisation loading (they can access all orgs)
994
- // Use combined super admin check (RBAC + direct check) to allow super admins to proceed immediately
995
- // IMPORTANT: If we're still checking super admin status, allow rendering to proceed (optimistic approach)
996
- // This prevents blocking super admins while their status is being determined
997
- // Also allow rendering if we already have a selectedOrganisationId (even if organisationLoading is still true)
998
- // This prevents blank pages when organisation context is available but loading state hasn't cleared yet
999
- if (user?.id && organisationLoading && !isSuperAdmin && !isCheckingSuperAdminDirect && !rbacLoading && !selectedOrganisationId) {
1000
- return (
1001
- <p className="grid place-items-center text-center size-full">
1002
- <LoadingSpinner
1003
- size="lg"/><br />
1004
- Loading organisation context...
1005
- </p>
1006
- );
1007
- }
1008
-
1009
- // Once loading is complete (whether success or error), allow rendering to proceed
1010
- // For users without organisations, allow access to profile pages (dashboard, member-profile, medical-profile, additional-contacts)
1011
- // These pages work with user context only and don't require organisation context
1012
- // The app can check hasValidOrganisationContext() to determine if org context is available for org-specific features
1013
-
1014
- // Show loading state while checking permissions
1015
- // Keep loading active until permission check completes to prevent exposing protected content
1016
- // Super admin status is checked via useRBAC hook (isSuperAdminFromRBAC)
1017
- if (enforcePermissions && isCheckingPermission) {
1018
- return (
1019
- <p className="grid place-items-center text-center size-full">
1020
- <LoadingSpinner
1021
- size="lg"/><br />
1022
- Checking permissions...
1023
- </p>
1024
- );
1025
- }
1026
-
1027
- // Show permission error (only after check is complete)
1028
- // Super admins bypass all permission checks, so don't show errors for them
1029
- // Context errors (missing event/org) should allow the page to render so it can show helpful messaging
1030
- // Real permission errors should still show "Access Denied"
1031
- if (enforcePermissions && permissionError && !isSuperAdmin && !isContextError) {
1032
- return (
1033
- <hgroup className="grid place-items-center text-center size-full">
1034
- <h2>Permission Error</h2>
1035
- <p>{permissionError.message}</p>
1036
- <Button onClick={() => navigate('/')}>Go Home</Button>
1037
- </hgroup>
1038
- );
1039
- }
576
+ const { gateContent } = usePaceAppLayoutGate({
577
+ user,
578
+ organisationLoading,
579
+ isSuperAdmin,
580
+ isCheckingSuperAdminDirect,
581
+ rbacLoading,
582
+ selectedOrganisationId,
583
+ enforcePermissions: config.enforcePermissions,
584
+ isCheckingPermission: permissions.isCheckingPermission,
585
+ permissionError: permissions.permissionError,
586
+ isContextError: permissions.isContextError,
587
+ hasPermission: permissions.hasPermission,
588
+ enforcePagePermissions: config.enforcePagePermissions,
589
+ pagePermissionFallback: config.pagePermissionFallback,
590
+ permissionFallback: config.permissionFallback,
591
+ onSignOut: handleSignOut,
592
+ });
1040
593
 
1041
- // Show permission fallback if user lacks permission
1042
- // Only show this if super admin check is complete and user is not a super admin
1043
- // Skip if it's a context error (missing event/org) - allow page to render and show helpful messaging
1044
- if (enforcePermissions && hasPermission === false && !isCheckingSuperAdminDirect && !isSuperAdmin && !isContextError) {
1045
- // NEW: Phase 1 - Use page permission fallback if available
1046
- if (enforcePagePermissions && pagePermissionFallback) {
1047
- return <>{pagePermissionFallback}</>;
1048
- }
1049
-
1050
- if (permissionFallback) {
1051
- return <>{permissionFallback}</>;
1052
- }
1053
-
1054
- return (
1055
- <AccessDenied
1056
- message="You don't have permission to access this page."
1057
- onGoBack={() => navigate('/')}
1058
- onSignOut={async () => {
1059
- await handleSignOut();
1060
- navigate('/login');
1061
- }}
1062
- showSignOut={true}
1063
- />
1064
- );
594
+ if (gateContent !== null) {
595
+ return <>{gateContent}</>;
1065
596
  }
1066
597
 
1067
598
  return (
1068
- <>
1069
- <Header
1070
- logo={customLogo || undefined}
1071
- logoUrl={!customLogo ? `/${appName.toLowerCase()}_logo_wide.svg` : undefined}
1072
- logoAlt={`${appName} Logo`}
1073
- logoHref={logoHref}
1074
- navItems={filteredMenuItems}
1075
- actions={headerActions}
1076
- userMenu={customUserMenu}
1077
- user={user}
1078
- onSignOut={handleSignOut}
1079
- onChangePassword={handleChangePassword}
1080
- currentPath={window.location.pathname}
1081
- onNavigate={(item) => {
1082
- if (item.href) {
1083
- navigate(item.href);
1084
- }
1085
- }}
1086
- showContextSelector={showContextSelector}
1087
- showOrganisations={showOrganisations}
1088
- showEvents={showEvents}
1089
- showUserMenu={showUserMenu}
1090
- className={headerClassName || "sticky top-0 z-[40] w-full"}
1091
- />
1092
- <main className="px-4 w-[min(var(--app-width),100%)] mx-auto py-8">
1093
- <Outlet />
1094
- </main>
1095
- <Footer />
1096
- </>
599
+ <PaceAppLayoutContent
600
+ appName={appName}
601
+ logoHref={logoHref}
602
+ filteredMenuItems={filteredMenuItems}
603
+ headerActions={headerActions}
604
+ customUserMenu={customUserMenu}
605
+ user={user}
606
+ onSignOut={handleSignOut}
607
+ onChangePassword={handleChangePassword}
608
+ headerClassName={headerClassName}
609
+ showContextSelector={showContextSelector}
610
+ showOrganisations={showOrganisations}
611
+ showEvents={showEvents}
612
+ showUserMenu={showUserMenu}
613
+ navigate={navigate}
614
+ />
1097
615
  );
1098
616
  }