@jmruthers/pace-core 0.5.76 → 0.5.78
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 +8 -0
- package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-Df3IozMG.d.ts} +10 -118
- package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
- package/dist/{DataTable-4GAVPIEG.js → DataTable-ETGVF4Y5.js} +50 -13
- package/dist/{PublicLoadingSpinner-BiNER8F5.d.ts → PublicLoadingSpinner-CnUaz0vG.d.ts} +5 -2
- package/dist/{UnifiedAuthProvider-Bj6YCf7c.d.ts → UnifiedAuthProvider-B391Aqum.d.ts} +42 -45
- package/dist/{UnifiedAuthProvider-3NKDOSOK.js → UnifiedAuthProvider-P5SOJAQ6.js} +4 -5
- package/dist/{api-DDMUKIUD.js → api-KG4A2X7P.js} +9 -3
- package/dist/{audit-6TOCAMKO.js → audit-65VNHEV2.js} +2 -2
- package/dist/{chunk-K34IM5CT.js → chunk-2OGV6IRV.js} +196 -626
- package/dist/chunk-2OGV6IRV.js.map +1 -0
- package/dist/{chunk-NTNILOBC.js → chunk-5BO3MI5Y.js} +4 -4
- package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
- package/dist/chunk-CVMVPYAL.js.map +1 -0
- package/dist/{chunk-URUTVZ7N.js → chunk-FL4ZCQLD.js} +2 -2
- package/dist/{chunk-LW7MMEAQ.js → chunk-FT2M4R4F.js} +2 -2
- package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
- package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
- package/dist/{chunk-KHJS6VIA.js → chunk-LRQ6RBJC.js} +157 -112
- package/dist/chunk-LRQ6RBJC.js.map +1 -0
- package/dist/{chunk-WN6XJWOS.js → chunk-MNJXXD6C.js} +274 -743
- package/dist/chunk-MNJXXD6C.js.map +1 -0
- package/dist/{chunk-KK73ZB4E.js → chunk-PTR5PMPE.js} +153 -132
- package/dist/chunk-PTR5PMPE.js.map +1 -0
- package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
- package/dist/chunk-Q7APDV6H.js.map +1 -0
- package/dist/{chunk-A4FUBC7B.js → chunk-QGVSOUJ2.js} +2 -4
- package/dist/{chunk-A4FUBC7B.js.map → chunk-QGVSOUJ2.js.map} +1 -1
- package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
- package/dist/chunk-S63MFSY6.js.map +1 -0
- package/dist/{chunk-AFGTSUAD.js → chunk-VSOKOFRF.js} +4 -4
- package/dist/chunk-WUXCWRL6.js +20 -0
- package/dist/chunk-WUXCWRL6.js.map +1 -0
- package/dist/{chunk-Y6TXWPJO.js → chunk-YVVGHRGI.js} +105 -31
- package/dist/chunk-YVVGHRGI.js.map +1 -0
- package/dist/{chunk-M5IWZRBT.js → chunk-ZMNXIJP4.js} +2187 -981
- package/dist/chunk-ZMNXIJP4.js.map +1 -0
- package/dist/components.d.ts +6 -6
- package/dist/components.js +14 -18
- package/dist/components.js.map +1 -1
- package/dist/{database-C3Szpi5J.d.ts → database-BXAfr2Y_.d.ts} +18 -0
- package/dist/hooks.d.ts +5 -5
- package/dist/hooks.js +8 -9
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +19 -27
- package/dist/index.js +21 -29
- package/dist/index.js.map +1 -1
- package/dist/{organisation-BtshODVF.d.ts → organisation-D6qRDtbF.d.ts} +1 -1
- package/dist/providers.d.ts +7 -21
- package/dist/providers.js +3 -10
- package/dist/rbac/index.d.ts +71 -221
- package/dist/rbac/index.js +15 -16
- package/dist/{types-CGX9Vyf5.d.ts → types-BDg1mAGG.d.ts} +36 -6
- package/dist/types.d.ts +3 -3
- package/dist/types.js +61 -18
- package/dist/types.js.map +1 -1
- package/dist/{unified-CM7T0aTK.d.ts → unified-DQ4VcT7H.d.ts} +1 -1
- package/dist/{usePublicRouteParams-B-CumWRc.d.ts → usePublicRouteParams-BlgwXweB.d.ts} +3 -3
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +52 -9
- package/dist/utils.js.map +1 -1
- package/docs/CONTENT_AUDIT_REPORT.md +253 -0
- package/docs/DOCUMENTATION_AUDIT.md +172 -0
- package/docs/README.md +142 -147
- package/docs/STYLE_GUIDE.md +37 -0
- package/docs/api/classes/ColumnFactory.md +17 -17
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +5 -5
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +8 -8
- package/docs/api/classes/RBACCache.md +35 -5
- package/docs/api/classes/RBACEngine.md +49 -20
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +4 -4
- 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/DataRecord.md +11 -0
- package/docs/api/interfaces/DataTableAction.md +65 -29
- package/docs/api/interfaces/DataTableColumn.md +36 -23
- package/docs/api/interfaces/DataTableProps.md +80 -38
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- 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/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 +11 -11
- package/docs/api/interfaces/NavigationContextType.md +9 -9
- 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 +7 -7
- 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 +16 -3
- 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/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/RBACLogger.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/SwitchProps.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 +94 -521
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +16 -16
- 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/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +251 -269
- package/docs/api-reference/components.md +193 -0
- package/docs/api-reference/hooks.md +265 -0
- package/docs/api-reference/providers.md +6 -0
- package/docs/api-reference/types.md +6 -0
- package/docs/api-reference/utilities.md +207 -0
- package/docs/architecture/README.md +6 -0
- package/docs/{database-schema-requirements.md → architecture/database-schema-requirements.md} +6 -0
- package/docs/architecture/rbac-security-architecture.md +258 -0
- package/docs/architecture/services.md +9 -1
- package/docs/best-practices/README.md +6 -0
- package/docs/best-practices/accessibility.md +6 -0
- package/docs/{common-patterns.md → best-practices/common-patterns.md} +6 -0
- package/docs/best-practices/deployment.md +6 -0
- package/docs/best-practices/performance.md +475 -2
- package/docs/best-practices/security.md +6 -0
- package/docs/best-practices/testing.md +6 -0
- package/docs/core-concepts/authentication.md +6 -0
- package/docs/core-concepts/events.md +6 -0
- package/docs/core-concepts/organisations.md +6 -0
- package/docs/core-concepts/permissions.md +6 -0
- package/docs/core-concepts/rbac-system.md +8 -0
- package/docs/documentation-index.md +121 -182
- package/docs/{consuming-app-vite-config.md → getting-started/consuming-app-vite-config.md} +6 -0
- package/docs/getting-started/documentation-index.md +40 -0
- package/docs/getting-started/examples/README.md +878 -35
- package/docs/{faq.md → getting-started/faq.md} +7 -1
- package/docs/getting-started/installation-guide.md +6 -0
- package/docs/{quick-reference.md → getting-started/quick-reference.md} +6 -0
- package/docs/implementation-guides/app-layout.md +6 -0
- package/docs/implementation-guides/authentication.md +1021 -0
- package/docs/implementation-guides/component-styling.md +6 -0
- package/docs/implementation-guides/data-tables.md +1264 -2076
- package/docs/implementation-guides/dynamic-colors.md +6 -0
- package/docs/implementation-guides/event-theming-summary.md +6 -0
- package/docs/{file-reference-system.md → implementation-guides/file-reference-system.md} +6 -0
- package/docs/implementation-guides/file-upload-storage.md +6 -0
- package/docs/implementation-guides/forms.md +6 -0
- package/docs/implementation-guides/inactivity-tracking.md +6 -0
- package/docs/implementation-guides/navigation.md +6 -0
- package/docs/implementation-guides/organisation-security.md +6 -0
- package/docs/implementation-guides/permission-enforcement.md +6 -0
- package/docs/implementation-guides/public-pages-advanced.md +6 -0
- package/docs/implementation-guides/public-pages.md +6 -0
- package/docs/migration/MIGRATION_GUIDE.md +827 -351
- package/docs/migration/README.md +7 -1
- package/docs/migration/organisation-context-timing-fix.md +6 -0
- package/docs/migration/rbac-migration.md +44 -1
- package/docs/migration/service-architecture.md +6 -0
- package/docs/migration/v0.4.15-tailwind-scanning.md +6 -0
- package/docs/migration/v0.4.16-css-first-approach.md +6 -0
- package/docs/migration/v0.4.17-source-path-fix.md +6 -0
- package/docs/rbac/README-rbac-rls-integration.md +6 -0
- package/docs/rbac/README.md +6 -0
- package/docs/rbac/advanced-patterns.md +6 -0
- package/docs/rbac/api-reference.md +7 -1
- package/docs/rbac/breaking-changes-v3.md +222 -0
- package/docs/rbac/examples/rbac-rls-integration-example.md +6 -0
- package/docs/rbac/examples.md +6 -0
- package/docs/rbac/getting-started.md +6 -0
- package/docs/rbac/migration-guide.md +260 -0
- package/docs/rbac/quick-start.md +70 -13
- package/docs/rbac/rbac-rls-integration.md +6 -0
- package/docs/rbac/super-admin-guide.md +6 -0
- package/docs/rbac/troubleshooting.md +6 -0
- package/docs/security/README.md +6 -0
- package/docs/security/checklist.md +6 -0
- package/docs/styles/README.md +7 -1
- package/docs/{usage.md → styles/usage.md} +6 -0
- package/docs/testing/README.md +6 -0
- package/docs/{visual-testing.md → testing/visual-testing.md} +6 -0
- package/docs/troubleshooting/README.md +387 -5
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +6 -0
- package/docs/troubleshooting/common-issues.md +6 -0
- package/docs/troubleshooting/database-view-compatibility.md +6 -0
- package/docs/troubleshooting/organisation-context-setup.md +6 -0
- package/docs/troubleshooting/react-hooks-issue-analysis.md +6 -0
- package/docs/troubleshooting/styling-issues.md +6 -0
- package/docs/troubleshooting/tailwind-content-scanning.md +6 -0
- package/package.json +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -1
- package/src/__tests__/helpers/test-providers.tsx +3 -53
- package/src/components/DataTable/DataTable.test.tsx +319 -0
- package/src/components/DataTable/DataTable.tsx +32 -11
- package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx.skip} +6 -4
- package/src/components/DataTable/__tests__/{DataTable.test.tsx → DataTable.test.tsx.skip} +6 -4
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +31 -9
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +601 -0
- package/src/components/DataTable/__tests__/keyboard.test.tsx +615 -0
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +639 -0
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx.skip +330 -0
- package/src/components/DataTable/components/AccessDeniedPage.tsx +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +88 -104
- package/src/components/DataTable/components/DataTableCore.tsx +309 -337
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +4 -2
- package/src/components/DataTable/components/DataTableModals.tsx +22 -1
- package/src/components/DataTable/components/EditableRow.tsx +69 -84
- package/src/components/DataTable/components/EmptyState.tsx +5 -1
- package/src/components/DataTable/components/ImportModal.tsx +65 -36
- package/src/components/DataTable/components/PaginationControls.tsx +40 -100
- package/src/components/DataTable/components/UnifiedTableBody.tsx +125 -148
- package/src/components/DataTable/context/DataTableContext.tsx +1 -1
- package/src/components/DataTable/core/ColumnFactory.ts +5 -0
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +12 -10
- package/src/components/DataTable/examples/HierarchicalExample.tsx +1 -1
- package/src/components/DataTable/examples/InitialPageSizeExample.tsx +1 -0
- package/src/components/DataTable/examples/PerformanceExample.tsx +1 -0
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +1 -5
- package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +167 -0
- package/src/components/DataTable/hooks/index.ts +7 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +32 -15
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +102 -0
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +89 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +117 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +71 -27
- package/src/components/DataTable/hooks/useDataTableState.ts +39 -11
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +33 -0
- package/src/components/DataTable/hooks/useHierarchicalState.ts +15 -1
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +447 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +94 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +10 -7
- package/src/components/DataTable/hooks/useTableHandlers.ts +174 -0
- package/src/components/DataTable/index.ts +12 -3
- package/src/components/DataTable/types.ts +129 -9
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +159 -22
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +111 -0
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +15 -29
- package/src/components/DataTable/utils/a11yUtils.ts +244 -0
- package/src/components/DataTable/utils/debugTools.ts +609 -0
- package/src/components/DataTable/utils/exportUtils.ts +114 -16
- package/src/components/DataTable/utils/flexibleImport.ts +202 -32
- package/src/components/DataTable/utils/hierarchicalUtils.ts +1 -1
- package/src/components/DataTable/utils/index.ts +2 -0
- package/src/components/DataTable/utils/paginationUtils.ts +350 -0
- package/src/components/DataTable/utils/rowUtils.ts +6 -5
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -24
- package/src/components/NavigationMenu/NavigationMenu.tsx +19 -8
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +1 -23
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +56 -6
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +137 -13
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +1 -1
- package/src/components/Select/Select.tsx +1 -0
- package/src/components/examples/PermissionExample.tsx +173 -0
- package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
- package/src/examples/PublicEventPage.tsx +274 -0
- package/src/examples/PublicPageApp.tsx +308 -0
- package/src/examples/PublicPageUsageExample.tsx +216 -0
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +12 -1
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +129 -17
- package/src/hooks/__tests__/useRBAC.unit.test.ts +151 -846
- package/src/hooks/useOrganisationPermissions.test.ts +42 -18
- package/src/hooks/useOrganisationPermissions.ts +12 -6
- package/src/hooks/useOrganisationSecurity.test.ts +138 -85
- package/src/hooks/useOrganisationSecurity.ts +41 -10
- package/src/index.ts +0 -1
- package/src/providers/AuthProvider.simplified.tsx +880 -0
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +8 -8
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +29 -19
- package/src/providers/index.ts +0 -1
- package/src/providers/services/EventServiceProvider.tsx +19 -15
- package/src/providers/services/InactivityServiceProvider.tsx +19 -15
- package/src/providers/services/OrganisationServiceProvider.tsx +19 -15
- package/src/providers/services/UnifiedAuthProvider.tsx +156 -127
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +1 -1
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -3
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +25 -27
- package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +313 -0
- package/src/rbac/__tests__/engine.comprehensive.test.ts +114 -348
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +28 -110
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +33 -85
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +2 -2
- package/src/rbac/adapters.tsx +26 -69
- package/src/rbac/api.test.ts +90 -27
- package/src/rbac/api.ts +61 -10
- package/src/rbac/audit.test.ts +33 -38
- package/src/rbac/audit.ts +21 -6
- package/src/rbac/cache.ts +33 -1
- package/src/rbac/components/NavigationGuard.tsx +11 -11
- package/src/rbac/components/NavigationProvider.test.tsx +11 -5
- package/src/rbac/components/NavigationProvider.tsx +37 -13
- package/src/rbac/components/PagePermissionGuard.tsx +111 -50
- package/src/rbac/components/PagePermissionProvider.tsx +5 -5
- package/src/rbac/components/PermissionEnforcer.tsx +11 -11
- package/src/rbac/components/RoleBasedRouter.tsx +5 -5
- package/src/rbac/components/SecureDataProvider.tsx +5 -5
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +8 -8
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +14 -14
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +12 -12
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +6 -6
- package/src/rbac/engine.test.simple.ts +19 -13
- package/src/rbac/engine.test.ts +1 -0
- package/src/rbac/engine.ts +330 -766
- package/src/rbac/errors.ts +156 -0
- package/src/rbac/hooks/usePermissions.ts +32 -10
- package/src/rbac/hooks/useRBAC.test.ts +126 -512
- package/src/rbac/hooks/useRBAC.ts +147 -193
- package/src/rbac/hooks/useResolvedScope.ts +12 -0
- package/src/rbac/index.ts +7 -4
- package/src/rbac/security.ts +109 -18
- package/src/rbac/types.ts +12 -1
- package/src/services/AuthService.ts +2 -15
- package/src/services/EventService.ts +43 -46
- package/src/services/OrganisationService.ts +51 -31
- package/src/services/__tests__/AuthService.test.ts +1 -1
- package/src/services/__tests__/EventService.test.ts +1 -1
- package/src/services/__tests__/OrganisationService.test.ts +1 -1
- package/src/services/base/BaseService.ts +8 -0
- package/src/styles/base.css +208 -0
- package/src/styles/semantic.css +24 -0
- package/src/types/database.generated.ts +7347 -0
- package/src/types/database.ts +20 -0
- package/src/utils/logger.ts +179 -0
- package/src/utils/organisationContext.ts +11 -4
- package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
- package/dist/appNameResolver-UURKN7NF.js +0 -22
- package/dist/audit-6TOCAMKO.js.map +0 -1
- package/dist/chunk-B2WTCLCV.js.map +0 -1
- package/dist/chunk-FGMFQSHX.js.map +0 -1
- package/dist/chunk-K34IM5CT.js.map +0 -1
- package/dist/chunk-KHJS6VIA.js.map +0 -1
- package/dist/chunk-KK73ZB4E.js.map +0 -1
- package/dist/chunk-M5IWZRBT.js.map +0 -1
- package/dist/chunk-ULBI5JGB.js +0 -109
- package/dist/chunk-ULBI5JGB.js.map +0 -1
- package/dist/chunk-WN6XJWOS.js.map +0 -1
- package/dist/chunk-XLZ7U46Z.js.map +0 -1
- package/dist/chunk-Y6TXWPJO.js.map +0 -1
- package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
- package/docs/TERMINOLOGY.md +0 -231
- package/docs/api/interfaces/RBACContextType.md +0 -468
- package/docs/api/interfaces/RBACProviderProps.md +0 -107
- package/docs/best-practices/performance-expansion.md +0 -473
- package/docs/breaking-changes.md +0 -179
- package/docs/consuming-app-example.md +0 -290
- package/docs/documentation-templates.md +0 -539
- package/docs/examples/navigation-menu-auth-fix.md +0 -344
- package/docs/getting-started/examples/basic-auth-app.md +0 -520
- package/docs/getting-started/examples/full-featured-app.md +0 -616
- package/docs/getting-started/quick-start.md +0 -376
- package/docs/implementation-guides/datatable-filtering.md +0 -313
- package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
- package/docs/implementation-guides/hierarchical-datatable.md +0 -850
- package/docs/implementation-guides/large-datasets.md +0 -281
- package/docs/implementation-guides/performance.md +0 -403
- package/docs/migration/quick-migration-guide.md +0 -320
- package/docs/migration-guide.md +0 -193
- package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
- package/docs/performance/README.md +0 -551
- package/docs/style-guide.md +0 -964
- package/docs/troubleshooting/authentication-issues.md +0 -334
- package/docs/troubleshooting/debugging.md +0 -1117
- package/docs/troubleshooting/migration.md +0 -918
- package/src/__tests__/hooks/usePermissions.test.ts +0 -261
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
- package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
- package/src/hooks/services/__tests__/useServiceHooks.test.tsx +0 -137
- package/src/hooks/services/usePermissions.ts +0 -70
- package/src/hooks/services/useRBACService.ts +0 -30
- package/src/hooks/usePermissionCheck.ts +0 -150
- package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
- package/src/providers/services/RBACServiceProvider.tsx +0 -79
- package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
- package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
- package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
- package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
- package/src/rbac/providers/RBACProvider.tsx +0 -645
- package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
- package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
- package/src/rbac/providers/index.ts +0 -11
- package/src/services/RBACService.ts +0 -522
- package/src/services/__tests__/RBACService.test.ts +0 -492
- package/src/services/interfaces/IRBACService.ts +0 -62
- package/src/utils/appNameResolver.test 2.ts +0 -494
- /package/dist/{DataTable-4GAVPIEG.js.map → DataTable-ETGVF4Y5.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-P5SOJAQ6.js.map} +0 -0
- /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
- /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
- /package/dist/{chunk-NTNILOBC.js.map → chunk-5BO3MI5Y.js.map} +0 -0
- /package/dist/{chunk-URUTVZ7N.js.map → chunk-FL4ZCQLD.js.map} +0 -0
- /package/dist/{chunk-LW7MMEAQ.js.map → chunk-FT2M4R4F.js.map} +0 -0
- /package/dist/{chunk-AFGTSUAD.js.map → chunk-VSOKOFRF.js.map} +0 -0
- /package/docs/{app.css.example → styles/app.css.example} +0 -0
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|
11
11
|
import { Alert, AlertDescription, AlertTitle } from '../../Alert/Alert';
|
|
12
12
|
import { Button } from '../../Button/Button';
|
|
13
|
+
import { createLogger } from '../../../utils/logger';
|
|
13
14
|
// Icons removed to avoid test mocking issues
|
|
14
15
|
|
|
15
16
|
// ============================================================================
|
|
@@ -45,6 +46,7 @@ export class DataTableErrorBoundary extends Component<
|
|
|
45
46
|
DataTableErrorBoundaryProps,
|
|
46
47
|
ErrorBoundaryState
|
|
47
48
|
> {
|
|
49
|
+
private logger = createLogger('DataTableErrorBoundary');
|
|
48
50
|
private retryTimeoutId: NodeJS.Timeout | null = null;
|
|
49
51
|
|
|
50
52
|
constructor(props: DataTableErrorBoundaryProps) {
|
|
@@ -74,7 +76,7 @@ export class DataTableErrorBoundary extends Component<
|
|
|
74
76
|
this.props.onError?.(error, errorInfo);
|
|
75
77
|
|
|
76
78
|
// Log error for debugging
|
|
77
|
-
|
|
79
|
+
this.logger.error('Error Boundary caught an error:', error, errorInfo);
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
componentWillUnmount() {
|
|
@@ -88,7 +90,7 @@ export class DataTableErrorBoundary extends Component<
|
|
|
88
90
|
const { retryCount } = this.state;
|
|
89
91
|
|
|
90
92
|
if (retryCount >= maxRetries) {
|
|
91
|
-
|
|
93
|
+
this.logger.warn('Error Boundary: Maximum retry attempts reached');
|
|
92
94
|
return;
|
|
93
95
|
}
|
|
94
96
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import React from 'react';
|
|
24
|
+
import React, { useEffect } from 'react';
|
|
25
25
|
import { ImportModal, type ImportModalConfig } from './ImportModal';
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -39,6 +39,11 @@ export interface DataTableModalsProps {
|
|
|
39
39
|
/** Configuration object for customizing import modal text content */
|
|
40
40
|
importModalConfig?: ImportModalConfig;
|
|
41
41
|
|
|
42
|
+
// Focus management
|
|
43
|
+
/** Function to store focus before opening modal */
|
|
44
|
+
onStoreFocus?: () => void;
|
|
45
|
+
/** Function to restore focus after closing modal */
|
|
46
|
+
onRestoreFocus?: () => void;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
/**
|
|
@@ -72,7 +77,23 @@ export function DataTableModals({
|
|
|
72
77
|
onCloseImportModal,
|
|
73
78
|
onImport,
|
|
74
79
|
importModalConfig,
|
|
80
|
+
onStoreFocus,
|
|
81
|
+
onRestoreFocus,
|
|
75
82
|
}: DataTableModalsProps) {
|
|
83
|
+
// Handle focus management for import modal
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (showImportModal) {
|
|
86
|
+
onStoreFocus?.();
|
|
87
|
+
} else {
|
|
88
|
+
// Restore focus when modal closes
|
|
89
|
+
const timeoutId = setTimeout(() => {
|
|
90
|
+
onRestoreFocus?.();
|
|
91
|
+
}, 100); // Small delay to ensure modal is fully closed
|
|
92
|
+
|
|
93
|
+
return () => clearTimeout(timeoutId);
|
|
94
|
+
}
|
|
95
|
+
}, [showImportModal, onStoreFocus, onRestoreFocus]);
|
|
96
|
+
|
|
76
97
|
return (
|
|
77
98
|
<>
|
|
78
99
|
<ImportModal
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { flexRender } from '@tanstack/react-table';
|
|
3
|
-
|
|
4
|
-
import { Input } from '../../Input/Input';
|
|
5
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../Select/Select';
|
|
2
|
+
import { flexRender, type Row, type Column } from '@tanstack/react-table';
|
|
3
|
+
|
|
6
4
|
import { Button } from '../../Button/Button';
|
|
7
|
-
import
|
|
5
|
+
import { Input } from '../../Input/Input';
|
|
6
|
+
import {
|
|
7
|
+
Select,
|
|
8
|
+
SelectContent,
|
|
9
|
+
SelectItem,
|
|
10
|
+
SelectTrigger,
|
|
11
|
+
SelectValue,
|
|
12
|
+
} from '../../Select/Select';
|
|
13
|
+
import { ActionButtons } from './ActionButtons';
|
|
14
|
+
import type { CellValue, DataRecord, DataTableAction, EditableColumnDef } from '../types';
|
|
8
15
|
|
|
9
16
|
interface EditableRowProps<TData extends DataRecord> {
|
|
10
|
-
row:
|
|
11
|
-
editingData: Record<string,
|
|
12
|
-
onEditingDataChange: (data: Record<string,
|
|
17
|
+
row: Row<TData>;
|
|
18
|
+
editingData: Record<string, CellValue>;
|
|
19
|
+
onEditingDataChange: (data: Record<string, CellValue>) => void;
|
|
13
20
|
onSave: () => void;
|
|
14
21
|
onCancel: () => void;
|
|
15
22
|
actions: DataTableAction<TData>[];
|
|
@@ -18,45 +25,32 @@ interface EditableRowProps<TData extends DataRecord> {
|
|
|
18
25
|
hierarchical?: boolean;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
value:
|
|
25
|
-
|
|
26
|
-
editingData: Record<string, any> = {},
|
|
27
|
-
placeholder?: string
|
|
28
|
+
const renderEditField = <TData extends DataRecord>(
|
|
29
|
+
column: Column<TData, unknown>,
|
|
30
|
+
value: CellValue,
|
|
31
|
+
onChange: (value: CellValue | Record<string, CellValue>) => void,
|
|
32
|
+
editingData: Record<string, CellValue> = {}
|
|
28
33
|
) => {
|
|
29
|
-
const columnDef = column.columnDef
|
|
30
|
-
|
|
31
|
-
// Check if column is editable (default: true)
|
|
34
|
+
const columnDef = column.columnDef as EditableColumnDef<TData>;
|
|
35
|
+
|
|
32
36
|
if (columnDef.editable === false) {
|
|
33
|
-
|
|
34
|
-
return <span className="text-sm text-gray-600">{value || ''}</span>;
|
|
37
|
+
return <span className="text-sm text-gray-600">{String(value ?? '')}</span>;
|
|
35
38
|
}
|
|
36
|
-
|
|
37
|
-
// Check for custom field type
|
|
39
|
+
|
|
38
40
|
if (columnDef.fieldType === 'select' && columnDef.fieldOptions) {
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const accessorKey = columnDef.editAccessorKey || column.id;
|
|
42
|
+
const currentValue = editingData[accessorKey] ?? value ?? '';
|
|
43
|
+
|
|
43
44
|
return (
|
|
44
45
|
<Select
|
|
45
|
-
value={String(
|
|
46
|
-
onValueChange={(newValue) => {
|
|
47
|
-
// If editAccessorKey is specified, update that field instead
|
|
48
|
-
if (columnDef.editAccessorKey) {
|
|
49
|
-
onChange({ [columnDef.editAccessorKey]: Number(newValue) });
|
|
50
|
-
} else {
|
|
51
|
-
onChange(newValue);
|
|
52
|
-
}
|
|
53
|
-
}}
|
|
46
|
+
value={String(currentValue)}
|
|
47
|
+
onValueChange={(newValue) => onChange({ [accessorKey]: newValue as CellValue })}
|
|
54
48
|
>
|
|
55
49
|
<SelectTrigger className="w-full h-7">
|
|
56
|
-
<SelectValue placeholder={
|
|
50
|
+
<SelectValue placeholder={`Select ${column.id}`} />
|
|
57
51
|
</SelectTrigger>
|
|
58
52
|
<SelectContent>
|
|
59
|
-
{columnDef.fieldOptions.map(
|
|
53
|
+
{columnDef.fieldOptions.map(option => (
|
|
60
54
|
<SelectItem key={option.value} value={String(option.value)}>
|
|
61
55
|
{option.label}
|
|
62
56
|
</SelectItem>
|
|
@@ -65,26 +59,36 @@ const renderEditField = (
|
|
|
65
59
|
</Select>
|
|
66
60
|
);
|
|
67
61
|
}
|
|
68
|
-
|
|
62
|
+
|
|
69
63
|
if (columnDef.fieldType === 'date') {
|
|
70
64
|
return (
|
|
71
65
|
<Input
|
|
72
66
|
type="date"
|
|
73
|
-
value={value
|
|
74
|
-
onChange={(e) => onChange(e.target.value)}
|
|
67
|
+
value={String(value ?? '')}
|
|
68
|
+
onChange={(e) => onChange(e.target.value as unknown as CellValue)}
|
|
75
69
|
className="w-full h-7"
|
|
76
70
|
/>
|
|
77
71
|
);
|
|
78
72
|
}
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
|
|
74
|
+
if (columnDef.fieldType === 'number') {
|
|
75
|
+
return (
|
|
76
|
+
<Input
|
|
77
|
+
type="number"
|
|
78
|
+
value={String(value ?? '')}
|
|
79
|
+
onChange={(e) => onChange(e.target.value as unknown as CellValue)}
|
|
80
|
+
className="w-full h-7"
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
81
85
|
return (
|
|
82
86
|
<Input
|
|
83
87
|
type="text"
|
|
84
|
-
value={value
|
|
85
|
-
onChange={(e) => onChange(e.target.value)}
|
|
88
|
+
value={String(value ?? '')}
|
|
89
|
+
onChange={(e) => onChange(e.target.value as unknown as CellValue)}
|
|
86
90
|
className="w-full h-7"
|
|
87
|
-
placeholder={
|
|
91
|
+
placeholder={`Enter ${column.id}`}
|
|
88
92
|
/>
|
|
89
93
|
);
|
|
90
94
|
};
|
|
@@ -102,62 +106,43 @@ export function EditableRow<TData extends DataRecord>({
|
|
|
102
106
|
}: EditableRowProps<TData>) {
|
|
103
107
|
const rowId = getRowId ? getRowId(row.original, row.index) : String(row.id);
|
|
104
108
|
|
|
105
|
-
// Convert DataTableAction to ActionButtons format with proper disabled handling
|
|
106
|
-
const convertedActions = actions.map(action => ({
|
|
107
|
-
label: action.label,
|
|
108
|
-
onClick: action.onClick,
|
|
109
|
-
icon: action.icon,
|
|
110
|
-
variant: action.variant === 'destructive' ? 'destructive' as const : 'default' as const,
|
|
111
|
-
disabled: action.disabled ? ((row?: any) => action.disabled!(row)) : false,
|
|
112
|
-
testId: action.testId || `action-${action.label.toLowerCase().replace(/\s+/g, '-')}-${rowId}`,
|
|
113
|
-
visible: true,
|
|
114
|
-
}));
|
|
115
|
-
|
|
116
109
|
return (
|
|
117
|
-
<tr
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
<tr
|
|
111
|
+
role="row"
|
|
112
|
+
aria-selected={typeof row.getIsSelected === 'function' ? (row.getIsSelected() ? 'true' : 'false') : 'false'}
|
|
113
|
+
aria-rowindex={row.index + 1}
|
|
114
|
+
>
|
|
115
|
+
{row.getVisibleCells().map(cell => (
|
|
116
|
+
<td key={cell.id} role="cell">
|
|
120
117
|
<div className={cell.column.columnDef.meta?.align === 'right' ? 'text-right' : ''}>
|
|
121
118
|
{cell.column.id !== 'actions' ? (
|
|
122
|
-
// Check if column has a custom cell renderer - if so, use it in edit mode
|
|
123
119
|
cell.column.columnDef.cell ? (
|
|
124
120
|
flexRender(cell.column.columnDef.cell, {
|
|
125
121
|
...cell.getContext(),
|
|
126
|
-
getIsEditing: () => true,
|
|
127
|
-
setValue: (value:
|
|
128
|
-
if (typeof value === 'object' && value !== null) {
|
|
129
|
-
onEditingDataChange({ ...editingData, ...value });
|
|
122
|
+
getIsEditing: () => true,
|
|
123
|
+
setValue: (value: CellValue | Record<string, CellValue>) => {
|
|
124
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
|
|
125
|
+
onEditingDataChange({ ...editingData, ...(value as Record<string, CellValue>) });
|
|
130
126
|
} else {
|
|
131
|
-
onEditingDataChange({ ...editingData, [cell.column.id]: value });
|
|
127
|
+
onEditingDataChange({ ...editingData, [cell.column.id]: value as CellValue });
|
|
132
128
|
}
|
|
133
129
|
}
|
|
134
130
|
})
|
|
135
131
|
) : (
|
|
136
|
-
// Fall back to default edit field rendering when no custom cell renderer
|
|
137
132
|
renderEditField(cell.column, editingData[cell.column.id], (value) => {
|
|
138
|
-
if (typeof value === 'object' && value !== null) {
|
|
139
|
-
|
|
140
|
-
onEditingDataChange({ ...editingData, ...value });
|
|
133
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
|
|
134
|
+
onEditingDataChange({ ...editingData, ...(value as Record<string, CellValue>) });
|
|
141
135
|
} else {
|
|
142
|
-
|
|
143
|
-
onEditingDataChange({ ...editingData, [cell.column.id]: value });
|
|
136
|
+
onEditingDataChange({ ...editingData, [cell.column.id]: value as CellValue });
|
|
144
137
|
}
|
|
145
138
|
}, editingData)
|
|
146
139
|
)
|
|
147
140
|
) : (
|
|
148
141
|
<div className="flex gap-1">
|
|
149
|
-
<Button
|
|
150
|
-
onClick={onSave}
|
|
151
|
-
size="sm"
|
|
152
|
-
variant="default"
|
|
153
|
-
>
|
|
142
|
+
<Button onClick={onSave} size="sm" variant="default">
|
|
154
143
|
Save
|
|
155
144
|
</Button>
|
|
156
|
-
<Button
|
|
157
|
-
onClick={onCancel}
|
|
158
|
-
size="sm"
|
|
159
|
-
variant="outline"
|
|
160
|
-
>
|
|
145
|
+
<Button onClick={onCancel} size="sm" variant="outline">
|
|
161
146
|
Cancel
|
|
162
147
|
</Button>
|
|
163
148
|
</div>
|
|
@@ -168,11 +153,11 @@ export function EditableRow<TData extends DataRecord>({
|
|
|
168
153
|
{actions.length > 0 && (
|
|
169
154
|
<td>
|
|
170
155
|
<ActionButtons
|
|
171
|
-
row={row
|
|
156
|
+
row={row}
|
|
172
157
|
rowId={rowId}
|
|
173
158
|
index={row.index}
|
|
174
|
-
actions={
|
|
175
|
-
isEditing
|
|
159
|
+
actions={actions}
|
|
160
|
+
isEditing
|
|
176
161
|
isParent={isParent}
|
|
177
162
|
hierarchical={hierarchical}
|
|
178
163
|
/>
|
|
@@ -28,7 +28,11 @@ export function EmptyState({
|
|
|
28
28
|
: "Get started by adding your first entry";
|
|
29
29
|
|
|
30
30
|
return (
|
|
31
|
-
<div
|
|
31
|
+
<div
|
|
32
|
+
role="status"
|
|
33
|
+
aria-live="polite"
|
|
34
|
+
className="flex flex-col items-center justify-center p-8 text-center"
|
|
35
|
+
>
|
|
32
36
|
<Icon
|
|
33
37
|
role="img"
|
|
34
38
|
aria-hidden="true"
|
|
@@ -40,6 +40,7 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } f
|
|
|
40
40
|
import { Button } from '../../Button/Button';
|
|
41
41
|
import { Input } from '../../Input/Input';
|
|
42
42
|
import { Upload, FileText, AlertCircle } from 'lucide-react';
|
|
43
|
+
import { createLogger } from '../../../utils/logger';
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Configuration interface for customizing ImportModal text content
|
|
@@ -76,7 +77,7 @@ interface ImportModalProps {
|
|
|
76
77
|
/** Callback function when the modal is closed */
|
|
77
78
|
onClose: () => void;
|
|
78
79
|
/** Callback function when data is imported successfully */
|
|
79
|
-
onImport: (data:
|
|
80
|
+
onImport: (data: Array<Record<string, unknown>>) => void | Promise<void>;
|
|
80
81
|
/** Configuration object for customizing modal text content */
|
|
81
82
|
config?: ImportModalConfig;
|
|
82
83
|
}
|
|
@@ -107,11 +108,13 @@ interface ImportModalProps {
|
|
|
107
108
|
* ```
|
|
108
109
|
*/
|
|
109
110
|
export function ImportModal({ isOpen, onClose, onImport, config = {} }: ImportModalProps) {
|
|
111
|
+
const logger = createLogger('ImportModal');
|
|
110
112
|
const [file, setFile] = useState<File | null>(null);
|
|
111
113
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
112
114
|
const [error, setError] = useState<string | null>(null);
|
|
113
|
-
const [previewData, setPreviewData] = useState<
|
|
115
|
+
const [previewData, setPreviewData] = useState<Array<Record<string, unknown>> | null>(null);
|
|
114
116
|
const [totalCount, setTotalCount] = useState<number>(0);
|
|
117
|
+
const [validationErrors, setValidationErrors] = useState<Array<{row: number; field: string; message: string}>>([]);
|
|
115
118
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
116
119
|
const isMountedRef = useRef(true);
|
|
117
120
|
|
|
@@ -135,38 +138,13 @@ export function ImportModal({ isOpen, onClose, onImport, config = {} }: ImportMo
|
|
|
135
138
|
totalRowsText = 'Total rows to import: {count}'
|
|
136
139
|
} = config;
|
|
137
140
|
|
|
138
|
-
const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
139
|
-
const selectedFile = event.target.files?.[0];
|
|
140
|
-
console.log('📁 File selected:', selectedFile?.name);
|
|
141
|
-
if (selectedFile) {
|
|
142
|
-
setFile(selectedFile);
|
|
143
|
-
setError(null);
|
|
144
|
-
|
|
145
|
-
// Generate preview
|
|
146
|
-
try {
|
|
147
|
-
const text = await selectedFile.text();
|
|
148
|
-
console.log('📄 File content length:', text.length);
|
|
149
|
-
const data = processCSV(text);
|
|
150
|
-
console.log('📊 Parsed CSV data:', data.length, 'rows');
|
|
151
|
-
console.log('👀 Setting preview data:', data.slice(0, 5));
|
|
152
|
-
setPreviewData(data.slice(0, 5)); // Show first 5 rows as preview
|
|
153
|
-
setTotalCount(data.length); // Store total count
|
|
154
|
-
console.log('👀 Preview data state should be updated');
|
|
155
|
-
} catch (err) {
|
|
156
|
-
console.error('❌ CSV parsing error:', err);
|
|
157
|
-
setError(err instanceof Error ? err.message : 'Failed to preview file');
|
|
158
|
-
setPreviewData(null);
|
|
159
|
-
setTotalCount(0);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
141
|
|
|
164
|
-
const processCSV = (csvText: string):
|
|
165
|
-
|
|
142
|
+
const processCSV = (csvText: string): Array<Record<string, unknown>> => {
|
|
143
|
+
logger.debug('Raw CSV text:', csvText.substring(0, 200) + '...');
|
|
166
144
|
|
|
167
145
|
const lines = csvText.split('\n').filter(line => line.trim());
|
|
168
|
-
|
|
169
|
-
|
|
146
|
+
logger.debug('CSV lines count:', lines.length);
|
|
147
|
+
logger.debug('First few lines:', lines.slice(0, 3));
|
|
170
148
|
|
|
171
149
|
if (lines.length < 2) {
|
|
172
150
|
throw new Error('CSV must have at least a header row and one data row');
|
|
@@ -195,19 +173,19 @@ export function ImportModal({ isOpen, onClose, onImport, config = {} }: ImportMo
|
|
|
195
173
|
};
|
|
196
174
|
|
|
197
175
|
const headers = parseCSVLine(lines[0]).map(h => h.replace(/"/g, '').trim());
|
|
198
|
-
|
|
176
|
+
logger.debug('Parsed headers:', headers);
|
|
199
177
|
|
|
200
178
|
const data = lines.slice(1).map((line, index) => {
|
|
201
179
|
const values = parseCSVLine(line).map(v => v.replace(/"/g, '').trim());
|
|
202
|
-
const row:
|
|
180
|
+
const row: Record<string, unknown> = {};
|
|
203
181
|
headers.forEach((header, colIndex) => {
|
|
204
182
|
row[header] = values[colIndex] || '';
|
|
205
183
|
});
|
|
206
|
-
|
|
184
|
+
logger.debug(`Row ${index + 1}:`, row);
|
|
207
185
|
return row;
|
|
208
186
|
});
|
|
209
187
|
|
|
210
|
-
|
|
188
|
+
logger.debug('Final parsed data:', data);
|
|
211
189
|
return data;
|
|
212
190
|
};
|
|
213
191
|
|
|
@@ -243,8 +221,36 @@ export function ImportModal({ isOpen, onClose, onImport, config = {} }: ImportMo
|
|
|
243
221
|
setError(null);
|
|
244
222
|
setPreviewData(null);
|
|
245
223
|
setTotalCount(0);
|
|
224
|
+
setValidationErrors([]);
|
|
246
225
|
onClose();
|
|
247
226
|
};
|
|
227
|
+
|
|
228
|
+
const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
229
|
+
const selectedFile = event.target.files?.[0];
|
|
230
|
+
logger.debug('File selected:', selectedFile?.name);
|
|
231
|
+
if (selectedFile) {
|
|
232
|
+
setFile(selectedFile);
|
|
233
|
+
setError(null);
|
|
234
|
+
setValidationErrors([]);
|
|
235
|
+
|
|
236
|
+
// Generate preview
|
|
237
|
+
try {
|
|
238
|
+
const text = await selectedFile.text();
|
|
239
|
+
logger.debug('File content length:', text.length);
|
|
240
|
+
const data = processCSV(text);
|
|
241
|
+
logger.debug('Parsed CSV data:', data.length, 'rows');
|
|
242
|
+
logger.debug('Setting preview data:', data.slice(0, 5));
|
|
243
|
+
setPreviewData(data.slice(0, 5)); // Show first 5 rows as preview
|
|
244
|
+
setTotalCount(data.length); // Store total count
|
|
245
|
+
logger.debug('Preview data state should be updated');
|
|
246
|
+
} catch (err) {
|
|
247
|
+
logger.error('CSV parsing error:', err);
|
|
248
|
+
setError(err instanceof Error ? err.message : 'Failed to preview file');
|
|
249
|
+
setPreviewData(null);
|
|
250
|
+
setTotalCount(0);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
};
|
|
248
254
|
|
|
249
255
|
return (
|
|
250
256
|
<Dialog open={isOpen} onOpenChange={handleClose}>
|
|
@@ -290,6 +296,29 @@ export function ImportModal({ isOpen, onClose, onImport, config = {} }: ImportMo
|
|
|
290
296
|
<span className="text-sm">{error}</span>
|
|
291
297
|
</div>
|
|
292
298
|
)}
|
|
299
|
+
|
|
300
|
+
{validationErrors.length > 0 && (
|
|
301
|
+
<div className="space-y-2">
|
|
302
|
+
<div className="flex items-center gap-2 p-3 bg-acc-50 border border-acc-200 rounded text-acc-700">
|
|
303
|
+
<AlertCircle className="h-4 w-4" />
|
|
304
|
+
<span className="text-sm font-medium">
|
|
305
|
+
{validationErrors.length} validation error{validationErrors.length !== 1 ? 's' : ''} found
|
|
306
|
+
</span>
|
|
307
|
+
</div>
|
|
308
|
+
<div className="max-h-32 overflow-y-auto space-y-1">
|
|
309
|
+
{validationErrors.slice(0, 10).map((err, idx) => (
|
|
310
|
+
<div key={idx} className="text-xs text-acc-600 p-2 bg-acc-50 border border-acc-200 rounded">
|
|
311
|
+
<span className="font-medium">Row {err.row}</span> • {err.field}: {err.message}
|
|
312
|
+
</div>
|
|
313
|
+
))}
|
|
314
|
+
{validationErrors.length > 10 && (
|
|
315
|
+
<div className="text-xs text-sec-500 italic">
|
|
316
|
+
... and {validationErrors.length - 10} more errors
|
|
317
|
+
</div>
|
|
318
|
+
)}
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
)}
|
|
293
322
|
|
|
294
323
|
|
|
295
324
|
{previewData && previewData.length > 0 && (
|
|
@@ -310,7 +339,7 @@ export function ImportModal({ isOpen, onClose, onImport, config = {} }: ImportMo
|
|
|
310
339
|
<tbody>
|
|
311
340
|
{previewData.map((row, index) => (
|
|
312
341
|
<tr key={index} className={index % 2 === 0 ? 'bg-app-main-50' : 'bg-sec-50'}>
|
|
313
|
-
{Object.values(row).map((value
|
|
342
|
+
{Object.values(row).map((value, cellIndex) => (
|
|
314
343
|
<td key={cellIndex} className="px-2 py-1 text-sec-700 border-b">
|
|
315
344
|
{String(value)}
|
|
316
345
|
</td>
|