@jmruthers/pace-core 0.5.175 → 0.5.177

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 (137) hide show
  1. package/dist/{DataTable-INOWMY42.js → DataTable-4VSEJQ7D.js} +3 -3
  2. package/dist/{chunk-S263HQKM.js → chunk-2W3CH222.js} +3 -3
  3. package/dist/{chunk-URRWD5AR.js → chunk-GQA2LIAE.js} +2 -2
  4. package/dist/{chunk-Y6COIKPX.js → chunk-LEBYAJVF.js} +2 -2
  5. package/dist/{chunk-KY4K2RTG.js → chunk-ONI7Y733.js} +13 -11
  6. package/dist/chunk-ONI7Y733.js.map +1 -0
  7. package/dist/components.js +3 -3
  8. package/dist/index.js +4 -4
  9. package/dist/rbac/index.d.ts +30 -1
  10. package/dist/rbac/index.js +2 -2
  11. package/dist/utils.js +1 -1
  12. package/docs/api/classes/ColumnFactory.md +1 -1
  13. package/docs/api/classes/ErrorBoundary.md +1 -1
  14. package/docs/api/classes/InvalidScopeError.md +1 -1
  15. package/docs/api/classes/MissingUserContextError.md +1 -1
  16. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  17. package/docs/api/classes/PermissionDeniedError.md +1 -1
  18. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  19. package/docs/api/classes/RBACAuditManager.md +1 -1
  20. package/docs/api/classes/RBACCache.md +1 -1
  21. package/docs/api/classes/RBACEngine.md +1 -1
  22. package/docs/api/classes/RBACError.md +1 -1
  23. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  24. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  25. package/docs/api/classes/StorageUtils.md +1 -1
  26. package/docs/api/enums/FileCategory.md +1 -1
  27. package/docs/api/interfaces/AggregateConfig.md +1 -1
  28. package/docs/api/interfaces/BadgeProps.md +1 -1
  29. package/docs/api/interfaces/ButtonProps.md +1 -1
  30. package/docs/api/interfaces/CalendarProps.md +1 -1
  31. package/docs/api/interfaces/CardProps.md +1 -1
  32. package/docs/api/interfaces/ColorPalette.md +1 -1
  33. package/docs/api/interfaces/ColorShade.md +1 -1
  34. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  35. package/docs/api/interfaces/DataRecord.md +1 -1
  36. package/docs/api/interfaces/DataTableAction.md +1 -1
  37. package/docs/api/interfaces/DataTableColumn.md +1 -1
  38. package/docs/api/interfaces/DataTableProps.md +1 -1
  39. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  40. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  41. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  42. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  43. package/docs/api/interfaces/EventLogoProps.md +1 -1
  44. package/docs/api/interfaces/ExportColumn.md +1 -1
  45. package/docs/api/interfaces/ExportOptions.md +1 -1
  46. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  47. package/docs/api/interfaces/FileMetadata.md +1 -1
  48. package/docs/api/interfaces/FileReference.md +1 -1
  49. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  50. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  51. package/docs/api/interfaces/FileUploadProps.md +1 -1
  52. package/docs/api/interfaces/FooterProps.md +1 -1
  53. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  54. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  55. package/docs/api/interfaces/InputProps.md +1 -1
  56. package/docs/api/interfaces/LabelProps.md +1 -1
  57. package/docs/api/interfaces/LoginFormProps.md +1 -1
  58. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  59. package/docs/api/interfaces/NavigationContextType.md +1 -1
  60. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  61. package/docs/api/interfaces/NavigationItem.md +1 -1
  62. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  63. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  64. package/docs/api/interfaces/Organisation.md +1 -1
  65. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  66. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  67. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  68. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  69. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  70. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  71. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  72. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  73. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  74. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  75. package/docs/api/interfaces/PaletteData.md +1 -1
  76. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  77. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  78. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  79. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  80. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  81. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  82. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  83. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  84. package/docs/api/interfaces/RBACConfig.md +1 -1
  85. package/docs/api/interfaces/RBACLogger.md +1 -1
  86. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  87. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  88. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  89. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  90. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  91. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  92. package/docs/api/interfaces/RouteConfig.md +1 -1
  93. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  94. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  95. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  96. package/docs/api/interfaces/StorageConfig.md +1 -1
  97. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  98. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  99. package/docs/api/interfaces/StorageListOptions.md +1 -1
  100. package/docs/api/interfaces/StorageListResult.md +1 -1
  101. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  102. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  103. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  104. package/docs/api/interfaces/StyleImport.md +1 -1
  105. package/docs/api/interfaces/SwitchProps.md +1 -1
  106. package/docs/api/interfaces/TabsContentProps.md +1 -1
  107. package/docs/api/interfaces/TabsListProps.md +1 -1
  108. package/docs/api/interfaces/TabsProps.md +1 -1
  109. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  110. package/docs/api/interfaces/TextareaProps.md +1 -1
  111. package/docs/api/interfaces/ToastActionElement.md +1 -1
  112. package/docs/api/interfaces/ToastProps.md +1 -1
  113. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  114. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  115. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  116. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  117. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  118. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  119. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  120. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  121. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  122. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  123. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  124. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  125. package/docs/api/interfaces/UserEventAccess.md +1 -1
  126. package/docs/api/interfaces/UserMenuProps.md +1 -1
  127. package/docs/api/interfaces/UserProfile.md +1 -1
  128. package/docs/api/modules.md +33 -4
  129. package/package.json +1 -1
  130. package/src/rbac/hooks/useRBAC.ts +3 -2
  131. package/src/rbac/hooks/useResourcePermissions.test.ts +64 -9
  132. package/src/rbac/hooks/useResourcePermissions.ts +40 -5
  133. package/dist/chunk-KY4K2RTG.js.map +0 -1
  134. /package/dist/{DataTable-INOWMY42.js.map → DataTable-4VSEJQ7D.js.map} +0 -0
  135. /package/dist/{chunk-S263HQKM.js.map → chunk-2W3CH222.js.map} +0 -0
  136. /package/dist/{chunk-URRWD5AR.js.map → chunk-GQA2LIAE.js.map} +0 -0
  137. /package/dist/{chunk-Y6COIKPX.js.map → chunk-LEBYAJVF.js.map} +0 -0
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / TextareaProps
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / TextareaProps
2
2
 
