@jmruthers/pace-core 0.5.184 → 0.5.185

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 (306) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +60 -1
  3. package/core-usage-manifest.json +312 -0
  4. package/dist/{DataTable-QAB34V6K.js → DataTable-IX2NBUTP.js} +6 -6
  5. package/dist/{DataTable-Bz8ffqyA.d.ts → DataTable-Z9NLVJh0.d.ts} +1 -1
  6. package/dist/{index-Bl--n7-T.d.ts → PublicPageProvider-BABf6JCh.d.ts} +21 -10
  7. package/dist/{UnifiedAuthProvider-7F6T4B6K.js → UnifiedAuthProvider-A4BCQRJY.js} +4 -2
  8. package/dist/{UnifiedAuthProvider-F86d7dSi.d.ts → UnifiedAuthProvider-BG0AL5eE.d.ts} +2 -1
  9. package/dist/{api-ROMBCNKU.js → api-BMFCXVQX.js} +2 -2
  10. package/dist/{chunk-RA3JUFMW.js → chunk-445GEP27.js} +154 -4
  11. package/dist/{chunk-RA3JUFMW.js.map → chunk-445GEP27.js.map} +1 -1
  12. package/dist/{chunk-CSOFYHAG.js → chunk-AISXLWGZ.js} +374 -60
  13. package/dist/chunk-AISXLWGZ.js.map +1 -0
  14. package/dist/{chunk-FUEYYMX5.js → chunk-FXFJRTKI.js} +24 -3
  15. package/dist/chunk-FXFJRTKI.js.map +1 -0
  16. package/dist/{chunk-NQPMQGS2.js → chunk-HC67NW5K.js} +379 -359
  17. package/dist/chunk-HC67NW5K.js.map +1 -0
  18. package/dist/chunk-HESYZWZW.js +388 -0
  19. package/dist/chunk-HESYZWZW.js.map +1 -0
  20. package/dist/{chunk-QUVSNGIP.js → chunk-HGPQUCBC.js} +34 -9
  21. package/dist/{chunk-QUVSNGIP.js.map → chunk-HGPQUCBC.js.map} +1 -1
  22. package/dist/{chunk-UHNYIBXL.js → chunk-IXSNYUCT.js} +1 -1
  23. package/dist/chunk-IXSNYUCT.js.map +1 -0
  24. package/dist/{chunk-MI7HBHN3.js → chunk-MX3EIJGQ.js} +4 -3
  25. package/dist/{chunk-MI7HBHN3.js.map → chunk-MX3EIJGQ.js.map} +1 -1
  26. package/dist/{chunk-PWAHJW4G.js → chunk-OKI34GZD.js} +86 -33
  27. package/dist/chunk-OKI34GZD.js.map +1 -0
  28. package/dist/{chunk-W22JP75J.js → chunk-STTZQK2I.js} +3 -3
  29. package/dist/chunk-THRPYOFK.js +215 -0
  30. package/dist/chunk-THRPYOFK.js.map +1 -0
  31. package/dist/{chunk-M7W4CP3M.js → chunk-U6WNSFX5.js} +2 -1
  32. package/dist/chunk-U6WNSFX5.js.map +1 -0
  33. package/dist/{chunk-QCDXODCA.js → chunk-XAUHJD3L.js} +2 -2
  34. package/dist/components.d.ts +182 -6
  35. package/dist/components.js +157 -11
  36. package/dist/components.js.map +1 -1
  37. package/dist/eslint-rules/pace-core-compliance.cjs +406 -0
  38. package/dist/{file-reference-D06mEEWW.d.ts → file-reference-BjR39ktt.d.ts} +7 -1
  39. package/dist/hooks.d.ts +7 -14
  40. package/dist/hooks.js +10 -22
  41. package/dist/hooks.js.map +1 -1
  42. package/dist/index.d.ts +11 -11
  43. package/dist/index.js +79 -16
  44. package/dist/index.js.map +1 -1
  45. package/dist/providers.d.ts +1 -1
  46. package/dist/providers.js +3 -1
  47. package/dist/rbac/index.d.ts +205 -14
  48. package/dist/rbac/index.js +28 -6
  49. package/dist/timezone-_pgH8qrY.d.ts +530 -0
  50. package/dist/{types-_x1f4QBF.d.ts → types-DUyCRSTj.d.ts} +1 -1
  51. package/dist/types.d.ts +1 -1
  52. package/dist/types.js +1 -1
  53. package/dist/{usePublicRouteParams-JJczomYq.d.ts → usePublicRouteParams-CvnC3d-e.d.ts} +113 -2
  54. package/dist/utils.d.ts +109 -151
  55. package/dist/utils.js +128 -138
  56. package/dist/utils.js.map +1 -1
  57. package/docs/api/README.md +60 -1
  58. package/docs/api/classes/ColumnFactory.md +1 -1
  59. package/docs/api/classes/ErrorBoundary.md +1 -1
  60. package/docs/api/classes/InvalidScopeError.md +1 -1
  61. package/docs/api/classes/Logger.md +178 -0
  62. package/docs/api/classes/MissingUserContextError.md +1 -1
  63. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  64. package/docs/api/classes/PermissionDeniedError.md +1 -1
  65. package/docs/api/classes/RBACAuditManager.md +2 -2
  66. package/docs/api/classes/RBACCache.md +1 -1
  67. package/docs/api/classes/RBACEngine.md +2 -2
  68. package/docs/api/classes/RBACError.md +1 -1
  69. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  70. package/docs/api/classes/SecureSupabaseClient.md +5 -5
  71. package/docs/api/classes/StorageUtils.md +1 -1
  72. package/docs/api/enums/FileCategory.md +1 -1
  73. package/docs/api/enums/LogLevel.md +54 -0
  74. package/docs/api/enums/RBACErrorCode.md +1 -1
  75. package/docs/api/enums/RPCFunction.md +1 -1
  76. package/docs/api/interfaces/AggregateConfig.md +1 -1
  77. package/docs/api/interfaces/BadgeProps.md +1 -1
  78. package/docs/api/interfaces/ButtonProps.md +1 -1
  79. package/docs/api/interfaces/CalendarProps.md +18 -2
  80. package/docs/api/interfaces/CardProps.md +1 -1
  81. package/docs/api/interfaces/ColorPalette.md +1 -1
  82. package/docs/api/interfaces/ColorShade.md +1 -1
  83. package/docs/api/interfaces/ComplianceResult.md +30 -0
  84. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  85. package/docs/api/interfaces/DataRecord.md +1 -1
  86. package/docs/api/interfaces/DataTableAction.md +1 -1
  87. package/docs/api/interfaces/DataTableColumn.md +1 -1
  88. package/docs/api/interfaces/DataTableProps.md +1 -1
  89. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  90. package/docs/api/interfaces/DatabaseComplianceResult.md +85 -0
  91. package/docs/api/interfaces/DatabaseIssue.md +41 -0
  92. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  93. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  94. package/docs/api/interfaces/EventAppRoleData.md +6 -6
  95. package/docs/api/interfaces/ExportColumn.md +1 -1
  96. package/docs/api/interfaces/ExportOptions.md +1 -1
  97. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  98. package/docs/api/interfaces/FileMetadata.md +1 -1
  99. package/docs/api/interfaces/FileReference.md +1 -1
  100. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  101. package/docs/api/interfaces/FileUploadOptions.md +24 -8
  102. package/docs/api/interfaces/FileUploadProps.md +24 -13
  103. package/docs/api/interfaces/FooterProps.md +1 -1
  104. package/docs/api/interfaces/FormFieldProps.md +1 -1
  105. package/docs/api/interfaces/FormProps.md +1 -1
  106. package/docs/api/interfaces/GrantEventAppRoleParams.md +9 -9
  107. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  108. package/docs/api/interfaces/InputProps.md +1 -1
  109. package/docs/api/interfaces/LabelProps.md +1 -1
  110. package/docs/api/interfaces/LoggerConfig.md +62 -0
  111. package/docs/api/interfaces/LoginFormProps.md +1 -1
  112. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  113. package/docs/api/interfaces/NavigationContextType.md +1 -1
  114. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  115. package/docs/api/interfaces/NavigationItem.md +1 -1
  116. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  117. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  118. package/docs/api/interfaces/Organisation.md +1 -1
  119. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  120. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  121. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  122. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  123. package/docs/api/interfaces/PaceAppLayoutProps.md +36 -23
  124. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  125. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  126. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  127. package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
  128. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  129. package/docs/api/interfaces/PaletteData.md +1 -1
  130. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  131. package/docs/api/interfaces/ProgressProps.md +1 -1
  132. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  133. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  134. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  135. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  136. package/docs/api/interfaces/QuickFix.md +52 -0
  137. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  138. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  139. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  140. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  141. package/docs/api/interfaces/RBACConfig.md +4 -4
  142. package/docs/api/interfaces/RBACContext.md +1 -1
  143. package/docs/api/interfaces/RBACLogger.md +1 -1
  144. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  145. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  146. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  147. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  148. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  149. package/docs/api/interfaces/RBACResult.md +1 -1
  150. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  151. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  152. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  153. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  154. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  155. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  156. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  157. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  158. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  159. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  160. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  161. package/docs/api/interfaces/RevokeEventAppRoleParams.md +7 -7
  162. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  163. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  164. package/docs/api/interfaces/RoleManagementResult.md +5 -5
  165. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  166. package/docs/api/interfaces/RouteConfig.md +1 -1
  167. package/docs/api/interfaces/RuntimeComplianceResult.md +55 -0
  168. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  169. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  170. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  171. package/docs/api/interfaces/SetupIssue.md +41 -0
  172. package/docs/api/interfaces/StorageConfig.md +1 -1
  173. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  174. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  175. package/docs/api/interfaces/StorageListOptions.md +1 -1
  176. package/docs/api/interfaces/StorageListResult.md +1 -1
  177. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  178. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  179. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  180. package/docs/api/interfaces/StyleImport.md +1 -1
  181. package/docs/api/interfaces/SwitchProps.md +1 -1
  182. package/docs/api/interfaces/TabsContentProps.md +1 -1
  183. package/docs/api/interfaces/TabsListProps.md +1 -1
  184. package/docs/api/interfaces/TabsProps.md +1 -1
  185. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  186. package/docs/api/interfaces/TextareaProps.md +1 -1
  187. package/docs/api/interfaces/ToastActionElement.md +1 -1
  188. package/docs/api/interfaces/ToastProps.md +1 -1
  189. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  190. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  191. package/docs/api/interfaces/UseFormDialogOptions.md +62 -0
  192. package/docs/api/interfaces/UseFormDialogReturn.md +117 -0
  193. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  194. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  195. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  196. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  197. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  198. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  199. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  200. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  201. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  202. package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
  203. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  204. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  205. package/docs/api/interfaces/UserEventAccess.md +1 -1
  206. package/docs/api/interfaces/UserMenuProps.md +1 -1
  207. package/docs/api/interfaces/UserProfile.md +1 -1
  208. package/docs/api/modules.md +738 -42
  209. package/docs/api-reference/hooks.md +111 -0
  210. package/docs/api-reference/rpc-functions.md +1 -1
  211. package/docs/api-reference/utilities.md +184 -0
  212. package/docs/getting-started/installation-guide.md +75 -16
  213. package/docs/getting-started/quick-start.md +61 -11
  214. package/docs/implementation-guides/authentication.md +88 -12
  215. package/docs/implementation-guides/file-reference-system.md +2 -1
  216. package/docs/implementation-guides/file-upload-storage.md +21 -0
  217. package/docs/rbac/README.md +1 -0
  218. package/docs/rbac/compliance/compliance-guide.md +544 -0
  219. package/docs/rbac/getting-started.md +158 -33
  220. package/docs/standards/pace-core-compliance.md +432 -0
  221. package/eslint-config-pace-core.cjs +93 -0
  222. package/package.json +15 -3
  223. package/scripts/analyze-bundle.js +232 -0
  224. package/scripts/build-css.js +56 -0
  225. package/scripts/build-docs-incremental.js +1015 -0
  226. package/scripts/check-pace-core-compliance.cjs +2353 -0
  227. package/scripts/generate-docs.js +157 -0
  228. package/scripts/setup-build-cache.js +73 -0
  229. package/scripts/utils/command-runner.js +131 -0
  230. package/scripts/utils/env.js +33 -0
  231. package/scripts/utils/index.js +10 -0
  232. package/scripts/utils/logger.js +88 -0
  233. package/scripts/utils/path-helpers.js +37 -0
  234. package/scripts/validate-formats.js +133 -0
  235. package/scripts/validate-master.js +155 -0
  236. package/scripts/validate-pre-publish.js +140 -0
  237. package/scripts/validate-theme.js +142 -0
  238. package/src/components/Calendar/Calendar.tsx +8 -1
  239. package/src/components/Card/Card.tsx +47 -8
  240. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +314 -0
  241. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +126 -0
  242. package/src/components/DatePickerWithTimezone/README.md +135 -0
  243. package/src/components/DatePickerWithTimezone/index.ts +10 -0
  244. package/src/components/DateTimeField/DateTimeField.test.tsx +358 -0
  245. package/src/components/DateTimeField/DateTimeField.tsx +232 -0
  246. package/src/components/DateTimeField/README.md +148 -0
  247. package/src/components/DateTimeField/index.ts +10 -0
  248. package/src/components/FileUpload/FileUpload.tsx +3 -0
  249. package/src/components/Header/Header.test.tsx +47 -18
  250. package/src/components/Header/Header.tsx +24 -6
  251. package/src/components/PaceAppLayout/PaceAppLayout.tsx +29 -20
  252. package/src/components/PaceAppLayout/README.md +9 -0
  253. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +37 -8
  254. package/src/components/ProtectedRoute/ProtectedRoute.tsx +12 -4
  255. package/src/components/index.ts +8 -0
  256. package/src/eslint-rules/pace-core-compliance.cjs +406 -0
  257. package/src/eslint-rules/pace-core-compliance.js +640 -0
  258. package/src/hooks/__tests__/useFormDialog.test.ts +478 -0
  259. package/src/hooks/index.ts +2 -0
  260. package/src/hooks/useFileReference.test.ts +1 -0
  261. package/src/hooks/useFormDialog.ts +147 -0
  262. package/src/index.ts +27 -0
  263. package/src/providers/services/OrganisationServiceProvider.tsx +6 -5
  264. package/src/providers/services/UnifiedAuthProvider.tsx +24 -3
  265. package/src/rbac/__tests__/scenarios.user-role.test.tsx +3 -0
  266. package/src/rbac/compliance/database-validator.ts +165 -0
  267. package/src/rbac/compliance/index.ts +38 -0
  268. package/src/rbac/compliance/quick-fix-suggestions.ts +209 -0
  269. package/src/rbac/compliance/runtime-compliance.ts +77 -0
  270. package/src/rbac/compliance/setup-validator.ts +131 -0
  271. package/src/rbac/components/PagePermissionGuard.tsx +8 -64
  272. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +35 -21
  273. package/src/rbac/docs/event-based-apps.md +285 -0
  274. package/src/rbac/errors.ts +11 -0
  275. package/src/rbac/hooks/useRoleManagement.ts +292 -12
  276. package/src/rbac/index.ts +30 -0
  277. package/src/services/OrganisationService.ts +4 -0
  278. package/src/types/file-reference.ts +6 -0
  279. package/src/utils/__tests__/timezone.test.ts +345 -0
  280. package/src/utils/file-reference/__tests__/file-reference.test.ts +2 -0
  281. package/src/utils/file-reference/index.ts +1 -0
  282. package/src/utils/formatting/formatDateTimeTimezone.test.ts +167 -0
  283. package/src/utils/formatting/formatting.ts +179 -0
  284. package/src/utils/index.ts +27 -1
  285. package/src/utils/location/index.ts +16 -0
  286. package/src/utils/location/location.test.ts +286 -0
  287. package/src/utils/location/location.ts +175 -0
  288. package/src/utils/timezone/index.ts +17 -0
  289. package/src/utils/timezone/timezone.test.ts +349 -0
  290. package/src/utils/timezone/timezone.ts +281 -0
  291. package/dist/chunk-CSOFYHAG.js.map +0 -1
  292. package/dist/chunk-FUEYYMX5.js.map +0 -1
  293. package/dist/chunk-HKIT6O7W.js +0 -198
  294. package/dist/chunk-HKIT6O7W.js.map +0 -1
  295. package/dist/chunk-KUEN3HFB.js +0 -94
  296. package/dist/chunk-KUEN3HFB.js.map +0 -1
  297. package/dist/chunk-M7W4CP3M.js.map +0 -1
  298. package/dist/chunk-NQPMQGS2.js.map +0 -1
  299. package/dist/chunk-PWAHJW4G.js.map +0 -1
  300. package/dist/chunk-UHNYIBXL.js.map +0 -1
  301. package/dist/formatting-5wETwiGF.d.ts +0 -162
  302. /package/dist/{DataTable-QAB34V6K.js.map → DataTable-IX2NBUTP.js.map} +0 -0
  303. /package/dist/{UnifiedAuthProvider-7F6T4B6K.js.map → UnifiedAuthProvider-A4BCQRJY.js.map} +0 -0
  304. /package/dist/{api-ROMBCNKU.js.map → api-BMFCXVQX.js.map} +0 -0
  305. /package/dist/{chunk-W22JP75J.js.map → chunk-STTZQK2I.js.map} +0 -0
  306. /package/dist/{chunk-QCDXODCA.js.map → chunk-XAUHJD3L.js.map} +0 -0
