@jmruthers/pace-core 0.6.2 → 0.6.3
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-THFPBKTP.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-KAGUYQ4J.js} +5 -4
- package/dist/{api-MVVQZLJI.js → api-IAGWF3ZG.js} +10 -10
- package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
- package/dist/{chunk-SFZUDBL5.js → chunk-2T2IG7T7.js} +70 -56
- package/dist/chunk-2T2IG7T7.js.map +1 -0
- package/dist/{chunk-MMZ7JXPU.js → chunk-6Z7LTB3D.js} +13 -21
- package/dist/{chunk-MMZ7JXPU.js.map → chunk-6Z7LTB3D.js.map} +1 -1
- package/dist/{chunk-6J4GEEJR.js → chunk-CNCQDFLN.js} +53 -27
- package/dist/chunk-CNCQDFLN.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-EHMR7VYL.js → chunk-DWUBLJJM.js} +361 -187
- package/dist/chunk-DWUBLJJM.js.map +1 -0
- package/dist/{chunk-2UOI2FG5.js → chunk-HFZBI76P.js} +4 -4
- package/dist/{chunk-F2IMUDXZ.js → chunk-M7MPQISP.js} +2 -2
- package/dist/{chunk-3XC4CPTD.js → chunk-PQBSKX33.js} +244 -5727
- package/dist/chunk-PQBSKX33.js.map +1 -0
- package/dist/chunk-QRPVRXYT.js +226 -0
- package/dist/chunk-QRPVRXYT.js.map +1 -0
- package/dist/{chunk-24UVZUZG.js → chunk-RWEBCB47.js} +129 -387
- package/dist/chunk-RWEBCB47.js.map +1 -0
- package/dist/{chunk-XWQCNGTQ.js → chunk-YDQHOZNA.js} +173 -79
- package/dist/chunk-YDQHOZNA.js.map +1 -0
- package/dist/{chunk-NECFR5MM.js → chunk-ZNIWI3UC.js} +562 -644
- package/dist/chunk-ZNIWI3UC.js.map +1 -0
- package/dist/components.d.ts +2 -2
- package/dist/components.js +12 -13
- package/dist/contextValidator-3JNZKUTX.js +9 -0
- package/dist/contextValidator-3JNZKUTX.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 +3 -3
- package/scripts/audit/core/checks/compliance.cjs +72 -0
- package/scripts/audit/core/checks/dependencies.cjs +559 -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 +135 -33
- 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 +186 -54
- 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-THFPBKTP.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-CH6Z342H.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
- /package/dist/{api-MVVQZLJI.js.map → api-IAGWF3ZG.js.map} +0 -0
- /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
- /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/dist/{chunk-2UOI2FG5.js.map → chunk-HFZBI76P.js.map} +0 -0
- /package/dist/{chunk-F2IMUDXZ.js.map → chunk-M7MPQISP.js.map} +0 -0
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Organisation Selector Component
|
|
3
|
-
* @package @jmruthers/pace-core
|
|
4
|
-
* @module Components/OrganisationSelector
|
|
5
|
-
* @since 0.4.0
|
|
6
|
-
*
|
|
7
|
-
* A secure organisation selector component that allows users to switch between organisations
|
|
8
|
-
* they have access to. Includes role display and security validation.
|
|
9
|
-
*
|
|
10
|
-
* Features:
|
|
11
|
-
* - Secure organisation switching with validation
|
|
12
|
-
* - Role display for each organisation
|
|
13
|
-
* - Real-time organisation validation
|
|
14
|
-
* - Accessible dropdown interface
|
|
15
|
-
* - Error handling for security violations
|
|
16
|
-
* - Loading states and feedback
|
|
17
|
-
* - Integration with OrganisationProvider
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```tsx
|
|
21
|
-
* // Basic organisation selector
|
|
22
|
-
* <OrganisationSelector
|
|
23
|
-
* onOrganisationChange={(org) => console.log('Switched to:', org.display_name)}
|
|
24
|
-
* />
|
|
25
|
-
*
|
|
26
|
-
* // Compact version for header
|
|
27
|
-
* <OrganisationSelector
|
|
28
|
-
* className="w-48"
|
|
29
|
-
* compact={true}
|
|
30
|
-
* showRole={true}
|
|
31
|
-
* />
|
|
32
|
-
*
|
|
33
|
-
* // With custom placeholder
|
|
34
|
-
* <OrganisationSelector
|
|
35
|
-
* placeholder="Choose organisation..."
|
|
36
|
-
* showNoOrganisationsMessage={true}
|
|
37
|
-
* />
|
|
38
|
-
* ```
|
|
39
|
-
*
|
|
40
|
-
* @accessibility
|
|
41
|
-
* - WCAG 2.1 AA compliant
|
|
42
|
-
* - Keyboard navigation support
|
|
43
|
-
* - Screen reader friendly
|
|
44
|
-
* - Focus management
|
|
45
|
-
* - ARIA labels and descriptions
|
|
46
|
-
* - High contrast support
|
|
47
|
-
*
|
|
48
|
-
* @security
|
|
49
|
-
* - Validates user access to organisations
|
|
50
|
-
* - Prevents switching to unauthorised organisations
|
|
51
|
-
* - Error handling for security violations
|
|
52
|
-
* - Real-time access validation
|
|
53
|
-
* - Secure organisation data handling
|
|
54
|
-
*/
|
|
55
|
-
|
|
56
|
-
import React, { useState, useCallback, useMemo } from 'react';
|
|
57
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../Select';
|
|
58
|
-
import { Alert, AlertDescription } from '../Alert/Alert';
|
|
59
|
-
import { Button } from '../Button/Button';
|
|
60
|
-
import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner';
|
|
61
|
-
import { RefreshCw, AlertCircle, Building2, Shield } from 'lucide-react';
|
|
62
|
-
import { useOrganisations } from '../../hooks/useOrganisations';
|
|
63
|
-
import type { Organisation } from '../../types/organisation';
|
|
64
|
-
import { logger } from '../../utils/core/logger';
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Props for the OrganisationSelector component.
|
|
68
|
-
*/
|
|
69
|
-
export interface OrganisationSelectorProps {
|
|
70
|
-
/** Placeholder text for the dropdown */
|
|
71
|
-
placeholder?: string;
|
|
72
|
-
/** Additional CSS classes */
|
|
73
|
-
className?: string;
|
|
74
|
-
/** Callback fired when organisation changes, providing full organisation object */
|
|
75
|
-
onOrganisationChange?: (org: Organisation) => void;
|
|
76
|
-
/** Show friendly message when no organisations available */
|
|
77
|
-
showNoOrganisationsMessage?: boolean;
|
|
78
|
-
/** Show retry button on errors */
|
|
79
|
-
showRetryButton?: boolean;
|
|
80
|
-
/** Show user's role in each organisation */
|
|
81
|
-
showRole?: boolean;
|
|
82
|
-
/** Compact display mode */
|
|
83
|
-
compact?: boolean;
|
|
84
|
-
/** Disabled state */
|
|
85
|
-
disabled?: boolean;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* OrganisationSelector component for secure organisation switching
|
|
90
|
-
*
|
|
91
|
-
* This component provides secure organisation selection with:
|
|
92
|
-
* - User membership validation
|
|
93
|
-
* - Role-based access display
|
|
94
|
-
* - Security error handling
|
|
95
|
-
* - Real-time organisation switching
|
|
96
|
-
* - Accessible interface design
|
|
97
|
-
*
|
|
98
|
-
* Security: Only shows organisations the user has valid access to
|
|
99
|
-
*/
|
|
100
|
-
export function OrganisationSelector({
|
|
101
|
-
placeholder = "Select organisation",
|
|
102
|
-
className,
|
|
103
|
-
onOrganisationChange,
|
|
104
|
-
showNoOrganisationsMessage = true,
|
|
105
|
-
showRetryButton = true,
|
|
106
|
-
showRole = false,
|
|
107
|
-
compact = false,
|
|
108
|
-
disabled = false
|
|
109
|
-
}: OrganisationSelectorProps) {
|
|
110
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
111
|
-
const [switchError, setSwitchError] = useState<string | null>(null);
|
|
112
|
-
|
|
113
|
-
const {
|
|
114
|
-
organisations,
|
|
115
|
-
selectedOrganisation,
|
|
116
|
-
isLoading: orgLoading,
|
|
117
|
-
error: orgError,
|
|
118
|
-
switchOrganisation,
|
|
119
|
-
getUserRole,
|
|
120
|
-
validateOrganisationAccess,
|
|
121
|
-
refreshOrganisations
|
|
122
|
-
} = useOrganisations();
|
|
123
|
-
|
|
124
|
-
// Removed debug logging useEffect - it was causing render loops because organisations array
|
|
125
|
-
// is recreated on every render, triggering the effect constantly
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const handleOrganisationChange = useCallback(async (orgId: string) => {
|
|
129
|
-
if (disabled || isLoading) return;
|
|
130
|
-
|
|
131
|
-
setSwitchError(null);
|
|
132
|
-
setIsLoading(true);
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
// Validate access before attempting switch
|
|
136
|
-
if (!validateOrganisationAccess(orgId)) {
|
|
137
|
-
throw new Error('You do not have access to this organisation');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
await switchOrganisation(orgId);
|
|
141
|
-
|
|
142
|
-
const newOrganisation = organisations.find(org => org.id === orgId);
|
|
143
|
-
if (newOrganisation && onOrganisationChange) {
|
|
144
|
-
onOrganisationChange(newOrganisation);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
} catch (error) {
|
|
148
|
-
logger.error('OrganisationSelector', 'Failed to switch organisation:', error);
|
|
149
|
-
setSwitchError(error instanceof Error ? error.message : 'Failed to switch organisation');
|
|
150
|
-
} finally {
|
|
151
|
-
setIsLoading(false);
|
|
152
|
-
}
|
|
153
|
-
}, [
|
|
154
|
-
disabled,
|
|
155
|
-
isLoading,
|
|
156
|
-
validateOrganisationAccess,
|
|
157
|
-
switchOrganisation,
|
|
158
|
-
organisations,
|
|
159
|
-
onOrganisationChange
|
|
160
|
-
]);
|
|
161
|
-
|
|
162
|
-
const handleRetry = useCallback(async () => {
|
|
163
|
-
setIsLoading(true);
|
|
164
|
-
setSwitchError(null);
|
|
165
|
-
try {
|
|
166
|
-
await refreshOrganisations();
|
|
167
|
-
} catch (error) {
|
|
168
|
-
logger.error('OrganisationSelector', 'Failed to refresh organisations:', error);
|
|
169
|
-
setSwitchError('Failed to refresh organisations');
|
|
170
|
-
} finally {
|
|
171
|
-
setIsLoading(false);
|
|
172
|
-
}
|
|
173
|
-
}, [refreshOrganisations]);
|
|
174
|
-
|
|
175
|
-
// Loading state
|
|
176
|
-
if (orgLoading) {
|
|
177
|
-
return (
|
|
178
|
-
<div className={`flex items-center gap-2 ${className}`}>
|
|
179
|
-
<LoadingSpinner size="sm" />
|
|
180
|
-
<span className="text-sm text-muted-foreground">
|
|
181
|
-
{compact ? "Loading..." : "Loading organisations..."}
|
|
182
|
-
</span>
|
|
183
|
-
</div>
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Error state
|
|
188
|
-
if (orgError) {
|
|
189
|
-
return (
|
|
190
|
-
<div className={`space-y-2 ${className}`}>
|
|
191
|
-
<Alert variant="destructive">
|
|
192
|
-
<AlertCircle className="size-4" />
|
|
193
|
-
<AlertDescription>
|
|
194
|
-
Failed to load organisations: {orgError.message}
|
|
195
|
-
</AlertDescription>
|
|
196
|
-
</Alert>
|
|
197
|
-
{showRetryButton && (
|
|
198
|
-
<Button
|
|
199
|
-
variant="outline"
|
|
200
|
-
size="sm"
|
|
201
|
-
onClick={handleRetry}
|
|
202
|
-
disabled={isLoading}
|
|
203
|
-
className="w-full"
|
|
204
|
-
>
|
|
205
|
-
<RefreshCw className={`size-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} />
|
|
206
|
-
Retry
|
|
207
|
-
</Button>
|
|
208
|
-
)}
|
|
209
|
-
</div>
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// No organisations available
|
|
214
|
-
if (organisations.length === 0) {
|
|
215
|
-
if (showNoOrganisationsMessage) {
|
|
216
|
-
return (
|
|
217
|
-
<div className={`space-y-2 ${className}`}>
|
|
218
|
-
<Alert>
|
|
219
|
-
<Building2 className="size-4" />
|
|
220
|
-
<AlertDescription>
|
|
221
|
-
No organisations available. Please contact your administrator to be added to an organisation.
|
|
222
|
-
</AlertDescription>
|
|
223
|
-
</Alert>
|
|
224
|
-
{showRetryButton && (
|
|
225
|
-
<Button
|
|
226
|
-
variant="outline"
|
|
227
|
-
size="sm"
|
|
228
|
-
onClick={handleRetry}
|
|
229
|
-
disabled={isLoading}
|
|
230
|
-
className="w-full"
|
|
231
|
-
>
|
|
232
|
-
<RefreshCw className={`size-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} />
|
|
233
|
-
Check Again
|
|
234
|
-
</Button>
|
|
235
|
-
)}
|
|
236
|
-
</div>
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Switch error display
|
|
243
|
-
const switchErrorDisplay = switchError && (
|
|
244
|
-
<Alert variant="destructive" className="mt-2">
|
|
245
|
-
<AlertCircle className="size-4" />
|
|
246
|
-
<AlertDescription>{switchError}</AlertDescription>
|
|
247
|
-
</Alert>
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
// Normal selector state - allow opening even if no organisation is selected
|
|
251
|
-
const isSelectDisabled = disabled || isLoading;
|
|
252
|
-
|
|
253
|
-
// Memoize the value to prevent render loops
|
|
254
|
-
const selectValue = useMemo(() => {
|
|
255
|
-
return selectedOrganisation?.id || '';
|
|
256
|
-
}, [selectedOrganisation?.id]);
|
|
257
|
-
|
|
258
|
-
return (
|
|
259
|
-
<div className={className}>
|
|
260
|
-
<Select
|
|
261
|
-
value={selectValue}
|
|
262
|
-
onValueChange={handleOrganisationChange}
|
|
263
|
-
disabled={isSelectDisabled}
|
|
264
|
-
>
|
|
265
|
-
<SelectTrigger
|
|
266
|
-
className="text-left"
|
|
267
|
-
variant="outline"
|
|
268
|
-
>
|
|
269
|
-
<SelectValue placeholder={placeholder}>
|
|
270
|
-
{selectedOrganisation && (
|
|
271
|
-
<div className="flex items-center gap-2">
|
|
272
|
-
{isLoading ? (
|
|
273
|
-
<LoadingSpinner size="sm" />
|
|
274
|
-
) : (
|
|
275
|
-
<Building2 className="size-4 flex-shrink-0" />
|
|
276
|
-
)}
|
|
277
|
-
<span className="truncate">{selectedOrganisation.display_name}</span>
|
|
278
|
-
</div>
|
|
279
|
-
)}
|
|
280
|
-
</SelectValue>
|
|
281
|
-
</SelectTrigger>
|
|
282
|
-
<SelectContent>
|
|
283
|
-
{organisations.map((org) => {
|
|
284
|
-
const userRole = getUserRole(org.id);
|
|
285
|
-
const hasAccess = validateOrganisationAccess(org.id);
|
|
286
|
-
|
|
287
|
-
return (
|
|
288
|
-
<SelectItem
|
|
289
|
-
key={org.id}
|
|
290
|
-
value={org.id}
|
|
291
|
-
disabled={!hasAccess}
|
|
292
|
-
className={!hasAccess ? 'opacity-50' : ''}
|
|
293
|
-
>
|
|
294
|
-
<div className="flex items-center justify-between w-full">
|
|
295
|
-
<div className="flex items-center gap-2">
|
|
296
|
-
<Building2 className="size-4" />
|
|
297
|
-
<div className="flex flex-col">
|
|
298
|
-
<span className="font-medium">{org.display_name}</span>
|
|
299
|
-
{!compact && org.description && (
|
|
300
|
-
<span className="text-xs text-muted-foreground truncate max-w-40">
|
|
301
|
-
{org.description}
|
|
302
|
-
</span>
|
|
303
|
-
)}
|
|
304
|
-
</div>
|
|
305
|
-
</div>
|
|
306
|
-
{showRole && (
|
|
307
|
-
<div className="flex items-center gap-1 ml-4">
|
|
308
|
-
<Shield className="size-3 text-muted-foreground" />
|
|
309
|
-
<span className="text-xs text-muted-foreground capitalize">
|
|
310
|
-
{userRole?.replace('_', ' ') || 'No Role'}
|
|
311
|
-
</span>
|
|
312
|
-
</div>
|
|
313
|
-
)}
|
|
314
|
-
</div>
|
|
315
|
-
</SelectItem>
|
|
316
|
-
);
|
|
317
|
-
})}
|
|
318
|
-
</SelectContent>
|
|
319
|
-
</Select>
|
|
320
|
-
{switchErrorDisplay && (
|
|
321
|
-
<div className="mt-2">
|
|
322
|
-
{switchErrorDisplay}
|
|
323
|
-
</div>
|
|
324
|
-
)}
|
|
325
|
-
</div>
|
|
326
|
-
);
|
|
327
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Organisation Selector Component Exports
|
|
3
|
-
* @package @jmruthers/pace-core
|
|
4
|
-
* @module Components/OrganisationSelector
|
|
5
|
-
* @since 0.4.0
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export { OrganisationSelector } from './OrganisationSelector';
|
|
9
|
-
export type { OrganisationSelectorProps } from './OrganisationSelector';
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|