3
3
  # Interface: TextareaProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / ToastActionElement
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / ToastActionElement
2
2
 
3
3
  # Interface: ToastActionElement
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / ToastProps
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / ToastProps
2
2
 
3
3
  # Interface: ToastProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UnifiedAuthContextType
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UnifiedAuthContextType
2
2
 
3
3
  # Interface: UnifiedAuthContextType
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UnifiedAuthProviderProps
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UnifiedAuthProviderProps
2
2
 
3
3
  # Interface: UnifiedAuthProviderProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UseInactivityTrackerOptions
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UseInactivityTrackerOptions
2
2
 
3
3
  # Interface: UseInactivityTrackerOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UseInactivityTrackerReturn
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UseInactivityTrackerReturn
2
2
 
3
3
  # Interface: UseInactivityTrackerReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UsePublicEventOptions
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UsePublicEventOptions
2
2
 
3
3
  # Interface: UsePublicEventOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UsePublicEventReturn
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UsePublicEventReturn
2
2
 
3
3
  # Interface: UsePublicEventReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayOptions
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayOptions
2
2
 
3
3
  # Interface: UsePublicFileDisplayOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayReturn
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayReturn
2
2
 
3
3
  # Interface: UsePublicFileDisplayReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UsePublicRouteParamsReturn
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UsePublicRouteParamsReturn
2
2
 
3
3
  # Interface: UsePublicRouteParamsReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UseResolvedScopeOptions
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UseResolvedScopeOptions
2
2
 
3
3
  # Interface: UseResolvedScopeOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UseResolvedScopeReturn
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UseResolvedScopeReturn
2
2
 
3
3
  # Interface: UseResolvedScopeReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UseResourcePermissionsOptions
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UseResourcePermissionsOptions
2
2
 
3
3
  # Interface: UseResourcePermissionsOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UserEventAccess
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UserEventAccess
2
2
 
3
3
  # Interface: UserEventAccess
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UserMenuProps
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UserMenuProps
2
2
 
3
3
  # Interface: UserMenuProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.175](../README.md) / [Exports](../modules.md) / UserProfile
1
+ [@jmruthers/pace-core - v0.5.177](../README.md) / [Exports](../modules.md) / UserProfile
2
2
 
3
3
  # Interface: UserProfile
4
4
 
@@ -1,6 +1,6 @@
1
- [@jmruthers/pace-core - v0.5.175](README.md) / Exports
1
+ [@jmruthers/pace-core - v0.5.177](README.md) / Exports
2
2
 
3
- # @jmruthers/pace-core - v0.5.175
3
+ # @jmruthers/pace-core - v0.5.177
4
4
 
