@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
@@ -0,0 +1,1430 @@
1
+ /**
2
+ * @file PagePermissionGuard Component Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module RBAC/Components/PagePermissionGuard
5
+ * @since 2.0.0
6
+ *
7
+ * Comprehensive tests for the PagePermissionGuard component covering all critical functionality.
8
+ */
9
+
10
+ import { render, screen, waitFor } from '@testing-library/react';
11
+ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
12
+ import { ReactNode } from 'react';
13
+ import { PagePermissionGuard } from './PagePermissionGuard';
14
+ import { useUnifiedAuth } from '../../../providers/services/UnifiedAuthProvider';
15
+
16
+ // Following testing standards: use timeout parameter to prevent hanging
17
+ const TEST_TIMEOUT = 10000; // 10 seconds per test
18
+
19
+ // Mock the RBAC hooks - mock individual file since barrel was removed
20
+ // CRITICAL: Use hoisted mock so vi.mocked() works correctly
21
+ const mockUseCanFn = vi.hoisted(() => vi.fn());
22
+ vi.mock('../hooks/usePermissions', () => ({
23
+ useCan: mockUseCanFn
24
+ }));
25
+
26
+ // Mock the auth provider
27
+ const mockUseUnifiedAuthFn = vi.fn();
28
+ vi.mock('../../providers/services/UnifiedAuthProvider', () => ({
29
+ useUnifiedAuth: () => mockUseUnifiedAuthFn(),
30
+ UnifiedAuthProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
31
+ }));
32
+
33
+ // Mock the event context utility
34
+ vi.mock('../../utils/eventContext', () => ({
35
+ createScopeFromEvent: vi.fn()
36
+ }));
37
+
38
+ // Mock useResolvedScope hook
39
+ vi.mock('../hooks/useResolvedScope', () => ({
40
+ useResolvedScope: vi.fn()
41
+ }));
42
+
43
+ // Mock the app name resolver
44
+ vi.mock('../../utils/app/appNameResolver', () => ({
45
+ getCurrentAppName: vi.fn()
46
+ }));
47
+
48
+ // Mock the RBAC API for super admin checks
49
+ vi.mock('../api', async () => {
50
+ const actual = await vi.importActual('../api');
51
+ return {
52
+ ...actual,
53
+ isSuperAdmin: vi.fn().mockResolvedValue(false) // Default to not super admin
54
+ };
55
+ });
56
+
57
+ import { useResolvedScope } from '../hooks/useResolvedScope';
58
+ import { getCurrentAppName } from '../../utils/app/appNameResolver';
59
+ import { isSuperAdmin } from '../api';
60
+
61
+ // Mock data
62
+ const mockUser = {
63
+ id: 'user-123',
64
+ email: 'test@example.com'
65
+ };
66
+
67
+ const mockScope = {
68
+ organisationId: 'org-123',
69
+ eventId: 'event-123',
70
+ appId: 'app-123'
71
+ };
72
+
73
+ const mockPageName = 'dashboard';
74
+ const mockOperation = 'read' as const;
75
+
76
+ // Test component
77
+ const TestComponent = ({ children }: { children: ReactNode }) => (
78
+ <main data-testid="test-component">{children}</main>
79
+ );
80
+
81
+ const TestFallback = () => (
82
+ <main data-testid="test-fallback">Access Denied</main>
83
+ );
84
+
85
+ const TestLoading = () => (
86
+ <main data-testid="test-loading">Loading...</main>
87
+ );
88
+
89
+ describe('PagePermissionGuard Component', () => {
90
+ const mockGetCurrentAppName = vi.mocked(getCurrentAppName);
91
+ const mockUseResolvedScope = vi.mocked(useResolvedScope);
92
+
93
+ beforeEach(() => {
94
+ vi.clearAllMocks();
95
+
96
+ // Default mock implementations
97
+ // For org-required apps: selectedOrganisation is available
98
+ // For event-required apps: selectedOrganisation is null, org derived from event
99
+ mockUseUnifiedAuthFn.mockReturnValue({
100
+ user: mockUser,
101
+ selectedOrganisation: { id: 'org-123' }, // Available for org-required apps, null for event-required
102
+ selectedEvent: { event_id: 'event-123', organisation_id: 'org-123' },
103
+ appId: 'app-123', // Required for scope resolution
104
+ appName: 'test-app',
105
+ appConfig: { requires_event: false }, // Default to org-required for tests
106
+ supabase: {
107
+ from: vi.fn().mockReturnValue({
108
+ select: vi.fn().mockReturnValue({
109
+ eq: vi.fn().mockReturnValue({
110
+ eq: vi.fn().mockReturnValue({
111
+ single: vi.fn().mockResolvedValue({
112
+ data: { id: 'app-123', name: 'test-app', is_active: true, requires_event: false },
113
+ error: null
114
+ })
115
+ })
116
+ })
117
+ })
118
+ })
119
+ } as any
120
+ });
121
+
122
+ mockGetCurrentAppName.mockReturnValue('test-app');
123
+
124
+ // Mock useResolvedScope to return resolved scope immediately
125
+ mockUseResolvedScope.mockReturnValue({
126
+ resolvedScope: {
127
+ organisationId: 'org-123',
128
+ eventId: 'event-123',
129
+ appId: 'app-123'
130
+ },
131
+ isLoading: false,
132
+ error: null
133
+ });
134
+
135
+ mockUseCanFn.mockReturnValue({
136
+ can: true,
137
+ isLoading: false,
138
+ error: null
139
+ });
140
+ });
141
+
142
+ describe('Rendering', () => {
143
+ it('renders children when permission is granted', async () => {
144
+ mockUseCanFn.mockReturnValue({
145
+ can: true,
146
+ isLoading: false,
147
+ error: null
148
+ });
149
+
150
+ render(
151
+ <PagePermissionGuard
152
+ pageName={mockPageName}
153
+ operation={mockOperation}
154
+ >
155
+ <TestComponent>Protected Page</TestComponent>
156
+ </PagePermissionGuard>
157
+ );
158
+
159
+ await waitFor(() => {
160
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
161
+ expect(screen.getByText('Protected Page')).toBeInTheDocument();
162
+ }, { interval: 10 });
163
+ });
164
+
165
+ it('renders fallback when permission is denied and shows loading state', async () => {
166
+ mockUseCanFn.mockReturnValue({
167
+ can: false,
168
+ isLoading: false,
169
+ error: null
170
+ });
171
+
172
+ const { rerender } = render(
173
+ <PagePermissionGuard
174
+ pageName={mockPageName}
175
+ operation={mockOperation}
176
+ fallback={<TestFallback />}
177
+ >
178
+ <TestComponent>Protected Page</TestComponent>
179
+ </PagePermissionGuard>
180
+ );
181
+ await waitFor(() => {
182
+ expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
183
+ expect(screen.queryByTestId('test-component')).not.toBeInTheDocument();
184
+ }, { interval: 10 });
185
+
186
+ mockUseCanFn.mockReturnValue({
187
+ can: false,
188
+ isLoading: true,
189
+ error: null
190
+ });
191
+ rerender(
192
+ <PagePermissionGuard
193
+ pageName={mockPageName}
194
+ operation={mockOperation}
195
+ loading={<TestLoading />}
196
+ >
197
+ <TestComponent>Protected Page</TestComponent>
198
+ </PagePermissionGuard>
199
+ );
200
+ expect(screen.getByTestId('test-loading')).toBeInTheDocument();
201
+ expect(screen.queryByTestId('test-component')).not.toBeInTheDocument();
202
+ });
203
+
204
+ it('uses default fallback and loading when none provided', async () => {
205
+ mockUseCanFn.mockReturnValue({
206
+ can: false,
207
+ isLoading: false,
208
+ error: null
209
+ });
210
+
211
+ const { rerender } = render(
212
+ <PagePermissionGuard
213
+ pageName={mockPageName}
214
+ operation={mockOperation}
215
+ >
216
+ <TestComponent>Protected Page</TestComponent>
217
+ </PagePermissionGuard>
218
+ );
219
+ await waitFor(() => {
220
+ expect(screen.getByText('Access Denied')).toBeInTheDocument();
221
+ expect(screen.getByText('You don\'t have permission to access this page.')).toBeInTheDocument();
222
+ }, { interval: 10, timeout: 2000 });
223
+
224
+ mockUseCanFn.mockReturnValue({
225
+ can: false,
226
+ isLoading: true,
227
+ error: null
228
+ });
229
+ rerender(
230
+ <PagePermissionGuard
231
+ pageName={mockPageName}
232
+ operation={mockOperation}
233
+ >
234
+ <TestComponent>Protected Page</TestComponent>
235
+ </PagePermissionGuard>
236
+ );
237
+ expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
238
+ });
239
+ });
240
+
241
+ describe('Permission Checking', () => {
242
+ it('builds correct permission string, uses custom pageId, handles different operations, and handles errors gracefully', async () => {
243
+ const customPageId = 'custom-dashboard';
244
+
245
+ mockUseCanFn.mockReturnValue({
246
+ can: true,
247
+ isLoading: false,
248
+ error: null
249
+ });
250
+
251
+ const { rerender } = render(
252
+ <PagePermissionGuard
253
+ pageName={mockPageName}
254
+ operation={mockOperation}
255
+ >
256
+ <TestComponent>Protected Page</TestComponent>
257
+ </PagePermissionGuard>
258
+ );
259
+
260
+ await waitFor(() => {
261
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
262
+ }, { interval: 10 });
263
+
264
+ // Check that useCan was called with correct parameters
265
+ // precomputedSuperAdmin can be null (checking), true/false (checked), or undefined
266
+ expect(mockUseCanFn).toHaveBeenCalled();
267
+ const callArgs = mockUseCanFn.mock.calls[0];
268
+ expect(callArgs[0]).toBe('user-123');
269
+ expect(callArgs[1]).toEqual({
270
+ organisationId: 'org-123',
271
+ eventId: 'event-123',
272
+ appId: 'app-123'
273
+ });
274
+ expect(callArgs[2]).toBe('read:page.dashboard');
275
+ expect(callArgs[3]).toBe('dashboard');
276
+ expect(callArgs[4]).toBe(true);
277
+ // precomputedSuperAdmin can be null, true, false, or undefined
278
+ expect(callArgs[5] === null || callArgs[5] === true || callArgs[5] === false || callArgs[5] === undefined).toBe(true);
279
+ expect(callArgs[6]).toBe('test-app');
280
+
281
+ vi.clearAllMocks();
282
+ rerender(
283
+ <PagePermissionGuard
284
+ pageName={mockPageName}
285
+ operation={mockOperation}
286
+ pageId={customPageId}
287
+ >
288
+ <TestComponent>Protected Page</TestComponent>
289
+ </PagePermissionGuard>
290
+ );
291
+ await waitFor(() => {
292
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
293
+ }, { interval: 10 });
294
+
295
+ // Check that useCan was called with correct parameters for custom pageId
296
+ expect(mockUseCanFn).toHaveBeenCalled();
297
+ const customPageIdCallArgs = mockUseCanFn.mock.calls[0];
298
+ expect(customPageIdCallArgs[0]).toBe('user-123');
299
+ expect(customPageIdCallArgs[1]).toEqual({
300
+ organisationId: 'org-123',
301
+ eventId: 'event-123',
302
+ appId: 'app-123'
303
+ });
304
+ expect(customPageIdCallArgs[2]).toBe('read:page.dashboard');
305
+ expect(customPageIdCallArgs[3]).toBe(customPageId);
306
+ expect(customPageIdCallArgs[4]).toBe(true);
307
+ // precomputedSuperAdmin can be null, true, false, or undefined
308
+ expect(customPageIdCallArgs[5] === null || customPageIdCallArgs[5] === true || customPageIdCallArgs[5] === false || customPageIdCallArgs[5] === undefined).toBe(true);
309
+ expect(customPageIdCallArgs[6]).toBe('test-app');
310
+
311
+ const operations = ['create', 'update'] as const;
312
+ for (const operation of operations) {
313
+ vi.clearAllMocks();
314
+ mockUseCanFn.mockReturnValue({
315
+ can: true,
316
+ isLoading: false,
317
+ error: null
318
+ });
319
+ rerender(
320
+ <PagePermissionGuard
321
+ pageName={mockPageName}
322
+ operation={operation}
323
+ >
324
+ <TestComponent>Protected Page</TestComponent>
325
+ </PagePermissionGuard>
326
+ );
327
+ await waitFor(() => {
328
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
329
+ }, { interval: 10 });
330
+
331
+ // Check that useCan was called with correct parameters for operation
332
+ expect(mockUseCanFn).toHaveBeenCalled();
333
+ const operationCallArgs = mockUseCanFn.mock.calls[0];
334
+ expect(operationCallArgs[0]).toBe('user-123');
335
+ expect(operationCallArgs[1]).toEqual({
336
+ organisationId: 'org-123',
337
+ eventId: 'event-123',
338
+ appId: 'app-123'
339
+ });
340
+ expect(operationCallArgs[2]).toBe(`${operation}:page.dashboard`);
341
+ expect(operationCallArgs[3]).toBe('dashboard');
342
+ expect(operationCallArgs[4]).toBe(true);
343
+ // precomputedSuperAdmin can be null, true, false, or undefined
344
+ expect(operationCallArgs[5] === null || operationCallArgs[5] === true || operationCallArgs[5] === false || operationCallArgs[5] === undefined).toBe(true);
345
+ expect(operationCallArgs[6]).toBe('test-app');
346
+ }
347
+
348
+ const error = new Error('Permission check failed');
349
+ mockUseCanFn.mockReturnValue({
350
+ can: false,
351
+ isLoading: false,
352
+ error
353
+ });
354
+ rerender(
355
+ <PagePermissionGuard
356
+ pageName={mockPageName}
357
+ operation={mockOperation}
358
+ fallback={<TestFallback />}
359
+ >
360
+ <TestComponent>Protected Page</TestComponent>
361
+ </PagePermissionGuard>
362
+ );
363
+ await waitFor(() => {
364
+ expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
365
+ }, { interval: 10, timeout: 2000 });
366
+ });
367
+ });
368
+
369
+ describe('App ID Resolution', () => {
370
+ it('resolves app ID from database', async () => {
371
+ // useResolvedScope will call getCurrentAppName to resolve app config
372
+ // The component uses appId from context when available
373
+ mockUseCanFn.mockReturnValue({
374
+ can: true,
375
+ isLoading: false,
376
+ error: null
377
+ });
378
+
379
+ render(
380
+ <PagePermissionGuard
381
+ pageName={mockPageName}
382
+ operation={mockOperation}
383
+ >
384
+ <TestComponent>Protected Page</TestComponent>
385
+ </PagePermissionGuard>
386
+ );
387
+
388
+ await waitFor(() => {
389
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
390
+ }, { interval: 10, timeout: 2000 });
391
+
392
+ // useResolvedScope calls getCurrentAppName to resolve app config
393
+ // The component uses contextAppId when available, but useResolvedScope still needs app name
394
+ // So getCurrentAppName may be called by useResolvedScope
395
+ // The important thing is that the component works correctly
396
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
397
+ });
398
+
399
+ it('handles app resolution errors in test environment', async () => {
400
+ mockGetCurrentAppName.mockReturnValue(null);
401
+
402
+ mockUseCanFn.mockReturnValue({
403
+ can: true,
404
+ isLoading: false,
405
+ error: null
406
+ });
407
+
408
+ // Set NODE_ENV to test
409
+ const originalEnv = process.env.NODE_ENV;
410
+ process.env.NODE_ENV = 'test';
411
+
412
+ render(
413
+ <PagePermissionGuard
414
+ pageName={mockPageName}
415
+ operation={mockOperation}
416
+ scope={mockScope}
417
+ >
418
+ <TestComponent>Protected Page</TestComponent>
419
+ </PagePermissionGuard>
420
+ );
421
+
422
+ await waitFor(() => {
423
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
424
+ }, { interval: 10 });
425
+
426
+ // Restore NODE_ENV
427
+ process.env.NODE_ENV = originalEnv;
428
+ });
429
+
430
+ it('handles app resolution errors in production', async () => {
431
+ mockGetCurrentAppName.mockReturnValue(null);
432
+
433
+ // When app resolution fails, useResolvedScope should return null scope or error
434
+ mockUseResolvedScope.mockReturnValue({
435
+ resolvedScope: null,
436
+ isLoading: true, // Still loading when app resolution fails
437
+ error: null
438
+ });
439
+
440
+ mockUseCanFn.mockReturnValue({
441
+ can: false,
442
+ isLoading: true,
443
+ error: null
444
+ });
445
+
446
+ // Mock import.meta.env.MODE for production using vi.stubEnv
447
+ vi.stubEnv('MODE', 'production');
448
+
449
+ render(
450
+ <PagePermissionGuard
451
+ pageName={mockPageName}
452
+ operation={mockOperation}
453
+ fallback={<TestFallback />}
454
+ >
455
+ <TestComponent>Protected Page</TestComponent>
456
+ </PagePermissionGuard>
457
+ );
458
+
459
+ // Component should show loading when scope is still resolving
460
+ expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
461
+
462
+ // Restore environment
463
+ vi.unstubAllEnvs();
464
+ });
465
+
466
+ it('validates app ID format in production', async () => {
467
+ mockGetCurrentAppName.mockReturnValue('test-app');
468
+
469
+ // Mock database returning invalid app ID
470
+ // In test mode, validation is skipped, so component should work normally
471
+ mockUseUnifiedAuthFn.mockReturnValue({
472
+ user: mockUser,
473
+ selectedOrganisation: { id: 'org-123' },
474
+ selectedEvent: { event_id: 'event-123' },
475
+ appId: 'app-123',
476
+ supabase: {
477
+ from: vi.fn().mockReturnValue({
478
+ select: vi.fn().mockReturnValue({
479
+ eq: vi.fn().mockReturnValue({
480
+ eq: vi.fn().mockReturnValue({
481
+ single: vi.fn().mockResolvedValue({
482
+ data: { id: 'invalid-app-id', name: 'test-app', is_active: true },
483
+ error: null
484
+ })
485
+ })
486
+ })
487
+ })
488
+ })
489
+ } as any
490
+ });
491
+
492
+ mockUseCanFn.mockReturnValue({
493
+ can: true,
494
+ isLoading: false,
495
+ error: null
496
+ });
497
+
498
+ render(
499
+ <PagePermissionGuard
500
+ pageName={mockPageName}
501
+ operation={mockOperation}
502
+ fallback={<TestFallback />}
503
+ >
504
+ <TestComponent>Protected Page</TestComponent>
505
+ </PagePermissionGuard>
506
+ );
507
+
508
+ // In test mode, validation is skipped, so component should render normally
509
+ await waitFor(() => {
510
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
511
+ }, { interval: 10, timeout: 2000 });
512
+ });
513
+ });
514
+
515
+ describe('Scope Resolution', () => {
516
+ it('uses provided scope when available', async () => {
517
+ const customScope = {
518
+ organisationId: 'custom-org',
519
+ eventId: 'custom-event',
520
+ appId: 'custom-app'
521
+ };
522
+
523
+ mockUseCanFn.mockReturnValue({
524
+ can: true,
525
+ isLoading: false,
526
+ error: null
527
+ });
528
+
529
+ render(
530
+ <PagePermissionGuard
531
+ pageName={mockPageName}
532
+ operation={mockOperation}
533
+ scope={customScope}
534
+ >
535
+ <TestComponent>Protected Page</TestComponent>
536
+ </PagePermissionGuard>
537
+ );
538
+
539
+ await waitFor(() => {
540
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
541
+ }, { interval: 10 });
542
+
543
+ expect(mockUseCanFn).toHaveBeenCalledWith(
544
+ 'user-123',
545
+ customScope,
546
+ 'read:page.dashboard',
547
+ 'dashboard',
548
+ true,
549
+ null, // precomputedSuperAdmin
550
+ 'test-app'
551
+ );
552
+ });
553
+
554
+ it('resolves scope from organisation and event context (org-required app)', async () => {
555
+ // For org-required apps, organisation is primary, event is optional
556
+ mockUseCanFn.mockReturnValue({
557
+ can: true,
558
+ isLoading: false,
559
+ error: null
560
+ });
561
+
562
+ render(
563
+ <PagePermissionGuard
564
+ pageName={mockPageName}
565
+ operation={mockOperation}
566
+ >
567
+ <TestComponent>Protected Page</TestComponent>
568
+ </PagePermissionGuard>
569
+ );
570
+
571
+ await waitFor(() => {
572
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
573
+ }, { interval: 10 });
574
+
575
+ expect(mockUseCanFn).toHaveBeenCalledWith(
576
+ 'user-123',
577
+ {
578
+ organisationId: 'org-123',
579
+ eventId: 'event-123',
580
+ appId: 'app-123'
581
+ },
582
+ 'read:page.dashboard',
583
+ 'dashboard',
584
+ true,
585
+ null, // precomputedSuperAdmin
586
+ 'test-app'
587
+ );
588
+ });
589
+
590
+ it('resolves scope from organisation only (org-required app)', async () => {
591
+ // For org-required apps, organisation is primary context, event is optional
592
+ mockUseUnifiedAuthFn.mockReturnValue({
593
+ user: mockUser,
594
+ selectedOrganisation: { id: 'org-123' },
595
+ selectedEvent: null,
596
+ appId: 'app-123',
597
+ appName: 'test-app',
598
+ supabase: {
599
+ from: vi.fn().mockReturnValue({
600
+ select: vi.fn().mockReturnValue({
601
+ eq: vi.fn().mockReturnValue({
602
+ eq: vi.fn().mockReturnValue({
603
+ single: vi.fn().mockResolvedValue({
604
+ data: { id: 'app-123', name: 'test-app', is_active: true },
605
+ error: null
606
+ })
607
+ })
608
+ })
609
+ })
610
+ })
611
+ } as any
612
+ });
613
+
614
+ mockUseResolvedScope.mockReturnValue({
615
+ resolvedScope: {
616
+ organisationId: 'org-123',
617
+ eventId: undefined,
618
+ appId: 'app-123'
619
+ },
620
+ isLoading: false,
621
+ error: null
622
+ });
623
+
624
+ mockUseCanFn.mockReturnValue({
625
+ can: true,
626
+ isLoading: false,
627
+ error: null
628
+ });
629
+
630
+ render(
631
+ <PagePermissionGuard
632
+ pageName={mockPageName}
633
+ operation={mockOperation}
634
+ >
635
+ <TestComponent>Protected Page</TestComponent>
636
+ </PagePermissionGuard>
637
+ );
638
+
639
+ await waitFor(() => {
640
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
641
+ }, { interval: 10 });
642
+
643
+ expect(mockUseCanFn).toHaveBeenCalledWith(
644
+ 'user-123',
645
+ {
646
+ organisationId: 'org-123',
647
+ eventId: undefined,
648
+ appId: 'app-123'
649
+ },
650
+ 'read:page.dashboard',
651
+ 'dashboard',
652
+ true,
653
+ null, // precomputedSuperAdmin
654
+ 'test-app'
655
+ );
656
+ });
657
+
658
+ it('resolves scope from event context when organisation not available (event-required app)', async () => {
659
+ // For event-required apps, selectedOrganisation is null, org is derived from event
660
+ mockUseUnifiedAuthFn.mockReturnValue({
661
+ user: mockUser,
662
+ selectedOrganisation: null, // Not available for event-required apps
663
+ selectedEvent: { event_id: 'event-123' },
664
+ appId: 'app-123',
665
+ appName: 'test-app',
666
+ supabase: {
667
+ from: vi.fn().mockReturnValue({
668
+ select: vi.fn().mockReturnValue({
669
+ eq: vi.fn().mockReturnValue({
670
+ eq: vi.fn().mockReturnValue({
671
+ single: vi.fn().mockResolvedValue({
672
+ data: { id: 'app-123', name: 'test-app', is_active: true },
673
+ error: null
674
+ })
675
+ })
676
+ })
677
+ })
678
+ })
679
+ } as any
680
+ });
681
+
682
+ // Mock useResolvedScope to return scope resolved from event
683
+ mockUseResolvedScope.mockReturnValue({
684
+ resolvedScope: {
685
+ organisationId: 'resolved-org',
686
+ eventId: 'event-123',
687
+ appId: 'app-123'
688
+ },
689
+ isLoading: false,
690
+ error: null
691
+ });
692
+
693
+ mockUseCanFn.mockReturnValue({
694
+ can: true,
695
+ isLoading: false,
696
+ error: null
697
+ });
698
+
699
+ render(
700
+ <PagePermissionGuard
701
+ pageName={mockPageName}
702
+ operation={mockOperation}
703
+ >
704
+ <TestComponent>Protected Page</TestComponent>
705
+ </PagePermissionGuard>
706
+ );
707
+
708
+ await waitFor(() => {
709
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
710
+ }, { interval: 10 });
711
+
712
+ expect(mockUseCanFn).toHaveBeenCalledWith(
713
+ 'user-123',
714
+ {
715
+ organisationId: 'resolved-org',
716
+ eventId: 'event-123',
717
+ appId: 'app-123' // Component uses appId from context, not from resolved scope
718
+ },
719
+ 'read:page.dashboard',
720
+ 'dashboard',
721
+ true,
722
+ null, // precomputedSuperAdmin
723
+ 'test-app'
724
+ );
725
+ });
726
+
727
+ it('handles scope resolution errors', async () => {
728
+ mockUseUnifiedAuthFn.mockReturnValue({
729
+ user: mockUser,
730
+ selectedOrganisation: null,
731
+ selectedEvent: { event_id: 'event-123' },
732
+ appId: undefined, // Not available for error case
733
+ appName: 'test-app',
734
+ supabase: {} as any
735
+ });
736
+
737
+ const error = new Error('Could not resolve organisation from event');
738
+ mockUseResolvedScope.mockReturnValue({
739
+ resolvedScope: null,
740
+ isLoading: false,
741
+ error
742
+ });
743
+
744
+ // Mock useCan to return quickly so component can process error
745
+ mockUseCanFn.mockReturnValue({
746
+ can: false,
747
+ isLoading: false,
748
+ error: null
749
+ });
750
+
751
+ render(
752
+ <PagePermissionGuard
753
+ pageName={mockPageName}
754
+ operation={mockOperation}
755
+ fallback={<TestFallback />}
756
+ >
757
+ <TestComponent>Protected Page</TestComponent>
758
+ </PagePermissionGuard>
759
+ );
760
+
761
+ // Component shows fallback when there's a scope error
762
+ await waitFor(() => {
763
+ expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
764
+ }, { interval: 10, timeout: 2000 });
765
+ });
766
+
767
+ it('handles missing context gracefully', async () => {
768
+ mockUseUnifiedAuthFn.mockReturnValue({
769
+ user: mockUser,
770
+ selectedOrganisation: null,
771
+ selectedEvent: null,
772
+ appId: undefined,
773
+ appName: 'test-app',
774
+ supabase: null
775
+ });
776
+
777
+ mockUseResolvedScope.mockReturnValue({
778
+ resolvedScope: null,
779
+ isLoading: true,
780
+ error: null
781
+ });
782
+
783
+ render(
784
+ <PagePermissionGuard
785
+ pageName={mockPageName}
786
+ operation={mockOperation}
787
+ fallback={<TestFallback />}
788
+ >
789
+ <TestComponent>Protected Page</TestComponent>
790
+ </PagePermissionGuard>
791
+ );
792
+
793
+ expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
794
+ });
795
+ });
796
+
797
+ describe('Security Features', () => {
798
+ it('invokes onDenied callback when strict mode blocks access', async () => {
799
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
800
+ const onDenied = vi.fn();
801
+
802
+ mockUseCanFn.mockReturnValue({
803
+ can: false,
804
+ isLoading: false,
805
+ error: null
806
+ });
807
+
808
+ render(
809
+ <PagePermissionGuard
810
+ pageName={mockPageName}
811
+ operation={mockOperation}
812
+ strictMode={true}
813
+ fallback={<TestFallback />}
814
+ onDenied={onDenied}
815
+ >
816
+ <TestComponent>Protected Page</TestComponent>
817
+ </PagePermissionGuard>
818
+ );
819
+
820
+ await waitFor(() => {
821
+ expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
822
+ }, { interval: 10 });
823
+
824
+ expect(onDenied).toHaveBeenCalledWith(mockPageName, mockOperation);
825
+
826
+ consoleSpy.mockRestore();
827
+ });
828
+
829
+ it('does not call onDenied multiple times when audit logging rerenders', async () => {
830
+ const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
831
+ const onDenied = vi.fn();
832
+
833
+ mockUseCanFn.mockReturnValue({
834
+ can: false,
835
+ isLoading: false,
836
+ error: null
837
+ });
838
+
839
+ const { rerender } = render(
840
+ <PagePermissionGuard
841
+ pageName={mockPageName}
842
+ operation={mockOperation}
843
+ auditLog={true}
844
+ fallback={<TestFallback />}
845
+ onDenied={onDenied}
846
+ >
847
+ <TestComponent>Protected Page</TestComponent>
848
+ </PagePermissionGuard>
849
+ );
850
+
851
+ await waitFor(() => {
852
+ expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
853
+ }, { interval: 10 });
854
+
855
+ rerender(
856
+ <PagePermissionGuard
857
+ pageName={mockPageName}
858
+ operation={mockOperation}
859
+ auditLog={true}
860
+ fallback={<TestFallback />}
861
+ onDenied={onDenied}
862
+ >
863
+ <TestComponent>Protected Page</TestComponent>
864
+ </PagePermissionGuard>
865
+ );
866
+
867
+ await waitFor(() => {
868
+ expect(onDenied).toHaveBeenCalledTimes(1);
869
+ }, { interval: 10 });
870
+
871
+ consoleSpy.mockRestore();
872
+ });
873
+
874
+ it('calls onDenied callback when access is denied', async () => {
875
+ const onDeniedSpy = vi.fn();
876
+
877
+ mockUseCanFn.mockReturnValue({
878
+ can: false,
879
+ isLoading: false,
880
+ error: null
881
+ });
882
+
883
+ render(
884
+ <PagePermissionGuard
885
+ pageName={mockPageName}
886
+ operation={mockOperation}
887
+ onDenied={onDeniedSpy}
888
+ fallback={<TestFallback />}
889
+ >
890
+ <TestComponent>Protected Page</TestComponent>
891
+ </PagePermissionGuard>
892
+ );
893
+
894
+ await waitFor(() => {
895
+ expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
896
+ }, { interval: 10, timeout: 2000 });
897
+
898
+ expect(onDeniedSpy).toHaveBeenCalledWith(mockPageName, mockOperation);
899
+ });
900
+
901
+ it('does not call onDenied when access is granted', async () => {
902
+ const onDeniedSpy = vi.fn();
903
+
904
+ mockUseCanFn.mockReturnValue({
905
+ can: true,
906
+ isLoading: false,
907
+ error: null
908
+ });
909
+
910
+ render(
911
+ <PagePermissionGuard
912
+ pageName={mockPageName}
913
+ operation={mockOperation}
914
+ onDenied={onDeniedSpy}
915
+ >
916
+ <TestComponent>Protected Page</TestComponent>
917
+ </PagePermissionGuard>
918
+ );
919
+
920
+ await waitFor(() => {
921
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
922
+ }, { interval: 10 });
923
+
924
+ expect(onDeniedSpy).not.toHaveBeenCalled();
925
+ });
926
+ });
927
+
928
+ describe('Configuration Options', () => {
929
+ it('respects strictMode setting', async () => {
930
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
931
+
932
+ mockUseCanFn.mockReturnValue({
933
+ can: false,
934
+ isLoading: false,
935
+ error: null
936
+ });
937
+
938
+ render(
939
+ <PagePermissionGuard
940
+ pageName={mockPageName}
941
+ operation={mockOperation}
942
+ strictMode={false}
943
+ fallback={<TestFallback />}
944
+ >
945
+ <TestComponent>Protected Page</TestComponent>
946
+ </PagePermissionGuard>
947
+ );
948
+
949
+ await waitFor(() => {
950
+ expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
951
+ }, { interval: 10, timeout: 2000 });
952
+
953
+ expect(consoleSpy).not.toHaveBeenCalledWith(
954
+ expect.stringContaining('STRICT MODE VIOLATION')
955
+ );
956
+
957
+ consoleSpy.mockRestore();
958
+ });
959
+
960
+ it('respects auditLog setting', async () => {
961
+ const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
962
+
963
+ mockUseCanFn.mockReturnValue({
964
+ can: false,
965
+ isLoading: false,
966
+ error: null
967
+ });
968
+
969
+ render(
970
+ <PagePermissionGuard
971
+ pageName={mockPageName}
972
+ operation={mockOperation}
973
+ auditLog={false}
974
+ fallback={<TestFallback />}
975
+ >
976
+ <TestComponent>Protected Page</TestComponent>
977
+ </PagePermissionGuard>
978
+ );
979
+
980
+ await waitFor(() => {
981
+ expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
982
+ }, { interval: 10, timeout: 2000 });
983
+
984
+ expect(consoleSpy).not.toHaveBeenCalledWith(
985
+ expect.stringContaining('Page access attempt')
986
+ );
987
+
988
+ consoleSpy.mockRestore();
989
+ });
990
+ });
991
+
992
+ describe('Error Handling', () => {
993
+ it('handles missing user gracefully', async () => {
994
+ mockUseUnifiedAuthFn.mockReturnValue({
995
+ user: null,
996
+ selectedOrganisation: null, // No org when user is missing
997
+ selectedEvent: null, // No event when user is missing
998
+ appId: undefined,
999
+ appName: 'test-app',
1000
+ supabase: null
1001
+ });
1002
+
1003
+ // When user is missing, scope resolution should return null
1004
+ mockUseResolvedScope.mockReturnValue({
1005
+ resolvedScope: null,
1006
+ isLoading: false,
1007
+ error: null
1008
+ });
1009
+
1010
+ mockUseCanFn.mockReturnValue({
1011
+ can: false,
1012
+ isLoading: false,
1013
+ error: null
1014
+ });
1015
+
1016
+ render(
1017
+ <PagePermissionGuard
1018
+ pageName={mockPageName}
1019
+ operation={mockOperation}
1020
+ fallback={<TestFallback />}
1021
+ >
1022
+ <TestComponent>Protected Page</TestComponent>
1023
+ </PagePermissionGuard>
1024
+ );
1025
+
1026
+ // Component shows loading when user is missing (no valid user to check permissions for)
1027
+ expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
1028
+ });
1029
+
1030
+ it('handles database errors during app resolution', async () => {
1031
+ mockUseUnifiedAuthFn.mockReturnValue({
1032
+ user: mockUser,
1033
+ selectedOrganisation: { id: 'org-123' },
1034
+ selectedEvent: { event_id: 'event-123' },
1035
+ appId: undefined, // Not resolved due to database error
1036
+ appName: 'test-app',
1037
+ supabase: {
1038
+ from: vi.fn().mockReturnValue({
1039
+ select: vi.fn().mockReturnValue({
1040
+ eq: vi.fn().mockReturnValue({
1041
+ eq: vi.fn().mockReturnValue({
1042
+ single: vi.fn().mockResolvedValue({
1043
+ data: null,
1044
+ error: { message: 'Database error' }
1045
+ })
1046
+ })
1047
+ })
1048
+ })
1049
+ })
1050
+ } as any
1051
+ });
1052
+
1053
+ // When database error occurs, useResolvedScope might still return a scope (with appId undefined)
1054
+ // or might return null. For PORTAL app, it can work without appId
1055
+ mockUseResolvedScope.mockReturnValue({
1056
+ resolvedScope: {
1057
+ organisationId: 'org-123',
1058
+ eventId: 'event-123',
1059
+ appId: undefined // App resolution failed
1060
+ },
1061
+ isLoading: false,
1062
+ error: null
1063
+ });
1064
+
1065
+ mockUseCanFn.mockReturnValue({
1066
+ can: true, // Permission check can still succeed without appId for page permissions
1067
+ isLoading: false,
1068
+ error: null
1069
+ });
1070
+
1071
+ // Set NODE_ENV to production
1072
+ const originalEnv = process.env.NODE_ENV;
1073
+ process.env.NODE_ENV = 'production';
1074
+
1075
+ render(
1076
+ <PagePermissionGuard
1077
+ pageName={mockPageName}
1078
+ operation={mockOperation}
1079
+ fallback={<TestFallback />}
1080
+ >
1081
+ <TestComponent>Protected Page</TestComponent>
1082
+ </PagePermissionGuard>
1083
+ );
1084
+
1085
+ // Component can still work if scope is resolved (even without appId for page permissions)
1086
+ await waitFor(() => {
1087
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
1088
+ }, { interval: 10, timeout: 2000 });
1089
+
1090
+ // Restore NODE_ENV
1091
+ process.env.NODE_ENV = originalEnv;
1092
+ });
1093
+ });
1094
+
1095
+ describe('Super Admin Bypass Logic', () => {
1096
+ it('renders without errors when super admin check is in progress', async () => {
1097
+ vi.mocked(isSuperAdmin).mockResolvedValue(false);
1098
+
1099
+ mockUseUnifiedAuthFn.mockReturnValue({
1100
+ user: mockUser,
1101
+ selectedOrganisation: { id: 'org-123' },
1102
+ selectedEvent: { event_id: 'event-123' },
1103
+ appId: 'app-123',
1104
+ appName: 'test-app',
1105
+ supabase: {} as any
1106
+ });
1107
+
1108
+ mockUseResolvedScope.mockReturnValue({
1109
+ resolvedScope: mockScope,
1110
+ isLoading: false,
1111
+ error: null
1112
+ });
1113
+
1114
+ // Super admin check is async, so initially isSuperAdmin will be null
1115
+ mockUseCanFn.mockReturnValue({
1116
+ can: true, // Normal permission check
1117
+ isLoading: false,
1118
+ error: null
1119
+ });
1120
+
1121
+ render(
1122
+ <PagePermissionGuard
1123
+ pageName={mockPageName}
1124
+ operation={mockOperation}
1125
+ >
1126
+ <TestComponent>Protected Page</TestComponent>
1127
+ </PagePermissionGuard>
1128
+ );
1129
+
1130
+ // Wait for super admin check to complete
1131
+ await waitFor(() => {
1132
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
1133
+ }, { timeout: TEST_TIMEOUT, interval: 100 });
1134
+ }, TEST_TIMEOUT);
1135
+
1136
+ it('handles super admin check gracefully when user is not super admin', async () => {
1137
+ vi.mocked(isSuperAdmin).mockResolvedValue(false);
1138
+
1139
+ mockUseUnifiedAuthFn.mockReturnValue({
1140
+ user: mockUser,
1141
+ selectedOrganisation: { id: 'org-123' },
1142
+ selectedEvent: { event_id: 'event-123' },
1143
+ appId: 'app-123',
1144
+ appName: 'test-app',
1145
+ supabase: {} as any
1146
+ });
1147
+
1148
+ mockUseResolvedScope.mockReturnValue({
1149
+ resolvedScope: mockScope,
1150
+ isLoading: false,
1151
+ error: null
1152
+ });
1153
+
1154
+ mockUseCanFn.mockReturnValue({
1155
+ can: true,
1156
+ isLoading: false,
1157
+ error: null
1158
+ });
1159
+
1160
+ render(
1161
+ <PagePermissionGuard
1162
+ pageName={mockPageName}
1163
+ operation={mockOperation}
1164
+ >
1165
+ <TestComponent>Protected Page</TestComponent>
1166
+ </PagePermissionGuard>
1167
+ );
1168
+
1169
+ await waitFor(() => {
1170
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
1171
+ }, { interval: 10 });
1172
+
1173
+ // Should use normal permission check (not super admin)
1174
+ expect(mockUseCanFn).toHaveBeenCalled();
1175
+ }, TEST_TIMEOUT);
1176
+
1177
+ it('handles super admin check timeout gracefully', async () => {
1178
+ // Mock isSuperAdmin to timeout
1179
+ vi.mocked(isSuperAdmin).mockImplementation(() =>
1180
+ new Promise((_, reject) =>
1181
+ setTimeout(() => reject(new Error('Super admin check timeout')), 11000)
1182
+ )
1183
+ );
1184
+
1185
+ mockUseUnifiedAuthFn.mockReturnValue({
1186
+ user: mockUser,
1187
+ selectedOrganisation: { id: 'org-123' },
1188
+ selectedEvent: { event_id: 'event-123' },
1189
+ appId: 'app-123',
1190
+ appName: 'test-app',
1191
+ supabase: {} as any
1192
+ });
1193
+
1194
+ mockUseResolvedScope.mockReturnValue({
1195
+ resolvedScope: mockScope,
1196
+ isLoading: false,
1197
+ error: null
1198
+ });
1199
+
1200
+ mockUseCanFn.mockReturnValue({
1201
+ can: true, // Fallback to normal permission check
1202
+ isLoading: false,
1203
+ error: null
1204
+ });
1205
+
1206
+ render(
1207
+ <PagePermissionGuard
1208
+ pageName={mockPageName}
1209
+ operation={mockOperation}
1210
+ >
1211
+ <TestComponent>Protected Page</TestComponent>
1212
+ </PagePermissionGuard>
1213
+ );
1214
+
1215
+ // Should fall back to normal permission check after timeout
1216
+ await waitFor(() => {
1217
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
1218
+ }, { timeout: 12000, interval: 100 });
1219
+ }, 12000);
1220
+ });
1221
+
1222
+ describe('PORTAL/ADMIN App Scope Handling', () => {
1223
+ it('allows scope without organisation for PORTAL app', async () => {
1224
+ mockUseUnifiedAuthFn.mockReturnValue({
1225
+ user: mockUser,
1226
+ selectedOrganisation: null, // No organisation for PORTAL
1227
+ selectedEvent: null,
1228
+ appId: 'app-123',
1229
+ appName: 'PORTAL',
1230
+ supabase: {} as any
1231
+ });
1232
+
1233
+ mockUseResolvedScope.mockReturnValue({
1234
+ resolvedScope: {
1235
+ organisationId: undefined,
1236
+ eventId: undefined,
1237
+ appId: 'app-123'
1238
+ },
1239
+ isLoading: false,
1240
+ error: null
1241
+ });
1242
+
1243
+ mockUseCanFn.mockReturnValue({
1244
+ can: true,
1245
+ isLoading: false,
1246
+ error: null
1247
+ });
1248
+
1249
+ render(
1250
+ <PagePermissionGuard
1251
+ pageName={mockPageName}
1252
+ operation={mockOperation}
1253
+ >
1254
+ <TestComponent>Protected Page</TestComponent>
1255
+ </PagePermissionGuard>
1256
+ );
1257
+
1258
+ await waitFor(() => {
1259
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
1260
+ }, { interval: 10 });
1261
+
1262
+ // Should call useCan with scope that has undefined organisationId (allowed for PORTAL)
1263
+ expect(mockUseCanFn).toHaveBeenCalledWith(
1264
+ 'user-123',
1265
+ expect.objectContaining({
1266
+ appId: 'app-123'
1267
+ }),
1268
+ 'read:page.dashboard',
1269
+ 'dashboard',
1270
+ true,
1271
+ null, // precomputedSuperAdmin
1272
+ 'PORTAL'
1273
+ );
1274
+ }, TEST_TIMEOUT);
1275
+
1276
+ it('allows scope without organisation for ADMIN app', async () => {
1277
+ mockUseUnifiedAuthFn.mockReturnValue({
1278
+ user: mockUser,
1279
+ selectedOrganisation: null,
1280
+ selectedEvent: null,
1281
+ appId: 'app-123',
1282
+ appName: 'ADMIN',
1283
+ supabase: {} as any
1284
+ });
1285
+
1286
+ mockUseResolvedScope.mockReturnValue({
1287
+ resolvedScope: {
1288
+ organisationId: undefined,
1289
+ eventId: undefined,
1290
+ appId: 'app-123'
1291
+ },
1292
+ isLoading: false,
1293
+ error: null
1294
+ });
1295
+
1296
+ mockUseCanFn.mockReturnValue({
1297
+ can: true,
1298
+ isLoading: false,
1299
+ error: null
1300
+ });
1301
+
1302
+ render(
1303
+ <PagePermissionGuard
1304
+ pageName={mockPageName}
1305
+ operation={mockOperation}
1306
+ >
1307
+ <TestComponent>Protected Page</TestComponent>
1308
+ </PagePermissionGuard>
1309
+ );
1310
+
1311
+ await waitFor(() => {
1312
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
1313
+ }, { interval: 10 });
1314
+
1315
+ expect(mockUseCanFn).toHaveBeenCalledWith(
1316
+ 'user-123',
1317
+ expect.objectContaining({
1318
+ appId: 'app-123'
1319
+ }),
1320
+ 'read:page.dashboard',
1321
+ 'dashboard',
1322
+ true,
1323
+ null,
1324
+ 'ADMIN'
1325
+ );
1326
+ }, TEST_TIMEOUT);
1327
+
1328
+ it('uses contextAppId when resolved scope appId is missing for PORTAL/ADMIN', async () => {
1329
+ mockUseUnifiedAuthFn.mockReturnValue({
1330
+ user: mockUser,
1331
+ selectedOrganisation: null,
1332
+ selectedEvent: null,
1333
+ appId: 'context-app-123', // Available from context
1334
+ appName: 'PORTAL',
1335
+ supabase: {} as any
1336
+ });
1337
+
1338
+ mockUseResolvedScope.mockReturnValue({
1339
+ resolvedScope: {
1340
+ organisationId: undefined,
1341
+ eventId: undefined,
1342
+ appId: undefined // Missing from resolved scope
1343
+ },
1344
+ isLoading: false,
1345
+ error: null
1346
+ });
1347
+
1348
+ mockUseCanFn.mockReturnValue({
1349
+ can: true,
1350
+ isLoading: false,
1351
+ error: null
1352
+ });
1353
+
1354
+ render(
1355
+ <PagePermissionGuard
1356
+ pageName={mockPageName}
1357
+ operation={mockOperation}
1358
+ >
1359
+ <TestComponent>Protected Page</TestComponent>
1360
+ </PagePermissionGuard>
1361
+ );
1362
+
1363
+ await waitFor(() => {
1364
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
1365
+ }, { interval: 10 });
1366
+
1367
+ // Should use contextAppId as fallback
1368
+ expect(mockUseCanFn).toHaveBeenCalledWith(
1369
+ 'user-123',
1370
+ expect.objectContaining({
1371
+ appId: 'context-app-123'
1372
+ }),
1373
+ 'read:page.dashboard',
1374
+ 'dashboard',
1375
+ true,
1376
+ null,
1377
+ 'PORTAL'
1378
+ );
1379
+ }, TEST_TIMEOUT);
1380
+ });
1381
+
1382
+ describe('Timeout Warnings', () => {
1383
+ it('handles long loading states gracefully', async () => {
1384
+ mockUseUnifiedAuthFn.mockReturnValue({
1385
+ user: mockUser,
1386
+ selectedOrganisation: { id: 'org-123' },
1387
+ selectedEvent: { event_id: 'event-123' },
1388
+ appId: 'app-123',
1389
+ appName: 'test-app',
1390
+ supabase: {} as any
1391
+ });
1392
+
1393
+ mockUseResolvedScope.mockReturnValue({
1394
+ resolvedScope: mockScope,
1395
+ isLoading: false,
1396
+ error: null
1397
+ });
1398
+
1399
+ // Make permission check slow initially
1400
+ mockUseCanFn.mockReturnValue({
1401
+ can: true,
1402
+ isLoading: true, // Still loading
1403
+ error: null
1404
+ });
1405
+
1406
+ render(
1407
+ <PagePermissionGuard
1408
+ pageName={mockPageName}
1409
+ operation={mockOperation}
1410
+ >
1411
+ <TestComponent>Protected Page</TestComponent>
1412
+ </PagePermissionGuard>
1413
+ );
1414
+
1415
+ // Component should show loading state
1416
+ expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
1417
+
1418
+ // Then resolve the permission check
1419
+ mockUseCanFn.mockReturnValue({
1420
+ can: true,
1421
+ isLoading: false,
1422
+ error: null
1423
+ });
1424
+
1425
+ await waitFor(() => {
1426
+ expect(screen.getByTestId('test-component')).toBeInTheDocument();
1427
+ }, { timeout: TEST_TIMEOUT, interval: 100 });
1428
+ }, TEST_TIMEOUT);
1429
+ });
1430
+ });