@jmruthers/pace-core 0.5.54 → 0.5.56
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/README.md +0 -4
- package/dist/{DataTable-7FMFXA7A.js → DataTable-DJQTKX33.js} +11 -11
- package/dist/{PublicLoadingSpinner-Bq_-BeK-.d.ts → PublicLoadingSpinner-SL8WaQN7.d.ts} +2 -21
- package/dist/{api-H5A3H4IR.js → api-LUNF5O6M.js} +3 -3
- package/dist/{appConfig-BVGyuvI7.d.ts → appConfig-DjpeG6P-.d.ts} +9 -1
- package/dist/{appNameResolver-7GHF5ED2.js → appNameResolver-UURKN7NF.js} +2 -2
- package/dist/{audit-BUW3LMJB.js → audit-6TOCAMKO.js} +2 -2
- package/dist/{chunk-NRK4AIHQ.js → chunk-2DFCT6D3.js} +3 -3
- package/dist/{chunk-GIO7BFE7.js → chunk-3JKVVLD3.js} +66 -169
- package/dist/{chunk-GIO7BFE7.js.map → chunk-3JKVVLD3.js.map} +1 -1
- package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
- package/dist/chunk-5BSLGBYI.js.map +1 -0
- package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
- package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
- package/dist/{chunk-EL2O4IUX.js → chunk-ASXSJGPW.js} +20 -24
- package/dist/{chunk-EL2O4IUX.js.map → chunk-ASXSJGPW.js.map} +1 -1
- package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
- package/dist/chunk-B2WTCLCV.js.map +1 -0
- package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
- package/dist/chunk-D7ARGIA3.js.map +1 -0
- package/dist/{chunk-MYP2EGHX.js → chunk-GIDCWCHF.js} +21 -14
- package/dist/chunk-GIDCWCHF.js.map +1 -0
- package/dist/{chunk-MSFACPQQ.js → chunk-HYNGIE5T.js} +11 -11
- package/dist/{chunk-MSFACPQQ.js.map → chunk-HYNGIE5T.js.map} +1 -1
- package/dist/{chunk-TRIZ7IB7.js → chunk-I5GID3EX.js} +148 -288
- package/dist/chunk-I5GID3EX.js.map +1 -0
- package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
- package/dist/chunk-KLPVOPRI.js.map +1 -0
- package/dist/{chunk-BC3S53OZ.js → chunk-N6XMGSGD.js} +30 -14
- package/dist/chunk-N6XMGSGD.js.map +1 -0
- package/dist/{chunk-6MTY77WU.js → chunk-QB4GXDUM.js} +3 -3
- package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
- package/dist/chunk-STT7INZR.js.map +1 -0
- package/dist/{chunk-NYUJ4FJR.js → chunk-UETTVYKU.js} +7 -7
- package/dist/chunk-UETTVYKU.js.map +1 -0
- package/dist/{chunk-22KLBHPS.js → chunk-W66AZIOH.js} +2 -2
- package/dist/chunk-W66AZIOH.js.map +1 -0
- package/dist/{chunk-NZ655MWE.js → chunk-YEHO6FDW.js} +5 -4
- package/dist/chunk-YEHO6FDW.js.map +1 -0
- package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
- package/dist/chunk-YNUBMSMV.js.map +1 -0
- package/dist/{chunk-74C6SNEC.js → chunk-ZPK5656W.js} +3 -3
- package/dist/{chunk-74C6SNEC.js.map → chunk-ZPK5656W.js.map} +1 -1
- package/dist/components.d.ts +22 -899
- package/dist/components.js +436 -3118
- package/dist/components.js.map +1 -1
- package/dist/file-reference-9xUOnwyt.d.ts +70 -0
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +10 -10
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +49 -9
- package/dist/index.js +190 -25
- package/dist/index.js.map +1 -1
- package/dist/{organisation-CO3Sh3_D.d.ts → organisation-t-vvQC3g.d.ts} +1 -8
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +5 -5
- package/dist/rbac/index.d.ts +65 -46
- package/dist/rbac/index.js +10 -12
- package/dist/styles/core.css +0 -125
- package/dist/types.d.ts +2 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/dist/{usePublicRouteParams-B2OcAsur.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +1 -1
- package/dist/utils.d.ts +3 -4
- package/dist/utils.js +44 -13
- package/dist/utils.js.map +1 -1
- package/docs/FILE_REFERENCE_SYSTEM.md +440 -0
- package/docs/INDEX.md +7 -5
- package/docs/README.md +0 -1
- package/docs/api/README.md +0 -4
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.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/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +12 -12
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +6 -6
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +281 -0
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.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/DataAccessRecord.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/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.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/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 +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.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/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.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/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.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 +1 -1
- 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/UsePublicRouteParamsReturn.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 +204 -200
- package/docs/api-reference/components.md +141 -163
- package/docs/api-reference/hooks.md +347 -0
- package/docs/core-concepts/rbac-system.md +69 -16
- package/docs/getting-started/examples/basic-auth-app.md +0 -1
- package/docs/implementation-guides/datatable-rbac-usage.md +12 -11
- package/docs/implementation-guides/file-upload-storage.md +733 -0
- package/docs/implementation-guides/inactivity-tracking.md +779 -0
- package/docs/implementation-guides/organisation-security.md +748 -0
- package/docs/implementation-guides/public-pages-advanced.md +1022 -0
- package/docs/migration/MIGRATION_GUIDE.md +684 -0
- package/docs/migration/README.md +13 -2
- package/docs/migration/rbac-migration.md +73 -0
- package/docs/rbac/examples/rbac-rls-integration-example.md +11 -13
- package/docs/style-guide.md +269 -1
- package/package.json +1 -1
- package/src/__tests__/TESTING_GUIDELINES.md +331 -18
- package/src/__tests__/helpers/supabaseMock.ts +99 -0
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -7
- package/src/__tests__/shared.ts +6 -0
- package/src/components/DataTable/components/ActionButtons.tsx +2 -2
- package/src/components/DataTable/components/DataTableCore.tsx +2 -2
- package/src/components/DataTable/components/UnifiedTableBody.tsx +1 -1
- package/src/components/DataTable/utils/debugTools.ts +2 -2
- package/src/components/Dialog/Dialog.test.tsx +12 -2
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +6 -6
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +2 -2
- package/src/components/FileDisplay.tsx +233 -0
- package/src/components/FileUpload.tsx +176 -0
- package/src/components/Footer/Footer.test.tsx +7 -7
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +13 -6
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +30 -3
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +558 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
- package/src/components/PublicLayout/PublicPageDebugger.tsx +2 -2
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +2 -2
- package/src/components/PublicLayout/PublicPageProvider.tsx +2 -2
- package/src/components/Select/Select.test.tsx +50 -15
- package/src/components/SuperAdminGuard.tsx +2 -2
- package/src/components/__tests__/SuperAdminGuard.test.tsx +559 -0
- package/src/components/index.ts +0 -183
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +1 -1
- package/src/hooks/__tests__/useRBAC.unit.test.ts +191 -138
- package/src/hooks/public/usePublicEvent.ts +2 -2
- package/src/hooks/useAppConfig.ts +3 -3
- package/src/hooks/useComponentPerformance.ts +1 -1
- package/src/hooks/useDataTablePerformance.ts +1 -1
- package/src/hooks/useFileReference.ts +232 -0
- package/src/hooks/useOrganisationPermissions.test.ts +254 -344
- package/src/hooks/useOrganisationPermissions.ts +15 -7
- package/src/hooks/useOrganisationSecurity.test.ts +390 -402
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePermissionCache.test.ts +264 -395
- package/src/hooks/usePermissionCache.ts +34 -4
- package/src/hooks/useSecureDataAccess.test.ts +486 -0
- package/src/hooks/useSecureDataAccess.ts +4 -1
- package/src/providers/InactivityProvider.tsx +2 -2
- package/src/providers/OrganisationProvider.test.simple.tsx +168 -0
- package/src/providers/OrganisationProvider.test.tsx +168 -0
- package/src/providers/OrganisationProvider.tsx +25 -31
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +205 -0
- package/src/providers/UnifiedAuthProvider.test.tsx +128 -0
- package/src/providers/__tests__/InactivityProvider.test.tsx +3 -4
- package/src/providers/__tests__/OrganisationProvider.test.tsx +19 -14
- package/src/rbac/__tests__/integration.authflow.test.tsx +123 -0
- package/src/rbac/__tests__/integration.navigation.test.tsx +72 -0
- package/src/rbac/__tests__/integration.securedata.test.tsx +92 -0
- package/src/rbac/__tests__/integration.smoke.test.tsx +73 -0
- package/src/rbac/__tests__/rbac-core.test.tsx +26 -22
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +411 -0
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +285 -0
- package/src/rbac/__tests__/rbac-functions.test.ts +655 -0
- package/src/rbac/__tests__/rbac-integration.test.ts +532 -0
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +196 -0
- package/src/rbac/api.test.ts +6 -6
- package/src/rbac/api.ts +2 -2
- package/src/rbac/audit.test.ts +485 -0
- package/src/rbac/audit.ts +7 -1
- package/src/rbac/cache-invalidation.ts +318 -0
- package/src/rbac/cache.test.ts +286 -0
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +559 -0
- package/src/rbac/components/EnhancedNavigationMenu.tsx +29 -23
- package/src/rbac/components/NavigationProvider.test.tsx +449 -0
- package/src/rbac/components/PagePermissionGuard.tsx +4 -4
- package/src/rbac/components/PagePermissionProvider.test.tsx +479 -0
- package/src/rbac/components/SecureDataProvider.test.tsx +511 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +159 -430
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +4 -5
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +112 -118
- package/src/rbac/config.test.ts +410 -0
- package/src/rbac/engine.test.simple.ts +237 -0
- package/src/rbac/engine.test.ts +233 -0
- package/src/rbac/engine.ts +37 -41
- package/src/rbac/examples/CompleteRBACExample.tsx +3 -3
- package/src/rbac/examples/EventBasedApp.tsx +4 -4
- package/src/rbac/hooks/useRBAC.simple.test.ts +16 -0
- package/src/rbac/hooks/useRBAC.test.ts +207 -455
- package/src/rbac/hooks/useRBAC.ts +30 -22
- package/src/rbac/permissions.test.ts +128 -0
- package/src/rbac/permissions.ts +56 -141
- package/src/rbac/providers/RBACProvider.tsx +1 -1
- package/src/rbac/secureClient.test.ts +444 -0
- package/src/rbac/security.test.ts +390 -0
- package/src/rbac/security.ts +1 -1
- package/src/rbac/types.test.ts +382 -0
- package/src/rbac/types.ts +2 -2
- package/src/styles/core.css +0 -125
- package/src/types/file-reference.ts +77 -0
- package/src/types/rbac-functions.ts +290 -0
- package/src/types/supabase.ts +10 -28
- package/src/types/unified.ts +4 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +81 -55
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +21 -12
- package/src/utils/__tests__/organisationContext.unit.test.ts +13 -7
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +3 -3
- package/src/utils/__tests__/sessionTracking.unit.test.ts +32 -12
- package/src/utils/appConfig.ts +1 -1
- package/src/utils/appIdResolver.test.ts +503 -0
- package/src/utils/appIdResolver.ts +1 -1
- package/src/utils/appNameResolver.test.ts +494 -0
- package/src/utils/appNameResolver.ts +3 -2
- package/src/utils/bundleAnalysis.ts +3 -3
- package/src/utils/debugLogger.ts +1 -1
- package/src/utils/file-reference.ts +263 -0
- package/src/utils/formatDate.test.ts +2 -2
- package/src/utils/organisationContext.test.ts +340 -0
- package/src/utils/organisationContext.ts +19 -6
- package/src/utils/performanceBudgets.ts +2 -2
- package/src/utils/permissionUtils.test.ts +393 -0
- package/src/utils/permissionUtils.ts +5 -2
- package/src/utils/secureDataAccess.test.ts +715 -0
- package/src/utils/secureDataAccess.ts +21 -5
- package/src/utils/sessionTracking.ts +34 -4
- package/src/utils/storage/__tests__/helpers.unit.test.ts +328 -0
- package/src/utils/storage/__tests__/index.unit.test.ts +16 -0
- package/src/utils/storage/helpers.ts +20 -25
- package/src/utils/storage/index.ts +29 -1
- package/src/vite-env.d.ts +17 -0
- package/dist/chunk-22KLBHPS.js.map +0 -1
- package/dist/chunk-7BNPOCLL.js.map +0 -1
- package/dist/chunk-BC3S53OZ.js.map +0 -1
- package/dist/chunk-GWSBHC4J.js.map +0 -1
- package/dist/chunk-MYP2EGHX.js.map +0 -1
- package/dist/chunk-MZBUOP4P.js.map +0 -1
- package/dist/chunk-NYUJ4FJR.js.map +0 -1
- package/dist/chunk-NZ655MWE.js.map +0 -1
- package/dist/chunk-SS3E6QLB.js.map +0 -1
- package/dist/chunk-TRIZ7IB7.js.map +0 -1
- package/dist/chunk-WJARTBCT.js.map +0 -1
- package/dist/chunk-YDJW5XTN.js.map +0 -1
- package/docs/print-components/README.md +0 -258
- package/docs/print-components/api-reference.md +0 -636
- package/docs/print-components/examples/README.md +0 -204
- package/docs/print-components/examples/basic-report.tsx +0 -92
- package/docs/print-components/examples/card-catalog.tsx +0 -149
- package/docs/print-components/examples/cover-page-report.tsx +0 -163
- package/docs/print-components/quick-start.md +0 -363
- package/src/components/PrintButton/PrintButton.tsx +0 -321
- package/src/components/PrintButton/PrintButtonGroup.tsx +0 -84
- package/src/components/PrintButton/PrintToolbar.tsx +0 -94
- package/src/components/PrintButton/__tests__/PrintButton.test.tsx +0 -271
- package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +0 -438
- package/src/components/PrintButton/index.ts +0 -33
- package/src/components/PrintButton/types.ts +0 -173
- package/src/components/PrintCard/PrintCard.tsx +0 -154
- package/src/components/PrintCard/PrintCardContent.tsx +0 -57
- package/src/components/PrintCard/PrintCardFooter.tsx +0 -60
- package/src/components/PrintCard/PrintCardGrid.tsx +0 -91
- package/src/components/PrintCard/PrintCardHeader.tsx +0 -78
- package/src/components/PrintCard/PrintCardImage.tsx +0 -81
- package/src/components/PrintCard/examples/PrintCardShowcase.tsx +0 -239
- package/src/components/PrintCard/index.ts +0 -34
- package/src/components/PrintCard/types.ts +0 -171
- package/src/components/PrintDataTable/PrintDataTable.tsx +0 -215
- package/src/components/PrintDataTable/PrintTableGroup.tsx +0 -90
- package/src/components/PrintDataTable/PrintTableRow.tsx +0 -76
- package/src/components/PrintDataTable/index.ts +0 -25
- package/src/components/PrintDataTable/types.ts +0 -67
- package/src/components/PrintFooter/PrintFooter.tsx +0 -183
- package/src/components/PrintFooter/PrintFooterContent.tsx +0 -71
- package/src/components/PrintFooter/PrintFooterInfo.tsx +0 -86
- package/src/components/PrintFooter/PrintPageNumber.tsx +0 -90
- package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +0 -390
- package/src/components/PrintFooter/index.ts +0 -30
- package/src/components/PrintFooter/types.ts +0 -149
- package/src/components/PrintGrid/PrintGrid.tsx +0 -180
- package/src/components/PrintGrid/PrintGridBreakpoint.tsx +0 -109
- package/src/components/PrintGrid/PrintGridContainer.tsx +0 -128
- package/src/components/PrintGrid/PrintGridItem.tsx +0 -220
- package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +0 -359
- package/src/components/PrintGrid/index.ts +0 -31
- package/src/components/PrintGrid/types.ts +0 -159
- package/src/components/PrintHeader/PrintCoverHeader.tsx +0 -230
- package/src/components/PrintHeader/PrintHeader.tsx +0 -150
- package/src/components/PrintHeader/index.ts +0 -17
- package/src/components/PrintHeader/types.ts +0 -42
- package/src/components/PrintLayout/PrintLayout.tsx +0 -122
- package/src/components/PrintLayout/PrintLayoutContext.tsx +0 -66
- package/src/components/PrintLayout/PrintPageBreak.tsx +0 -52
- package/src/components/PrintLayout/examples/PrintShowcase.tsx +0 -230
- package/src/components/PrintLayout/index.ts +0 -19
- package/src/components/PrintLayout/types.ts +0 -37
- package/src/components/PrintPageBreak/PrintPageBreak.tsx +0 -120
- package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +0 -90
- package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +0 -112
- package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +0 -279
- package/src/components/PrintPageBreak/index.ts +0 -23
- package/src/components/PrintPageBreak/types.ts +0 -94
- package/src/components/PrintSection/PrintColumn.tsx +0 -104
- package/src/components/PrintSection/PrintDivider.tsx +0 -101
- package/src/components/PrintSection/PrintSection.tsx +0 -129
- package/src/components/PrintSection/PrintSectionContent.tsx +0 -75
- package/src/components/PrintSection/PrintSectionHeader.tsx +0 -97
- package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +0 -258
- package/src/components/PrintSection/index.ts +0 -33
- package/src/components/PrintSection/types.ts +0 -155
- package/src/components/PrintText/PrintText.tsx +0 -116
- package/src/components/PrintText/index.ts +0 -16
- package/src/components/PrintText/types.ts +0 -24
- package/src/rbac/__tests__/integration.test.tsx +0 -218
- package/src/utils/print/PrintDataProcessor.ts +0 -390
- package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +0 -397
- package/src/utils/print/index.ts +0 -29
- package/src/utils/print/types.ts +0 -196
- package/src/utils/print/usePrintOptimization.ts +0 -272
- /package/dist/{DataTable-7FMFXA7A.js.map → DataTable-DJQTKX33.js.map} +0 -0
- /package/dist/{api-H5A3H4IR.js.map → api-LUNF5O6M.js.map} +0 -0
- /package/dist/{appNameResolver-7GHF5ED2.js.map → appNameResolver-UURKN7NF.js.map} +0 -0
- /package/dist/{audit-BUW3LMJB.js.map → audit-6TOCAMKO.js.map} +0 -0
- /package/dist/{chunk-NRK4AIHQ.js.map → chunk-2DFCT6D3.js.map} +0 -0
- /package/dist/{chunk-6MTY77WU.js.map → chunk-QB4GXDUM.js.map} +0 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// File Display Component
|
|
2
|
+
// Provides a file display interface using the file reference system
|
|
3
|
+
|
|
4
|
+
import React, { useState, useEffect } from 'react';
|
|
5
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
6
|
+
import { FileReference, FileCategory } from '../types/file-reference';
|
|
7
|
+
import { useFileReferenceForRecord } from '../hooks/useFileReference';
|
|
8
|
+
|
|
9
|
+
export interface FileDisplayProps {
|
|
10
|
+
supabase: SupabaseClient;
|
|
11
|
+
table_name: string;
|
|
12
|
+
record_id: string;
|
|
13
|
+
organisation_id: string;
|
|
14
|
+
category?: FileCategory;
|
|
15
|
+
showUpload?: boolean;
|
|
16
|
+
showDelete?: boolean;
|
|
17
|
+
className?: string;
|
|
18
|
+
children?: React.ReactNode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function FileDisplay({
|
|
22
|
+
supabase,
|
|
23
|
+
table_name,
|
|
24
|
+
record_id,
|
|
25
|
+
organisation_id,
|
|
26
|
+
category,
|
|
27
|
+
showUpload = false,
|
|
28
|
+
showDelete = false,
|
|
29
|
+
className = '',
|
|
30
|
+
children
|
|
31
|
+
}: FileDisplayProps) {
|
|
32
|
+
const {
|
|
33
|
+
isLoading,
|
|
34
|
+
error,
|
|
35
|
+
fileUrl,
|
|
36
|
+
fileReference,
|
|
37
|
+
fileReferences,
|
|
38
|
+
fileCount,
|
|
39
|
+
loadFileReference,
|
|
40
|
+
loadFileUrl,
|
|
41
|
+
loadFileReferences,
|
|
42
|
+
loadFileCount,
|
|
43
|
+
deleteFile,
|
|
44
|
+
clearError
|
|
45
|
+
} = useFileReferenceForRecord(supabase, table_name, record_id, organisation_id);
|
|
46
|
+
|
|
47
|
+
const [imageError, setImageError] = useState(false);
|
|
48
|
+
|
|
49
|
+
// Load file data on mount
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
loadFileCount();
|
|
52
|
+
if (category) {
|
|
53
|
+
loadFileReference();
|
|
54
|
+
} else {
|
|
55
|
+
loadFileReferences();
|
|
56
|
+
}
|
|
57
|
+
}, [loadFileCount, loadFileReference, loadFileReferences, category]);
|
|
58
|
+
|
|
59
|
+
// Load file URL when file reference is available
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (fileReference) {
|
|
62
|
+
loadFileUrl();
|
|
63
|
+
}
|
|
64
|
+
}, [fileReference, loadFileUrl]);
|
|
65
|
+
|
|
66
|
+
const handleDelete = async () => {
|
|
67
|
+
if (window.confirm('Are you sure you want to delete this file?')) {
|
|
68
|
+
const success = await deleteFile(true);
|
|
69
|
+
if (success) {
|
|
70
|
+
setImageError(false);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleImageError = () => {
|
|
76
|
+
setImageError(true);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const getFileIcon = (fileType: string) => {
|
|
80
|
+
if (fileType.startsWith('image/')) return '🖼️';
|
|
81
|
+
if (fileType.startsWith('video/')) return '🎥';
|
|
82
|
+
if (fileType.startsWith('audio/')) return '🎵';
|
|
83
|
+
if (fileType.includes('pdf')) return '📄';
|
|
84
|
+
if (fileType.includes('word')) return '📝';
|
|
85
|
+
if (fileType.includes('excel') || fileType.includes('spreadsheet')) return '📊';
|
|
86
|
+
if (fileType.includes('powerpoint') || fileType.includes('presentation')) return '📊';
|
|
87
|
+
return '📁';
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const formatFileSize = (bytes: number) => {
|
|
91
|
+
if (bytes === 0) return '0 Bytes';
|
|
92
|
+
const k = 1024;
|
|
93
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
94
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
95
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (isLoading) {
|
|
99
|
+
return (
|
|
100
|
+
<div className={`flex items-center justify-center p-4 ${className}`}>
|
|
101
|
+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-main-500"></div>
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (error) {
|
|
107
|
+
return (
|
|
108
|
+
<div className={`p-4 bg-acc-50 border border-acc-200 rounded-lg ${className}`}>
|
|
109
|
+
<div className="text-acc-600">
|
|
110
|
+
Error loading file: {error}
|
|
111
|
+
</div>
|
|
112
|
+
<button
|
|
113
|
+
onClick={clearError}
|
|
114
|
+
className="mt-2 text-sm text-acc-700 hover:text-acc-800 underline"
|
|
115
|
+
>
|
|
116
|
+
Try again
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (fileCount === 0) {
|
|
123
|
+
return (
|
|
124
|
+
<div className={`text-sec-500 text-center p-4 ${className}`}>
|
|
125
|
+
No files found
|
|
126
|
+
{children}
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Single file display (when category is specified)
|
|
132
|
+
if (category && fileReference) {
|
|
133
|
+
const isImage = fileReference.file_metadata.fileType?.startsWith('image/');
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<div className={`space-y-2 ${className}`}>
|
|
137
|
+
{isImage && fileUrl && !imageError ? (
|
|
138
|
+
<div className="relative">
|
|
139
|
+
<img
|
|
140
|
+
src={fileUrl}
|
|
141
|
+
alt={fileReference.file_metadata.fileName || 'File'}
|
|
142
|
+
className="max-w-full h-auto rounded-lg border border-sec-200"
|
|
143
|
+
onError={handleImageError}
|
|
144
|
+
/>
|
|
145
|
+
{showDelete && (
|
|
146
|
+
<button
|
|
147
|
+
onClick={handleDelete}
|
|
148
|
+
className="absolute top-2 right-2 bg-acc-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm hover:bg-acc-600"
|
|
149
|
+
title="Delete file"
|
|
150
|
+
>
|
|
151
|
+
×
|
|
152
|
+
</button>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
) : (
|
|
156
|
+
<div className="flex items-center space-x-3 p-3 bg-sec-50 rounded-lg border border-sec-200">
|
|
157
|
+
<span className="text-2xl">
|
|
158
|
+
{getFileIcon(fileReference.file_metadata.fileType || '')}
|
|
159
|
+
</span>
|
|
160
|
+
<div className="flex-1 min-w-0">
|
|
161
|
+
<div className="font-medium text-sec-900 truncate">
|
|
162
|
+
{fileReference.file_metadata.fileName || 'Unknown file'}
|
|
163
|
+
</div>
|
|
164
|
+
<div className="text-sm text-sec-500">
|
|
165
|
+
{fileReference.file_metadata.fileSize && formatFileSize(fileReference.file_metadata.fileSize)}
|
|
166
|
+
{fileReference.file_metadata.fileType && ` • ${fileReference.file_metadata.fileType}`}
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
{showDelete && (
|
|
170
|
+
<button
|
|
171
|
+
onClick={handleDelete}
|
|
172
|
+
className="text-acc-500 hover:text-acc-700 p-1"
|
|
173
|
+
title="Delete file"
|
|
174
|
+
>
|
|
175
|
+
×
|
|
176
|
+
</button>
|
|
177
|
+
)}
|
|
178
|
+
</div>
|
|
179
|
+
)}
|
|
180
|
+
{children}
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Multiple files display
|
|
186
|
+
return (
|
|
187
|
+
<div className={`space-y-2 ${className}`}>
|
|
188
|
+
{fileReferences.map((fileRef) => {
|
|
189
|
+
const isImage = fileRef.file_metadata.fileType?.startsWith('image/');
|
|
190
|
+
const fileUrl = fileRef.is_public
|
|
191
|
+
? `https://your-supabase-url.supabase.co/storage/v1/object/public/files/${fileRef.file_path}`
|
|
192
|
+
: null; // Would need to get signed URL for private files
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<div key={fileRef.id} className="flex items-center space-x-3 p-3 bg-sec-50 rounded-lg border border-sec-200">
|
|
196
|
+
{isImage && fileUrl ? (
|
|
197
|
+
<img
|
|
198
|
+
src={fileUrl}
|
|
199
|
+
alt={fileRef.file_metadata.fileName || 'File'}
|
|
200
|
+
className="w-12 h-12 object-cover rounded"
|
|
201
|
+
onError={handleImageError}
|
|
202
|
+
/>
|
|
203
|
+
) : (
|
|
204
|
+
<span className="text-2xl">
|
|
205
|
+
{getFileIcon(fileRef.file_metadata.fileType || '')}
|
|
206
|
+
</span>
|
|
207
|
+
)}
|
|
208
|
+
<div className="flex-1 min-w-0">
|
|
209
|
+
<div className="font-medium text-sec-900 truncate">
|
|
210
|
+
{fileRef.file_metadata.fileName || 'Unknown file'}
|
|
211
|
+
</div>
|
|
212
|
+
<div className="text-sm text-sec-500">
|
|
213
|
+
{fileRef.file_metadata.fileSize && formatFileSize(fileRef.file_metadata.fileSize)}
|
|
214
|
+
{fileRef.file_metadata.fileType && ` • ${fileRef.file_metadata.fileType}`}
|
|
215
|
+
{fileRef.file_metadata.category && ` • ${fileRef.file_metadata.category}`}
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
{showDelete && (
|
|
219
|
+
<button
|
|
220
|
+
onClick={() => deleteFile(true)}
|
|
221
|
+
className="text-acc-500 hover:text-acc-700 p-1"
|
|
222
|
+
title="Delete file"
|
|
223
|
+
>
|
|
224
|
+
×
|
|
225
|
+
</button>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
})}
|
|
230
|
+
{children}
|
|
231
|
+
</div>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// File Upload Component
|
|
2
|
+
// Provides a file upload interface using the file reference system
|
|
3
|
+
|
|
4
|
+
import React, { useState, useCallback, useRef } from 'react';
|
|
5
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
6
|
+
import { FileCategory } from '../types/file-reference';
|
|
7
|
+
import { useFileReference } from '../hooks/useFileReference';
|
|
8
|
+
|
|
9
|
+
export interface FileUploadProps {
|
|
10
|
+
supabase: SupabaseClient;
|
|
11
|
+
table_name: string;
|
|
12
|
+
record_id: string;
|
|
13
|
+
organisation_id: string;
|
|
14
|
+
app_id: string;
|
|
15
|
+
category: FileCategory;
|
|
16
|
+
accept?: string;
|
|
17
|
+
maxSize?: number;
|
|
18
|
+
multiple?: boolean;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
className?: string;
|
|
21
|
+
onUploadSuccess?: (result: any) => void;
|
|
22
|
+
onUploadError?: (error: string) => void;
|
|
23
|
+
children?: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function FileUpload({
|
|
27
|
+
supabase,
|
|
28
|
+
table_name,
|
|
29
|
+
record_id,
|
|
30
|
+
organisation_id,
|
|
31
|
+
app_id,
|
|
32
|
+
category,
|
|
33
|
+
accept = '*/*',
|
|
34
|
+
maxSize = 10 * 1024 * 1024, // 10MB default
|
|
35
|
+
multiple = false,
|
|
36
|
+
disabled = false,
|
|
37
|
+
className = '',
|
|
38
|
+
onUploadSuccess,
|
|
39
|
+
onUploadError,
|
|
40
|
+
children
|
|
41
|
+
}: FileUploadProps) {
|
|
42
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
43
|
+
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
44
|
+
const { uploadFile, isLoading, error } = useFileReference(supabase);
|
|
45
|
+
|
|
46
|
+
const handleFileSelect = useCallback(async (files: FileList | null) => {
|
|
47
|
+
if (!files || files.length === 0) return;
|
|
48
|
+
|
|
49
|
+
const fileArray = Array.from(files);
|
|
50
|
+
|
|
51
|
+
// Validate file sizes
|
|
52
|
+
const oversizedFiles = fileArray.filter(file => file.size > maxSize);
|
|
53
|
+
if (oversizedFiles.length > 0) {
|
|
54
|
+
const errorMessage = `Files exceed maximum size of ${Math.round(maxSize / 1024 / 1024)}MB: ${oversizedFiles.map(f => f.name).join(', ')}`;
|
|
55
|
+
onUploadError?.(errorMessage);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Upload files
|
|
60
|
+
for (const file of fileArray) {
|
|
61
|
+
try {
|
|
62
|
+
const result = await uploadFile({
|
|
63
|
+
table_name,
|
|
64
|
+
record_id,
|
|
65
|
+
organisation_id,
|
|
66
|
+
app_id,
|
|
67
|
+
category,
|
|
68
|
+
is_public: false
|
|
69
|
+
}, file);
|
|
70
|
+
|
|
71
|
+
if (result) {
|
|
72
|
+
onUploadSuccess?.(result);
|
|
73
|
+
} else {
|
|
74
|
+
onUploadError?.('Upload failed');
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
const errorMessage = err instanceof Error ? err.message : 'Upload failed';
|
|
78
|
+
onUploadError?.(errorMessage);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}, [uploadFile, table_name, record_id, organisation_id, app_id, category, maxSize, onUploadSuccess, onUploadError]);
|
|
82
|
+
|
|
83
|
+
const handleDragOver = useCallback((e: React.DragEvent) => {
|
|
84
|
+
e.preventDefault();
|
|
85
|
+
e.stopPropagation();
|
|
86
|
+
if (!disabled) {
|
|
87
|
+
setIsDragging(true);
|
|
88
|
+
}
|
|
89
|
+
}, [disabled]);
|
|
90
|
+
|
|
91
|
+
const handleDragLeave = useCallback((e: React.DragEvent) => {
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
e.stopPropagation();
|
|
94
|
+
setIsDragging(false);
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
97
|
+
const handleDrop = useCallback((e: React.DragEvent) => {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
e.stopPropagation();
|
|
100
|
+
setIsDragging(false);
|
|
101
|
+
|
|
102
|
+
if (disabled) return;
|
|
103
|
+
|
|
104
|
+
const files = e.dataTransfer.files;
|
|
105
|
+
handleFileSelect(files);
|
|
106
|
+
}, [disabled, handleFileSelect]);
|
|
107
|
+
|
|
108
|
+
const handleFileInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
109
|
+
handleFileSelect(e.target.files);
|
|
110
|
+
// Reset input value to allow re-uploading the same file
|
|
111
|
+
if (e.target) {
|
|
112
|
+
e.target.value = '';
|
|
113
|
+
}
|
|
114
|
+
}, [handleFileSelect]);
|
|
115
|
+
|
|
116
|
+
const handleClick = useCallback(() => {
|
|
117
|
+
if (!disabled && fileInputRef.current) {
|
|
118
|
+
fileInputRef.current.click();
|
|
119
|
+
}
|
|
120
|
+
}, [disabled]);
|
|
121
|
+
|
|
122
|
+
const dragClasses = isDragging ? 'border-main-500 bg-main-50' : 'border-sec-300 hover:border-sec-400';
|
|
123
|
+
const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:bg-sec-50';
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div
|
|
127
|
+
className={`relative border-2 border-dashed rounded-lg p-6 text-center transition-colors ${dragClasses} ${disabledClasses} ${className}`}
|
|
128
|
+
onDragOver={handleDragOver}
|
|
129
|
+
onDragLeave={handleDragLeave}
|
|
130
|
+
onDrop={handleDrop}
|
|
131
|
+
onClick={handleClick}
|
|
132
|
+
>
|
|
133
|
+
<input
|
|
134
|
+
ref={fileInputRef}
|
|
135
|
+
type="file"
|
|
136
|
+
accept={accept}
|
|
137
|
+
multiple={multiple}
|
|
138
|
+
onChange={handleFileInputChange}
|
|
139
|
+
className="hidden"
|
|
140
|
+
disabled={disabled}
|
|
141
|
+
/>
|
|
142
|
+
|
|
143
|
+
{children || (
|
|
144
|
+
<div className="space-y-2">
|
|
145
|
+
<div className="text-sec-600">
|
|
146
|
+
{isDragging ? (
|
|
147
|
+
'Drop files here...'
|
|
148
|
+
) : (
|
|
149
|
+
<>
|
|
150
|
+
<span className="font-medium">Click to upload</span>
|
|
151
|
+
{' '}or drag and drop
|
|
152
|
+
</>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
<div className="text-sm text-sec-500">
|
|
156
|
+
{accept !== '*/*' && `Accepted formats: ${accept}`}
|
|
157
|
+
{maxSize && ` • Max size: ${Math.round(maxSize / 1024 / 1024)}MB`}
|
|
158
|
+
{multiple && ' • Multiple files allowed'}
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
|
|
163
|
+
{isLoading && (
|
|
164
|
+
<div className="absolute inset-0 bg-white bg-opacity-75 flex items-center justify-center">
|
|
165
|
+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-main-500"></div>
|
|
166
|
+
</div>
|
|
167
|
+
)}
|
|
168
|
+
|
|
169
|
+
{error && (
|
|
170
|
+
<div className="mt-2 text-sm text-acc-600">
|
|
171
|
+
{error}
|
|
172
|
+
</div>
|
|
173
|
+
)}
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
@@ -47,13 +47,13 @@ describe('Footer Component', () => {
|
|
|
47
47
|
it('renders with default company name and year', () => {
|
|
48
48
|
renderWithProviders(<Footer />);
|
|
49
49
|
|
|
50
|
-
expect(screen.getByText(/© Copyright 2022–
|
|
50
|
+
expect(screen.getByText(/© Copyright 2022–2025 all rights reserved, Solvera Solutions Pty Ltd\./)).toBeInTheDocument();
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
it('renders with custom company name', () => {
|
|
54
54
|
renderWithProviders(<Footer companyName="Test Company Inc." />);
|
|
55
55
|
|
|
56
|
-
expect(screen.getByText(/© Copyright 2022–
|
|
56
|
+
expect(screen.getByText(/© Copyright 2022–2025 all rights reserved, Test Company Inc\./)).toBeInTheDocument();
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
it('renders with custom year', () => {
|
|
@@ -275,7 +275,7 @@ describe('Footer Component', () => {
|
|
|
275
275
|
it('handles empty string company name', () => {
|
|
276
276
|
renderWithProviders(<Footer companyName="" />);
|
|
277
277
|
|
|
278
|
-
expect(screen.getByText(/© Copyright 2022–
|
|
278
|
+
expect(screen.getByText(/© Copyright 2022–2025 all rights reserved, \./)).toBeInTheDocument();
|
|
279
279
|
});
|
|
280
280
|
|
|
281
281
|
it('handles zero year', () => {
|
|
@@ -300,20 +300,20 @@ describe('Footer Component', () => {
|
|
|
300
300
|
renderWithProviders(<Footer copyright="" />);
|
|
301
301
|
|
|
302
302
|
// When copyright is empty string, it falls back to default copyright
|
|
303
|
-
expect(screen.getByText(/© Copyright 2022–
|
|
303
|
+
expect(screen.getByText(/© Copyright 2022–2025 all rights reserved, Solvera Solutions Pty Ltd\./)).toBeInTheDocument();
|
|
304
304
|
});
|
|
305
305
|
|
|
306
306
|
it('handles special characters in company name', () => {
|
|
307
307
|
renderWithProviders(<Footer companyName="Company & Co. (Ltd.)" />);
|
|
308
308
|
|
|
309
|
-
expect(screen.getByText(/© Copyright 2022–
|
|
309
|
+
expect(screen.getByText(/© Copyright 2022–2025 all rights reserved, Company & Co\. \(Ltd\.\)\./)).toBeInTheDocument();
|
|
310
310
|
});
|
|
311
311
|
|
|
312
312
|
it('handles very long company name', () => {
|
|
313
313
|
const longName = 'A'.repeat(100);
|
|
314
314
|
renderWithProviders(<Footer companyName={longName} />);
|
|
315
315
|
|
|
316
|
-
expect(screen.getByText(new RegExp(`© Copyright 2022–
|
|
316
|
+
expect(screen.getByText(new RegExp(`© Copyright 2022–2025 all rights reserved, ${longName}\\.`))).toBeInTheDocument();
|
|
317
317
|
});
|
|
318
318
|
|
|
319
319
|
it('handles links with special characters', () => {
|
|
@@ -348,7 +348,7 @@ describe('Footer Component', () => {
|
|
|
348
348
|
renderWithProviders(<Footer />);
|
|
349
349
|
|
|
350
350
|
expect(screen.getByText(/Solvera Solutions Pty Ltd/)).toBeInTheDocument();
|
|
351
|
-
expect(screen.getByText(/
|
|
351
|
+
expect(screen.getByText(/2025/)).toBeInTheDocument();
|
|
352
352
|
});
|
|
353
353
|
|
|
354
354
|
it('overrides default values when props are provided', () => {
|
|
@@ -17,12 +17,12 @@ const mockAuthContext = {
|
|
|
17
17
|
user: { id: 'test-user', email: 'test@example.com' },
|
|
18
18
|
isAuthenticated: true,
|
|
19
19
|
isLoading: false,
|
|
20
|
-
hasPermission: vi.fn(),
|
|
21
|
-
hasRole: vi.fn(),
|
|
22
|
-
hasAccessLevel: vi.fn(),
|
|
20
|
+
hasPermission: vi.fn().mockReturnValue(true), // Allow all permissions
|
|
21
|
+
hasRole: vi.fn().mockReturnValue(true), // Allow all roles
|
|
22
|
+
hasAccessLevel: vi.fn().mockReturnValue(true), // Allow all access levels
|
|
23
23
|
permissions: ['dashboard:read', 'users:read'],
|
|
24
|
-
roles: ['user'],
|
|
25
|
-
accessLevel: AccessLevel.
|
|
24
|
+
roles: ['user', 'admin'], // Include admin role
|
|
25
|
+
accessLevel: AccessLevel.ADMIN, // Set to admin level
|
|
26
26
|
signOut: vi.fn(),
|
|
27
27
|
refreshSession: vi.fn(),
|
|
28
28
|
appName: 'Test App',
|
|
@@ -43,7 +43,7 @@ const mockAuthContext = {
|
|
|
43
43
|
|
|
44
44
|
// Mock the useUnifiedAuth hook
|
|
45
45
|
vi.mock('../../providers/UnifiedAuthProvider', () => ({
|
|
46
|
-
useUnifiedAuth: () => mockAuthContext,
|
|
46
|
+
useUnifiedAuth: vi.fn(() => mockAuthContext),
|
|
47
47
|
}));
|
|
48
48
|
|
|
49
49
|
// Mock console methods to avoid noise in tests
|
|
@@ -93,6 +93,12 @@ describe('NavigationMenu Component', () => {
|
|
|
93
93
|
|
|
94
94
|
beforeEach(() => {
|
|
95
95
|
vi.clearAllMocks();
|
|
96
|
+
|
|
97
|
+
// Ensure the mock functions are properly set up
|
|
98
|
+
mockAuthContext.hasPermission.mockReturnValue(true);
|
|
99
|
+
mockAuthContext.hasRole.mockReturnValue(true);
|
|
100
|
+
mockAuthContext.hasAccessLevel.mockReturnValue(true);
|
|
101
|
+
|
|
96
102
|
// Reset console mocks
|
|
97
103
|
console.log = vi.fn();
|
|
98
104
|
console.warn = vi.fn();
|
|
@@ -512,6 +518,7 @@ describe('NavigationMenu Component', () => {
|
|
|
512
518
|
|
|
513
519
|
it('handles permission-based navigation', async () => {
|
|
514
520
|
const user = userEvent.setup();
|
|
521
|
+
|
|
515
522
|
renderWithProviders(
|
|
516
523
|
<NavigationMenu
|
|
517
524
|
items={permissionBasedNavItems}
|
|
@@ -143,7 +143,20 @@ const defaultMockContext = {
|
|
|
143
143
|
describe('OrganisationSelector Component', () => {
|
|
144
144
|
beforeEach(() => {
|
|
145
145
|
vi.clearAllMocks();
|
|
146
|
-
|
|
146
|
+
|
|
147
|
+
// Re-setup the default mock context after clearing mocks
|
|
148
|
+
const mockContext = {
|
|
149
|
+
organisations: mockOrganisations,
|
|
150
|
+
selectedOrganisation: mockSelectedOrganisation,
|
|
151
|
+
isLoading: false,
|
|
152
|
+
error: null,
|
|
153
|
+
switchOrganisation: vi.fn().mockResolvedValue(undefined),
|
|
154
|
+
getUserRole: vi.fn().mockReturnValue('admin'),
|
|
155
|
+
validateOrganisationAccess: vi.fn().mockReturnValue(true), // Ensure all orgs are accessible by default
|
|
156
|
+
refreshOrganisations: vi.fn().mockResolvedValue(undefined),
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
mockUseOrganisations.mockReturnValue(mockContext);
|
|
147
160
|
});
|
|
148
161
|
|
|
149
162
|
afterEach(() => {
|
|
@@ -347,10 +360,17 @@ describe('OrganisationSelector Component', () => {
|
|
|
347
360
|
const user = userEvent.setup();
|
|
348
361
|
const onOrganisationChange = vi.fn();
|
|
349
362
|
const switchOrganisation = vi.fn().mockResolvedValue(undefined);
|
|
363
|
+
const validateOrganisationAccess = vi.fn().mockReturnValue(true);
|
|
350
364
|
|
|
351
365
|
mockUseOrganisations.mockReturnValue({
|
|
352
|
-
|
|
366
|
+
organisations: mockOrganisations,
|
|
367
|
+
selectedOrganisation: mockSelectedOrganisation,
|
|
368
|
+
isLoading: false,
|
|
369
|
+
error: null,
|
|
353
370
|
switchOrganisation,
|
|
371
|
+
getUserRole: vi.fn().mockReturnValue('admin'),
|
|
372
|
+
validateOrganisationAccess,
|
|
373
|
+
refreshOrganisations: vi.fn().mockResolvedValue(undefined),
|
|
354
374
|
});
|
|
355
375
|
|
|
356
376
|
renderWithProviders(
|
|
@@ -367,10 +387,17 @@ describe('OrganisationSelector Component', () => {
|
|
|
367
387
|
const user = userEvent.setup();
|
|
368
388
|
const onOrganisationChange = vi.fn();
|
|
369
389
|
const switchOrganisation = vi.fn().mockResolvedValue(undefined);
|
|
390
|
+
const validateOrganisationAccess = vi.fn().mockReturnValue(true);
|
|
370
391
|
|
|
371
392
|
mockUseOrganisations.mockReturnValue({
|
|
372
|
-
|
|
393
|
+
organisations: mockOrganisations,
|
|
394
|
+
selectedOrganisation: mockSelectedOrganisation,
|
|
395
|
+
isLoading: false,
|
|
396
|
+
error: null,
|
|
373
397
|
switchOrganisation,
|
|
398
|
+
getUserRole: vi.fn().mockReturnValue('admin'),
|
|
399
|
+
validateOrganisationAccess,
|
|
400
|
+
refreshOrganisations: vi.fn().mockResolvedValue(undefined),
|
|
374
401
|
});
|
|
375
402
|
|
|
376
403
|
renderWithProviders(
|
|
@@ -287,7 +287,7 @@ export function OrganisationSelector({
|
|
|
287
287
|
<div className="flex items-center gap-1 ml-4">
|
|
288
288
|
<Shield className="h-3 w-3 text-muted-foreground" />
|
|
289
289
|
<span className="text-xs text-muted-foreground capitalize">
|
|
290
|
-
{userRole
|
|
290
|
+
{userRole?.replace('_', ' ') || 'No Role'}
|
|
291
291
|
</span>
|
|
292
292
|
</div>
|
|
293
293
|
)}
|