@jmruthers/pace-core 0.5.191 → 0.6.1
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 +29 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +372 -0
- package/cursor-rules/01-standards-compliance.mdc +275 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +341 -0
- package/cursor-rules/04-testing-standards.mdc +315 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +392 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
- package/cursor-rules/CHANGELOG.md +101 -0
- package/cursor-rules/README.md +191 -0
- package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
- package/dist/{DataTable-WKRZD47S.js → DataTable-DQ7RSOHE.js} +8 -7
- package/dist/{PublicPageProvider-ULXC_u6U.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +37 -156
- package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
- package/dist/{UnifiedAuthProvider-FTSG5XH7.js → UnifiedAuthProvider-ATAP5UTR.js} +3 -3
- package/dist/{api-IHKALJZD.js → api-N774RPUA.js} +2 -2
- package/dist/{chunk-6C4YBBJM.js → chunk-3QRJFVBR.js} +1 -1
- package/dist/chunk-3QRJFVBR.js.map +1 -0
- package/dist/{chunk-OETXORNB.js → chunk-3XTALGJF.js} +211 -136
- package/dist/chunk-3XTALGJF.js.map +1 -0
- package/dist/{chunk-6TQDD426.js → chunk-4N5C5XZU.js} +47 -228
- package/dist/chunk-4N5C5XZU.js.map +1 -0
- package/dist/{chunk-LOMZXPSN.js → chunk-4ZC4GX36.js} +47 -74
- package/dist/chunk-4ZC4GX36.js.map +1 -0
- package/dist/{chunk-6LTQQAT6.js → chunk-BYFSK72L.js} +357 -158
- package/dist/chunk-BYFSK72L.js.map +1 -0
- package/dist/{chunk-XYXSXPUK.js → chunk-EXUD6RNJ.js} +50 -10
- package/dist/chunk-EXUD6RNJ.js.map +1 -0
- package/dist/{chunk-VKB2CO4Z.js → chunk-GLK6VM3F.js} +244 -249
- package/dist/chunk-GLK6VM3F.js.map +1 -0
- package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-XNYQOL3Z.js → chunk-JBKQ3SAO.js} +9 -18
- package/dist/chunk-JBKQ3SAO.js.map +1 -0
- package/dist/{chunk-ROXMHMY2.js → chunk-KNC55RTG.js} +13 -3
- package/dist/{chunk-ROXMHMY2.js.map → chunk-KNC55RTG.js.map} +1 -1
- package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
- package/dist/{chunk-ULHIJK66.js → chunk-T33XF5ZC.js} +255 -140
- package/dist/chunk-T33XF5ZC.js.map +1 -0
- package/dist/{chunk-VRGWKHDB.js → chunk-XM25TVIE.js} +100 -33
- package/dist/chunk-XM25TVIE.js.map +1 -0
- package/dist/components.d.ts +4 -4
- package/dist/components.js +9 -9
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +20 -25
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +18 -21
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +2 -20
- package/dist/rbac/index.js +7 -9
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -3
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +2 -2
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +10 -10
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +24 -11
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +2 -2
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +60 -38
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +194 -209
- package/docs/getting-started/cursor-rules.md +262 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/MIGRATION_GUIDE.md +4 -4
- package/docs/migration/REACT_19_MIGRATION.md +227 -0
- package/docs/migration/database-changes-december-2025.md +2 -1
- package/docs/rbac/event-based-apps.md +124 -6
- package/docs/standards/README.md +39 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +11 -6
- package/scripts/audit-consuming-app.cjs +961 -0
- package/scripts/check-pace-core-compliance.cjs +315 -61
- package/scripts/install-cursor-rules.cjs +236 -0
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/__tests__/rls-policies.test.ts +3 -1
- package/src/components/Badge/Badge.tsx +2 -4
- package/src/components/Button/Button.tsx +5 -4
- package/src/components/Calendar/Calendar.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
- package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +88 -16
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/DataTableCore.tsx +4 -7
- package/src/components/DataTable/components/DataTableModals.tsx +1 -1
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +86 -17
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +6 -5
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -3
- package/src/components/FileDisplay/FileDisplay.tsx +16 -4
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +30 -26
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.tsx +28 -30
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +8 -8
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -11
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -1
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +75 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +98 -69
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -8
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
- package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
- package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
- package/src/components/Select/Select.tsx +33 -22
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/UserMenu/UserMenu.tsx +1 -1
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +14 -7
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/public/usePublicEvent.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/services/useAuthService.ts +21 -3
- package/src/hooks/services/useEventService.ts +21 -3
- package/src/hooks/services/useInactivityService.ts +21 -3
- package/src/hooks/services/useOrganisationService.ts +21 -3
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useFileDisplay.ts +10 -17
- package/src/hooks/useFocusManagement.ts +2 -2
- package/src/hooks/useFocusTrap.ts +4 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +1 -1
- package/src/hooks/usePermissionCache.ts +1 -1
- package/src/hooks/useSecureDataAccess.test.ts +16 -9
- package/src/hooks/useSecureDataAccess.ts +22 -6
- package/src/hooks/useToast.ts +2 -2
- package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/EventServiceProvider.tsx +0 -8
- package/src/providers/services/UnifiedAuthProvider.tsx +196 -46
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +34 -40
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
- package/src/rbac/adapters.tsx +3 -22
- package/src/rbac/api.test.ts +2 -2
- package/src/rbac/api.ts +7 -1
- package/src/rbac/components/EnhancedNavigationMenu.tsx +3 -16
- package/src/rbac/components/NavigationGuard.tsx +2 -11
- package/src/rbac/components/NavigationProvider.tsx +1 -2
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +46 -13
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.tsx +1 -2
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
- package/src/rbac/engine.ts +14 -2
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/usePermissions.ts +51 -11
- package/src/rbac/hooks/useRBAC.ts +3 -13
- package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
- package/src/rbac/hooks/useResolvedScope.ts +58 -33
- package/src/rbac/hooks/useSecureSupabase.ts +4 -9
- package/src/rbac/secureClient.ts +43 -0
- package/src/services/EventService.ts +4 -57
- package/src/services/InactivityService.ts +127 -34
- package/src/services/OrganisationService.ts +68 -10
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-6LTQQAT6.js.map +0 -1
- package/dist/chunk-6TQDD426.js.map +0 -1
- package/dist/chunk-LOMZXPSN.js.map +0 -1
- package/dist/chunk-OETXORNB.js.map +0 -1
- package/dist/chunk-ULHIJK66.js.map +0 -1
- package/dist/chunk-VKB2CO4Z.js.map +0 -1
- package/dist/chunk-VRGWKHDB.js.map +0 -1
- package/dist/chunk-XNYQOL3Z.js.map +0 -1
- package/dist/chunk-XYXSXPUK.js.map +0 -1
- package/scripts/check-pace-core-compliance.js +0 -512
- package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
- package/src/utils/context/superAdminOverride.ts +0 -58
- /package/dist/{DataTable-WKRZD47S.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-FTSG5XH7.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
- /package/dist/{api-IHKALJZD.js.map → api-N774RPUA.js.map} +0 -0
- /package/dist/{chunk-QWWZ5CAQ.js.map → chunk-LXQLPRQ2.js.map} +0 -0
- /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
|
@@ -165,12 +165,8 @@ describe('ProtectedRoute Component', () => {
|
|
|
165
165
|
|
|
166
166
|
expect(screen.getByTestId('outlet')).toBeInTheDocument();
|
|
167
167
|
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
expect(consoleDebugSpy).toHaveBeenCalledWith(
|
|
171
|
-
expect.stringContaining('[DEBUG] [ProtectedRoute] Events available but none selected - allowing render so selector is visible')
|
|
172
|
-
);
|
|
173
|
-
}, { timeout: 1000 });
|
|
168
|
+
// Note: ProtectedRoute no longer logs this debug message - it just allows rendering
|
|
169
|
+
// The component allows rendering when events exist but none selected to make the selector visible
|
|
174
170
|
});
|
|
175
171
|
|
|
176
172
|
it('renders session restoration loader when session is restoring', () => {
|
|
@@ -461,9 +457,9 @@ describe('ProtectedRoute Component', () => {
|
|
|
461
457
|
renderWithProviders(<ProtectedRoute requireEvent={true} />);
|
|
462
458
|
|
|
463
459
|
expect(screen.getByTestId('outlet')).toBeInTheDocument();
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
460
|
+
|
|
461
|
+
// Note: ProtectedRoute no longer logs this debug message - it just allows rendering
|
|
462
|
+
// The component allows rendering when events exist but none selected to make the selector visible
|
|
467
463
|
});
|
|
468
464
|
|
|
469
465
|
it('renders outlet when event is selected', () => {
|
|
@@ -359,7 +359,6 @@ export function ProtectedRoute({
|
|
|
359
359
|
// The event selector will be visible and user can select, or auto-selection will kick in
|
|
360
360
|
if (!selectedEvent) {
|
|
361
361
|
// Log for debugging - this is expected behavior, not an error
|
|
362
|
-
logger.debug('ProtectedRoute', 'Events available but none selected - allowing render so selector is visible');
|
|
363
362
|
return <Outlet />;
|
|
364
363
|
}
|
|
365
364
|
|
|
@@ -102,7 +102,6 @@ export function PublicPageProvider({ children, appName }: PublicPageProviderProp
|
|
|
102
102
|
return null;
|
|
103
103
|
}
|
|
104
104
|
const client = createClient<Database>(supabaseUrl, supabaseKey);
|
|
105
|
-
logger.info('PublicPageProvider', 'Supabase client created successfully for public pages');
|
|
106
105
|
return client;
|
|
107
106
|
}, [supabaseUrl, supabaseKey]);
|
|
108
107
|
|
|
@@ -53,7 +53,7 @@ export interface UseSelectStateProps {
|
|
|
53
53
|
export interface UseSelectEventsProps {
|
|
54
54
|
state: SelectState;
|
|
55
55
|
actions: SelectActions;
|
|
56
|
-
selectRef: React.RefObject<HTMLElement>;
|
|
56
|
+
selectRef: React.RefObject<HTMLElement | null>;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export interface UseSelectSearchProps {
|
|
@@ -299,12 +299,13 @@ export const useSelectSearch = ({
|
|
|
299
299
|
if (!React.isValidElement(child)) return child;
|
|
300
300
|
|
|
301
301
|
// Check if this is a SelectItem by looking for the data-testid or value prop
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
302
|
+
const childProps = child.props as { 'data-testid'?: string; value?: unknown; children?: React.ReactNode; [key: string]: unknown };
|
|
303
|
+
const isSelectItem = childProps &&
|
|
304
|
+
(childProps['data-testid'] === 'select-item' ||
|
|
305
|
+
childProps.value !== undefined);
|
|
305
306
|
|
|
306
307
|
if (isSelectItem) {
|
|
307
|
-
const childText = React.Children.toArray(
|
|
308
|
+
const childText = React.Children.toArray(childProps.children).join(' ').toLowerCase();
|
|
308
309
|
const searchLower = searchTerm.toLowerCase();
|
|
309
310
|
|
|
310
311
|
if (childText.includes(searchLower)) {
|
|
@@ -313,8 +314,8 @@ export const useSelectSearch = ({
|
|
|
313
314
|
return null;
|
|
314
315
|
}
|
|
315
316
|
|
|
316
|
-
if (
|
|
317
|
-
const filteredChildChildren = filterChildren(
|
|
317
|
+
if (childProps.children) {
|
|
318
|
+
const filteredChildChildren = filterChildren(childProps.children);
|
|
318
319
|
if (React.Children.count(filteredChildChildren) > 0) {
|
|
319
320
|
return React.cloneElement(child, {}, filteredChildChildren);
|
|
320
321
|
}
|
|
@@ -359,8 +360,11 @@ const useSelectContext = () => {
|
|
|
359
360
|
const getTextContent = (children: React.ReactNode): string => {
|
|
360
361
|
if (typeof children === 'string') return children;
|
|
361
362
|
if (typeof children === 'number') return children.toString();
|
|
362
|
-
if (React.isValidElement(children)
|
|
363
|
-
|
|
363
|
+
if (React.isValidElement(children)) {
|
|
364
|
+
const props = children.props as { children?: React.ReactNode; [key: string]: unknown };
|
|
365
|
+
if (props.children) {
|
|
366
|
+
return getTextContent(props.children);
|
|
367
|
+
}
|
|
364
368
|
}
|
|
365
369
|
if (Array.isArray(children)) {
|
|
366
370
|
return children.map(getTextContent).join('');
|
|
@@ -443,7 +447,7 @@ export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectS
|
|
|
443
447
|
const internalRef = React.useRef<HTMLFormElement>(null);
|
|
444
448
|
const selectRef = React.useMemo(() => {
|
|
445
449
|
if (ref && typeof ref === 'object' && 'current' in ref) {
|
|
446
|
-
return ref as React.RefObject<HTMLFormElement>;
|
|
450
|
+
return ref as React.RefObject<HTMLFormElement | null>;
|
|
447
451
|
}
|
|
448
452
|
return internalRef;
|
|
449
453
|
}, [ref]);
|
|
@@ -620,7 +624,7 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
|
|
|
620
624
|
const opensUpward = direction === 'up';
|
|
621
625
|
|
|
622
626
|
// Use ref to store the latest handleClick to avoid re-creating the effect
|
|
623
|
-
const handleClickRef = React.useRef<(e: React.MouseEvent) => void>();
|
|
627
|
+
const handleClickRef = React.useRef<(e: React.MouseEvent) => void>(undefined);
|
|
624
628
|
|
|
625
629
|
const handleClick = React.useCallback((e: React.MouseEvent) => {
|
|
626
630
|
if (disabled) {
|
|
@@ -687,22 +691,26 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
|
|
|
687
691
|
};
|
|
688
692
|
|
|
689
693
|
if (asChild) {
|
|
690
|
-
const
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
(child
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
694
|
+
const childElement = children as React.ReactElement<{ children?: React.ReactNode; [key: string]: unknown }>;
|
|
695
|
+
const childChildren = React.Children.toArray(childElement.props.children);
|
|
696
|
+
const hasChevron = childChildren.some(child => {
|
|
697
|
+
if (React.isValidElement(child)) {
|
|
698
|
+
const childProps = child.props as { 'data-testid'?: string; [key: string]: unknown };
|
|
699
|
+
return child.type === ChevronDown ||
|
|
700
|
+
(child.type === 'svg' && childProps['data-testid'] === 'chevron-down') ||
|
|
701
|
+
(typeof child === 'object' && 'type' in child && typeof child.type === 'function' && child.type.name === 'ChevronDown');
|
|
702
|
+
}
|
|
703
|
+
return false;
|
|
704
|
+
});
|
|
697
705
|
|
|
698
706
|
// Merge child's className with triggerProps className
|
|
699
|
-
const childClassName = (children as React.ReactElement).props.className;
|
|
707
|
+
const childClassName = (children as React.ReactElement<any>).props.className;
|
|
700
708
|
const mergedClassName = cn(
|
|
701
709
|
triggerProps.className,
|
|
702
710
|
childClassName
|
|
703
711
|
);
|
|
704
712
|
|
|
705
|
-
return React.cloneElement(children as React.ReactElement
|
|
713
|
+
return React.cloneElement(children as React.ReactElement<any>, {
|
|
706
714
|
...triggerProps,
|
|
707
715
|
className: mergedClassName,
|
|
708
716
|
children: hasChevron ? childChildren : [
|
|
@@ -915,8 +923,11 @@ export const SelectItem = React.forwardRef<HTMLLIElement, SelectItemProps>(
|
|
|
915
923
|
const getTextContent = (children: React.ReactNode): string => {
|
|
916
924
|
if (typeof children === 'string') return children;
|
|
917
925
|
if (typeof children === 'number') return children.toString();
|
|
918
|
-
if (React.isValidElement(children)
|
|
919
|
-
|
|
926
|
+
if (React.isValidElement(children)) {
|
|
927
|
+
const props = children.props as { children?: React.ReactNode; [key: string]: unknown };
|
|
928
|
+
if (props.children) {
|
|
929
|
+
return getTextContent(props.children);
|
|
930
|
+
}
|
|
920
931
|
}
|
|
921
932
|
if (Array.isArray(children)) {
|
|
922
933
|
return children.map(getTextContent).join('');
|
|
@@ -93,35 +93,33 @@ export interface TextareaProps
|
|
|
93
93
|
* />
|
|
94
94
|
* ```
|
|
95
95
|
*/
|
|
96
|
-
|
|
97
|
-
(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
);
|
|
96
|
+
function Textarea({ className, variant = 'default', size = 'md', error, ref, ...props }: TextareaProps & { ref?: React.Ref<HTMLTextAreaElement> }) {
|
|
97
|
+
return (
|
|
98
|
+
<textarea
|
|
99
|
+
className={cn(
|
|
100
|
+
// Base styles (matching Input component)
|
|
101
|
+
'flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
|
102
|
+
|
|
103
|
+
// Variant styles
|
|
104
|
+
{
|
|
105
|
+
'border-input': variant === 'default' && !error,
|
|
106
|
+
'border-destructive focus-visible:ring-destructive': variant === 'destructive' || error,
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
// Size styles
|
|
110
|
+
{
|
|
111
|
+
'min-h-[60px] px-2 py-1 text-xs': size === 'sm',
|
|
112
|
+
'min-h-[80px] px-3 py-2 text-sm': size === 'md',
|
|
113
|
+
'min-h-[100px] px-4 py-3 text-base': size === 'lg',
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
className
|
|
117
|
+
)}
|
|
118
|
+
ref={ref}
|
|
119
|
+
{...props}
|
|
120
|
+
/>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
125
123
|
|
|
126
124
|
Textarea.displayName = 'Textarea';
|
|
127
125
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { screen, fireEvent, waitFor, act } from '@testing-library/react';
|
|
3
3
|
import { renderHook } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
4
5
|
import '@testing-library/jest-dom';
|
|
5
6
|
import { vi, describe, it, beforeEach, afterEach } from 'vitest';
|
|
6
7
|
import { renderWithProviders } from '../../__tests__/helpers/test-utils';
|
|
@@ -16,7 +17,7 @@ import { Button } from '../../components/Button/Button';
|
|
|
16
17
|
import { Input } from '../../components/Input/Input';
|
|
17
18
|
import { Form } from '../../components/Form/Form';
|
|
18
19
|
import { z } from 'zod';
|
|
19
|
-
import { useForm } from 'react-hook-form';
|
|
20
|
+
import { useForm, useFormState } from 'react-hook-form';
|
|
20
21
|
|
|
21
22
|
// Mock the Logger module
|
|
22
23
|
vi.mock('../../utils/core/logger', () => {
|
|
@@ -137,47 +138,57 @@ const ValidatedForm = ({ onSubmit }: ValidatedFormProps = {}) => {
|
|
|
137
138
|
onSubmit={handleSubmit}
|
|
138
139
|
>
|
|
139
140
|
{(methods) => (
|
|
140
|
-
|
|
141
|
-
<div>
|
|
142
|
-
<Input
|
|
143
|
-
{...methods.register('name')}
|
|
144
|
-
data-testid="name-input"
|
|
145
|
-
placeholder="Name"
|
|
146
|
-
/>
|
|
147
|
-
{methods.formState.errors.name && (
|
|
148
|
-
<div data-testid="name-error">{methods.formState.errors.name.message}</div>
|
|
149
|
-
)}
|
|
150
|
-
</div>
|
|
151
|
-
<div>
|
|
152
|
-
<Input
|
|
153
|
-
{...methods.register('email')}
|
|
154
|
-
data-testid="email-input"
|
|
155
|
-
placeholder="Email"
|
|
156
|
-
/>
|
|
157
|
-
{methods.formState.errors.email && (
|
|
158
|
-
<div data-testid="email-error">{methods.formState.errors.email.message}</div>
|
|
159
|
-
)}
|
|
160
|
-
</div>
|
|
161
|
-
<div>
|
|
162
|
-
<Input
|
|
163
|
-
{...methods.register('age', { valueAsNumber: true })}
|
|
164
|
-
data-testid="age-input"
|
|
165
|
-
type="number"
|
|
166
|
-
placeholder="Age"
|
|
167
|
-
/>
|
|
168
|
-
{methods.formState.errors.age && (
|
|
169
|
-
<div data-testid="age-error">{methods.formState.errors.age.message}</div>
|
|
170
|
-
)}
|
|
171
|
-
</div>
|
|
172
|
-
<Button type="submit" data-testid="submit-button">
|
|
173
|
-
Submit
|
|
174
|
-
</Button>
|
|
175
|
-
</>
|
|
141
|
+
<ValidatedFormFields methods={methods} />
|
|
176
142
|
)}
|
|
177
143
|
</Form>
|
|
178
144
|
);
|
|
179
145
|
};
|
|
180
146
|
|
|
147
|
+
// Separate component to use useFormState hook for reactive error updates
|
|
148
|
+
const ValidatedFormFields = ({ methods }: { methods: ReturnType<typeof useForm> }) => {
|
|
149
|
+
// Use useFormState to subscribe to form state changes, including errors
|
|
150
|
+
const { errors } = useFormState({ control: methods.control });
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<>
|
|
154
|
+
<div>
|
|
155
|
+
<Input
|
|
156
|
+
{...methods.register('name')}
|
|
157
|
+
data-testid="name-input"
|
|
158
|
+
placeholder="Name"
|
|
159
|
+
/>
|
|
160
|
+
{errors.name && (
|
|
161
|
+
<div data-testid="name-error">{errors.name.message}</div>
|
|
162
|
+
)}
|
|
163
|
+
</div>
|
|
164
|
+
<div>
|
|
165
|
+
<Input
|
|
166
|
+
{...methods.register('email')}
|
|
167
|
+
data-testid="email-input"
|
|
168
|
+
placeholder="Email"
|
|
169
|
+
/>
|
|
170
|
+
{errors.email && (
|
|
171
|
+
<div data-testid="email-error">{errors.email.message}</div>
|
|
172
|
+
)}
|
|
173
|
+
</div>
|
|
174
|
+
<div>
|
|
175
|
+
<Input
|
|
176
|
+
{...methods.register('age', { valueAsNumber: true })}
|
|
177
|
+
data-testid="age-input"
|
|
178
|
+
type="number"
|
|
179
|
+
placeholder="Age"
|
|
180
|
+
/>
|
|
181
|
+
{errors.age && (
|
|
182
|
+
<div data-testid="age-error">{errors.age.message}</div>
|
|
183
|
+
)}
|
|
184
|
+
</div>
|
|
185
|
+
<Button type="submit" data-testid="submit-button">
|
|
186
|
+
Submit
|
|
187
|
+
</Button>
|
|
188
|
+
</>
|
|
189
|
+
);
|
|
190
|
+
};
|
|
191
|
+
|
|
181
192
|
// Test component for useKeyboardShortcuts integration
|
|
182
193
|
const KeyboardShortcutsComponent = () => {
|
|
183
194
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
@@ -312,14 +323,13 @@ describe('Hooks Integration', () => {
|
|
|
312
323
|
});
|
|
313
324
|
|
|
314
325
|
it('handles rapid input changes correctly', async () => {
|
|
326
|
+
const user = userEvent.setup();
|
|
315
327
|
renderWithProviders(<DebouncedSearch />);
|
|
316
328
|
|
|
317
329
|
const searchInput = screen.getByTestId('search-input');
|
|
318
330
|
|
|
319
|
-
// Type rapidly
|
|
320
|
-
|
|
321
|
-
fireEvent.change(searchInput, { target: { value: 'ab' } });
|
|
322
|
-
fireEvent.change(searchInput, { target: { value: 'abc' } });
|
|
331
|
+
// Type rapidly using userEvent
|
|
332
|
+
await user.type(searchInput, 'abc');
|
|
323
333
|
|
|
324
334
|
// Should not show results immediately
|
|
325
335
|
expect(screen.queryByTestId('result-0')).not.toBeInTheDocument();
|
|
@@ -329,7 +339,10 @@ describe('Hooks Integration', () => {
|
|
|
329
339
|
expect(screen.getByTestId('debounced-term')).toHaveTextContent('Debounced: abc');
|
|
330
340
|
}, { timeout: 400 });
|
|
331
341
|
|
|
332
|
-
|
|
342
|
+
// Wait for results to appear after debounced term updates
|
|
343
|
+
await waitFor(() => {
|
|
344
|
+
expect(screen.getByTestId('result-0')).toHaveTextContent('Result for: abc');
|
|
345
|
+
}, { timeout: 500 });
|
|
333
346
|
});
|
|
334
347
|
});
|
|
335
348
|
|
|
@@ -362,6 +375,7 @@ describe('Hooks Integration', () => {
|
|
|
362
375
|
|
|
363
376
|
describe('useZodForm Integration', () => {
|
|
364
377
|
it('validates form with real-time feedback', async () => {
|
|
378
|
+
const user = userEvent.setup();
|
|
365
379
|
renderWithProviders(<ValidatedForm />);
|
|
366
380
|
|
|
367
381
|
const nameInput = screen.getByTestId('name-input');
|
|
@@ -369,21 +383,32 @@ describe('Hooks Integration', () => {
|
|
|
369
383
|
const ageInput = screen.getByTestId('age-input');
|
|
370
384
|
const submitButton = screen.getByTestId('submit-button');
|
|
371
385
|
|
|
372
|
-
// Test validation errors
|
|
373
|
-
await
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
await
|
|
379
|
-
|
|
380
|
-
|
|
386
|
+
// Test validation errors - use userEvent for proper async handling
|
|
387
|
+
await user.clear(nameInput);
|
|
388
|
+
await user.type(nameInput, 'a');
|
|
389
|
+
await user.clear(emailInput);
|
|
390
|
+
await user.type(emailInput, 'invalid-email');
|
|
391
|
+
await user.clear(ageInput);
|
|
392
|
+
await user.type(ageInput, '15');
|
|
393
|
+
|
|
394
|
+
// Submit the form to trigger validation
|
|
395
|
+
await user.click(submitButton);
|
|
381
396
|
|
|
397
|
+
// Wait for validation errors to appear after form submission
|
|
398
|
+
// The render prop function needs to re-render when formState.errors changes
|
|
399
|
+
// Wait for all errors to appear together
|
|
382
400
|
await waitFor(() => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
401
|
+
const nameError = screen.queryByTestId('name-error');
|
|
402
|
+
const emailError = screen.queryByTestId('email-error');
|
|
403
|
+
const ageError = screen.queryByTestId('age-error');
|
|
404
|
+
|
|
405
|
+
expect(nameError).toBeInTheDocument();
|
|
406
|
+
expect(nameError).toHaveTextContent('Name must be at least 2 characters');
|
|
407
|
+
expect(emailError).toBeInTheDocument();
|
|
408
|
+
expect(emailError).toHaveTextContent('Invalid email address');
|
|
409
|
+
expect(ageError).toBeInTheDocument();
|
|
410
|
+
expect(ageError).toHaveTextContent('Must be at least 18 years old');
|
|
411
|
+
}, { timeout: 5000 });
|
|
387
412
|
});
|
|
388
413
|
|
|
389
414
|
it('submits form with valid data', async () => {
|
|
@@ -5,7 +5,7 @@ import { useUnifiedAuth } from '../../providers';
|
|
|
5
5
|
import { useOrganisations } from '../../hooks/useOrganisations';
|
|
6
6
|
import { testDataGenerators } from '../../__tests__/helpers/test-utils';
|
|
7
7
|
import { useResolvedScope } from '../../rbac/hooks/useResolvedScope';
|
|
8
|
-
import {
|
|
8
|
+
import { useOrganisationSecurity } from '../useOrganisationSecurity';
|
|
9
9
|
|
|
10
10
|
// Mock dependencies
|
|
11
11
|
vi.mock('../../providers', () => ({
|
|
@@ -28,8 +28,8 @@ vi.mock('../../rbac/hooks/useResolvedScope', () => ({
|
|
|
28
28
|
useResolvedScope: vi.fn(),
|
|
29
29
|
}));
|
|
30
30
|
|
|
31
|
-
vi.mock('
|
|
32
|
-
|
|
31
|
+
vi.mock('../useOrganisationSecurity', () => ({
|
|
32
|
+
useOrganisationSecurity: vi.fn(),
|
|
33
33
|
}));
|
|
34
34
|
|
|
35
35
|
const mockUseUnifiedAuth = {
|
|
@@ -114,10 +114,17 @@ describe('useSecureDataAccess', () => {
|
|
|
114
114
|
isLoading: false,
|
|
115
115
|
error: null,
|
|
116
116
|
});
|
|
117
|
-
// Default mock for
|
|
118
|
-
vi.mocked(
|
|
119
|
-
isSuperAdmin: false,
|
|
120
|
-
|
|
117
|
+
// Default mock for useOrganisationSecurity - not super admin
|
|
118
|
+
vi.mocked(useOrganisationSecurity).mockReturnValue({
|
|
119
|
+
superAdminContext: { isSuperAdmin: false, hasGlobalAccess: false, canManageAllOrganisations: false },
|
|
120
|
+
validateOrganisationAccess: vi.fn(),
|
|
121
|
+
hasMinimumRole: vi.fn(),
|
|
122
|
+
canAccessChildOrganisations: vi.fn(),
|
|
123
|
+
checkPermission: vi.fn(),
|
|
124
|
+
getPermissions: vi.fn(),
|
|
125
|
+
logOrganisationAccess: vi.fn(),
|
|
126
|
+
canManageOrganisation: vi.fn(),
|
|
127
|
+
} as any);
|
|
121
128
|
});
|
|
122
129
|
|
|
123
130
|
describe('validateContext', () => {
|