5
5
  **`File`**
6
6
 
@@ -7019,11 +7019,22 @@ This hook encapsulates the common pattern of checking create/update/delete/read
7019
7019
  permissions for a resource type. It handles scope resolution, user context,
7020
7020
  and provides a simple API for permission checking.
7021
7021
 
7022
+ **Page Permission Support:**
7023
+ When an `appId` is available in the resolved scope, the resource name is passed
7024
+ as `pageId` to enable page-based permission checks. This allows the hook to work
7025
+ with both resource-based permissions (when appId is not available) and page-based
7026
+ permissions (when appId is available and the resource is a registered page).
7027
+
7028
+ The RPC function `rbac_check_permission_simplified` will automatically resolve
7029
+ the page name to a page ID and check page permissions if the resource matches
7030
+ a registered page in `rbac_app_pages`. If the resource is not a registered page,
7031
+ it will fall back to resource-based permission checking.
7032
+
7022
7033
  #### Parameters
7023
7034
 
7024
7035
  | Name | Type | Description |
7025
7036
  | :------ | :------ | :------ |
7026
- | `resource` | `string` | The resource name (e.g., 'contacts', 'risks', 'journal') |
7037
+ | `resource` | `string` | The resource name (e.g., 'contacts', 'risks', 'planning') Can be a resource name or a page name registered in rbac_app_pages |
7027
7038
  | `options` | [`UseResourcePermissionsOptions`](interfaces/UseResourcePermissionsOptions.md) | Optional configuration |
7028
7039
 
7029
7040
  #### Returns
@@ -7047,9 +7058,27 @@ function useContacts() {
7047
7058
  }
