@jmruthers/pace-core 0.6.2 → 0.6.4
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.
- package/CHANGELOG.md +45 -0
- package/cursor-rules/00-pace-core-compliance.mdc +34 -2
- package/dist/{AuthService-BPvc3Ka0.d.ts → AuthService-Cb34EQs3.d.ts} +9 -1
- package/dist/{DataTable-TPTKCX4D.js → DataTable-E7YQZD7D.js} +9 -8
- package/dist/{PublicPageProvider-DC6kCaqf.d.ts → PublicPageProvider-DEMpysFR.d.ts} +45 -67
- package/dist/{UnifiedAuthProvider-CVcTjx-d.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +1 -8
- package/dist/{UnifiedAuthProvider-CH6Z342H.js → UnifiedAuthProvider-QPXO24B4.js} +5 -4
- package/dist/{api-MVVQZLJI.js → api-6LVZTHDS.js} +10 -10
- package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
- package/dist/chunk-36LVWXB2.js +227 -0
- package/dist/chunk-36LVWXB2.js.map +1 -0
- package/dist/{chunk-24UVZUZG.js → chunk-3LPHPB62.js} +129 -387
- package/dist/chunk-3LPHPB62.js.map +1 -0
- package/dist/{chunk-2UOI2FG5.js → chunk-5EC5MEWX.js} +4 -4
- package/dist/{chunk-3XC4CPTD.js → chunk-7JPAB3T5.js} +244 -5727
- package/dist/chunk-7JPAB3T5.js.map +1 -0
- package/dist/{chunk-6J4GEEJR.js → chunk-ATKZM7RX.js} +53 -27
- package/dist/chunk-ATKZM7RX.js.map +1 -0
- package/dist/{chunk-EHMR7VYL.js → chunk-AVMLPIM7.js} +443 -189
- package/dist/chunk-AVMLPIM7.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-NECFR5MM.js → chunk-I6DAQMWX.js} +575 -647
- package/dist/chunk-I6DAQMWX.js.map +1 -0
- package/dist/{chunk-F2IMUDXZ.js → chunk-M7MPQISP.js} +2 -2
- package/dist/{chunk-XWQCNGTQ.js → chunk-NN6WWZ5U.js} +173 -79
- package/dist/chunk-NN6WWZ5U.js.map +1 -0
- package/dist/{chunk-MMZ7JXPU.js → chunk-OEWDTMG7.js} +13 -21
- package/dist/{chunk-MMZ7JXPU.js.map → chunk-OEWDTMG7.js.map} +1 -1
- package/dist/{chunk-SFZUDBL5.js → chunk-YKRAFF5K.js} +70 -56
- package/dist/chunk-YKRAFF5K.js.map +1 -0
- package/dist/components.d.ts +2 -2
- package/dist/components.js +12 -13
- package/dist/contextValidator-OOPCLPZW.js +9 -0
- package/dist/contextValidator-OOPCLPZW.js.map +1 -0
- package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +7 -6
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +21 -16
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +4 -3
- package/dist/rbac/index.d.ts +67 -27
- package/dist/rbac/index.js +15 -8
- package/dist/styles/index.js +1 -1
- package/dist/theming/runtime.js +1 -1
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-1oMokgLF.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +7 -16
- package/dist/utils.js +5 -7
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +14 -16
- package/docs/api/modules.md +3796 -2513
- package/docs/components/context-selector.md +126 -0
- package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
- package/docs/pace-mint-fix-auto-selection.md +218 -0
- package/docs/pace-mint-rbac-setup.md +391 -0
- package/docs/rbac/secure-client-protection.md +330 -0
- package/package.json +10 -5
- package/scripts/audit/core/checks/compliance.cjs +72 -0
- package/scripts/audit/core/checks/dependencies.cjs +568 -28
- package/scripts/audit/core/checks/documentation.cjs +68 -3
- package/scripts/audit/core/checks/environment.cjs +2 -14
- package/scripts/audit/core/checks/error-handling.cjs +47 -6
- package/src/components/ContextSelector/ContextSelector.tsx +384 -0
- package/src/components/ContextSelector/index.ts +3 -0
- package/src/components/DataTable/components/RowComponent.tsx +19 -19
- package/src/components/DataTable/components/UnifiedTableBody.tsx +2 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +8 -6
- package/src/components/Dialog/Dialog.tsx +29 -1
- package/src/components/FileDisplay/FileDisplay.tsx +42 -10
- package/src/components/Header/Header.test.tsx +43 -73
- package/src/components/Header/Header.tsx +44 -45
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +9 -9
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +157 -36
- package/src/components/PaceAppLayout/README.md +14 -17
- package/src/components/PaceAppLayout/test-setup.tsx +2 -2
- package/src/components/index.ts +5 -5
- package/src/eslint-rules/pace-core-compliance.cjs +106 -0
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
- package/src/hooks/useAppConfig.ts +15 -30
- package/src/hooks/useFileDisplay.ts +77 -50
- package/src/index.ts +4 -5
- package/src/providers/services/AuthServiceProvider.tsx +17 -7
- package/src/providers/services/EventServiceProvider.tsx +33 -5
- package/src/providers/services/UnifiedAuthProvider.tsx +90 -134
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
- package/src/rbac/adapters.tsx +2 -2
- package/src/rbac/api.test.ts +59 -51
- package/src/rbac/api.ts +178 -132
- package/src/rbac/components/PagePermissionGuard.tsx +38 -10
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
- package/src/rbac/hooks/permissions/useAccessLevel.ts +1 -1
- package/src/rbac/hooks/permissions/useCan.ts +41 -11
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +1 -1
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +1 -1
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +1 -1
- package/src/rbac/hooks/useCan.test.ts +0 -9
- package/src/rbac/hooks/useRBAC.test.ts +1 -5
- package/src/rbac/hooks/useRBAC.ts +36 -37
- package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
- package/src/rbac/hooks/useResolvedScope.ts +35 -40
- package/src/rbac/hooks/useSecureSupabase.ts +7 -7
- package/src/rbac/index.ts +7 -0
- package/src/rbac/secureClient.test.ts +22 -18
- package/src/rbac/secureClient.ts +103 -16
- package/src/rbac/security.ts +0 -17
- package/src/rbac/types.ts +1 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
- package/src/rbac/utils/clientSecurity.ts +93 -0
- package/src/rbac/utils/contextValidator.ts +77 -168
- package/src/services/AuthService.ts +39 -7
- package/src/services/EventService.ts +285 -56
- package/src/services/OrganisationService.ts +81 -14
- package/src/services/__tests__/EventService.test.ts +1 -2
- package/src/services/base/BaseService.ts +3 -0
- package/src/utils/dynamic/dynamicUtils.ts +7 -4
- package/dist/chunk-24UVZUZG.js.map +0 -1
- package/dist/chunk-3XC4CPTD.js.map +0 -1
- package/dist/chunk-6J4GEEJR.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js +0 -38
- package/dist/chunk-EHMR7VYL.js.map +0 -1
- package/dist/chunk-NECFR5MM.js.map +0 -1
- package/dist/chunk-SFZUDBL5.js.map +0 -1
- package/dist/chunk-XWQCNGTQ.js.map +0 -1
- package/docs/api/classes/ColumnFactory.md +0 -243
- package/docs/api/classes/InvalidScopeError.md +0 -73
- package/docs/api/classes/Logger.md +0 -178
- package/docs/api/classes/MissingUserContextError.md +0 -66
- package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
- package/docs/api/classes/PermissionDeniedError.md +0 -73
- package/docs/api/classes/RBACAuditManager.md +0 -297
- package/docs/api/classes/RBACCache.md +0 -322
- package/docs/api/classes/RBACEngine.md +0 -171
- package/docs/api/classes/RBACError.md +0 -76
- package/docs/api/classes/RBACNotInitializedError.md +0 -66
- package/docs/api/classes/SecureSupabaseClient.md +0 -163
- package/docs/api/classes/StorageUtils.md +0 -328
- package/docs/api/enums/FileCategory.md +0 -184
- package/docs/api/enums/LogLevel.md +0 -54
- package/docs/api/enums/RBACErrorCode.md +0 -228
- package/docs/api/enums/RPCFunction.md +0 -118
- package/docs/api/interfaces/AddressFieldProps.md +0 -241
- package/docs/api/interfaces/AddressFieldRef.md +0 -94
- package/docs/api/interfaces/AggregateConfig.md +0 -43
- package/docs/api/interfaces/AutocompleteOptions.md +0 -75
- package/docs/api/interfaces/AvatarProps.md +0 -128
- package/docs/api/interfaces/BadgeProps.md +0 -34
- package/docs/api/interfaces/ButtonProps.md +0 -56
- package/docs/api/interfaces/CalendarProps.md +0 -73
- package/docs/api/interfaces/CardProps.md +0 -69
- package/docs/api/interfaces/ColorPalette.md +0 -7
- package/docs/api/interfaces/ColorShade.md +0 -66
- package/docs/api/interfaces/ComplianceResult.md +0 -30
- package/docs/api/interfaces/DataAccessRecord.md +0 -96
- package/docs/api/interfaces/DataRecord.md +0 -11
- package/docs/api/interfaces/DataTableAction.md +0 -252
- package/docs/api/interfaces/DataTableColumn.md +0 -504
- package/docs/api/interfaces/DataTableProps.md +0 -625
- package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
- package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
- package/docs/api/interfaces/DatabaseIssue.md +0 -41
- package/docs/api/interfaces/EmptyStateConfig.md +0 -61
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
- package/docs/api/interfaces/ErrorBoundaryProps.md +0 -147
- package/docs/api/interfaces/ErrorBoundaryProviderProps.md +0 -36
- package/docs/api/interfaces/ErrorBoundaryState.md +0 -75
- package/docs/api/interfaces/EventAppRoleData.md +0 -71
- package/docs/api/interfaces/ExportColumn.md +0 -90
- package/docs/api/interfaces/ExportOptions.md +0 -126
- package/docs/api/interfaces/FileDisplayProps.md +0 -249
- package/docs/api/interfaces/FileMetadata.md +0 -129
- package/docs/api/interfaces/FileReference.md +0 -118
- package/docs/api/interfaces/FileSizeLimits.md +0 -7
- package/docs/api/interfaces/FileUploadOptions.md +0 -139
- package/docs/api/interfaces/FileUploadProps.md +0 -296
- package/docs/api/interfaces/FooterProps.md +0 -107
- package/docs/api/interfaces/FormFieldProps.md +0 -166
- package/docs/api/interfaces/FormProps.md +0 -113
- package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
- package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
- package/docs/api/interfaces/InputProps.md +0 -56
- package/docs/api/interfaces/LabelProps.md +0 -107
- package/docs/api/interfaces/LoggerConfig.md +0 -62
- package/docs/api/interfaces/LoginFormProps.md +0 -187
- package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
- package/docs/api/interfaces/NavigationContextType.md +0 -164
- package/docs/api/interfaces/NavigationGuardProps.md +0 -139
- package/docs/api/interfaces/NavigationItem.md +0 -120
- package/docs/api/interfaces/NavigationMenuProps.md +0 -221
- package/docs/api/interfaces/NavigationProviderProps.md +0 -117
- package/docs/api/interfaces/Organisation.md +0 -140
- package/docs/api/interfaces/OrganisationContextType.md +0 -388
- package/docs/api/interfaces/OrganisationMembership.md +0 -140
- package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
- package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
- package/docs/api/interfaces/PaceAppLayoutProps.md +0 -409
- package/docs/api/interfaces/PaceLoginPageProps.md +0 -49
- package/docs/api/interfaces/PageAccessRecord.md +0 -85
- package/docs/api/interfaces/PagePermissionContextType.md +0 -140
- package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
- package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
- package/docs/api/interfaces/PaletteData.md +0 -41
- package/docs/api/interfaces/ParsedAddress.md +0 -120
- package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
- package/docs/api/interfaces/ProgressProps.md +0 -42
- package/docs/api/interfaces/ProtectedRouteProps.md +0 -78
- package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
- package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
- package/docs/api/interfaces/PublicPageLayoutProps.md +0 -185
- package/docs/api/interfaces/QuickFix.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
- package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
- package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
- package/docs/api/interfaces/RBACConfig.md +0 -133
- package/docs/api/interfaces/RBACContext.md +0 -52
- package/docs/api/interfaces/RBACLogger.md +0 -112
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
- package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
- package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
- package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
- package/docs/api/interfaces/RBACResult.md +0 -58
- package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
- package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
- package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
- package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
- package/docs/api/interfaces/RBACRolesListParams.md +0 -52
- package/docs/api/interfaces/RBACRolesListResult.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
- package/docs/api/interfaces/ResourcePermissions.md +0 -155
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
- package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
- package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
- package/docs/api/interfaces/RoleManagementResult.md +0 -52
- package/docs/api/interfaces/RouteAccessRecord.md +0 -107
- package/docs/api/interfaces/RouteConfig.md +0 -134
- package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
- package/docs/api/interfaces/SecureDataContextType.md +0 -168
- package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
- package/docs/api/interfaces/SetupIssue.md +0 -41
- package/docs/api/interfaces/StorageConfig.md +0 -41
- package/docs/api/interfaces/StorageFileInfo.md +0 -74
- package/docs/api/interfaces/StorageFileMetadata.md +0 -151
- package/docs/api/interfaces/StorageListOptions.md +0 -99
- package/docs/api/interfaces/StorageListResult.md +0 -41
- package/docs/api/interfaces/StorageUploadOptions.md +0 -101
- package/docs/api/interfaces/StorageUploadResult.md +0 -63
- package/docs/api/interfaces/StorageUrlOptions.md +0 -60
- package/docs/api/interfaces/StyleImport.md +0 -19
- package/docs/api/interfaces/SwitchProps.md +0 -34
- package/docs/api/interfaces/TabsContentProps.md +0 -9
- package/docs/api/interfaces/TabsListProps.md +0 -9
- package/docs/api/interfaces/TabsProps.md +0 -9
- package/docs/api/interfaces/TabsTriggerProps.md +0 -50
- package/docs/api/interfaces/TextareaProps.md +0 -53
- package/docs/api/interfaces/ToastActionElement.md +0 -12
- package/docs/api/interfaces/ToastProps.md +0 -9
- package/docs/api/interfaces/UnifiedAuthContextType.md +0 -823
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -173
- package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
- package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -138
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -84
- package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
- package/docs/api/interfaces/UsePublicEventReturn.md +0 -71
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -123
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -97
- package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
- package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
- package/docs/api/interfaces/UserEventAccess.md +0 -121
- package/docs/api/interfaces/UserMenuProps.md +0 -88
- package/docs/api/interfaces/UserProfile.md +0 -63
- package/src/components/EventSelector/EventSelector.test.tsx +0 -720
- package/src/components/EventSelector/EventSelector.tsx +0 -423
- package/src/components/EventSelector/index.ts +0 -3
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -327
- package/src/components/OrganisationSelector/index.ts +0 -9
- /package/dist/{DataTable-TPTKCX4D.js.map → DataTable-E7YQZD7D.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-CH6Z342H.js.map → UnifiedAuthProvider-QPXO24B4.js.map} +0 -0
- /package/dist/{api-MVVQZLJI.js.map → api-6LVZTHDS.js.map} +0 -0
- /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
- /package/dist/{chunk-2UOI2FG5.js.map → chunk-5EC5MEWX.js.map} +0 -0
- /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/dist/{chunk-F2IMUDXZ.js.map → chunk-M7MPQISP.js.map} +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Context Selector - Unified Intelligent Selector
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `ContextSelector` (formerly `HybridContextSelector`) is the **single unified selector** for all pace-core applications. It intelligently shows all organisations and events a user can access based on their roles and permissions.
|
|
6
|
+
|
|
7
|
+
## Why Unified Selector?
|
|
8
|
+
|
|
9
|
+
Previously, apps had to choose between:
|
|
10
|
+
- `OrganisationSelector` - for org-based apps
|
|
11
|
+
- `EventSelector` - for event-based apps
|
|
12
|
+
- `HybridContextSelector` - for hybrid apps
|
|
13
|
+
|
|
14
|
+
This created complexity and required apps to know their scope type upfront. With page-level scope, we can now intelligently show **everything** the user can access in one place.
|
|
15
|
+
|
|
16
|
+
## How It Works
|
|
17
|
+
|
|
18
|
+
The `ContextSelector` automatically:
|
|
19
|
+
|
|
20
|
+
1. **Shows all accessible organisations** - Based on user's organisation roles
|
|
21
|
+
2. **Shows all accessible events** - Based on user's event-app roles and organisation membership
|
|
22
|
+
3. **Groups items intelligently** - Organisations and Events in separate sections
|
|
23
|
+
4. **Handles super admins** - Shows all orgs and events
|
|
24
|
+
5. **Respects permissions** - Only shows what user can actually access
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Basic Usage
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { ContextSelector } from '@jmruthers/pace-core';
|
|
32
|
+
|
|
33
|
+
function MyHeader() {
|
|
34
|
+
const { switchOrganisation } = useOrganisations();
|
|
35
|
+
const { events, setSelectedEvent } = useEvents();
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<ContextSelector
|
|
39
|
+
onOrganisationSelect={(org) => switchOrganisation(org.id)}
|
|
40
|
+
onEventSelect={(event) => {
|
|
41
|
+
const fullEvent = events.find(e => e.event_id === event.event_id);
|
|
42
|
+
setSelectedEvent(fullEvent || event);
|
|
43
|
+
}}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### In Header Component
|
|
50
|
+
|
|
51
|
+
The `Header` component now uses `ContextSelector` by default:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<Header
|
|
55
|
+
logoUrl="/logo.svg"
|
|
56
|
+
showContextSelector={true} // Default: shows unified selector
|
|
57
|
+
user={user}
|
|
58
|
+
onSignOut={handleSignOut}
|
|
59
|
+
/>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### In PaceAppLayout
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
<PaceAppLayout
|
|
66
|
+
appName="MINT"
|
|
67
|
+
showContextSelector={true} // Default: shows unified selector
|
|
68
|
+
navItems={navItems}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</PaceAppLayout>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Migration from Separate Selectors
|
|
75
|
+
|
|
76
|
+
### Before (Separate Selectors)
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
<Header
|
|
80
|
+
showOrgSelector={true}
|
|
81
|
+
showEventSelector={true}
|
|
82
|
+
// ...
|
|
83
|
+
/>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### After (Unified Selector)
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
<Header
|
|
90
|
+
showContextSelector={true} // Single prop replaces both
|
|
91
|
+
// ...
|
|
92
|
+
/>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Benefits
|
|
96
|
+
|
|
97
|
+
1. **Simpler API** - One selector instead of three
|
|
98
|
+
2. **Better UX** - Users see everything they can access in one place
|
|
99
|
+
3. **Intelligent** - Automatically determines what to show based on permissions
|
|
100
|
+
4. **Future-proof** - Works with any app configuration (org-based, event-based, hybrid)
|
|
101
|
+
5. **No configuration needed** - Just use it, it figures out what to show
|
|
102
|
+
|
|
103
|
+
## Migration from Separate Selectors
|
|
104
|
+
|
|
105
|
+
**⚠️ BREAKING CHANGE**: The separate selectors have been removed. You must migrate to `ContextSelector`.
|
|
106
|
+
|
|
107
|
+
### Removed Components
|
|
108
|
+
- `OrganisationSelector` - Use `ContextSelector` instead
|
|
109
|
+
- `EventSelector` - Use `ContextSelector` instead
|
|
110
|
+
- `HybridContextSelector` - Use `ContextSelector` instead
|
|
111
|
+
|
|
112
|
+
### Removed Props
|
|
113
|
+
- `Header.showEventSelector` - Use `showContextSelector` instead
|
|
114
|
+
- `Header.showOrgSelector` - Use `showContextSelector` instead
|
|
115
|
+
- `PaceAppLayout.showEventSelector` - Use `showContextSelector` instead
|
|
116
|
+
- `PaceAppLayout.showOrgSelector` - Use `showContextSelector` instead
|
|
117
|
+
|
|
118
|
+
## Implementation Details
|
|
119
|
+
|
|
120
|
+
The selector uses:
|
|
121
|
+
- `useOrganisations()` - Gets accessible organisations
|
|
122
|
+
- `useEvents()` - Gets accessible events (already filtered by permissions)
|
|
123
|
+
- `useRBAC()` - Checks super admin status
|
|
124
|
+
|
|
125
|
+
Both hooks already filter based on user permissions, so the selector automatically shows the correct items.
|
|
126
|
+
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# RBAC Scope Migration Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This migration moves RBAC scope from app-level (`rbac_apps.requires_event`) to page-level (`rbac_app_pages.scope_type`). This enables hybrid apps like `pace-mint` to have both event-based and organisation-based pages within the same app.
|
|
6
|
+
|
|
7
|
+
**⚠️ BREAKING CHANGE**: This migration removes app-level scope configuration entirely. All scope is now configured at the page level with no backward compatibility.
|
|
8
|
+
|
|
9
|
+
## What Changed
|
|
10
|
+
|
|
11
|
+
**Removed**: App-level scope configuration (`rbac_apps.requires_event`)
|
|
12
|
+
**Now**: Page-level scope only (`rbac_app_pages.scope_type`)
|
|
13
|
+
|
|
14
|
+
### Why This Change?
|
|
15
|
+
|
|
16
|
+
1. **Single Source of Truth**: Scope is stored in one place (page-level), making it easier to understand and maintain
|
|
17
|
+
2. **More Flexible**: Each page can have its own scope, enabling hybrid apps like pace-mint
|
|
18
|
+
3. **Simpler Queries**: No need to join with `rbac_apps` to determine scope
|
|
19
|
+
4. **More Explicit**: No inheritance logic - scope is always clear from the page itself
|
|
20
|
+
5. **No Backward Compatibility Overhead**: Clean break from old patterns
|
|
21
|
+
|
|
22
|
+
## Breaking Changes
|
|
23
|
+
|
|
24
|
+
### Removed APIs
|
|
25
|
+
|
|
26
|
+
1. **`UnifiedAuthProvider.appConfig` prop** - No longer needed
|
|
27
|
+
2. **`useAppConfig().requiresEvent`** - Use `getPageScopeType(pageId)` instead
|
|
28
|
+
3. **`useAppConfig().supportsDirectAccess`** - No longer relevant
|
|
29
|
+
4. **`ContextValidator.validateScope()`** - Use `resolveScopeForPage()` instead
|
|
30
|
+
5. **`ContextValidator.resolveRequiredContext()`** - Use `resolveScopeForPage()` instead
|
|
31
|
+
6. **`ContextValidator.isContextReady()`** - Context readiness checked per page
|
|
32
|
+
7. **`RBACSecurityValidator.validateContextRequirements()`** - No longer needed
|
|
33
|
+
8. **`getAppConfig()`, `getAppConfigByName()`, `getAppConfigWithClient()`** - Use `getPageScopeType()` instead
|
|
34
|
+
|
|
35
|
+
### Changed APIs
|
|
36
|
+
|
|
37
|
+
1. **`useRBAC(pageId)`** - Now requires `pageId` parameter for proper scope resolution
|
|
38
|
+
2. **`getPermissionMap()`, `getRoleContext()`, `getAccessLevel()`, `isPermitted()`** - No longer accept `appConfig` parameter
|
|
39
|
+
|
|
40
|
+
### Removed Components
|
|
41
|
+
|
|
42
|
+
- `OrganisationSelector` - Use `ContextSelector` instead
|
|
43
|
+
- `EventSelector` - Use `ContextSelector` instead
|
|
44
|
+
- `HybridContextSelector` - Use `ContextSelector` instead
|
|
45
|
+
|
|
46
|
+
### Removed Props
|
|
47
|
+
|
|
48
|
+
- `Header.showEventSelector` - Use `showContextSelector` instead
|
|
49
|
+
- `Header.showOrgSelector` - Use `showContextSelector` instead
|
|
50
|
+
- `PaceAppLayout.showEventSelector` - Use `showContextSelector` instead
|
|
51
|
+
- `PaceAppLayout.showOrgSelector` - Use `showContextSelector` instead
|
|
52
|
+
|
|
53
|
+
## Migration Steps
|
|
54
|
+
|
|
55
|
+
### Step 1: Run Database Migration
|
|
56
|
+
|
|
57
|
+
Run the migration to populate `rbac_app_pages.scope_type`:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
supabase migration up
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Migration 1:** `supabase/migrations/20251231124932_add_page_scope_type_to_rbac_app_pages.sql`
|
|
64
|
+
- Adds `scope_type` column to `rbac_app_pages`
|
|
65
|
+
- Populates `scope_type` for all existing pages based on their app's `requires_event`
|
|
66
|
+
- Makes `scope_type` NOT NULL (required for all pages)
|
|
67
|
+
- Creates `get_page_scope_type()` function
|
|
68
|
+
- **Drops `rbac_apps.requires_event` column** (no longer needed)
|
|
69
|
+
|
|
70
|
+
**Migration 2:** `supabase/migrations/20251231131610_update_rpc_functions_remove_requires_event.sql`
|
|
71
|
+
- Updates `data_rbac_apps_list` to remove `requires_event` from return type
|
|
72
|
+
- Updates `data_user_events_get` to remove `requires_event` check (always checks event app roles if app_id provided)
|
|
73
|
+
|
|
74
|
+
### Step 2: Verify Page Scope Types
|
|
75
|
+
|
|
76
|
+
Check that all pages have `scope_type` set:
|
|
77
|
+
|
|
78
|
+
```sql
|
|
79
|
+
-- Check for any pages without scope_type (should return 0 rows after migration)
|
|
80
|
+
SELECT
|
|
81
|
+
ap.page_name,
|
|
82
|
+
ap.scope_type,
|
|
83
|
+
a.name as app_name
|
|
84
|
+
FROM rbac_app_pages ap
|
|
85
|
+
JOIN rbac_apps a ON ap.app_id = a.id
|
|
86
|
+
WHERE ap.scope_type IS NULL;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If any pages have `NULL` scope_type after migration, update them:
|
|
90
|
+
|
|
91
|
+
```sql
|
|
92
|
+
-- For event-based pages
|
|
93
|
+
UPDATE rbac_app_pages
|
|
94
|
+
SET scope_type = 'event'
|
|
95
|
+
WHERE page_name = 'your-page-name'
|
|
96
|
+
AND app_id = (SELECT id FROM rbac_apps WHERE name = 'YOUR_APP');
|
|
97
|
+
|
|
98
|
+
-- For organisation-based pages
|
|
99
|
+
UPDATE rbac_app_pages
|
|
100
|
+
SET scope_type = 'organisation'
|
|
101
|
+
WHERE page_name = 'your-page-name'
|
|
102
|
+
AND app_id = (SELECT id FROM rbac_apps WHERE name = 'YOUR_APP');
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Step 3: Update UnifiedAuthProvider
|
|
106
|
+
|
|
107
|
+
**Before:**
|
|
108
|
+
```tsx
|
|
109
|
+
<UnifiedAuthProvider
|
|
110
|
+
appConfig={{ requires_event: true }}
|
|
111
|
+
// ... other props
|
|
112
|
+
>
|
|
113
|
+
{children}
|
|
114
|
+
</UnifiedAuthProvider>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**After:**
|
|
118
|
+
```tsx
|
|
119
|
+
<UnifiedAuthProvider
|
|
120
|
+
// appConfig prop removed - scope is now page-level
|
|
121
|
+
// ... other props
|
|
122
|
+
>
|
|
123
|
+
{children}
|
|
124
|
+
</UnifiedAuthProvider>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Step 4: Update useRBAC Calls
|
|
128
|
+
|
|
129
|
+
**Before:**
|
|
130
|
+
```tsx
|
|
131
|
+
const { permissionMap } = useRBAC(); // No pageId
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**After:**
|
|
135
|
+
```tsx
|
|
136
|
+
const { permissionMap } = useRBAC(pageId); // Pass pageId for proper scope resolution
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Step 5: Replace useAppConfig Usage
|
|
140
|
+
|
|
141
|
+
**Before:**
|
|
142
|
+
```tsx
|
|
143
|
+
const { requiresEvent, supportsDirectAccess } = useAppConfig();
|
|
144
|
+
|
|
145
|
+
if (requiresEvent) {
|
|
146
|
+
// Show event selector
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**After:**
|
|
151
|
+
```tsx
|
|
152
|
+
import { getPageScopeType } from '@jmruthers/pace-core';
|
|
153
|
+
|
|
154
|
+
const pageScopeType = await getPageScopeType(pageId, appId, appName);
|
|
155
|
+
|
|
156
|
+
if (pageScopeType === 'event' || pageScopeType === 'both') {
|
|
157
|
+
// Show event selector (or use ContextSelector which handles this automatically)
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Step 6: Update ContextValidator Usage
|
|
162
|
+
|
|
163
|
+
**Before:**
|
|
164
|
+
```tsx
|
|
165
|
+
import { ContextValidator } from '@jmruthers/pace-core';
|
|
166
|
+
|
|
167
|
+
const validation = await ContextValidator.resolveRequiredContext(
|
|
168
|
+
scope,
|
|
169
|
+
appConfig,
|
|
170
|
+
appName,
|
|
171
|
+
supabase
|
|
172
|
+
);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**After:**
|
|
176
|
+
```tsx
|
|
177
|
+
import { ContextValidator, getPageScopeType } from '@jmruthers/pace-core';
|
|
178
|
+
|
|
179
|
+
const pageScopeType = await getPageScopeType(pageId, appId, appName);
|
|
180
|
+
const validation = await ContextValidator.resolveScopeForPage(
|
|
181
|
+
scope,
|
|
182
|
+
pageScopeType,
|
|
183
|
+
appName,
|
|
184
|
+
supabase
|
|
185
|
+
);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Step 7: Update Permission Checks
|
|
189
|
+
|
|
190
|
+
**Before:**
|
|
191
|
+
```tsx
|
|
192
|
+
const permissionMap = await getPermissionMap(
|
|
193
|
+
{ userId, scope },
|
|
194
|
+
appConfig,
|
|
195
|
+
appName
|
|
196
|
+
);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**After:**
|
|
200
|
+
```tsx
|
|
201
|
+
const permissionMap = await getPermissionMap(
|
|
202
|
+
{ userId, scope }
|
|
203
|
+
// appConfig and appName no longer needed
|
|
204
|
+
);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Step 8: Update Selector Components
|
|
208
|
+
|
|
209
|
+
**Before:**
|
|
210
|
+
```tsx
|
|
211
|
+
<Header
|
|
212
|
+
showOrgSelector={true}
|
|
213
|
+
showEventSelector={true}
|
|
214
|
+
/>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**After:**
|
|
218
|
+
```tsx
|
|
219
|
+
<Header
|
|
220
|
+
showContextSelector={true} // Single unified selector
|
|
221
|
+
/>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Page Scope Types
|
|
225
|
+
|
|
226
|
+
After migration, all pages must have one of these `scope_type` values:
|
|
227
|
+
|
|
228
|
+
### `'event'`
|
|
229
|
+
Page requires event context. Organisation is derived from event.
|
|
230
|
+
|
|
231
|
+
### `'organisation'`
|
|
232
|
+
Page requires organisation context. Event is optional.
|
|
233
|
+
|
|
234
|
+
### `'both'`
|
|
235
|
+
Page can be accessed with either event or organisation context. Permissions are merged (union), with higher permissions taking precedence.
|
|
236
|
+
|
|
237
|
+
## Creating New Pages
|
|
238
|
+
|
|
239
|
+
When creating new pages, always set `scope_type` explicitly:
|
|
240
|
+
|
|
241
|
+
```sql
|
|
242
|
+
-- Create an event-based page
|
|
243
|
+
INSERT INTO rbac_app_pages (app_id, page_name, page_description, scope_type)
|
|
244
|
+
VALUES (
|
|
245
|
+
(SELECT id FROM rbac_apps WHERE name = 'MINT'),
|
|
246
|
+
'budget',
|
|
247
|
+
'Event Budget Management',
|
|
248
|
+
'event' -- ✅ Explicit scope type
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
-- Create an organisation-based page
|
|
252
|
+
INSERT INTO rbac_app_pages (app_id, page_name, page_description, scope_type)
|
|
253
|
+
VALUES (
|
|
254
|
+
(SELECT id FROM rbac_apps WHERE name = 'MINT'),
|
|
255
|
+
'dashboard',
|
|
256
|
+
'Organisation Dashboard',
|
|
257
|
+
'organisation' -- ✅ Explicit scope type
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
-- Create a 'both' page (can use either context)
|
|
261
|
+
INSERT INTO rbac_app_pages (app_id, page_name, page_description, scope_type)
|
|
262
|
+
VALUES (
|
|
263
|
+
(SELECT id FROM rbac_apps WHERE name = 'MINT'),
|
|
264
|
+
'reports',
|
|
265
|
+
'Reports (Event or Organisation)',
|
|
266
|
+
'both' -- ✅ Can be accessed with either context
|
|
267
|
+
);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Hybrid Apps (e.g., pace-mint)
|
|
271
|
+
|
|
272
|
+
For apps that have both event-based and organisation-based pages:
|
|
273
|
+
|
|
274
|
+
1. Set `scope_type = 'event'` for event-based pages (e.g., budget pages)
|
|
275
|
+
2. Set `scope_type = 'organisation'` for organisation-based pages (e.g., settings pages)
|
|
276
|
+
3. Set `scope_type = 'both'` for pages that can use either context
|
|
277
|
+
|
|
278
|
+
Example:
|
|
279
|
+
```sql
|
|
280
|
+
-- Event-based budget page
|
|
281
|
+
UPDATE rbac_app_pages
|
|
282
|
+
SET scope_type = 'event'
|
|
283
|
+
WHERE page_name = 'budget' AND app_id = (SELECT id FROM rbac_apps WHERE name = 'MINT');
|
|
284
|
+
|
|
285
|
+
-- Organisation-based settings page
|
|
286
|
+
UPDATE rbac_app_pages
|
|
287
|
+
SET scope_type = 'organisation'
|
|
288
|
+
WHERE page_name = 'settings' AND app_id = (SELECT id FROM rbac_apps WHERE name = 'MINT');
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## API Changes Summary
|
|
292
|
+
|
|
293
|
+
### `getPageScopeType()`
|
|
294
|
+
|
|
295
|
+
**Before**: Could return `null` (inherit from app)
|
|
296
|
+
|
|
297
|
+
**After**: Always returns a value (`'event'`, `'organisation'`, or `'both'`)
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
// After migration, this always returns a value
|
|
301
|
+
const scopeType = await getPageScopeType(pageId, appId, appName);
|
|
302
|
+
// scopeType is now: 'event' | 'organisation' | 'both' (never null)
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### `resolveScopeForPage()`
|
|
306
|
+
|
|
307
|
+
**Before**: Accepted `appConfig` parameter and could inherit from app
|
|
308
|
+
|
|
309
|
+
**After**: `pageScopeType` is required (never null), `appConfig` removed
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
// After migration
|
|
313
|
+
const validation = await ContextValidator.resolveScopeForPage(
|
|
314
|
+
scope,
|
|
315
|
+
pageScopeType, // Required, never null
|
|
316
|
+
appName,
|
|
317
|
+
supabase
|
|
318
|
+
);
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Benefits
|
|
322
|
+
|
|
323
|
+
1. **Clearer Intent**: Each page explicitly declares its scope requirements
|
|
324
|
+
2. **Easier Debugging**: No need to trace through app-level config to understand page scope
|
|
325
|
+
3. **Better Performance**: Direct lookup from page table, no join needed
|
|
326
|
+
4. **More Flexible**: Hybrid apps can have mixed scopes without special handling
|
|
327
|
+
5. **No Backward Compatibility Overhead**: Clean, maintainable codebase
|
|
328
|
+
|
|
329
|
+
## Testing Checklist
|
|
330
|
+
|
|
331
|
+
- [ ] Database migration completed successfully
|
|
332
|
+
- [ ] All pages have `scope_type` set (no NULL values)
|
|
333
|
+
- [ ] `UnifiedAuthProvider` no longer uses `appConfig` prop
|
|
334
|
+
- [ ] All `useRBAC()` calls pass `pageId` parameter
|
|
335
|
+
- [ ] All `useAppConfig().requiresEvent` replaced with `getPageScopeType()`
|
|
336
|
+
- [ ] All deprecated `ContextValidator` methods replaced
|
|
337
|
+
- [ ] All selector components updated to use `ContextSelector`
|
|
338
|
+
- [ ] Permission checks work correctly for all page types
|
|
339
|
+
- [ ] Event-based pages work correctly
|
|
340
|
+
- [ ] Organisation-based pages work correctly
|
|
341
|
+
- [ ] Hybrid pages (scope_type = 'both') work correctly
|
|
342
|
+
|
|
343
|
+
## Troubleshooting
|
|
344
|
+
|
|
345
|
+
### Pages Not Showing in Navigation
|
|
346
|
+
|
|
347
|
+
If pages aren't showing after migration:
|
|
348
|
+
|
|
349
|
+
1. **Check scope_type is set**:
|
|
350
|
+
```sql
|
|
351
|
+
SELECT page_name, scope_type FROM rbac_app_pages WHERE app_id = 'your-app-id';
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
2. **Verify scope matches context**:
|
|
355
|
+
- Event-based pages require `eventId` to be selected
|
|
356
|
+
- Organisation-based pages require `organisationId` to be selected
|
|
357
|
+
- 'both' pages work with either context
|
|
358
|
+
|
|
359
|
+
3. **Check permissions**: Ensure user has permissions for the page in the correct scope
|
|
360
|
+
|
|
361
|
+
### Permission Denied Errors
|
|
362
|
+
|
|
363
|
+
If you get permission denied errors:
|
|
364
|
+
|
|
365
|
+
1. **Verify scope_type matches selected context**:
|
|
366
|
+
- If page is `'event'`, ensure an event is selected
|
|
367
|
+
- If page is `'organisation'`, ensure an organisation is selected
|
|
368
|
+
|
|
369
|
+
2. **Check page permissions are configured** for the correct scope type
|
|
370
|
+
|
|
371
|
+
3. **Verify user's roles are active** (valid_from/valid_to dates)
|
|
372
|
+
|
|
373
|
+
### ContextSelector Not Showing Items
|
|
374
|
+
|
|
375
|
+
1. Check user has organisation memberships or event-app roles
|
|
376
|
+
2. Verify organisations/events are active
|
|
377
|
+
3. Check RLS policies allow user to see the items
|
|
378
|
+
4. Ensure super admin status if testing as super admin
|
|
379
|
+
|
|
380
|
+
## Related Documentation
|
|
381
|
+
|
|
382
|
+
- [pace-mint RBAC Setup Guide](../pace-mint-rbac-setup.md) - Specific guide for hybrid apps
|
|
383
|
+
- [Context Selector Component](../components/context-selector.md) - Unified selector documentation
|
|
384
|
+
- [RBAC System Overview](../rbac/README.md) - General RBAC documentation
|
|
385
|
+
- [Event-Based Apps](../rbac/event-based-apps.md) - Event-based app patterns
|