@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.
Files changed (299) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/cursor-rules/00-pace-core-compliance.mdc +34 -2
  3. package/dist/{AuthService-BPvc3Ka0.d.ts → AuthService-Cb34EQs3.d.ts} +9 -1
  4. package/dist/{DataTable-TPTKCX4D.js → DataTable-E7YQZD7D.js} +9 -8
  5. package/dist/{PublicPageProvider-DC6kCaqf.d.ts → PublicPageProvider-DEMpysFR.d.ts} +45 -67
  6. package/dist/{UnifiedAuthProvider-CVcTjx-d.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +1 -8
  7. package/dist/{UnifiedAuthProvider-CH6Z342H.js → UnifiedAuthProvider-QPXO24B4.js} +5 -4
  8. package/dist/{api-MVVQZLJI.js → api-6LVZTHDS.js} +10 -10
  9. package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
  10. package/dist/chunk-36LVWXB2.js +227 -0
  11. package/dist/chunk-36LVWXB2.js.map +1 -0
  12. package/dist/{chunk-24UVZUZG.js → chunk-3LPHPB62.js} +129 -387
  13. package/dist/chunk-3LPHPB62.js.map +1 -0
  14. package/dist/{chunk-2UOI2FG5.js → chunk-5EC5MEWX.js} +4 -4
  15. package/dist/{chunk-3XC4CPTD.js → chunk-7JPAB3T5.js} +244 -5727
  16. package/dist/chunk-7JPAB3T5.js.map +1 -0
  17. package/dist/{chunk-6J4GEEJR.js → chunk-ATKZM7RX.js} +53 -27
  18. package/dist/chunk-ATKZM7RX.js.map +1 -0
  19. package/dist/{chunk-EHMR7VYL.js → chunk-AVMLPIM7.js} +443 -189
  20. package/dist/chunk-AVMLPIM7.js.map +1 -0
  21. package/dist/chunk-DGUM43GV.js +11 -0
  22. package/dist/{chunk-NECFR5MM.js → chunk-I6DAQMWX.js} +575 -647
  23. package/dist/chunk-I6DAQMWX.js.map +1 -0
  24. package/dist/{chunk-F2IMUDXZ.js → chunk-M7MPQISP.js} +2 -2
  25. package/dist/{chunk-XWQCNGTQ.js → chunk-NN6WWZ5U.js} +173 -79
  26. package/dist/chunk-NN6WWZ5U.js.map +1 -0
  27. package/dist/{chunk-MMZ7JXPU.js → chunk-OEWDTMG7.js} +13 -21
  28. package/dist/{chunk-MMZ7JXPU.js.map → chunk-OEWDTMG7.js.map} +1 -1
  29. package/dist/{chunk-SFZUDBL5.js → chunk-YKRAFF5K.js} +70 -56
  30. package/dist/chunk-YKRAFF5K.js.map +1 -0
  31. package/dist/components.d.ts +2 -2
  32. package/dist/components.js +12 -13
  33. package/dist/contextValidator-OOPCLPZW.js +9 -0
  34. package/dist/contextValidator-OOPCLPZW.js.map +1 -0
  35. package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
  36. package/dist/hooks.d.ts +2 -2
  37. package/dist/hooks.js +7 -6
  38. package/dist/hooks.js.map +1 -1
  39. package/dist/index.d.ts +7 -7
  40. package/dist/index.js +21 -16
  41. package/dist/index.js.map +1 -1
  42. package/dist/providers.d.ts +3 -3
  43. package/dist/providers.js +4 -3
  44. package/dist/rbac/index.d.ts +67 -27
  45. package/dist/rbac/index.js +15 -8
  46. package/dist/styles/index.js +1 -1
  47. package/dist/theming/runtime.js +1 -1
  48. package/dist/types.js +1 -1
  49. package/dist/{usePublicRouteParams-1oMokgLF.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +7 -16
  50. package/dist/utils.js +5 -7
  51. package/dist/utils.js.map +1 -1
  52. package/docs/api/README.md +14 -16
  53. package/docs/api/modules.md +3796 -2513
  54. package/docs/components/context-selector.md +126 -0
  55. package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
  56. package/docs/pace-mint-fix-auto-selection.md +218 -0
  57. package/docs/pace-mint-rbac-setup.md +391 -0
  58. package/docs/rbac/secure-client-protection.md +330 -0
  59. package/package.json +10 -5
  60. package/scripts/audit/core/checks/compliance.cjs +72 -0
  61. package/scripts/audit/core/checks/dependencies.cjs +568 -28
  62. package/scripts/audit/core/checks/documentation.cjs +68 -3
  63. package/scripts/audit/core/checks/environment.cjs +2 -14
  64. package/scripts/audit/core/checks/error-handling.cjs +47 -6
  65. package/src/components/ContextSelector/ContextSelector.tsx +384 -0
  66. package/src/components/ContextSelector/index.ts +3 -0
  67. package/src/components/DataTable/components/RowComponent.tsx +19 -19
  68. package/src/components/DataTable/components/UnifiedTableBody.tsx +2 -2
  69. package/src/components/DataTable/hooks/useDataTablePermissions.ts +8 -6
  70. package/src/components/Dialog/Dialog.tsx +29 -1
  71. package/src/components/FileDisplay/FileDisplay.tsx +42 -10
  72. package/src/components/Header/Header.test.tsx +43 -73
  73. package/src/components/Header/Header.tsx +44 -45
  74. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
  75. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
  76. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
  77. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +9 -9
  78. package/src/components/PaceAppLayout/PaceAppLayout.tsx +157 -36
  79. package/src/components/PaceAppLayout/README.md +14 -17
  80. package/src/components/PaceAppLayout/test-setup.tsx +2 -2
  81. package/src/components/index.ts +5 -5
  82. package/src/eslint-rules/pace-core-compliance.cjs +106 -0
  83. package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
  84. package/src/hooks/useAppConfig.ts +15 -30
  85. package/src/hooks/useFileDisplay.ts +77 -50
  86. package/src/index.ts +4 -5
  87. package/src/providers/services/AuthServiceProvider.tsx +17 -7
  88. package/src/providers/services/EventServiceProvider.tsx +33 -5
  89. package/src/providers/services/UnifiedAuthProvider.tsx +90 -134
  90. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
  91. package/src/rbac/adapters.tsx +2 -2
  92. package/src/rbac/api.test.ts +59 -51
  93. package/src/rbac/api.ts +178 -132
  94. package/src/rbac/components/PagePermissionGuard.tsx +38 -10
  95. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
  96. package/src/rbac/hooks/permissions/useAccessLevel.ts +1 -1
  97. package/src/rbac/hooks/permissions/useCan.ts +41 -11
  98. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +1 -1
  99. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +1 -1
  100. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +1 -1
  101. package/src/rbac/hooks/useCan.test.ts +0 -9
  102. package/src/rbac/hooks/useRBAC.test.ts +1 -5
  103. package/src/rbac/hooks/useRBAC.ts +36 -37
  104. package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
  105. package/src/rbac/hooks/useResolvedScope.ts +35 -40
  106. package/src/rbac/hooks/useSecureSupabase.ts +7 -7
  107. package/src/rbac/index.ts +7 -0
  108. package/src/rbac/secureClient.test.ts +22 -18
  109. package/src/rbac/secureClient.ts +103 -16
  110. package/src/rbac/security.ts +0 -17
  111. package/src/rbac/types.ts +1 -0
  112. package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
  113. package/src/rbac/utils/clientSecurity.ts +93 -0
  114. package/src/rbac/utils/contextValidator.ts +77 -168
  115. package/src/services/AuthService.ts +39 -7
  116. package/src/services/EventService.ts +285 -56
  117. package/src/services/OrganisationService.ts +81 -14
  118. package/src/services/__tests__/EventService.test.ts +1 -2
  119. package/src/services/base/BaseService.ts +3 -0
  120. package/src/utils/dynamic/dynamicUtils.ts +7 -4
  121. package/dist/chunk-24UVZUZG.js.map +0 -1
  122. package/dist/chunk-3XC4CPTD.js.map +0 -1
  123. package/dist/chunk-6J4GEEJR.js.map +0 -1
  124. package/dist/chunk-7D4SUZUM.js +0 -38
  125. package/dist/chunk-EHMR7VYL.js.map +0 -1
  126. package/dist/chunk-NECFR5MM.js.map +0 -1
  127. package/dist/chunk-SFZUDBL5.js.map +0 -1
  128. package/dist/chunk-XWQCNGTQ.js.map +0 -1
  129. package/docs/api/classes/ColumnFactory.md +0 -243
  130. package/docs/api/classes/InvalidScopeError.md +0 -73
  131. package/docs/api/classes/Logger.md +0 -178
  132. package/docs/api/classes/MissingUserContextError.md +0 -66
  133. package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
  134. package/docs/api/classes/PermissionDeniedError.md +0 -73
  135. package/docs/api/classes/RBACAuditManager.md +0 -297
  136. package/docs/api/classes/RBACCache.md +0 -322
  137. package/docs/api/classes/RBACEngine.md +0 -171
  138. package/docs/api/classes/RBACError.md +0 -76
  139. package/docs/api/classes/RBACNotInitializedError.md +0 -66
  140. package/docs/api/classes/SecureSupabaseClient.md +0 -163
  141. package/docs/api/classes/StorageUtils.md +0 -328
  142. package/docs/api/enums/FileCategory.md +0 -184
  143. package/docs/api/enums/LogLevel.md +0 -54
  144. package/docs/api/enums/RBACErrorCode.md +0 -228
  145. package/docs/api/enums/RPCFunction.md +0 -118
  146. package/docs/api/interfaces/AddressFieldProps.md +0 -241
  147. package/docs/api/interfaces/AddressFieldRef.md +0 -94
  148. package/docs/api/interfaces/AggregateConfig.md +0 -43
  149. package/docs/api/interfaces/AutocompleteOptions.md +0 -75
  150. package/docs/api/interfaces/AvatarProps.md +0 -128
  151. package/docs/api/interfaces/BadgeProps.md +0 -34
  152. package/docs/api/interfaces/ButtonProps.md +0 -56
  153. package/docs/api/interfaces/CalendarProps.md +0 -73
  154. package/docs/api/interfaces/CardProps.md +0 -69
  155. package/docs/api/interfaces/ColorPalette.md +0 -7
  156. package/docs/api/interfaces/ColorShade.md +0 -66
  157. package/docs/api/interfaces/ComplianceResult.md +0 -30
  158. package/docs/api/interfaces/DataAccessRecord.md +0 -96
  159. package/docs/api/interfaces/DataRecord.md +0 -11
  160. package/docs/api/interfaces/DataTableAction.md +0 -252
  161. package/docs/api/interfaces/DataTableColumn.md +0 -504
  162. package/docs/api/interfaces/DataTableProps.md +0 -625
  163. package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
  164. package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
  165. package/docs/api/interfaces/DatabaseIssue.md +0 -41
  166. package/docs/api/interfaces/EmptyStateConfig.md +0 -61
  167. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
  168. package/docs/api/interfaces/ErrorBoundaryProps.md +0 -147
  169. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +0 -36
  170. package/docs/api/interfaces/ErrorBoundaryState.md +0 -75
  171. package/docs/api/interfaces/EventAppRoleData.md +0 -71
  172. package/docs/api/interfaces/ExportColumn.md +0 -90
  173. package/docs/api/interfaces/ExportOptions.md +0 -126
  174. package/docs/api/interfaces/FileDisplayProps.md +0 -249
  175. package/docs/api/interfaces/FileMetadata.md +0 -129
  176. package/docs/api/interfaces/FileReference.md +0 -118
  177. package/docs/api/interfaces/FileSizeLimits.md +0 -7
  178. package/docs/api/interfaces/FileUploadOptions.md +0 -139
  179. package/docs/api/interfaces/FileUploadProps.md +0 -296
  180. package/docs/api/interfaces/FooterProps.md +0 -107
  181. package/docs/api/interfaces/FormFieldProps.md +0 -166
  182. package/docs/api/interfaces/FormProps.md +0 -113
  183. package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
  184. package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
  185. package/docs/api/interfaces/InputProps.md +0 -56
  186. package/docs/api/interfaces/LabelProps.md +0 -107
  187. package/docs/api/interfaces/LoggerConfig.md +0 -62
  188. package/docs/api/interfaces/LoginFormProps.md +0 -187
  189. package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
  190. package/docs/api/interfaces/NavigationContextType.md +0 -164
  191. package/docs/api/interfaces/NavigationGuardProps.md +0 -139
  192. package/docs/api/interfaces/NavigationItem.md +0 -120
  193. package/docs/api/interfaces/NavigationMenuProps.md +0 -221
  194. package/docs/api/interfaces/NavigationProviderProps.md +0 -117
  195. package/docs/api/interfaces/Organisation.md +0 -140
  196. package/docs/api/interfaces/OrganisationContextType.md +0 -388
  197. package/docs/api/interfaces/OrganisationMembership.md +0 -140
  198. package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
  199. package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
  200. package/docs/api/interfaces/PaceAppLayoutProps.md +0 -409
  201. package/docs/api/interfaces/PaceLoginPageProps.md +0 -49
  202. package/docs/api/interfaces/PageAccessRecord.md +0 -85
  203. package/docs/api/interfaces/PagePermissionContextType.md +0 -140
  204. package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
  205. package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
  206. package/docs/api/interfaces/PaletteData.md +0 -41
  207. package/docs/api/interfaces/ParsedAddress.md +0 -120
  208. package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
  209. package/docs/api/interfaces/ProgressProps.md +0 -42
  210. package/docs/api/interfaces/ProtectedRouteProps.md +0 -78
  211. package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
  212. package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
  213. package/docs/api/interfaces/PublicPageLayoutProps.md +0 -185
  214. package/docs/api/interfaces/QuickFix.md +0 -52
  215. package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
  216. package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
  217. package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
  218. package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
  219. package/docs/api/interfaces/RBACConfig.md +0 -133
  220. package/docs/api/interfaces/RBACContext.md +0 -52
  221. package/docs/api/interfaces/RBACLogger.md +0 -112
  222. package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
  223. package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
  224. package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
  225. package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
  226. package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
  227. package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
  228. package/docs/api/interfaces/RBACResult.md +0 -58
  229. package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
  230. package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
  231. package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
  232. package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
  233. package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
  234. package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
  235. package/docs/api/interfaces/RBACRolesListParams.md +0 -52
  236. package/docs/api/interfaces/RBACRolesListResult.md +0 -74
  237. package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
  238. package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
  239. package/docs/api/interfaces/ResourcePermissions.md +0 -155
  240. package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
  241. package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
  242. package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
  243. package/docs/api/interfaces/RoleManagementResult.md +0 -52
  244. package/docs/api/interfaces/RouteAccessRecord.md +0 -107
  245. package/docs/api/interfaces/RouteConfig.md +0 -134
  246. package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
  247. package/docs/api/interfaces/SecureDataContextType.md +0 -168
  248. package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
  249. package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
  250. package/docs/api/interfaces/SetupIssue.md +0 -41
  251. package/docs/api/interfaces/StorageConfig.md +0 -41
  252. package/docs/api/interfaces/StorageFileInfo.md +0 -74
  253. package/docs/api/interfaces/StorageFileMetadata.md +0 -151
  254. package/docs/api/interfaces/StorageListOptions.md +0 -99
  255. package/docs/api/interfaces/StorageListResult.md +0 -41
  256. package/docs/api/interfaces/StorageUploadOptions.md +0 -101
  257. package/docs/api/interfaces/StorageUploadResult.md +0 -63
  258. package/docs/api/interfaces/StorageUrlOptions.md +0 -60
  259. package/docs/api/interfaces/StyleImport.md +0 -19
  260. package/docs/api/interfaces/SwitchProps.md +0 -34
  261. package/docs/api/interfaces/TabsContentProps.md +0 -9
  262. package/docs/api/interfaces/TabsListProps.md +0 -9
  263. package/docs/api/interfaces/TabsProps.md +0 -9
  264. package/docs/api/interfaces/TabsTriggerProps.md +0 -50
  265. package/docs/api/interfaces/TextareaProps.md +0 -53
  266. package/docs/api/interfaces/ToastActionElement.md +0 -12
  267. package/docs/api/interfaces/ToastProps.md +0 -9
  268. package/docs/api/interfaces/UnifiedAuthContextType.md +0 -823
  269. package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -173
  270. package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
  271. package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
  272. package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -138
  273. package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
  274. package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
  275. package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -84
  276. package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
  277. package/docs/api/interfaces/UsePublicEventReturn.md +0 -71
  278. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
  279. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -123
  280. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -97
  281. package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
  282. package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
  283. package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
  284. package/docs/api/interfaces/UserEventAccess.md +0 -121
  285. package/docs/api/interfaces/UserMenuProps.md +0 -88
  286. package/docs/api/interfaces/UserProfile.md +0 -63
  287. package/src/components/EventSelector/EventSelector.test.tsx +0 -720
  288. package/src/components/EventSelector/EventSelector.tsx +0 -423
  289. package/src/components/EventSelector/index.ts +0 -3
  290. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
  291. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -327
  292. package/src/components/OrganisationSelector/index.ts +0 -9
  293. /package/dist/{DataTable-TPTKCX4D.js.map → DataTable-E7YQZD7D.js.map} +0 -0
  294. /package/dist/{UnifiedAuthProvider-CH6Z342H.js.map → UnifiedAuthProvider-QPXO24B4.js.map} +0 -0
  295. /package/dist/{api-MVVQZLJI.js.map → api-6LVZTHDS.js.map} +0 -0
  296. /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
  297. /package/dist/{chunk-2UOI2FG5.js.map → chunk-5EC5MEWX.js.map} +0 -0
  298. /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
  299. /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';