7048
7059
  ```
7049
7060
 
7061
+ **`Example`**
7062
+
7063
+ ```tsx
7064
+ // Works with page names when appId is available in scope
7065
+ function usePlanning() {
7066
+ const { canCreate, canUpdate, canDelete } = useResourcePermissions('planning');
7067
+
7068
+ // Will check page permissions if 'planning' is registered in rbac_app_pages
7069
+ // Falls back to resource permissions if not a registered page
7070
+ const deleteItem = async (id: string) => {
7071
+ if (!canDelete('planning')) {
7072
+ throw new Error("Permission denied");
7073
+ }
7074
+ // ... perform deletion
7075
+ };
7076
+ }
7077
+ ```
7078
+
7050
7079
  #### Defined in
7051
7080
 
7052
- [packages/core/src/rbac/hooks/useResourcePermissions.ts:102](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/rbac/hooks/useResourcePermissions.ts#L102)
7081
+ [packages/core/src/rbac/hooks/useResourcePermissions.ts:131](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/rbac/hooks/useResourcePermissions.ts#L131)
7053
7082
 
7054
7083
  ___
7055
7084
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.5.175",
3
+ "version": "0.5.177",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -232,7 +232,7 @@ export function useRBAC(pageId?: string): UserRBACContext {
232
232
  } finally {
233
233
  setIsLoading(false);
234
234
  }
235
- }, [appName, logger, resetState, selectedEvent, selectedEvent?.event_id, selectedOrganisation, selectedOrganisation?.id, session, user, requiresEvent, eventLoading, appConfig, orgContextReady, orgLoading]);
235
+ }, [appName, logger, resetState, selectedEvent?.event_id, selectedOrganisation?.id, session, user, requiresEvent, eventLoading, appConfig, orgContextReady, orgLoading]);
236
236
 
237
237
  const hasGlobalPermission = useCallback(
238
238
  (permission: string): boolean => {
@@ -266,6 +266,7 @@ export function useRBAC(pageId?: string): UserRBACContext {
266
266
  requiresEvent,
267
267
  eventLoading,
268
268
  hasSelectedEvent: !!selectedEvent,
269
+ selectedEventId: selectedEvent?.event_id,
269
270
  hasUser: !!user,
270
271
  hasSession: !!session,
271
272
  hasSelectedOrganisation: !!selectedOrganisation,
@@ -274,7 +275,7 @@ export function useRBAC(pageId?: string): UserRBACContext {
274
275
  orgLoading
275
276
  });
276
277
  loadRBACContext();
277
- }, [loadRBACContext, appName, requiresEvent, eventLoading, selectedEvent, user, session, selectedOrganisation, orgContextReady, orgLoading]);
278
+ }, [loadRBACContext, appName, requiresEvent, eventLoading, selectedEvent?.event_id, user, session, selectedOrganisation?.id, orgContextReady, orgLoading]);
278
279
 
279
280
  return {
280
281
  user,
@@ -137,32 +137,33 @@ describe('useResourcePermissions Hook', () => {
137
137
  renderHook(() => useResourcePermissions('contacts'));
138
138
 
139
139
  expect(mockUseCan).toHaveBeenCalledTimes(4); // create, update, delete, read
140
+ // When appId is available in scope, resource name is passed as pageId to enable page permission checks
140
141
  expect(mockUseCan).toHaveBeenCalledWith(
141
142
  'user-123',
142
143
  mockScope,
143
144
  'create:contacts',
144
- undefined,
145
+ 'contacts', // pageId is resource name when appId is available
145
146
  true
146
147
  );
147
148
  expect(mockUseCan).toHaveBeenCalledWith(
148
149
  'user-123',
149
150
  mockScope,
150
151
  'update:contacts',
151
- undefined,
152
+ 'contacts', // pageId is resource name when appId is available
152
153
  true
153
154
  );
154
155
  expect(mockUseCan).toHaveBeenCalledWith(
155
156
  'user-123',
156
157
  mockScope,
157
158
  'delete:contacts',
158
- undefined,
159
+ 'contacts', // pageId is resource name when appId is available
159
160
  true
160
161
  );
161
162
  expect(mockUseCan).toHaveBeenCalledWith(
162
163
  'user-123',
163
164
  mockScope,
164
165
  'read:contacts',
165
- undefined,
166
+ 'contacts', // pageId is resource name when appId is available
166
167
  true
167
168
  );
168
169
  });
@@ -294,7 +295,7 @@ describe('useResourcePermissions Hook', () => {
294
295
  expect.any(String),
295
296
  resolvedScope,
296
297
  expect.any(String),
297
- undefined,
298
+ 'contacts', // pageId is resource name when appId is available
298
299
  true
299
300
  );
300
301
  });
@@ -589,7 +590,7 @@ describe('useResourcePermissions Hook', () => {
589
590
  expect.any(String),
590
591
  expect.any(Object),
591
592
  'read:contacts',
592
- undefined,
593
+ 'contacts', // pageId is resource name when appId is available
593
594
  true
594
595
  );
595
596
  });
@@ -610,21 +611,75 @@ describe('useResourcePermissions Hook', () => {
610
611
  expect.any(String),
611
612
  expect.any(Object),
612
613
  'create:risks',
613
- undefined,
614
+ 'risks', // pageId is resource name when appId is available
614
615
  true
615
616
  );
616
617
  expect(mockUseCan).toHaveBeenCalledWith(
617
618
  expect.any(String),
618
619
  expect.any(Object),
619
620
  'update:risks',
620
- undefined,
621
+ 'risks', // pageId is resource name when appId is available
621
622
  true
622
623
  );
623
624
  expect(mockUseCan).toHaveBeenCalledWith(
624
625
  expect.any(String),
625
626
  expect.any(Object),
626
627
  'delete:risks',
627
- undefined,
628
+ 'risks', // pageId is resource name when appId is available
629
+ true
630
+ );
631
+ });
632
+ });
633
+
634
+ describe('Page Permission Support', () => {
635
+ it('passes resource name as pageId when appId is available in scope', () => {
636
+ const scopeWithAppId: Scope = {
637
+ organisationId: 'org-123',
638
+ eventId: 'event-123',
639
+ appId: 'app-123',
640
+ };
641
+
642
+ mockUseResolvedScope.mockReturnValue({
643
+ resolvedScope: scopeWithAppId,
644
+ isLoading: false,
645
+ error: null,
646
+ });
647
+
648
+ renderHook(() => useResourcePermissions('planning'));
649
+
650
+ // When appId is available, resource name should be passed as pageId
651
+ // This enables the RPC function to check page permissions
652
+ expect(mockUseCan).toHaveBeenCalledWith(
653
+ 'user-123',
654
+ scopeWithAppId,
655
+ 'create:planning',
656
+ 'planning', // Resource name passed as pageId to enable page permission checks
657
+ true
658
+ );
659
+ });
660
+
661
+ it('does not pass pageId when appId is not available', () => {
662
+ const scopeWithoutAppId: Scope = {
663
+ organisationId: 'org-123',
664
+ eventId: 'event-123',
665
+ appId: undefined,
666
+ };
667
+
668
+ mockUseResolvedScope.mockReturnValue({
669
+ resolvedScope: scopeWithoutAppId,
670
+ isLoading: false,
671
+ error: null,
672
+ });
673
+
674
+ renderHook(() => useResourcePermissions('planning'));
675
+
676
+ // When appId is not available, pageId should be undefined
677
+ // This falls back to resource-based permission checking
678
+ expect(mockUseCan).toHaveBeenCalledWith(
679
+ 'user-123',
680
+ scopeWithoutAppId,
681
+ 'create:planning',
682
+ undefined, // No pageId when appId is not available
628
683
  true
629
684
  );
630
685
  });
@@ -79,7 +79,19 @@ export interface ResourcePermissions {
79
79
  * permissions for a resource type. It handles scope resolution, user context,
80
80
  * and provides a simple API for permission checking.
81
81
  *
82
- * @param resource - The resource name (e.g., 'contacts', 'risks', 'journal')
82
+ * **Page Permission Support:**
83
+ * When an `appId` is available in the resolved scope, the resource name is passed
84
+ * as `pageId` to enable page-based permission checks. This allows the hook to work
85
+ * with both resource-based permissions (when appId is not available) and page-based
86
+ * permissions (when appId is available and the resource is a registered page).
87
+ *
88
+ * The RPC function `rbac_check_permission_simplified` will automatically resolve
89
+ * the page name to a page ID and check page permissions if the resource matches
90
+ * a registered page in `rbac_app_pages`. If the resource is not a registered page,
91
+ * it will fall back to resource-based permission checking.
92
+ *
93
+ * @param resource - The resource name (e.g., 'contacts', 'risks', 'planning')
94
+ * Can be a resource name or a page name registered in rbac_app_pages
83
95
  * @param options - Optional configuration
84
96
  * @param options.enableRead - Whether to check read permissions (default: false)
85
97
  * @param options.requireScope - Whether scope resolution is required (default: true)
@@ -98,6 +110,23 @@ export interface ResourcePermissions {
98
110
  * };
99
111
  * }
100
112
  * ```