@@ -486,6 +486,117 @@ function UserForm() {
486
486
  }
487
487
  ```
488
488
 
489
+ ### useFormDialog
490
+
491
+ Generic React hook for managing form dialog state (open/close, form data, reset behavior).
492
+
493
+ ```typescript
494
+ interface UseFormDialogOptions<T = unknown> {
495
+ onOpenChange?: (open: boolean) => void;
496
+ resetOnClose?: boolean; // Default: true
497
+ }
498
+
499
+ interface UseFormDialogReturn<T = unknown> {
500
+ isOpen: boolean;
501
+ formData: T | null;
502
+ openDialog: (data?: T | null) => void;
503
+ closeDialog: () => void;
504
+ handleOpenChange: (open: boolean) => void;
505
+ }
506
+
507
+ function useFormDialog<T = unknown>(
508
+ options?: UseFormDialogOptions<T>
509
+ ): UseFormDialogReturn<T>;
510
+ ```
511
+
512
+ #### Usage
513
+
514
+ ```tsx
515
+ import { useFormDialog } from '@jmruthers/pace-core';
516
+ import { Dialog, DialogContent, DialogTrigger } from '@jmruthers/pace-core';
517
+
518
+ interface ContactFormData {
519
+ id: string;
520
+ name: string;
521
+ email: string;
522
+ }
523
+
524
+ function ContactDialog() {
525
+ const { isOpen, formData, openDialog, closeDialog, handleOpenChange } =
526
+ useFormDialog<ContactFormData>();
527
+
528
+ return (
529
+ <>
530
+ <button onClick={() => openDialog({ id: '1', name: 'John', email: 'john@example.com' })}>
531
+ Edit Contact
532
+ </button>
533
+ <Dialog open={isOpen} onOpenChange={handleOpenChange}>
534
+ <DialogContent>
535
+ {formData && <p>Editing: {formData.name}</p>}
536
+ {/* Form content */}
537
+ </DialogContent>
538
+ </Dialog>
539
+ </>
540
+ );
541
+ }
542
+ ```
543
+
544
+ #### Basic Usage
545
+
546
+ ```tsx
547
+ // Simple dialog without form data
548
+ const { isOpen, openDialog, closeDialog } = useFormDialog();
549
+
550
+ // Open dialog
551
+ openDialog();
552
+
553
+ // Close dialog
554
+ closeDialog();
555
+ ```
556
+
557
+ #### With Form Data
558
+
559
+ ```tsx
560
+ interface UserData {
561
+ id: string;
562
+ name: string;
563
+ }
564
+
565
+ const { isOpen, formData, openDialog } = useFormDialog<UserData>();
566
+
567
+ // Open with data
568
+ openDialog({ id: '1', name: 'John' });
569
+
570
+ // Access form data
571
+ if (formData) {
572
+ console.log(formData.name);
573
+ }
574
+ ```
575
+
576
+ #### Controlled Usage
577
+
578
+ ```tsx
579
+ const { handleOpenChange, isOpen } = useFormDialog({
580
+ onOpenChange: (open) => {
581
+ console.log('Dialog state:', open);
582
+ },
583
+ resetOnClose: false // Keep form data after closing
584
+ });
585
+ ```
586
+
587
+ #### Options
588
+
589
+ - `onOpenChange?: (open: boolean) => void` - Callback invoked when dialog state changes
590
+ - `resetOnClose?: boolean` - Whether to reset form data when dialog closes (default: `true`)
591
+
592
+ #### Return Values
593
+
594
+ - `isOpen: boolean` - Current open state of the dialog
595
+ - `formData: T | null` - Current form data (null when closed or no data provided)
596
+ - `openDialog: (data?: T | null) => void` - Open dialog with optional form data
597
+ - `closeDialog: () => void` - Close dialog
598
+ - `handleOpenChange: (open: boolean) => void` - Handler for controlled dialog components
599
+
489
600
  ## Utility Hooks
490
601
 
491
602
  ### useDebounce
@@ -120,7 +120,7 @@ Documented in [File Reference System Guide](../implementation-guides/file-refere
120
120
  - `data_file_reference_count_get` - Get count of file references for a record
121
121
 
122
122
  **Write Operations (exception to naming standard):**
123
- - `data_file_reference_create` - Create a file reference (uses `data_*` prefix for consistency within file reference system)
123
+ - `data_file_reference_create` - Create a file reference with context-aware permission checks. Requires `p_page_context` parameter to check appropriate page-level permissions (e.g., 'create:page.configuration' or 'update:page.configuration'). Uses `data_*` prefix for consistency within file reference system.
124
124
  - `data_file_reference_delete` - Delete a file reference (uses `data_*` prefix for consistency within file reference system)
125
125
 
126
126
  ### RBAC System
@@ -369,6 +369,190 @@ import { getDateRange } from '@jmruthers/pace-core';
369
369
  const dateRange = getDateRange(startDate, endDate, 'day');
370
370
  ```
