@jmruthers/pace-core 0.5.75 → 0.5.77

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 (507) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-SBHZQtCH.d.ts} +5 -118
  3. package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
  4. package/dist/DataTable-QCNCV6IK.js +157 -0
  5. package/dist/{PublicLoadingSpinner-BKNBT6b6.d.ts → PublicLoadingSpinner-CnUaz0vG.d.ts} +33 -19
  6. package/dist/{UnifiedAuthProvider-Bj6YCf7c.d.ts → UnifiedAuthProvider-B391Aqum.d.ts} +42 -45
  7. package/dist/{UnifiedAuthProvider-3NKDOSOK.js → UnifiedAuthProvider-Z2FWNW7O.js} +4 -5
  8. package/dist/{api-DDMUKIUD.js → api-KG4A2X7P.js} +9 -3
  9. package/dist/{audit-6TOCAMKO.js → audit-65VNHEV2.js} +2 -2
  10. package/dist/{chunk-2DFZ432F.js → chunk-7PX43UYN.js} +197 -629
  11. package/dist/chunk-7PX43UYN.js.map +1 -0
  12. package/dist/{chunk-DAXLNIDY.js → chunk-C4RQ3GQA.js} +108 -32
  13. package/dist/chunk-C4RQ3GQA.js.map +1 -0
  14. package/dist/{chunk-LW7MMEAQ.js → chunk-CRKP3HXI.js} +2 -2
  15. package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
  16. package/dist/chunk-CVMVPYAL.js.map +1 -0
  17. package/dist/{chunk-CY3AHGO4.js → chunk-DDPG7FCX.js} +3395 -3254
  18. package/dist/chunk-DDPG7FCX.js.map +1 -0
  19. package/dist/{chunk-URUTVZ7N.js → chunk-DVHZ5L55.js} +2 -2
  20. package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
  21. package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
  22. package/dist/{chunk-WN6XJWOS.js → chunk-JDQ7T3QB.js} +256 -743
  23. package/dist/chunk-JDQ7T3QB.js.map +1 -0
  24. package/dist/{chunk-ZTT2AXMX.js → chunk-LMYTEMUH.js} +153 -132
  25. package/dist/chunk-LMYTEMUH.js.map +1 -0
  26. package/dist/{chunk-33PHABLB.js → chunk-NKT2DLZI.js} +13 -130
  27. package/dist/chunk-NKT2DLZI.js.map +1 -0
  28. package/dist/chunk-PUKTJMRT.js +732 -0
  29. package/dist/chunk-PUKTJMRT.js.map +1 -0
  30. package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
  31. package/dist/chunk-Q7APDV6H.js.map +1 -0
  32. package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
  33. package/dist/chunk-S63MFSY6.js.map +1 -0
  34. package/dist/{chunk-NTNILOBC.js → chunk-TLD5BEU6.js} +4 -4
  35. package/dist/chunk-WUXCWRL6.js +20 -0
  36. package/dist/chunk-WUXCWRL6.js.map +1 -0
  37. package/dist/{chunk-YNUBMSMV.js → chunk-YCKPEMJA.js} +186 -263
  38. package/dist/chunk-YCKPEMJA.js.map +1 -0
  39. package/dist/{chunk-A4FUBC7B.js → chunk-Z3T6RK3K.js} +2 -4
  40. package/dist/{chunk-A4FUBC7B.js.map → chunk-Z3T6RK3K.js.map} +1 -1
  41. package/dist/components.d.ts +6 -6
  42. package/dist/components.js +17 -20
  43. package/dist/components.js.map +1 -1
  44. package/dist/{database-C3Szpi5J.d.ts → database-BXAfr2Y_.d.ts} +18 -0
  45. package/dist/hooks.d.ts +21 -44
  46. package/dist/hooks.js +12 -13
  47. package/dist/hooks.js.map +1 -1
  48. package/dist/index.d.ts +19 -27
  49. package/dist/index.js +27 -33
  50. package/dist/index.js.map +1 -1
  51. package/dist/{organisation-BtshODVF.d.ts → organisation-D6qRDtbF.d.ts} +1 -1
  52. package/dist/providers.d.ts +7 -21
  53. package/dist/providers.js +3 -10
  54. package/dist/rbac/index.d.ts +118 -215
  55. package/dist/rbac/index.js +18 -18
  56. package/dist/{types-CGX9Vyf5.d.ts → types-BDg1mAGG.d.ts} +36 -6
  57. package/dist/types.d.ts +3 -3
  58. package/dist/types.js +61 -18
  59. package/dist/types.js.map +1 -1
  60. package/dist/{unified-CM7T0aTK.d.ts → unified-DQ4VcT7H.d.ts} +1 -1
  61. package/dist/{usePublicRouteParams-B-CumWRc.d.ts → usePublicRouteParams-BlgwXweB.d.ts} +3 -3
  62. package/dist/utils.d.ts +2 -2
  63. package/dist/utils.js +52 -9
  64. package/dist/utils.js.map +1 -1
  65. package/docs/CONTENT_AUDIT_REPORT.md +253 -0
  66. package/docs/DOCUMENTATION_AUDIT.md +172 -0
  67. package/docs/README.md +142 -147
  68. package/docs/STYLE_GUIDE.md +37 -0
  69. package/docs/api/classes/ColumnFactory.md +17 -17
  70. package/docs/api/classes/ErrorBoundary.md +1 -1
  71. package/docs/api/classes/InvalidScopeError.md +4 -4
  72. package/docs/api/classes/MissingUserContextError.md +4 -4
  73. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  74. package/docs/api/classes/PermissionDeniedError.md +5 -5
  75. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  76. package/docs/api/classes/RBACAuditManager.md +8 -8
  77. package/docs/api/classes/RBACCache.md +35 -5
  78. package/docs/api/classes/RBACEngine.md +49 -20
  79. package/docs/api/classes/RBACError.md +4 -4
  80. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  81. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  82. package/docs/api/classes/StorageUtils.md +1 -1
  83. package/docs/api/enums/FileCategory.md +1 -1
  84. package/docs/api/interfaces/AggregateConfig.md +4 -4
  85. package/docs/api/interfaces/ButtonProps.md +1 -1
  86. package/docs/api/interfaces/CardProps.md +1 -1
  87. package/docs/api/interfaces/ColorPalette.md +1 -1
  88. package/docs/api/interfaces/ColorShade.md +1 -1
  89. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  90. package/docs/api/interfaces/DataRecord.md +11 -0
  91. package/docs/api/interfaces/DataTableAction.md +65 -29
  92. package/docs/api/interfaces/DataTableColumn.md +36 -23
  93. package/docs/api/interfaces/DataTableProps.md +80 -38
  94. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  95. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  96. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  97. package/docs/api/interfaces/EventLogoProps.md +1 -1
  98. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  99. package/docs/api/interfaces/FileMetadata.md +1 -1
  100. package/docs/api/interfaces/FileReference.md +1 -1
  101. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  102. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  103. package/docs/api/interfaces/FileUploadProps.md +1 -1
  104. package/docs/api/interfaces/FooterProps.md +1 -1
  105. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  106. package/docs/api/interfaces/InputProps.md +1 -1
  107. package/docs/api/interfaces/LabelProps.md +1 -1
  108. package/docs/api/interfaces/LoginFormProps.md +1 -1
  109. package/docs/api/interfaces/NavigationAccessRecord.md +11 -11
  110. package/docs/api/interfaces/NavigationContextType.md +9 -9
  111. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  112. package/docs/api/interfaces/NavigationItem.md +1 -1
  113. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  114. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  115. package/docs/api/interfaces/Organisation.md +1 -1
  116. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  117. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  118. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  119. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  120. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  121. package/docs/api/interfaces/PaceLoginPageProps.md +16 -3
  122. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  123. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  124. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  125. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  126. package/docs/api/interfaces/PaletteData.md +1 -1
  127. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  128. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  129. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  130. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  131. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  132. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  133. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  134. package/docs/api/interfaces/RBACConfig.md +1 -1
  135. package/docs/api/interfaces/RBACLogger.md +1 -1
  136. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  137. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  138. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  139. package/docs/api/interfaces/RouteConfig.md +2 -2
  140. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  141. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  142. package/docs/api/interfaces/StorageConfig.md +1 -1
  143. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  144. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  145. package/docs/api/interfaces/StorageListOptions.md +1 -1
  146. package/docs/api/interfaces/StorageListResult.md +1 -1
  147. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  148. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  149. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  150. package/docs/api/interfaces/StyleImport.md +1 -1
  151. package/docs/api/interfaces/SwitchProps.md +1 -1
  152. package/docs/api/interfaces/ToastActionElement.md +1 -1
  153. package/docs/api/interfaces/ToastProps.md +1 -1
  154. package/docs/api/interfaces/UnifiedAuthContextType.md +94 -521
  155. package/docs/api/interfaces/UnifiedAuthProviderProps.md +16 -16
  156. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  157. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  158. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  159. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  160. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  161. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  162. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  163. package/docs/api/interfaces/UseResolvedScopeOptions.md +47 -0
  164. package/docs/api/interfaces/UseResolvedScopeReturn.md +47 -0
  165. package/docs/api/interfaces/UserEventAccess.md +11 -11
  166. package/docs/api/interfaces/UserMenuProps.md +1 -1
  167. package/docs/api/interfaces/UserProfile.md +1 -1
  168. package/docs/api/modules.md +303 -275
  169. package/docs/api-reference/components.md +193 -0
  170. package/docs/api-reference/hooks.md +265 -0
  171. package/docs/api-reference/providers.md +32 -7
  172. package/docs/api-reference/types.md +6 -0
  173. package/docs/api-reference/utilities.md +207 -0
  174. package/docs/architecture/README.md +6 -0
  175. package/docs/{database-schema-requirements.md → architecture/database-schema-requirements.md} +6 -0
  176. package/docs/architecture/rbac-security-architecture.md +258 -0
  177. package/docs/architecture/services.md +9 -1
  178. package/docs/best-practices/README.md +26 -0
  179. package/docs/best-practices/accessibility.md +572 -0
  180. package/docs/{common-patterns.md → best-practices/common-patterns.md} +6 -0
  181. package/docs/best-practices/deployment.md +6 -0
  182. package/docs/best-practices/performance.md +475 -2
  183. package/docs/best-practices/security.md +6 -0
  184. package/docs/best-practices/testing.md +6 -0
  185. package/docs/core-concepts/authentication.md +21 -7
  186. package/docs/core-concepts/events.md +6 -0
  187. package/docs/core-concepts/organisations.md +6 -0
  188. package/docs/core-concepts/permissions.md +6 -0
  189. package/docs/core-concepts/rbac-system.md +6 -0
  190. package/docs/documentation-index.md +121 -182
  191. package/docs/{consuming-app-vite-config.md → getting-started/consuming-app-vite-config.md} +6 -0
  192. package/docs/getting-started/documentation-index.md +40 -0
  193. package/docs/getting-started/examples/README.md +878 -35
  194. package/docs/{faq.md → getting-started/faq.md} +7 -1
  195. package/docs/getting-started/installation-guide.md +6 -0
  196. package/docs/{quick-reference.md → getting-started/quick-reference.md} +6 -0
  197. package/docs/implementation-guides/app-layout.md +6 -0
  198. package/docs/implementation-guides/authentication.md +1021 -0
  199. package/docs/implementation-guides/component-styling.md +416 -0
  200. package/docs/implementation-guides/data-tables.md +1264 -2076
  201. package/docs/implementation-guides/dynamic-colors.md +6 -0
  202. package/docs/implementation-guides/event-theming-summary.md +6 -0
  203. package/docs/{file-reference-system.md → implementation-guides/file-reference-system.md} +6 -0
  204. package/docs/implementation-guides/file-upload-storage.md +6 -0
  205. package/docs/implementation-guides/forms.md +6 -0
  206. package/docs/implementation-guides/inactivity-tracking.md +6 -0
  207. package/docs/implementation-guides/navigation.md +6 -0
  208. package/docs/implementation-guides/organisation-security.md +6 -0
  209. package/docs/implementation-guides/permission-enforcement.md +6 -0
  210. package/docs/implementation-guides/public-pages-advanced.md +6 -0
  211. package/docs/implementation-guides/public-pages.md +6 -0
  212. package/docs/migration/MIGRATION_GUIDE.md +827 -351
  213. package/docs/migration/README.md +7 -1
  214. package/docs/migration/organisation-context-timing-fix.md +6 -0
  215. package/docs/migration/rbac-migration.md +44 -1
  216. package/docs/migration/service-architecture.md +6 -0
  217. package/docs/migration/v0.4.15-tailwind-scanning.md +6 -0
  218. package/docs/migration/v0.4.16-css-first-approach.md +6 -0
  219. package/docs/migration/v0.4.17-source-path-fix.md +6 -0
  220. package/docs/rbac/README-rbac-rls-integration.md +6 -0
  221. package/docs/rbac/README.md +6 -0
  222. package/docs/rbac/advanced-patterns.md +6 -0
  223. package/docs/rbac/api-reference.md +7 -1
  224. package/docs/rbac/breaking-changes-v3.md +222 -0
  225. package/docs/rbac/examples/rbac-rls-integration-example.md +6 -0
  226. package/docs/rbac/examples.md +6 -0
  227. package/docs/rbac/getting-started.md +6 -0
  228. package/docs/rbac/migration-guide.md +260 -0
  229. package/docs/rbac/quick-start.md +6 -0
  230. package/docs/rbac/rbac-rls-integration.md +6 -0
  231. package/docs/rbac/super-admin-guide.md +6 -0
  232. package/docs/rbac/troubleshooting.md +6 -0
  233. package/docs/security/README.md +6 -0
  234. package/docs/security/checklist.md +6 -0
  235. package/docs/styles/README.md +7 -1
  236. package/docs/{usage.md → styles/usage.md} +6 -0
  237. package/docs/testing/README.md +6 -0
  238. package/docs/{visual-testing.md → testing/visual-testing.md} +6 -0
  239. package/docs/troubleshooting/README.md +387 -5
  240. package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +6 -0
  241. package/docs/troubleshooting/common-issues.md +6 -0
  242. package/docs/troubleshooting/database-view-compatibility.md +6 -0
  243. package/docs/troubleshooting/organisation-context-setup.md +6 -0
  244. package/docs/troubleshooting/react-hooks-issue-analysis.md +6 -0
  245. package/docs/troubleshooting/styling-issues.md +6 -0
  246. package/docs/troubleshooting/tailwind-content-scanning.md +6 -0
  247. package/package.json +1 -1
  248. package/src/__tests__/TEST_GUIDE_CURSOR.md +290 -0
  249. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -1
  250. package/src/__tests__/helpers/supabaseMock.ts +48 -2
  251. package/src/__tests__/helpers/test-providers.tsx +3 -53
  252. package/src/components/DataTable/DataTable.test.tsx +319 -0
  253. package/src/components/DataTable/DataTable.tsx +32 -11
  254. package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx.skip} +6 -4
  255. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +17 -6
  256. package/src/components/DataTable/__tests__/{DataTable.test.tsx → DataTable.test.tsx.skip} +6 -4
  257. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +96 -10
  258. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +601 -0
  259. package/src/components/DataTable/__tests__/keyboard.test.tsx +615 -0
  260. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +639 -0
  261. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx.skip +330 -0
  262. package/src/components/DataTable/components/AccessDeniedPage.tsx +2 -2
  263. package/src/components/DataTable/components/ActionButtons.tsx +88 -104
  264. package/src/components/DataTable/components/DataTableCore.tsx +442 -665
  265. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +4 -2
  266. package/src/components/DataTable/components/DataTableModals.tsx +22 -1
  267. package/src/components/DataTable/components/EditableRow.tsx +69 -84
  268. package/src/components/DataTable/components/EmptyState.tsx +5 -1
  269. package/src/components/DataTable/components/ImportModal.tsx +65 -36
  270. package/src/components/DataTable/components/PaginationControls.tsx +40 -100
  271. package/src/components/DataTable/components/UnifiedTableBody.tsx +222 -278
  272. package/src/components/DataTable/components/index.ts +1 -2
  273. package/src/components/DataTable/context/DataTableContext.tsx +1 -1
  274. package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +208 -275
  275. package/src/components/DataTable/core/ColumnFactory.ts +5 -0
  276. package/src/components/DataTable/core/index.ts +1 -8
  277. package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +12 -10
  278. package/src/components/DataTable/examples/HierarchicalExample.tsx +1 -1
  279. package/src/components/DataTable/examples/InitialPageSizeExample.tsx +1 -0
  280. package/src/components/DataTable/examples/PerformanceExample.tsx +1 -0
  281. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +521 -0
  282. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +570 -0
  283. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +167 -0
  284. package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +214 -0
  285. package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +224 -0
  286. package/src/components/DataTable/hooks/index.ts +13 -0
  287. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +32 -15
  288. package/src/components/DataTable/hooks/useColumnReordering.ts +1 -0
  289. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +102 -0
  290. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +89 -0
  291. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +117 -0
  292. package/src/components/DataTable/hooks/useDataTablePermissions.ts +193 -0
  293. package/src/components/DataTable/hooks/useDataTableState.ts +51 -17
  294. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +33 -0
  295. package/src/components/DataTable/hooks/useHierarchicalState.ts +41 -9
  296. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +447 -0
  297. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +94 -0
  298. package/src/components/DataTable/hooks/useTableColumns.ts +156 -0
  299. package/src/components/DataTable/hooks/useTableHandlers.ts +174 -0
  300. package/src/components/DataTable/index.ts +13 -12
  301. package/src/components/DataTable/types.ts +129 -9
  302. package/src/components/DataTable/utils/__tests__/COVERAGE_NOTE.md +89 -0
  303. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +162 -28
  304. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +573 -0
  305. package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +247 -0
  306. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +8 -6
  307. package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +466 -0
  308. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +251 -0
  309. package/src/components/DataTable/utils/a11yUtils.ts +244 -0
  310. package/src/components/DataTable/utils/debugTools.ts +47 -21
  311. package/src/components/DataTable/utils/errorHandling.ts +52 -460
  312. package/src/components/DataTable/utils/exportUtils.ts +157 -28
  313. package/src/components/DataTable/utils/flexibleImport.ts +202 -32
  314. package/src/components/DataTable/utils/hierarchicalSorting.ts +50 -3
  315. package/src/components/DataTable/utils/hierarchicalUtils.ts +167 -34
  316. package/src/components/DataTable/utils/index.ts +7 -0
  317. package/src/components/DataTable/utils/paginationUtils.ts +350 -0
  318. package/src/components/DataTable/utils/rowUtils.ts +69 -0
  319. package/src/components/EventSelector/EventSelector.test.tsx +672 -0
  320. package/src/components/Label/__tests__/Label.test.tsx +434 -0
  321. package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -24
  322. package/src/components/NavigationMenu/NavigationMenu.tsx +19 -8
  323. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +1 -23
  324. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +56 -6
  325. package/src/components/PaceLoginPage/PaceLoginPage.tsx +137 -13
  326. package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +190 -0
  327. package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +185 -0
  328. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +1 -1
  329. package/src/components/PublicLayout/__tests__/PublicPageProvider.test.tsx +313 -0
  330. package/src/components/Select/Select.test.tsx +143 -120
  331. package/src/components/Select/Select.tsx +48 -212
  332. package/src/components/Select/hooks.ts +36 -1
  333. package/src/components/Select/index.ts +2 -1
  334. package/src/components/examples/PermissionExample.tsx +173 -0
  335. package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
  336. package/src/examples/PublicEventPage.tsx +274 -0
  337. package/src/examples/PublicPageApp.tsx +308 -0
  338. package/src/examples/PublicPageUsageExample.tsx +216 -0
  339. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +12 -1
  340. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +129 -17
  341. package/src/hooks/__tests__/useRBAC.unit.test.ts +151 -846
  342. package/src/hooks/useOrganisationPermissions.test.ts +42 -18
  343. package/src/hooks/useOrganisationPermissions.ts +12 -6
  344. package/src/hooks/useOrganisationSecurity.test.ts +138 -85
  345. package/src/hooks/useOrganisationSecurity.ts +41 -10
  346. package/src/hooks/useSecureDataAccess.test.ts +32 -29
  347. package/src/index.ts +0 -1
  348. package/src/providers/AuthProvider.simplified.tsx +880 -0
  349. package/src/providers/UnifiedAuthProvider.test.simple.tsx +8 -8
  350. package/src/providers/__tests__/ProviderLifecycle.test.tsx +341 -0
  351. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +29 -19
  352. package/src/providers/index.ts +0 -1
  353. package/src/providers/services/EventServiceProvider.tsx +19 -15
  354. package/src/providers/services/InactivityServiceProvider.tsx +19 -15
  355. package/src/providers/services/OrganisationServiceProvider.tsx +19 -15
  356. package/src/providers/services/UnifiedAuthProvider.tsx +156 -127
  357. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +1 -1
  358. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -3
  359. package/src/rbac/README.md +1 -1
  360. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +25 -27
  361. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +313 -0
  362. package/src/rbac/__tests__/engine.comprehensive.test.ts +114 -348
  363. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +28 -110
  364. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +33 -85
  365. package/src/rbac/__tests__/scenarios.user-role.test.tsx +2 -2
  366. package/src/rbac/adapters.tsx +26 -69
  367. package/src/rbac/api.test.ts +90 -27
  368. package/src/rbac/api.ts +61 -10
  369. package/src/rbac/audit.test.ts +33 -38
  370. package/src/rbac/audit.ts +21 -6
  371. package/src/rbac/cache.ts +33 -1
  372. package/src/rbac/components/NavigationGuard.tsx +11 -11
  373. package/src/rbac/components/NavigationProvider.test.tsx +11 -5
  374. package/src/rbac/components/NavigationProvider.tsx +37 -13
  375. package/src/rbac/components/PagePermissionGuard.tsx +111 -50
  376. package/src/rbac/components/PagePermissionProvider.tsx +5 -5
  377. package/src/rbac/components/PermissionEnforcer.tsx +11 -11
  378. package/src/rbac/components/RoleBasedRouter.tsx +5 -5
  379. package/src/rbac/components/SecureDataProvider.tsx +5 -5
  380. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +8 -8
  381. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +14 -14
  382. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +12 -12
  383. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +6 -6
  384. package/src/rbac/engine.test.simple.ts +19 -13
  385. package/src/rbac/engine.test.ts +1 -0
  386. package/src/rbac/engine.ts +330 -766
  387. package/src/rbac/errors.ts +156 -0
  388. package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +437 -0
  389. package/src/rbac/hooks/index.ts +2 -0
  390. package/src/rbac/hooks/usePermissions.ts +32 -10
  391. package/src/rbac/hooks/useRBAC.test.ts +126 -512
  392. package/src/rbac/hooks/useRBAC.ts +147 -193
  393. package/src/rbac/hooks/useResolvedScope.ts +244 -0
  394. package/src/rbac/index.ts +7 -4
  395. package/src/rbac/security.ts +109 -18
  396. package/src/rbac/types.ts +12 -1
  397. package/src/services/AuthService.ts +2 -15
  398. package/src/services/EventService.ts +26 -46
  399. package/src/services/OrganisationService.ts +51 -31
  400. package/src/services/__tests__/AuthService.test.ts +1 -1
  401. package/src/services/__tests__/EventService.test.ts +1 -1
  402. package/src/services/__tests__/InactivityService.lifecycle.test.ts +411 -0
  403. package/src/services/__tests__/OrganisationService.pagination.test.ts +375 -0
  404. package/src/services/__tests__/OrganisationService.test.ts +1 -1
  405. package/src/styles/base.css +208 -0
  406. package/src/styles/semantic.css +24 -0
  407. package/src/types/__tests__/README.md +114 -0
  408. package/src/types/__tests__/validation.test.ts +731 -0
  409. package/src/types/database.generated.ts +7347 -0
  410. package/src/types/database.ts +20 -0
  411. package/src/utils/__tests__/file-reference.test.ts +383 -0
  412. package/src/utils/__tests__/performanceBenchmark.test.ts +175 -0
  413. package/src/utils/appNameResolver.test.ts +54 -0
  414. package/src/utils/logger.ts +179 -0
  415. package/src/utils/organisationContext.ts +11 -4
  416. package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
  417. package/src/validation/__tests__/csrf.unit.test.ts +63 -0
  418. package/src/validation/__tests__/passwordSchema.unit.test.ts +105 -0
  419. package/dist/DataTable-HWZQGASI.js +0 -102
  420. package/dist/appNameResolver-UURKN7NF.js +0 -22
  421. package/dist/audit-6TOCAMKO.js.map +0 -1
  422. package/dist/chunk-2CHATWBF.js +0 -523
  423. package/dist/chunk-2CHATWBF.js.map +0 -1
  424. package/dist/chunk-2DFZ432F.js.map +0 -1
  425. package/dist/chunk-33PHABLB.js.map +0 -1
  426. package/dist/chunk-B2WTCLCV.js.map +0 -1
  427. package/dist/chunk-CY3AHGO4.js.map +0 -1
  428. package/dist/chunk-DAXLNIDY.js.map +0 -1
  429. package/dist/chunk-FGMFQSHX.js.map +0 -1
  430. package/dist/chunk-TYHR5X4W.js +0 -33
  431. package/dist/chunk-TYHR5X4W.js.map +0 -1
  432. package/dist/chunk-ULBI5JGB.js +0 -109
  433. package/dist/chunk-ULBI5JGB.js.map +0 -1
  434. package/dist/chunk-WN6XJWOS.js.map +0 -1
  435. package/dist/chunk-XLZ7U46Z.js.map +0 -1
  436. package/dist/chunk-YNUBMSMV.js.map +0 -1
  437. package/dist/chunk-ZTT2AXMX.js.map +0 -1
  438. package/dist/eventContext-BBA42P6G.js +0 -14
  439. package/dist/eventContext-BBA42P6G.js.map +0 -1
  440. package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
  441. package/docs/api/interfaces/RBACContextType.md +0 -468
  442. package/docs/api/interfaces/RBACProviderProps.md +0 -107
  443. package/docs/breaking-changes.md +0 -179
  444. package/docs/consuming-app-example.md +0 -290
  445. package/docs/documentation-style-checklist.md +0 -294
  446. package/docs/examples/navigation-menu-auth-fix.md +0 -344
  447. package/docs/getting-started/examples/basic-auth-app.md +0 -520
  448. package/docs/getting-started/examples/full-featured-app.md +0 -616
  449. package/docs/getting-started/quick-start.md +0 -426
  450. package/docs/implementation-guides/datatable-filtering.md +0 -313
  451. package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
  452. package/docs/implementation-guides/hierarchical-datatable.md +0 -850
  453. package/docs/implementation-guides/large-datasets.md +0 -281
  454. package/docs/implementation-guides/performance.md +0 -403
  455. package/docs/migration/quick-migration-guide.md +0 -320
  456. package/docs/migration-guide.md +0 -193
  457. package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
  458. package/docs/performance/README.md +0 -551
  459. package/docs/style-guide.md +0 -925
  460. package/docs/troubleshooting/authentication-issues.md +0 -334
  461. package/docs/troubleshooting/debugging.md +0 -1117
  462. package/docs/troubleshooting/migration.md +0 -918
  463. package/src/__tests__/hooks/usePermissions.test.ts +0 -261
  464. package/src/components/DataTable/components/DataTableBody.tsx +0 -488
  465. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -144
  466. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -515
  467. package/src/components/DataTable/core/ActionManager.ts +0 -235
  468. package/src/components/DataTable/core/ColumnManager.ts +0 -215
  469. package/src/components/DataTable/core/DataManager.ts +0 -188
  470. package/src/components/DataTable/core/DataTableContext.tsx +0 -181
  471. package/src/components/DataTable/core/LocalDataAdapter.ts +0 -264
  472. package/src/components/DataTable/core/PluginRegistry.ts +0 -229
  473. package/src/components/DataTable/core/StateManager.ts +0 -311
  474. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -634
  475. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -193
  476. package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -519
  477. package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -714
  478. package/src/components/DataTable/core/interfaces.ts +0 -338
  479. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
  480. package/src/components/Select/Select.bug-test.tsx +0 -69
  481. package/src/components/Select/Select.refactored.tsx +0 -497
  482. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
  483. package/src/hooks/services/usePermissions.ts +0 -70
  484. package/src/hooks/services/useRBACService.ts +0 -30
  485. package/src/hooks/usePermissionCheck.ts +0 -150
  486. package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
  487. package/src/providers/services/RBACServiceProvider.tsx +0 -79
  488. package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
  489. package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
  490. package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
  491. package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
  492. package/src/rbac/providers/RBACProvider.tsx +0 -645
  493. package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
  494. package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
  495. package/src/rbac/providers/index.ts +0 -11
  496. package/src/services/RBACService.ts +0 -522
  497. package/src/services/__tests__/RBACService.test.ts +0 -492
  498. package/src/services/interfaces/IRBACService.ts +0 -62
  499. package/src/utils/appNameResolver.test 2.ts +0 -494
  500. /package/dist/{DataTable-HWZQGASI.js.map → DataTable-QCNCV6IK.js.map} +0 -0
  501. /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-Z2FWNW7O.js.map} +0 -0
  502. /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
  503. /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
  504. /package/dist/{chunk-LW7MMEAQ.js.map → chunk-CRKP3HXI.js.map} +0 -0
  505. /package/dist/{chunk-URUTVZ7N.js.map → chunk-DVHZ5L55.js.map} +0 -0
  506. /package/dist/{chunk-NTNILOBC.js.map → chunk-TLD5BEU6.js.map} +0 -0
  507. /package/docs/{app.css.example → styles/app.css.example} +0 -0