113
+ *
114
+ * @example
115
+ * ```tsx
116
+ * // Works with page names when appId is available in scope
117
+ * function usePlanning() {
118
+ * const { canCreate, canUpdate, canDelete } = useResourcePermissions('planning');
119
+ *
120
+ * // Will check page permissions if 'planning' is registered in rbac_app_pages
121
+ * // Falls back to resource permissions if not a registered page
122
+ * const deleteItem = async (id: string) => {
123
+ * if (!canDelete('planning')) {
124
+ * throw new Error("Permission denied");
125
+ * }
126
+ * // ... perform deletion
127
+ * };
128
+ * }
129
+ * ```
101
130
  */
102
131
  export function useResourcePermissions(
103
132
  resource: string,
@@ -135,12 +164,18 @@ export function useResourcePermissions(
135
164
  appId: undefined
136
165
  };
137
166
 
167
+ // If we have an appId in scope, pass the resource name as pageId to enable page permission checks
168
+ // The RPC function rbac_check_permission_simplified will resolve the page name to a page ID
169
+ // and check page permissions if the resource is a registered page
170
+ // This allows useResourcePermissions to work with both resource-based and page-based permissions
171
+ const pageId = scope.appId ? resource : undefined;
172
+
138
173
  // Permission checks for create, update, delete
139
174
  const { can: canCreateResult, isLoading: createLoading, error: createError } = useCan(
140
175
  user?.id || '',
141
176
  scope,
142
177
  `create:${resource}` as const,
143
- undefined, // pageId
178
+ pageId, // Pass resource name as pageId when appId is available to enable page permission checks
144
179
  true // useCache
145
180
  );
146
181
 
@@ -148,7 +183,7 @@ export function useResourcePermissions(
148
183
  user?.id || '',
149
184
  scope,
150
185
  `update:${resource}` as const,
151
- undefined, // pageId
186
+ pageId, // Pass resource name as pageId when appId is available to enable page permission checks
152
187
  true // useCache
153
188
  );
154
189
 
@@ -156,7 +191,7 @@ export function useResourcePermissions(
156
191
  user?.id || '',
157
192
  scope,
158
193
  `delete:${resource}` as const,
159
- undefined, // pageId
194
+ pageId, // Pass resource name as pageId when appId is available to enable page permission checks
160
195
  true // useCache
161
196
  );
162
197
 
@@ -165,7 +200,7 @@ export function useResourcePermissions(
165
200
  user?.id || '',
166
201
  scope,
167
202
  `read:${resource}` as const,
168
- undefined, // pageId
203
+ pageId, // Pass resource name as pageId when appId is available to enable page permission checks
169
204
  true // useCache
170
205
  );
171
206