371
371
 
372
+ ## Timezone Utilities
373
+
374
+ Comprehensive timezone conversion and formatting utilities using `date-fns-tz` and native `Intl` APIs. All functions handle DST transitions and return safe error values ('Invalid date' strings) rather than throwing exceptions.
375
+
376
+ ### formatInTimeZone
377
+
378
+ Format a date in a specific timezone using date-fns format strings.
379
+
380
+ ```typescript
381
+ function formatInTimeZone(
382
+ date: Date | string | number,
383
+ timeZone: string,
384
+ formatStr: string
385
+ ): string;
386
+ ```
387
+
388
+ #### Usage
389
+
390
+ ```typescript
391
+ import { formatInTimeZone } from '@jmruthers/pace-core';
392
+
393
+ // Format UTC date in specific timezone
394
+ formatInTimeZone(new Date('2024-01-15T10:00:00Z'), 'America/New_York', 'MMM dd, yyyy HH:mm');
395
+ // "Jan 15, 2024 05:00"
396
+
397
+ // With ISO string
398
+ formatInTimeZone('2024-01-15T10:00:00Z', 'Europe/London', 'yyyy-MM-dd');
399
+ // "2024-01-15"
400
+ ```
401
+
402
+ ### getTimezoneAbbreviation
403
+
404
+ Get the timezone abbreviation (EST, PST, etc.) for a date in a specific timezone.
405
+
406
+ ```typescript
407
+ function getTimezoneAbbreviation(date: Date, timeZone: string): string;
408
+ ```
409
+
410
+ #### Usage
411
+
412
+ ```typescript
413
+ import { getTimezoneAbbreviation } from '@jmruthers/pace-core';
414
+
415
+ getTimezoneAbbreviation(new Date('2024-01-15T10:00:00Z'), 'America/New_York');
416
+ // "EST" or "EDT" depending on DST
417
+ ```
418
+
419
+ ### formatTimeInTimeZone
420
+
421
+ Format time only in a specific timezone (HH:mm format).
422
+
423
+ ```typescript
424
+ function formatTimeInTimeZone(date: Date | string, timeZone: string): string;
425
+ ```
426
+
427
+ #### Usage
428
+
429
+ ```typescript
430
+ import { formatTimeInTimeZone } from '@jmruthers/pace-core';
431
+
432
+ formatTimeInTimeZone(new Date('2024-01-15T10:00:00Z'), 'America/New_York');
433
+ // "05:00"
434
+ ```
435
+
436
+ ### getUserTimeZone
437
+
438
+ Get the user's browser timezone.
439
+
440
+ ```typescript
441
+ function getUserTimeZone(): string;
442
+ ```
443
+
444
+ #### Usage
445
+
446
+ ```typescript
447
+ import { getUserTimeZone } from '@jmruthers/pace-core';
448
+
449
+ const userTz = getUserTimeZone();
450
+ // "America/New_York" (or user's actual timezone)
451
+ ```
452
+
453
+ ### toZonedTime
454
+
455
+ Convert a UTC date to a specific timezone's local time representation.
456
+
457
+ ```typescript
458
+ function toZonedTime(date: Date, timezone: string): Date;
459
+ ```
460
+
461
+ #### Usage
462
+
463
+ ```typescript
464
+ import { toZonedTime } from '@jmruthers/pace-core';
465
+
466
+ const utcDate = new Date('2024-01-15T10:00:00Z');
467
+ const localDate = toZonedTime(utcDate, 'America/New_York');
468
+ // Returns Date object representing Jan 15, 2024 05:00 in local time
469
+ ```
470
+
471
+ ### fromZonedTime
472
+
473
+ Convert a local time in a specific timezone to UTC.
474
+
475
+ ```typescript
476
+ function fromZonedTime(localDate: Date, timezone: string): Date;
477
+ ```
478
+
479
+ #### Usage
480
+
481
+ ```typescript
482
+ import { fromZonedTime } from '@jmruthers/pace-core';
483
+
484
+ const localInput = new Date(2024, 0, 15, 10, 0); // Jan 15, 2024 10:00 AM (local)
485
+ const utcDate = fromZonedTime(localInput, 'America/New_York');
486
+ // Returns Date object representing Jan 15, 2024 15:00 UTC
487
+ ```
488
+
489
+ ### roundToNearestMinutes
490
+
491
+ Round a date to the nearest X minutes.
492
+
493
+ ```typescript
494
+ function roundToNearestMinutes(date: Date, minutesStep?: number): Date;
495
+ ```
496
+
497
+ #### Usage
498
+
499
+ ```typescript
500
+ import { roundToNearestMinutes } from '@jmruthers/pace-core';
501
+
502
+ const date = new Date('2024-01-15T10:23:00Z');
503
+ roundToNearestMinutes(date, 5);
504
+ // Returns Date object for 10:25:00
505
+
506
+ roundToNearestMinutes(date, 15);
507
+ // Returns Date object for 10:30:00
508
+ ```
509
+
510
+ **Note:** `minutesStep` must be one of: 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, or 30. Other values will be rounded to the nearest valid step.
511
+
512
+ ### getTimeZoneDifference
513
+
514
+ Calculate the time difference between two timezones in hours.
515
+
516
+ ```typescript
517
+ function getTimeZoneDifference(fromTimeZone: string, toTimeZone: string): number;
518
+ ```
519
+
520
+ #### Usage
521
+
522
+ ```typescript
523
+ import { getTimeZoneDifference } from '@jmruthers/pace-core';
524
+
525
+ getTimeZoneDifference('America/New_York', 'America/Los_Angeles');
526
+ // Returns -3 (EST is 3 hours ahead of PST)
527
+
528
+ getTimeZoneDifference('UTC', 'America/New_York');
529
+ // Returns -5 (or -4 during DST)
530
+ ```
531
+
532
+ #### Complete Example
533
+
534
+ ```typescript
535
+ import {
536
+ toZonedTime,
537
+ fromZonedTime,
538
+ formatInTimeZone,
539
+ getUserTimeZone
540
+ } from '@jmruthers/pace-core';
541
+
542
+ // Convert UTC to local timezone
543
+ const utcDate = new Date('2024-01-15T10:00:00Z');
544
+ const userTz = getUserTimeZone();
545
+ const localDate = toZonedTime(utcDate, userTz);
546
+
547
+ // Format with timezone
548
+ const formatted = formatInTimeZone(utcDate, userTz, 'MMM dd, yyyy HH:mm (zzz)');
549
+ // "Jan 15, 2024 05:00 (EST)"
550
+
551
+ // Convert local time to UTC
552
+ const localInput = new Date(2024, 0, 15, 10, 0);
553
+ const utcResult = fromZonedTime(localInput, userTz);
554
+ ```
555
+
372
556
  ## String Utilities