@@ -0,0 +1,880 @@
1
+ /**
2
+ * @file Simplified Auth Provider
3
+ * @package @jmruthers/pace-core
4
+ * @module Providers
5
+ * @since 2.0.0
6
+ *
7
+ * BREAKING CHANGES FROM v1:
8
+ * - Single provider instead of nested contexts
9
+ * - No service classes (direct React state)
10
+ * - No observer pattern (React's built-in re-rendering)
11
+ * - No UnifiedAuthProvider nesting
12
+ *
13
+ * This provides:
14
+ * - Authentication (user, session)
15
+ * - Organisation management
16
+ * - Event management
17
+ * - Inactivity tracking
18
+ *
19
+ * All in one provider with React state management.
20
+ */
21
+
22
+ import React, {
23
+ createContext,
24
+ useContext,
25
+ useState,
26
+ useEffect,
27
+ useCallback,
28
+ useMemo,
29
+ useRef
30
+ } from 'react';
31
+ import {
32
+ type SupabaseClient,
33
+ type User,
34
+ type Session,
35
+ type AuthError
36
+ } from '@supabase/supabase-js';
37
+ import type { UUID } from '../rbac/types';
38
+
39
+ // ============================================================================
40
+ // TYPES
41
+ // ============================================================================
42
+
43
+ export interface Organisation {
44
+ id: UUID;
45
+ name: string;
46
+ display_name?: string;
47
+ subscription_tier?: string;
48
+ settings?: Record<string, any>;
49
+ is_active: boolean;
50
+ created_at: string;
51
+ updated_at?: string;
52
+ }
53
+
54
+ export interface Event {
55
+ id: string;
56
+ event_id: string;
57
+ name: string;
58
+ organisation_id: UUID;
59
+ start_date?: string;
60
+ end_date?: string;
61
+ status?: string;
62
+ settings?: Record<string, any>;
63
+ }
64
+
65
+ export interface AuthContextType {
66
+ // ========== Auth State ==========
67
+ user: User | null;
68
+ session: Session | null;
69
+ isAuthenticated: boolean;
70
+ authLoading: boolean;
71
+ authError: AuthError | null;
72
+ supabase: SupabaseClient;
73
+
74
+ // ========== Auth Methods ==========
75
+ signIn: (email: string, password?: string) => Promise<{ error: AuthError | null }>;
76
+ signUp: (email: string, password: string) => Promise<{ error: AuthError | null }>;
77
+ signOut: () => Promise<{ error: AuthError | null }>;
78
+ resetPassword: (email: string) => Promise<{ error: AuthError | null }>;
79
+ updatePassword: (password: string) => Promise<{ error: AuthError | null }>;
80
+ refreshSession: () => Promise<{ error: AuthError | null }>;
81
+
82
+ // ========== Organisation State ==========
83
+ selectedOrganisation: Organisation | null;
84
+ organisations: Organisation[];
85
+ organisationLoading: boolean;
86
+ organisationError: Error | null;
87
+ hasValidOrganisationContext: boolean;
88
+ isContextReady: boolean;
89
+
90
+ // ========== Organisation Methods ==========
91
+ switchOrganisation: (orgId: UUID) => Promise<void>;
92
+ refreshOrganisations: () => Promise<void>;
93
+ getUserRole: (orgId?: UUID) => string | null;
94
+ getPrimaryOrganisation: () => Organisation | null;
95
+
96
+ // ========== Event State ==========
97
+ events: Event[];
98
+ selectedEvent: Event | null;
99
+ eventLoading: boolean;
100
+ eventError: Error | null;
101
+
102
+ // ========== Event Methods ==========
103
+ setSelectedEvent: (event: Event | null) => void;
104
+ refreshEvents: () => Promise<void>;
105
+
106
+ // ========== Inactivity State ==========
107
+ showInactivityWarning: boolean;
108
+ inactivityTimeRemaining: number;
109
+ isIdle: boolean;
110
+ isTracking: boolean;
111
+
112
+ // ========== Inactivity Methods ==========
113
+ resetActivity: () => void;
114
+ startTracking: () => void;
115
+ stopTracking: () => void;
116
+
117
+ // ========== Session Management ==========
118
+ sessionId: string | null;
119
+ deviceFingerprint: string | null;
120
+ hasConcurrentSessions: boolean;
121
+ }
122
+
123
+ export interface AuthProviderProps {
124
+ children: React.ReactNode;
125
+ supabaseClient: SupabaseClient;
126
+ appName: string;
127
+ persistState?: boolean;
128
+ requireOrganisationContext?: boolean;
129
+
130
+ // Inactivity configuration (MANDATORY for security)
131
+ idleTimeoutMs: number;
132
+ warnBeforeMs: number;
133
+ onIdleLogout: (reason: 'inactivity') => void;
134
+ renderInactivityWarning?: (args: {
135
+ timeRemaining: number;
136
+ onStaySignedIn: () => void;
137
+ onSignOutNow: () => void;
138
+ }) => React.ReactNode;
139
+ }
140
+
141
+ // ============================================================================
142
+ // CONTEXT
143
+ // ============================================================================
144
+
145
+ const AuthContext = createContext<AuthContextType | null>(null);
146
+
147
+ // ============================================================================
148
+ // PROVIDER
149
+ // ============================================================================
150
+
151
+ export function AuthProvider({
152
+ children,
153
+ supabaseClient,
154
+ appName,
155
+ persistState = true,
156
+ requireOrganisationContext = true,
157
+ idleTimeoutMs = 30 * 60 * 1000, // 30 minutes - MANDATORY for security
158
+ warnBeforeMs = 60 * 1000, // 1 minute
159
+ onIdleLogout, // MANDATORY - must be provided
160
+ renderInactivityWarning,
161
+ }: AuthProviderProps) {
162
+ // MANDATORY: Inactivity timeout cannot be disabled
163
+ if (!idleTimeoutMs || idleTimeoutMs < 60000) {
164
+ throw new Error(
165
+ 'AuthProvider: idleTimeoutMs is MANDATORY and must be at least 60 seconds (60000ms) for security. ' +
166
+ 'The dangerouslyDisableInactivity flag has been removed.'
167
+ );
168
+ }
169
+
170
+ if (!onIdleLogout) {
171
+ throw new Error(
172
+ 'AuthProvider: onIdleLogout callback is MANDATORY and must be provided for security.'
173
+ );
174
+ }
175
+
176
+ // ==========================================================================
177
+ // AUTH STATE (replaces AuthService)
178
+ // ==========================================================================
179
+
180
+ const [user, setUser] = useState<User | null>(null);
181
+ const [session, setSession] = useState<Session | null>(null);
182
+ const [authLoading, setAuthLoading] = useState(true);
183
+ const [authError, setAuthError] = useState<AuthError | null>(null);
184
+
185
+ // ==========================================================================
186
+ // ORGANISATION STATE (replaces OrganisationService)
187
+ // ==========================================================================
188
+
189
+ const [selectedOrganisation, setSelectedOrganisation] = useState<Organisation | null>(null);
190
+ const [organisations, setOrganisations] = useState<Organisation[]>([]);
191
+ const [organisationLoading, setOrganisationLoading] = useState(false);
192
+ const [organisationError, setOrganisationError] = useState<Error | null>(null);
193
+
194
+ // ==========================================================================
195
+ // EVENT STATE (replaces EventService)
196
+ // ==========================================================================
197
+
198
+ const [events, setEvents] = useState<Event[]>([]);
199
+ const [selectedEvent, setSelectedEvent] = useState<Event | null>(null);
200
+ const [eventLoading, setEventLoading] = useState(false);
201
+ const [eventError, setEventError] = useState<Error | null>(null);
202
+
203
+ // ==========================================================================
204
+ // INACTIVITY STATE (replaces InactivityService)
205
+ // ==========================================================================
206
+
207
+ const [showInactivityWarning, setShowInactivityWarning] = useState(false);
208
+ const [inactivityTimeRemaining, setInactivityTimeRemaining] = useState(idleTimeoutMs);
209
+ const [isIdle, setIsIdle] = useState(false);
210
+ const [isTracking, setIsTracking] = useState(false);
211
+
212
+ const lastActivityRef = useRef<number>(Date.now());
213
+ const idleTimerRef = useRef<NodeJS.Timeout | null>(null);
214
+ const warningTimerRef = useRef<NodeJS.Timeout | null>(null);
215
+ const deviceFingerprintRef = useRef<string | null>(null);
216
+ const sessionIdRef = useRef<string | null>(null);
217
+
218
+ // ==========================================================================
219
+ // AUTH EFFECTS (replaces AuthService initialization)
220
+ // ==========================================================================
221
+
222
+ useEffect(() => {
223
+ // Setup auth state listener
224
+ const { data: { subscription } } = supabaseClient.auth.onAuthStateChange(
225
+ (event, newSession) => {
226
+ console.log('[AuthProvider] Auth state change:', event);
227
+
228
+ if (event === 'SIGNED_OUT') {
229
+ setSession(null);
230
+ setUser(null);
231
+ setAuthError(null);
232
+ } else if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {
233
+ setSession(newSession);
234
+ setUser(newSession?.user ?? null);
235
+ if (newSession) {
236
+ setAuthError(null);
237
+ }
238
+ } else if (event === 'INITIAL_SESSION') {
239
+ if (newSession) {
240
+ setSession(newSession);
241
+ setUser(newSession.user ?? null);
242
+ setAuthError(null);
243
+ }
244
+ }
245
+
246
+ setAuthLoading(false);
247
+ }
248
+ );
249
+
250
+ // Initial session check
251
+ supabaseClient.auth.getSession().then(({ data: { session: initialSession } }) => {
252
+ setSession(initialSession);
253
+ setUser(initialSession?.user ?? null);
254
+ setAuthLoading(false);
255
+ });
256
+
257
+ return () => {
258
+ subscription.unsubscribe();
259
+ };
260
+ }, [supabaseClient]);
261
+
262
+ // ==========================================================================
263
+ // ORGANISATION EFFECTS (replaces OrganisationService initialization)
264
+ // ==========================================================================
265
+
266
+ useEffect(() => {
267
+ if (!user) {
268
+ setOrganisations([]);
269
+ setSelectedOrganisation(null);
270
+ return;
271
+ }
272
+
273
+ // Load user's organisations
274
+ const loadOrganisations = async () => {
275
+ setOrganisationLoading(true);
276
+ setOrganisationError(null);
277
+
278
+ try {
279
+ // Use RPC to get organisations with roles
280
+ const { data, error } = await supabaseClient
281
+ .rpc('rbac_user_organisation_roles_get', {
282
+ p_user_id: user.id
283
+ }) as { data: Array<{ organisation_id: UUID }> | null; error: any };
284
+
285
+ if (error) throw error;
286
+
287
+ if (data && data.length > 0) {
288
+ // Fetch full organisation details
289
+ const orgIds = data.map(r => r.organisation_id);
290
+ const { data: orgs, error: orgsError } = await supabaseClient
291
+ .from('organisations')
292
+ .select('*')
293
+ .in('id', orgIds)
294
+ .eq('is_active', true) as { data: Organisation[] | null; error: any };
295
+
296
+ if (orgsError) throw orgsError;
297
+
298
+ setOrganisations(orgs || []);
299
+
300
+ // Auto-select first organisation if none selected
301
+ if (!selectedOrganisation && orgs && orgs.length > 0) {
302
+ const savedOrgId = persistState ? localStorage.getItem(`pace_${appName}_org`) : null;
303
+ const orgToSelect = savedOrgId
304
+ ? orgs.find(o => o.id === savedOrgId) || orgs[0]
305
+ : orgs[0];
306
+ setSelectedOrganisation(orgToSelect);
307
+ }
308
+ }
309
+ } catch (error) {
310
+ console.error('[AuthProvider] Failed to load organisations:', error);
311
+ setOrganisationError(error as Error);
312
+ } finally {
313
+ setOrganisationLoading(false);
314
+ }
315
+ };
316
+
317
+ loadOrganisations();
318
+ }, [user, supabaseClient, appName, persistState, selectedOrganisation]);
319
+
320
+ // ==========================================================================
321
+ // EVENT EFFECTS (replaces EventService initialization)
322
+ // ==========================================================================
323
+
324
+ useEffect(() => {
325
+ if (!user || !selectedOrganisation) {
326
+ setEvents([]);
327
+ setSelectedEvent(null);
328
+ return;
329
+ }
330
+
331
+ // Load user's events for the selected organisation
332
+ const loadEvents = async () => {
333
+ setEventLoading(true);
334
+ setEventError(null);
335
+
336
+ try {
337
+ // Use RPC to get user's events
338
+ const { data, error } = await supabaseClient
339
+ .rpc('data_user_events_get', {
340
+ p_user_id: user.id,
341
+ p_app_name: appName
342
+ }) as { data: Event[] | null; error: any };
343
+
344
+ if (error) throw error;
345
+
346
+ // Filter events for current organisation
347
+ const orgEvents = (data || []).filter(e => e.organisation_id === selectedOrganisation.id);
348
+ setEvents(orgEvents);
349
+
350
+ // Auto-select first event if none selected
351
+ if (!selectedEvent && orgEvents.length > 0) {
352
+ const savedEventId = persistState ? localStorage.getItem(`pace_${appName}_event`) : null;
353
+ const eventToSelect = savedEventId
354
+ ? orgEvents.find(e => e.event_id === savedEventId) || orgEvents[0]
355
+ : orgEvents[0];
356
+ setSelectedEvent(eventToSelect);
357
+ }
358
+ } catch (error) {
359
+ console.error('[AuthProvider] Failed to load events:', error);
360
+ setEventError(error as Error);
361
+ } finally {
362
+ setEventLoading(false);
363
+ }
364
+ };
365
+
366
+ loadEvents();
367
+ }, [user, selectedOrganisation, supabaseClient, appName, persistState, selectedEvent]);
368
+
369
+ // ==========================================================================
370
+ // INACTIVITY EFFECTS (replaces InactivityService)
371
+ // ==========================================================================
372
+
373
+ const resetActivity = useCallback(() => {
374
+ lastActivityRef.current = Date.now();
375
+ setInactivityTimeRemaining(idleTimeoutMs);
376
+ setShowInactivityWarning(false);
377
+ setIsIdle(false);
378
+ }, [idleTimeoutMs]);
379
+
380
+ const startTracking = useCallback(() => {
381
+ if (isTracking) return;
382
+
383
+ setIsTracking(true);
384
+ resetActivity();
385
+
386
+ // Activity event listeners
387
+ const activityEvents = ['mousedown', 'keydown', 'scroll', 'touchstart'];
388
+ const handleActivity = () => resetActivity();
389
+
390
+ activityEvents.forEach(event => {
391
+ window.addEventListener(event, handleActivity, { passive: true });
392
+ });
393
+
394
+ // Idle check interval
395
+ idleTimerRef.current = setInterval(() => {
396
+ const timeSinceActivity = Date.now() - lastActivityRef.current;
397
+ const remaining = idleTimeoutMs - timeSinceActivity;
398
+
399
+ setInactivityTimeRemaining(Math.max(0, remaining));
400
+
401
+ // Show warning
402
+ if (remaining <= warnBeforeMs && remaining > 0 && !showInactivityWarning) {
403
+ setShowInactivityWarning(true);
404
+ }
405
+
406
+ // Auto logout
407
+ if (remaining <= 0) {
408
+ setIsIdle(true);
409
+ onIdleLogout('inactivity');
410
+ }
411
+ }, 1000);
412
+
413
+ // Cleanup
414
+ return () => {
415
+ activityEvents.forEach(event => {
416
+ window.removeEventListener(event, handleActivity);
417
+ });
418
+ if (idleTimerRef.current) {
419
+ clearInterval(idleTimerRef.current);
420
+ }
421
+ if (warningTimerRef.current) {
422
+ clearTimeout(warningTimerRef.current);
423
+ }
424
+ };
425
+ }, [isTracking, idleTimeoutMs, warnBeforeMs, showInactivityWarning, onIdleLogout, resetActivity]);
426
+
427
+ const stopTracking = useCallback(() => {
428
+ setIsTracking(false);
429
+ if (idleTimerRef.current) {
430
+ clearInterval(idleTimerRef.current);
431
+ idleTimerRef.current = null;
432
+ }
433
+ if (warningTimerRef.current) {
434
+ clearTimeout(warningTimerRef.current);
435
+ warningTimerRef.current = null;
436
+ }
437
+ }, []);
438
+
439
+ // Generate and track device fingerprint
440
+ useEffect(() => {
441
+ if (user) {
442
+ // Generate device fingerprint (simple version - in production, use more sophisticated approach)
443
+ if (!deviceFingerprintRef.current) {
444
+ const fingerprint = btoa(JSON.stringify({
445
+ userAgent: navigator.userAgent,
446
+ language: navigator.language,
447
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
448
+ screen: `${window.screen.width}x${window.screen.height}`,
449
+ timestamp: Date.now()
450
+ })).substring(0, 64);
451
+ deviceFingerprintRef.current = fingerprint;
452
+ }
453
+
454
+ // Track session with device fingerprint
455
+ const trackSession = async () => {
456
+ if (!deviceFingerprintRef.current || !user) return;
457
+
458
+ try {
459
+ const { data, error } = await supabaseClient.rpc('rbac_session_track', {
460
+ p_user_id: user.id,
461
+ p_session_type: 'login',
462
+ p_app_id: null, // Will be resolved by the RPC function
463
+ p_ip_address: null, // Will be handled by backend
464
+ p_user_agent: navigator.userAgent,
465
+ p_device_fingerprint: deviceFingerprintRef.current
466
+ });
467
+
468
+ if (!error && data) {
469
+ sessionIdRef.current = data;
470
+
471
+ // Check for concurrent sessions
472
+ const { data: concurrentData } = await supabaseClient.rpc('rbac_detect_concurrent_sessions', {
473
+ p_user_id: user.id,
474
+ p_device_fingerprint: deviceFingerprintRef.current,
475
+ p_timeout_seconds: 300
476
+ });
477
+
478
+ if (concurrentData && concurrentData.has_concurrent) {
479
+ console.warn(
480
+ '[AuthProvider] Concurrent session detected:',
481
+ concurrentData.concurrent_sessions_count,
482
+ 'other session(s) active'
483
+ );
484
+ }
485
+ }
486
+ } catch (err) {
487
+ console.error('[AuthProvider] Session tracking failed:', err);
488
+ }
489
+ };
490
+
491
+ trackSession();
492
+ }
493
+ }, [user, supabaseClient]);
494
+
495
+ // Auto-start tracking when user is authenticated
496
+ useEffect(() => {
497
+ if (user && !authLoading) {
498
+ startTracking();
499
+ } else {
500
+ stopTracking();
501
+ }
502
+
503
+ return () => {
504
+ stopTracking();
505
+ };
506
+ }, [user, authLoading, startTracking, stopTracking]);
507
+
508
+ // ==========================================================================
509
+ // AUTH METHODS
510
+ // ==========================================================================
511
+
512
+ const signIn = useCallback(async (email: string, password?: string) => {
513
+ setAuthLoading(true);
514
+ setAuthError(null);
515
+
516
+ try {
517
+ let result;
518
+ if (password) {
519
+ // Email + password sign in
520
+ result = await supabaseClient.auth.signInWithPassword({ email, password });
521
+ } else {
522
+ // Magic link sign in
523
+ result = await supabaseClient.auth.signInWithOtp({ email });
524
+ }
525
+
526
+ if (result.error) {
527
+ setAuthError(result.error);
528
+ return { error: result.error };
529
+ }
530
+
531
+ return { error: null };
532
+ } catch (error) {
533
+ const authError = error as AuthError;
534
+ setAuthError(authError);
535
+ return { error: authError };
536
+ } finally {
537
+ setAuthLoading(false);
538
+ }
539
+ }, [supabaseClient]);
540
+
541
+ const signUp = useCallback(async (email: string, password: string) => {
542
+ setAuthLoading(true);
543
+ setAuthError(null);
544
+
545
+ try {
546
+ const { error } = await supabaseClient.auth.signUp({ email, password });
547
+
548
+ if (error) {
549
+ setAuthError(error);
550
+ return { error };
551
+ }
552
+
553
+ return { error: null };
554
+ } catch (error) {
555
+ const authError = error as AuthError;
556
+ setAuthError(authError);
557
+ return { error: authError };
558
+ } finally {
559
+ setAuthLoading(false);
560
+ }
561
+ }, [supabaseClient]);
562
+
563
+ const signOut = useCallback(async () => {
564
+ setAuthLoading(true);
565
+ setAuthError(null);
566
+
567
+ try {
568
+ const { error } = await supabaseClient.auth.signOut();
569
+
570
+ if (error) {
571
+ setAuthError(error);
572
+ return { error };
573
+ }
574
+
575
+ // Clear local state
576
+ setUser(null);
577
+ setSession(null);
578
+ setSelectedOrganisation(null);
579
+ setOrganisations([]);
580
+ setSelectedEvent(null);
581
+ setEvents([]);
582
+
583
+ // Clear persisted state
584
+ if (persistState) {
585
+ localStorage.removeItem(`pace_${appName}_org`);
586
+ localStorage.removeItem(`pace_${appName}_event`);
587
+ }
588
+
589
+ return { error: null };
590
+ } catch (error) {
591
+ const authError = error as AuthError;
592
+ setAuthError(authError);
593
+ return { error: authError };
594
+ } finally {
595
+ setAuthLoading(false);
596
+ }
597
+ }, [supabaseClient, appName, persistState]);
598
+
599
+ const resetPassword = useCallback(async (email: string) => {
600
+ setAuthError(null);
601
+
602
+ try {
603
+ const { error } = await supabaseClient.auth.resetPasswordForEmail(email);
604
+
605
+ if (error) {
606
+ setAuthError(error);
607
+ return { error };
608
+ }
609
+
610
+ return { error: null };
611
+ } catch (error) {
612
+ const authError = error as AuthError;
613
+ setAuthError(authError);
614
+ return { error: authError };
615
+ }
616
+ }, [supabaseClient]);
617
+
618
+ const updatePassword = useCallback(async (password: string) => {
619
+ setAuthError(null);
620
+
621
+ try {
622
+ const { error } = await supabaseClient.auth.updateUser({ password });
623
+
624
+ if (error) {
625
+ setAuthError(error);
626
+ return { error };
627
+ }
628
+
629
+ return { error: null };
630
+ } catch (error) {
631
+ const authError = error as AuthError;
632
+ setAuthError(authError);
633
+ return { error: authError };
634
+ }
635
+ }, [supabaseClient]);
636
+
637
+ const refreshSession = useCallback(async () => {
638
+ setAuthError(null);
639
+
640
+ try {
641
+ const { data, error } = await supabaseClient.auth.refreshSession();
642
+
643
+ if (error) {
644
+ setAuthError(error);
645
+ return { error };
646
+ }
647
+
648
+ if (data.session) {
649
+ setSession(data.session);
650
+ setUser(data.session.user);
651
+ }
652
+
653
+ return { error: null };
654
+ } catch (error) {
655
+ const authError = error as AuthError;
656
+ setAuthError(authError);
657
+ return { error: authError };
658
+ }
659
+ }, [supabaseClient]);
660
+
661
+ // ==========================================================================
662
+ // ORGANISATION METHODS
663
+ // ==========================================================================
664
+
665
+ const switchOrganisation = useCallback(async (orgId: UUID) => {
666
+ const org = organisations.find(o => o.id === orgId);
667
+ if (!org) {
668
+ throw new Error(`Organisation ${orgId} not found`);
669
+ }
670
+
671
+ setSelectedOrganisation(org);
672
+
673
+ if (persistState) {
674
+ localStorage.setItem(`pace_${appName}_org`, orgId);
675
+ }
676
+
677
+ // Clear event selection when switching orgs
678
+ setSelectedEvent(null);
679
+ if (persistState) {
680
+ localStorage.removeItem(`pace_${appName}_event`);
681
+ }
682
+ }, [organisations, appName, persistState]);
683
+
684
+ const refreshOrganisations = useCallback(async () => {
685
+ if (!user) return;
686
+
687
+ setOrganisationLoading(true);
688
+ setOrganisationError(null);
689
+
690
+ try {
691
+ const { data, error } = await supabaseClient
692
+ .rpc('rbac_user_organisation_roles_get', {
693
+ p_user_id: user.id
694
+ }) as { data: Array<{ organisation_id: UUID }> | null; error: any };
695
+
696
+ if (error) throw error;
697
+
698
+ if (data && data.length > 0) {
699
+ const orgIds = data.map(r => r.organisation_id);
700
+ const { data: orgs, error: orgsError } = await supabaseClient
701
+ .from('organisations')
702
+ .select('*')
703
+ .in('id', orgIds)
704
+ .eq('is_active', true) as { data: Organisation[] | null; error: any };
705
+
706
+ if (orgsError) throw orgsError;
707
+
708
+ setOrganisations(orgs || []);
709
+ }
710
+ } catch (error) {
711
+ console.error('[AuthProvider] Failed to refresh organisations:', error);
712
+ setOrganisationError(error as Error);
713
+ } finally {
714
+ setOrganisationLoading(false);
715
+ }
716
+ }, [user, supabaseClient]);
717
+
718
+ const getUserRole = useCallback((orgId?: UUID): string | null => {
719
+ // This would require fetching role data - placeholder for now
720
+ // In real implementation, we'd cache role data when loading organisations
721
+ return 'member';
722
+ }, []);
723
+
724
+ const getPrimaryOrganisation = useCallback((): Organisation | null => {
725
+ return organisations[0] || null;
726
+ }, [organisations]);
727
+
728
+ // ==========================================================================
729
+ // EVENT METHODS
730
+ // ==========================================================================
731
+
732
+ const handleSetSelectedEvent = useCallback((event: Event | null) => {
733
+ setSelectedEvent(event);
734
+
735
+ if (persistState) {
736
+ if (event) {
737
+ localStorage.setItem(`pace_${appName}_event`, event.event_id);
738
+ } else {
739
+ localStorage.removeItem(`pace_${appName}_event`);
740
+ }
741
+ }
742
+ }, [appName, persistState]);
743
+
744
+ const refreshEvents = useCallback(async () => {
745
+ if (!user || !selectedOrganisation) return;
746
+
747
+ setEventLoading(true);
748
+ setEventError(null);
749
+
750
+ try {
751
+ const { data, error } = await supabaseClient
752
+ .rpc('data_user_events_get', {
753
+ p_user_id: user.id,
754
+ p_app_name: appName
755
+ }) as { data: Event[] | null; error: any };
756
+
757
+ if (error) throw error;
758
+
759
+ const orgEvents = (data || []).filter(e => e.organisation_id === selectedOrganisation.id);
760
+ setEvents(orgEvents);
761
+
762
+ // Auto-select first event if none selected
763
+ if (!selectedEvent && orgEvents.length > 0) {
764
+ const savedEventId = persistState ? localStorage.getItem(`pace_${appName}_event`) : null;
765
+ const eventToSelect = savedEventId
766
+ ? orgEvents.find(e => e.event_id === savedEventId) || orgEvents[0]
767
+ : orgEvents[0];
768
+ setSelectedEvent(eventToSelect);
769
+ }
770
+ } catch (error) {
771
+ console.error('[AuthProvider] Failed to refresh events:', error);
772
+ setEventError(error as Error);
773
+ } finally {
774
+ setEventLoading(false);
775
+ }
776
+ }, [user, selectedOrganisation, supabaseClient, appName, selectedEvent, persistState]);
777
+
778
+ // ==========================================================================
779
+ // COMPUTED VALUES
780
+ // ==========================================================================
781
+
782
+ const isAuthenticated = !!(user && session);
783
+ const hasValidOrganisationContext = !!(selectedOrganisation && !organisationLoading);
784
+ const isContextReady = isAuthenticated && (!requireOrganisationContext || hasValidOrganisationContext);
785
+
786
+ // ==========================================================================
787
+ // CONTEXT VALUE
788
+ // ==========================================================================
789
+
790
+ const contextValue = useMemo<AuthContextType>(() => ({
791
+ // Auth
792
+ user,
793
+ session,
794
+ isAuthenticated,
795
+ authLoading,
796
+ authError,
797
+ supabase: supabaseClient,
798
+ signIn,
799
+ signUp,
800
+ signOut,
801
+ resetPassword,
802
+ updatePassword,
803
+ refreshSession,
804
+
805
+ // Organisation
806
+ selectedOrganisation,
807
+ organisations,
808
+ organisationLoading,
809
+ organisationError,
810
+ hasValidOrganisationContext,
811
+ isContextReady: hasValidOrganisationContext,
812
+ switchOrganisation,
813
+ refreshOrganisations,
814
+ getUserRole,
815
+ getPrimaryOrganisation,
816
+
817
+ // Event
818
+ events,
819
+ selectedEvent,
820
+ eventLoading,
821
+ eventError,
822
+ setSelectedEvent: handleSetSelectedEvent,
823
+ refreshEvents,
824
+
825
+ // Inactivity
826
+ showInactivityWarning,
827
+ inactivityTimeRemaining,
828
+ isIdle,
829
+ isTracking,
830
+ resetActivity,
831
+ startTracking,
832
+ stopTracking,
833
+
834
+ // Session Management
835
+ sessionId: sessionIdRef.current,
836
+ deviceFingerprint: deviceFingerprintRef.current,
837
+ hasConcurrentSessions: false, // TODO: implement state tracking
838
+ }), [
839
+ user, session, isAuthenticated, authLoading, authError,
840
+ selectedOrganisation, organisations, organisationLoading, organisationError,
841
+ hasValidOrganisationContext, isContextReady,
842
+ events, selectedEvent, eventLoading, eventError,
843
+ showInactivityWarning, inactivityTimeRemaining, isIdle, isTracking,
844
+ signIn, signUp, signOut, resetPassword, updatePassword, refreshSession,
845
+ switchOrganisation, refreshOrganisations, getUserRole, getPrimaryOrganisation,
846
+ handleSetSelectedEvent, refreshEvents,
847
+ resetActivity, startTracking, stopTracking,
848
+ supabaseClient
849
+ ]);
850
+
851
+ // ==========================================================================
852
+ // RENDER
853
+ // ==========================================================================
854
+
855
+ return (
856
+ <AuthContext.Provider value={contextValue}>
857
+ {children}
858
+ {showInactivityWarning && renderInactivityWarning && renderInactivityWarning({
859
+ timeRemaining: inactivityTimeRemaining,
860
+ onStaySignedIn: resetActivity,
861
+ onSignOutNow: signOut,
862
+ })}
863
+ </AuthContext.Provider>
864
+ );
865
+ }
866
+
867
+ // ============================================================================
868
+ // HOOK
869
+ // ============================================================================
870
+
871
+ export function useAuth(): AuthContextType {
872
+ const context = useContext(AuthContext);
873
+
874
+ if (!context) {
875
+ throw new Error('useAuth must be used within AuthProvider');
876
+ }
877
+
878
+ return context;
879
+ }
880
+