@jmruthers/pace-core 0.5.183 → 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 (307) 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-QETLRQI6.js → chunk-HC67NW5K.js} +380 -360
  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/PaceLoginPage/PaceLoginPage.tsx +1 -1
  254. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +37 -8
  255. package/src/components/ProtectedRoute/ProtectedRoute.tsx +12 -4
  256. package/src/components/index.ts +8 -0
  257. package/src/eslint-rules/pace-core-compliance.cjs +406 -0
  258. package/src/eslint-rules/pace-core-compliance.js +640 -0
  259. package/src/hooks/__tests__/useFormDialog.test.ts +478 -0
  260. package/src/hooks/index.ts +2 -0
  261. package/src/hooks/useFileReference.test.ts +1 -0
  262. package/src/hooks/useFormDialog.ts +147 -0
  263. package/src/index.ts +27 -0
  264. package/src/providers/services/OrganisationServiceProvider.tsx +6 -5
  265. package/src/providers/services/UnifiedAuthProvider.tsx +24 -3
  266. package/src/rbac/__tests__/scenarios.user-role.test.tsx +3 -0
  267. package/src/rbac/compliance/database-validator.ts +165 -0
  268. package/src/rbac/compliance/index.ts +38 -0
  269. package/src/rbac/compliance/quick-fix-suggestions.ts +209 -0
  270. package/src/rbac/compliance/runtime-compliance.ts +77 -0
  271. package/src/rbac/compliance/setup-validator.ts +131 -0
  272. package/src/rbac/components/PagePermissionGuard.tsx +8 -64
  273. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +35 -21
  274. package/src/rbac/docs/event-based-apps.md +285 -0
  275. package/src/rbac/errors.ts +11 -0
  276. package/src/rbac/hooks/useRoleManagement.ts +292 -12
  277. package/src/rbac/index.ts +30 -0
  278. package/src/services/OrganisationService.ts +4 -0
  279. package/src/types/file-reference.ts +6 -0
  280. package/src/utils/__tests__/timezone.test.ts +345 -0
  281. package/src/utils/file-reference/__tests__/file-reference.test.ts +2 -0
  282. package/src/utils/file-reference/index.ts +1 -0
  283. package/src/utils/formatting/formatDateTimeTimezone.test.ts +167 -0
  284. package/src/utils/formatting/formatting.ts +179 -0
  285. package/src/utils/index.ts +27 -1
  286. package/src/utils/location/index.ts +16 -0
  287. package/src/utils/location/location.test.ts +286 -0
  288. package/src/utils/location/location.ts +175 -0
  289. package/src/utils/timezone/index.ts +17 -0
  290. package/src/utils/timezone/timezone.test.ts +349 -0
  291. package/src/utils/timezone/timezone.ts +281 -0
  292. package/dist/chunk-CSOFYHAG.js.map +0 -1
  293. package/dist/chunk-FUEYYMX5.js.map +0 -1
  294. package/dist/chunk-HKIT6O7W.js +0 -198
  295. package/dist/chunk-HKIT6O7W.js.map +0 -1
  296. package/dist/chunk-KUEN3HFB.js +0 -94
  297. package/dist/chunk-KUEN3HFB.js.map +0 -1
  298. package/dist/chunk-M7W4CP3M.js.map +0 -1
  299. package/dist/chunk-PWAHJW4G.js.map +0 -1
  300. package/dist/chunk-QETLRQI6.js.map +0 -1
  301. package/dist/chunk-UHNYIBXL.js.map +0 -1
  302. package/dist/formatting-5wETwiGF.d.ts +0 -162
  303. /package/dist/{DataTable-QAB34V6K.js.map → DataTable-IX2NBUTP.js.map} +0 -0
  304. /package/dist/{UnifiedAuthProvider-7F6T4B6K.js.map → UnifiedAuthProvider-A4BCQRJY.js.map} +0 -0
  305. /package/dist/{api-ROMBCNKU.js.map → api-BMFCXVQX.js.map} +0 -0
  306. /package/dist/{chunk-W22JP75J.js.map → chunk-STTZQK2I.js.map} +0 -0
  307. /package/dist/{chunk-QCDXODCA.js.map → chunk-XAUHJD3L.js.map} +0 -0