373
557
 
374
558
  ### truncateText
@@ -56,7 +56,10 @@ npm uninstall tailwindcss@^3.0.0 postcss autoprefixer
56
56
 
57
57
  ### Step 3: Configure Vite
58
58
 
59
- **⚠️ CRITICAL**: Without this configuration, PACE Core components will not be styled.
59
+ **⚠️ CRITICAL**: This configuration is required for:
60
+ 1. **Tailwind CSS scanning** - Ensures PACE Core components are styled
61
+ 2. **React context consistency** - Prevents "useUnifiedAuth must be used within a UnifiedAuthProvider" errors
62
+ 3. **Router context availability** - Prevents "useNavigate() may be used only in the context of a <Router>" errors
60
63
 
61
64
  Create or update `vite.config.ts`:
62
65
 
@@ -64,6 +67,7 @@ Create or update `vite.config.ts`:
64
67
  import { defineConfig } from 'vite'
65
68
  import react from '@vitejs/plugin-react'
66
69
  import tailwindcss from '@tailwindcss/vite'
70
+ import path from 'path'
67
71
 
68
72
  export default defineConfig({
69
73
  plugins: [
@@ -76,6 +80,27 @@ export default defineConfig({
76
80
  ]
77
81
  })
78
82
  ],
83
+ resolve: {
84
+ alias: {
85
+ "@": path.resolve(__dirname, "./src"),
86
+ },
87
+ // CRITICAL: Dedupe React and React Router to ensure single instances
88
+ // This prevents Router context issues when pace-core uses react-router-dom
89
+ dedupe: ['react', 'react-dom', 'react-router-dom']
90
+ },
91
+ // CRITICAL: Exclude pace-core from pre-bundling to prevent React context mismatches
92
+ // Pre-bundling creates separate React instances, causing context errors
93
+ optimizeDeps: {
94
+ include: [
95
+ 'react',
96
+ 'react-dom',
97
+ 'react/jsx-runtime',
98
+ '@radix-ui/react-slot'
99
+ ],
100
+ // CRITICAL: Exclude pace-core and react-router-dom from pre-bundling
101
+ // This ensures pace-core uses the same React instance as your app
102
+ exclude: ['@jmruthers/pace-core', 'react-router-dom']
103
+ },
79
104
  server: {
80
105
  port: 3000,
81
106
  open: true,
@@ -279,27 +304,61 @@ VITE_SUPABASE_URL=https://your-project.supabase.co
279
304
  VITE_SUPABASE_ANON_KEY=your-publishable-key-or-anon-key-here
280
305
  ```
281
306
 
282
- Wrap your app with providers:
307
+ Wrap your app with providers in the correct nesting order:
283
308
 
284
309
  ```tsx
285
- import { UnifiedAuthProvider } from '@jmruthers/pace-core';
310
+ // src/main.tsx - CRITICAL: Correct provider nesting order
311
+ import React from 'react';
312
+ import ReactDOM from 'react-dom/client';
313
+ import { BrowserRouter } from 'react-router-dom';
314
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
315
+ import { UnifiedAuthProvider, OrganisationProvider, setupRBAC } from '@jmruthers/pace-core';
286
316
  import { supabase } from './lib/supabase';
317
+ import App from './App';
287
318
 
288
- function App() {
289
- return (
290
- <UnifiedAuthProvider
291
- supabaseClient={supabase}
292
- appName="my-app"
293
- idleTimeoutMs={30 * 60 * 1000}
294
- warnBeforeMs={5 * 60 * 1000}
295
- onIdleLogout={() => window.location.href = '/login'}
296
- >
297
- <YourApp />
298
- </UnifiedAuthProvider>
299
- );
300
- }
319
+ // Import CSS
320
+ import '@jmruthers/pace-core/src/styles/core.css';
321
+
322
+ // ⚠️ REQUIRED: Setup RBAC before rendering
323
+ setupRBAC(supabase);
324
+
325
+ const queryClient = new QueryClient();
326
+
327
+ ReactDOM.createRoot(document.getElementById('root')!).render(
328
+ <React.StrictMode>
329
+ {/* CRITICAL: Correct nesting order */}
330
+ <QueryClientProvider client={queryClient}>
331
+ <BrowserRouter>
332
+ <UnifiedAuthProvider
333
+ supabaseClient={supabase}
334
+ appName="my-app"
335
+ idleTimeoutMs={30 * 60 * 1000} // 30 minutes
336
+ warnBeforeMs={5 * 60 * 1000} // 5 minutes warning
337
+ onIdleLogout={() => window.location.href = '/login'}
338
+ >
339
+ <OrganisationProvider>
340
+ <App />
341
+ </OrganisationProvider>
342
+ </UnifiedAuthProvider>
343
+ </BrowserRouter>
344
+ </QueryClientProvider>
345
+ </React.StrictMode>
346
+ );
301
347
  ```
302
348
 
349
+ **⚠️ CRITICAL Provider Nesting Order:**
350
+ 1. `QueryClientProvider` (outermost)
351
+ 2. `BrowserRouter`
352
+ 3. `UnifiedAuthProvider`
353
+ 4. `OrganisationProvider`
354
+ 5. `App` (innermost)
355
+
356
+ **Common mistakes to avoid:**
357
+ - ❌ `BrowserRouter` inside `UnifiedAuthProvider` (causes Router context errors)
358
+ - ❌ `UnifiedAuthProvider` wrapping `BrowserRouter` (causes context errors)
359
+ - ❌ Missing `BrowserRouter` (causes `useNavigate` errors)
360
+ - ❌ Missing `QueryClientProvider` (required for React Query features)
361
+
303
362
  ## 🚨 Breaking Changes (v0.5.65+)
304
363
 
305
364
  **CRITICAL**: As of v0.5.65+, inactivity timeouts are **mandatory** in `UnifiedAuthProvider`.
@@ -45,12 +45,15 @@ npm install -D tailwindcss@^4.0.0 @tailwindcss/vite
45
45
 
46
46
  ## Step 3: Configure Vite
47
47
 
48
+ **⚠️ CRITICAL**: This configuration prevents React context and Router context errors.
49
+
48
50
  Create or update `vite.config.ts`:
49
51
 
50
52
  ```typescript
51
53
  import { defineConfig } from 'vite';
52
54
  import react from '@vitejs/plugin-react';
53
55
  import tailwindcss from '@tailwindcss/vite';
56
+ import path from 'path';
54
57
 
55
58
  export default defineConfig({
56
59
  plugins: [
@@ -62,6 +65,23 @@ export default defineConfig({
62
65
  ]
63
66
  })
64
67
  ],
68
+ resolve: {
69
+ alias: {
70
+ "@": path.resolve(__dirname, "./src"),
71
+ },
72
+ // CRITICAL: Dedupe React and React Router to ensure single instances
73
+ dedupe: ['react', 'react-dom', 'react-router-dom']
74
+ },
75
+ // CRITICAL: Exclude pace-core from pre-bundling to prevent React context mismatches
76
+ optimizeDeps: {
77
+ include: [
78
+ 'react',
79
+ 'react-dom',
80
+ 'react/jsx-runtime'
81
+ ],
82
+ // CRITICAL: Exclude pace-core and react-router-dom from pre-bundling
83
+ exclude: ['@jmruthers/pace-core', 'react-router-dom']
84
+ },
65
85
  server: {
66
86
  port: 3000,
67
87
  open: true,
@@ -135,39 +155,66 @@ CREATE POLICY "Users can delete their own tasks" ON tasks
135
155
 
136
156
  ## Step 7: Create Your App
137
157
 
158
+ **⚠️ CRITICAL**: Provider nesting order matters! Follow this exact structure.
159
+
138
160
  Update `src/main.tsx`:
139
161
 
140
162
  ```tsx
141
163
  import React from 'react';
142
164
  import ReactDOM from 'react-dom/client';
143
165
  import { BrowserRouter } from 'react-router-dom';
144
- import { UnifiedAuthProvider } from '@jmruthers/pace-core';
166
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
167
+ import { UnifiedAuthProvider, OrganisationProvider, setupRBAC } from '@jmruthers/pace-core';
145
168
  import { supabase } from './lib/supabase';
146
169
  import App from './App';
147
170
  import './app.css';
148
171
 
172
+ // ⚠️ REQUIRED: Setup RBAC before rendering
173
+ setupRBAC(supabase);
174
+
175
+ const queryClient = new QueryClient();
176
+
149
177
  ReactDOM.createRoot(document.getElementById('root')!).render(
150
178
  <React.StrictMode>
151
- <UnifiedAuthProvider
152
- supabaseClient={supabase}
153
- appName="My Task App"
154
- idleTimeoutMs={30 * 60 * 1000} // 30 minutes
155
- warnBeforeMs={60 * 1000} // 1 minute warning
156
- onIdleLogout={() => window.location.assign('/login')}
157
- >
179
+ {/* CRITICAL: Correct nesting order */}
180
+ <QueryClientProvider client={queryClient}>
158
181
  <BrowserRouter>
159
- <App />
182
+ <UnifiedAuthProvider
183
+ supabaseClient={supabase}
184
+ appName="My Task App"
185
+ idleTimeoutMs={30 * 60 * 1000} // 30 minutes
186
+ warnBeforeMs={60 * 1000} // 1 minute warning
187
+ onIdleLogout={() => window.location.assign('/login')}
188
+ >
189
+ <OrganisationProvider>
190
+ <App />
191
+ </OrganisationProvider>
192
+ </UnifiedAuthProvider>
160
193
  </BrowserRouter>
161
- </UnifiedAuthProvider>
194
+ </QueryClientProvider>
162
195
  </React.StrictMode>
163
196
  );
164
197
  ```
165
198
 
199
+ **Provider Nesting Order (CRITICAL):**
200
+ 1. `React.StrictMode` (outermost)
201
+ 2. `QueryClientProvider`
202
+ 3. `BrowserRouter`
203
+ 4. `UnifiedAuthProvider`
204
+ 5. `OrganisationProvider`
205
+ 6. `App` (innermost)
206
+
207
+ **Why this order matters:**
208
+ - `BrowserRouter` must wrap `UnifiedAuthProvider` to provide Router context
209
+ - `UnifiedAuthProvider` must be inside `BrowserRouter` to use Router hooks
210
+ - `QueryClientProvider` wraps everything for React Query support
211
+ - Wrong nesting causes "useNavigate() may be used only in the context of a <Router>" errors
212
+
166
213
  Create `src/App.tsx`:
167
214
 
168
215
  ```tsx
169
216
  import { Routes, Route, Navigate } from 'react-router-dom';
170
- import { useUnifiedAuth, PaceLoginPage, PaceAppLayout, ProtectedRoute } from '@jmruthers/pace-core';
217
+ import { PaceLoginPage, PaceAppLayout, ProtectedRoute, useUnifiedAuth } from '@jmruthers/pace-core';
171
218
  import { TasksPage } from './pages/TasksPage';
172
219
  import { DashboardPage } from './pages/DashboardPage';
173
220
 
@@ -180,6 +227,7 @@ function App() {
180
227
  path="/login"
181
228
  element={isAuthenticated ? <Navigate to="/" replace /> : <PaceLoginPage appName="My Task App" />}
182
229
  />
230
+ {/* ProtectedRoute from pace-core package - handles auth and event selection */}
183
231
  <Route element={<ProtectedRoute />}>
184
232
  <Route element={<PaceAppLayout appName="My Task App" />}>
185
233
  <Route path="/" element={<DashboardPage />} />
@@ -193,6 +241,8 @@ function App() {
193
241
  export default App;
194
242
  ```
195
243
 
244
+ **Note:** `ProtectedRoute` is imported from `@jmruthers/pace-core` - do not create a local version. The package version handles authentication, session restoration, and optional event selection.
245
+
196
246
  ## Step 8: Create the Dashboard Page
197
247
 
198
248
  Create `src/pages/DashboardPage.tsx`:
@@ -69,17 +69,64 @@ if (!supabaseUrl || !supabaseAnonKey) {
69
69
  export const supabase = createClient(supabaseUrl, supabaseAnonKey)
70
70
  ```
71
71
 
72
- ### 4. App Setup with Authentication
72
+ ### 4. Configure Vite (CRITICAL)
73
73
 
74
- **CRITICAL**: UnifiedAuthProvider now requires inactivity timeout configuration:
74
+ **⚠️ CRITICAL**: This configuration prevents React context and Router context errors.
75
+
76
+ Add to your `vite.config.ts`:
77
+
78
+ ```typescript
79
+ import { defineConfig } from 'vite';
80
+ import react from '@vitejs/plugin-react';
81
+ import tailwindcss from '@tailwindcss/vite';
82
+ import path from 'path';
83
+
84
+ export default defineConfig({
85
+ plugins: [
86
+ react(),
87
+ tailwindcss({
88
+ content: [
89
+ './src/**/*.{js,ts,jsx,tsx}',
90
+ './node_modules/@jmruthers/pace-core/src/**/*.{js,ts,jsx,tsx}'
91
+ ]
92
+ })
93
+ ],
94
+ resolve: {
95
+ alias: {
96
+ "@": path.resolve(__dirname, "./src"),
97
+ },
98
+ // CRITICAL: Dedupe React and React Router to ensure single instances
99
+ dedupe: ['react', 'react-dom', 'react-router-dom']
100
+ },
101
+ // CRITICAL: Exclude pace-core from pre-bundling to prevent React context mismatches
102
+ optimizeDeps: {
103
+ include: [
104
+ 'react',
105
+ 'react-dom',
106
+ 'react/jsx-runtime'
107
+ ],
108
+ exclude: ['@jmruthers/pace-core', 'react-router-dom']
109
+ },
110
+ });
111
+ ```
112
+
113
+ ### 5. App Setup with Authentication
114
+
115
+ **⚠️ CRITICAL**:
116
+ 1. Provider nesting order matters - follow the exact structure below
117
+ 2. UnifiedAuthProvider requires inactivity timeout configuration
118
+ 3. setupRBAC() must be called before rendering
75
119
 
76
120
  ```tsx
77
121
  // src/main.tsx
78
122
  import React from 'react'
79
123
  import ReactDOM from 'react-dom/client'
80
124
  import { BrowserRouter } from 'react-router-dom'
125
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
81
126
  import {
82
- UnifiedAuthProvider
127
+ UnifiedAuthProvider,
128
+ OrganisationProvider,
129
+ setupRBAC
83
130
  } from '@jmruthers/pace-core'
84
131
  import { supabase } from './lib/supabase'
85
132
  import App from './App.tsx'
@@ -87,17 +134,46 @@ import App from './App.tsx'
87
134
  // CRITICAL: Import the CSS system - includes everything you need
88
135
  import '@jmruthers/pace-core/src/styles/core.css'
89
136
 
137
+ // ⚠️ REQUIRED: Setup RBAC before rendering
138
+ setupRBAC(supabase);
139
+
140
+ const queryClient = new QueryClient();
141
+
90
142
  ReactDOM.createRoot(document.getElementById('root')!).render(
91
143
  <React.StrictMode>
92
- <BrowserRouter>
93
- <UnifiedAuthProvider
94
- supabaseClient={supabase}
95
- appName="my-app"
96
- requireOrganisationContext={true}
97
- idleTimeoutMs={30 * 60 * 1000} // 30 minutes - REQUIRED
98
- warnBeforeMs={60 * 1000} // 60 seconds - REQUIRED
99
- onIdleLogout={() => window.location.href = '/login'} // REQUIRED
100
- >
144
+ {/* CRITICAL: Correct nesting order */}
145
+ <QueryClientProvider client={queryClient}>
146
+ <BrowserRouter>
147
+ <UnifiedAuthProvider
148
+ supabaseClient={supabase}
149
+ appName="my-app"
150
+ requireOrganisationContext={true}
151
+ idleTimeoutMs={30 * 60 * 1000} // 30 minutes - REQUIRED
152
+ warnBeforeMs={60 * 1000} // 60 seconds - REQUIRED
153
+ onIdleLogout={() => window.location.href = '/login'} // REQUIRED
154
+ >
155
+ <OrganisationProvider>
156
+ <App />
157
+ </OrganisationProvider>
158
+ </UnifiedAuthProvider>
159
+ </BrowserRouter>
160
+ </QueryClientProvider>
161
+ </React.StrictMode>
162
+ )
163
+ ```
164
+
165
+ **Provider Nesting Order (CRITICAL):**
166
+ 1. `React.StrictMode` (outermost)
167
+ 2. `QueryClientProvider`
168
+ 3. `BrowserRouter`
169
+ 4. `UnifiedAuthProvider`
170
+ 5. `OrganisationProvider`
171
+ 6. `App` (innermost)
172
+
173
+ **Why this order matters:**
174
+ - `BrowserRouter` must wrap `UnifiedAuthProvider` to provide Router context
175
+ - `UnifiedAuthProvider` must be inside `BrowserRouter` to use Router hooks
176
+ - Wrong nesting causes "useNavigate() may be used only in the context of a <Router>" errors
101
177
  <App />
102
178
  </UnifiedAuthProvider>
103
179
  </BrowserRouter>