@@ -4,6 +4,7 @@ import {
4
4
  OrganisationContextRequiredError,
5
5
  InvalidScopeError,
6
6
  MissingUserContextError,
7
+ RBACNotInitializedError,
7
8
  } from './types';
8
9
 
9
10
  export enum RBACErrorCategory {
@@ -154,3 +155,13 @@ export function mapErrorCategoryToSecurityEventType(category: RBACErrorCategory)
154
155
  return 'unknown_error';
155
156
  }
156
157
  }
158
+
159
+ // Re-export error classes for convenience
160
+ export {
161
+ RBACError,
162
+ PermissionDeniedError,
163
+ OrganisationContextRequiredError,
164
+ InvalidScopeError,
165
+ MissingUserContextError,
166
+ RBACNotInitializedError,
167
+ };
@@ -13,25 +13,46 @@
13
13
  * import { useRoleManagement } from '@jmruthers/pace-core/rbac';
14
14
  *
15
15
  * function UserRolesComponent() {
16
- * const { revokeEventAppRole, grantEventAppRole, isLoading, error } = useRoleManagement();
16
+ * const {
17
+ * revokeEventAppRole,
18
+ * grantEventAppRole,
19
+ * grantGlobalRole,
20
+ * revokeGlobalRole,
21
+ * grantOrganisationRole,
22
+ * revokeOrganisationRole,
23
+ * isLoading,
24
+ * error
25
+ * } = useRoleManagement();
17
26
  *
18
- * const handleRevokeRole = async (roleId: string, roleData: EventAppRoleData) => {
19
- * const result = await revokeEventAppRole({
20
- * userId: roleData.user_id,
21
- * organisationId: roleData.organisation_id,
22
- * eventId: roleData.event_id,
23
- * appId: roleData.app_id,
24
- * role: roleData.role
27
+ * // Grant a global role
28
+ * const handleGrantGlobalRole = async () => {
29
+ * const result = await grantGlobalRole({
30
+ * user_id: userId,
31
+ * role: 'super_admin'
25
32
  * });
33
+ * if (result.success) {
34
+ * toast({ title: 'Role granted successfully' });
35
+ * }
36
+ * };
26
37
  *
38
+ * // Grant an organisation role
39
+ * const handleGrantOrgRole = async () => {
40
+ * const result = await grantOrganisationRole({
41
+ * user_id: userId,
42
+ * organisation_id: orgId,
43
+ * role: 'org_admin'
44
+ * });
27
45
  * if (result.success) {
28
- * toast({ title: 'Role revoked successfully' });
29
- * } else {
30
- * toast({ title: 'Failed to revoke role', variant: 'destructive' });
46
+ * toast({ title: 'Role granted successfully' });
31
47
  * }
32
48
  * };
33
49
  *
34
- * return <button onClick={() => handleRevokeRole(roleId, roleData)}>Revoke Role</button>;
50
+ * return (
51
+ * <div>
52
+ * <button onClick={handleGrantGlobalRole}>Grant Super Admin</button>
53
+ * <button onClick={handleGrantOrgRole}>Grant Org Admin</button>
54
+ * </div>
55
+ * );
35
56
  * }
36
57
  * ```
37
58
  */
@@ -48,6 +69,17 @@ export interface EventAppRoleData {
48
69
  role: 'viewer' | 'participant' | 'planner' | 'event_admin';
49
70
  }
50
71
 
72
+ export interface OrganisationRoleData {
73
+ user_id: UUID;
74
+ organisation_id: UUID;
75
+ role: 'supporter' | 'member' | 'leader' | 'org_admin';
76
+ }
77
+
78
+ export interface GlobalRoleData {
79
+ user_id: UUID;
80
+ role: 'super_admin';
81
+ }
82
+
51
83
  export interface RevokeEventAppRoleParams extends EventAppRoleData {
52
84
  revoked_by?: UUID;
53
85
  }
@@ -58,6 +90,26 @@ export interface GrantEventAppRoleParams extends EventAppRoleData {
58
90
  valid_to?: string | null;
59
91
  }
60
92
 
93
+ export interface RevokeOrganisationRoleParams extends OrganisationRoleData {
94
+ revoked_by?: UUID;
95
+ }
96
+
97
+ export interface GrantOrganisationRoleParams extends OrganisationRoleData {
98
+ granted_by?: UUID;
99
+ valid_from?: string;
100
+ valid_to?: string | null;
101
+ }
102
+
103
+ export interface RevokeGlobalRoleParams extends GlobalRoleData {
104
+ revoked_by?: UUID;
105
+ }
106
+
107
+ export interface GrantGlobalRoleParams extends GlobalRoleData {
108
+ granted_by?: UUID;
109
+ valid_from?: string;
110
+ valid_to?: string | null;
111
+ }
112
+
61
113
  export interface RoleManagementResult {
62
114
  success: boolean;
63
115
  message?: string;
@@ -244,10 +296,238 @@ export function useRoleManagement() {
244
296
  }
245
297
  }, [user?.id, supabase]);
246
298
 
299
+ /**
300
+ * Grant a global role using the unified RPC function
301
+ *
302
+ * This function uses the `rbac_role_grant` RPC which:
303
+ * - Runs with SECURITY DEFINER privileges
304
+ * - Includes proper permission checks
305
+ * - Automatically populates audit fields (granted_by, timestamps)
306
+ * - Complies with Row-Level Security policies
307
+ *
308
+ * @param params - Role grant parameters
309
+ * @returns Promise resolving to operation result with role ID
310
+ */
311
+ const grantGlobalRole = useCallback(async (
312
+ params: GrantGlobalRoleParams
313
+ ): Promise<RoleManagementResult> => {
314
+ setIsLoading(true);
315
+ setError(null);
316
+
317
+ try {
318
+ const { data, error: rpcError } = await supabase.rpc('rbac_role_grant', {
319
+ p_user_id: params.user_id,
320
+ p_role_type: 'global',
321
+ p_role_name: params.role,
322
+ p_context_id: null, // Global roles don't need context
323
+ p_granted_by: params.granted_by || user?.id || undefined
324
+ });
325
+
326
+ if (rpcError) {
327
+ throw new Error(rpcError.message || 'Failed to grant role');
328
+ }
329
+
330
+ // rbac_role_grant returns a table with success, message, role_id, error_code
331
+ const result = Array.isArray(data) && data.length > 0 ? data[0] : null;
332
+
333
+ if (!result || !result.success) {
334
+ return {
335
+ success: false,
336
+ error: result?.message || result?.error_code || 'Failed to grant role',
337
+ message: result?.message
338
+ };
339
+ }
340
+
341
+ return {
342
+ success: true,
343
+ message: result.message || 'Role granted successfully',
344
+ roleId: result.role_id
345
+ };
346
+ } catch (err) {
347
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
348
+ setError(err instanceof Error ? err : new Error(errorMessage));
349
+ return {
350
+ success: false,
351
+ error: errorMessage
352
+ };
353
+ } finally {
354
+ setIsLoading(false);
355
+ }
356
+ }, [user?.id, supabase]);
357
+
358
+ /**
359
+ * Revoke a global role using the unified RPC function
360
+ *
361
+ * This function uses the `rbac_role_revoke` RPC which:
362
+ * - Runs with SECURITY DEFINER privileges
363
+ * - Includes proper permission checks
364
+ * - Automatically populates audit fields (revoked_by, timestamps)
365
+ * - Complies with Row-Level Security policies
366
+ *
367
+ * @param params - Role revocation parameters
368
+ * @returns Promise resolving to operation result
369
+ */
370
+ const revokeGlobalRole = useCallback(async (
371
+ params: RevokeGlobalRoleParams
372
+ ): Promise<RoleManagementResult> => {
373
+ setIsLoading(true);
374
+ setError(null);
375
+
376
+ try {
377
+ const { data, error: rpcError } = await supabase.rpc('rbac_role_revoke', {
378
+ p_user_id: params.user_id,
379
+ p_role_type: 'global',
380
+ p_role_name: params.role,
381
+ p_context_id: null, // Global roles don't need context
382
+ p_revoked_by: params.revoked_by || user?.id || undefined
383
+ });
384
+
385
+ if (rpcError) {
386
+ throw new Error(rpcError.message || 'Failed to revoke role');
387
+ }
388
+
389
+ // rbac_role_revoke returns a table with success, message, revoked_count, error_code
390
+ const result = Array.isArray(data) && data.length > 0 ? data[0] : null;
391
+
392
+ return {
393
+ success: result?.success === true,
394
+ message: result?.message || undefined,
395
+ error: result?.success === false ? (result?.message || result?.error_code || 'Unknown error') : undefined
396
+ };
397
+ } catch (err) {
398
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
399
+ setError(err instanceof Error ? err : new Error(errorMessage));
400
+ return {
401
+ success: false,
402
+ error: errorMessage
403
+ };
404
+ } finally {
405
+ setIsLoading(false);
406
+ }
407
+ }, [user?.id, supabase]);
408
+
409
+ /**
410
+ * Grant an organisation role using the unified RPC function
411
+ *
412
+ * This function uses the `rbac_role_grant` RPC which:
413
+ * - Runs with SECURITY DEFINER privileges
414
+ * - Includes proper permission checks
415
+ * - Automatically populates audit fields (granted_by, timestamps)
416
+ * - Complies with Row-Level Security policies
417
+ *
418
+ * @param params - Role grant parameters
419
+ * @returns Promise resolving to operation result with role ID
420
+ */
421
+ const grantOrganisationRole = useCallback(async (
422
+ params: GrantOrganisationRoleParams
423
+ ): Promise<RoleManagementResult> => {
424
+ setIsLoading(true);
425
+ setError(null);
426
+
427
+ try {
428
+ const { data, error: rpcError } = await supabase.rpc('rbac_role_grant', {
429
+ p_user_id: params.user_id,
430
+ p_role_type: 'organisation',
431
+ p_role_name: params.role,
432
+ p_context_id: params.organisation_id, // Organisation ID as context
433
+ p_granted_by: params.granted_by || user?.id || undefined
434
+ });
435
+
436
+ if (rpcError) {
437
+ throw new Error(rpcError.message || 'Failed to grant role');
438
+ }
439
+
440
+ // rbac_role_grant returns a table with success, message, role_id, error_code
441
+ const result = Array.isArray(data) && data.length > 0 ? data[0] : null;
442
+
443
+ if (!result || !result.success) {
444
+ return {
445
+ success: false,
446
+ error: result?.message || result?.error_code || 'Failed to grant role',
447
+ message: result?.message
448
+ };
449
+ }
450
+
451
+ return {
452
+ success: true,
453
+ message: result.message || 'Role granted successfully',
454
+ roleId: result.role_id
455
+ };
456
+ } catch (err) {
457
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
458
+ setError(err instanceof Error ? err : new Error(errorMessage));
459
+ return {
460
+ success: false,
461
+ error: errorMessage
462
+ };
463
+ } finally {
464
+ setIsLoading(false);
465
+ }
466
+ }, [user?.id, supabase]);
467
+
468
+ /**
469
+ * Revoke an organisation role using the unified RPC function
470
+ *
471
+ * This function uses the `rbac_role_revoke` RPC which:
472
+ * - Runs with SECURITY DEFINER privileges
473
+ * - Includes proper permission checks
474
+ * - Automatically populates audit fields (revoked_by, timestamps)
475
+ * - Complies with Row-Level Security policies
476
+ *
477
+ * @param params - Role revocation parameters
478
+ * @returns Promise resolving to operation result
479
+ */
480
+ const revokeOrganisationRole = useCallback(async (
481
+ params: RevokeOrganisationRoleParams
482
+ ): Promise<RoleManagementResult> => {
483
+ setIsLoading(true);
484
+ setError(null);
485
+
486
+ try {
487
+ const { data, error: rpcError } = await supabase.rpc('rbac_role_revoke', {
488
+ p_user_id: params.user_id,
489
+ p_role_type: 'organisation',
490
+ p_role_name: params.role,
491
+ p_context_id: params.organisation_id, // Organisation ID as context
492
+ p_revoked_by: params.revoked_by || user?.id || undefined
493
+ });
494
+
495
+ if (rpcError) {
496
+ throw new Error(rpcError.message || 'Failed to revoke role');
497
+ }
498
+
499
+ // rbac_role_revoke returns a table with success, message, revoked_count, error_code
500
+ const result = Array.isArray(data) && data.length > 0 ? data[0] : null;
501
+
502
+ return {
503
+ success: result?.success === true,
504
+ message: result?.message || undefined,
505
+ error: result?.success === false ? (result?.message || result?.error_code || 'Unknown error') : undefined
506
+ };
507
+ } catch (err) {
508
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
509
+ setError(err instanceof Error ? err : new Error(errorMessage));
510
+ return {
511
+ success: false,
512
+ error: errorMessage
513
+ };
514
+ } finally {
515
+ setIsLoading(false);
516
+ }
517
+ }, [user?.id, supabase]);
518
+
247
519
  return {
520
+ // Event app roles (existing)
248
521
  revokeEventAppRole,
249
522
  grantEventAppRole,
250
523
  revokeRoleById,
524
+ // Global roles (new)
525
+ grantGlobalRole,
526
+ revokeGlobalRole,
527
+ // Organisation roles (new)
528
+ grantOrganisationRole,
529
+ revokeOrganisationRole,
530
+ // Shared state
251
531
  isLoading,
252
532
  error
253
533
  };
package/src/rbac/index.ts CHANGED
@@ -113,3 +113,33 @@ export {
113
113
 
114
114
  // Permissions
115
115
  export * from './permissions';
116
+
117
+ // Compliance
118
+ export {
119
+ isRBACInitialized,
120
+ validateRBACSetup,
121
+ getSetupIssues,
122
+ type SetupIssue,
123
+ type ComplianceResult,
124
+ } from './compliance/setup-validator';
125
+
126
+ export {
127
+ checkRuntimeCompliance,
128
+ validateAndWarn,
129
+ type RuntimeComplianceResult,
130
+ } from './compliance/runtime-compliance';
131
+
132
+ export {
133
+ validateDatabaseConfiguration,
134
+ type DatabaseComplianceResult,
135
+ type DatabaseIssue,
136
+ } from './compliance/database-validator';
137
+
138
+ export {
139
+ getQuickFixes,
140
+ getCustomAuthCodeFixes,
141
+ getDuplicateConfigFixes,
142
+ getUnprotectedPageFixes,
143
+ getDirectSupabaseAuthFixes,
144
+ type QuickFix,
145
+ } from './compliance/quick-fix-suggestions';
@@ -519,6 +519,7 @@ export class OrganisationService extends BaseService implements IOrganisationSer
519
519
 
520
520
  // Auto-select organisation: try persisted, then primary, then first
521
521
  let initialOrg: Organisation | null = null;
522
+ let selectionMethod: 'persisted' | 'admin' | 'first' = 'first';
522
523
 
523
524
  // 1. Try to restore from localStorage
524
525
  try {
@@ -530,6 +531,7 @@ export class OrganisationService extends BaseService implements IOrganisationSer
530
531
  const validPersistedOrg = activeOrgs.find(org => org.id === persistedOrg.id);
531
532
  if (validPersistedOrg) {
532
533
  initialOrg = validPersistedOrg;
534
+ selectionMethod = 'persisted';
533
535
  } else {
534
536
  logger.warn("OrganisationService", "Persisted organisation not found in active orgs, clearing cache");
535
537
  localStorage.removeItem('pace-core-selected-organisation');
@@ -552,6 +554,7 @@ export class OrganisationService extends BaseService implements IOrganisationSer
552
554
  const foundOrg = organisations.find((org) => org.id === adminMembership.organisation_id);
553
555
  if (foundOrg) {
554
556
  initialOrg = foundOrg;
557
+ selectionMethod = 'admin';
555
558
  }
556
559
  }
557
560
  }
@@ -559,6 +562,7 @@ export class OrganisationService extends BaseService implements IOrganisationSer
559
562
  // 3. Fall back to first organisation
560
563
  if (!initialOrg) {
561
564
  initialOrg = activeOrgs[0];
565
+ selectionMethod = 'first';
562
566
  }
563
567
 
564
568
  if (!initialOrg) {
@@ -51,12 +51,18 @@ export enum FileCategory {
51
51
  TRAC_TRANSPORT = 'trac_transport'
52
52
  }
53
53
 
54
+ /**
55
+ * Options for uploading a file with a file reference
56
+ * @property pageContext - The page context where the file upload occurs (e.g., 'configuration', 'forms', 'applications')
57
+ * Used for context-aware permission checks. Required to check appropriate page-level permissions.
58
+ */
54
59
  export interface FileUploadOptions {
55
60
  table_name: string;
56
61
  record_id: string;
57
62
  organisation_id: string;
58
63
  app_id: AppId;
59
64
  category: FileCategory;
65
+ pageContext: string;
60
66
  is_public?: boolean;
61
67
  custom_metadata?: Record<string, unknown>;
62
68
  }