@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
|
@@ -13,7 +13,12 @@
|
|
|
13
13
|
* - Data sanitization
|
|
14
14
|
* - Custom filename support
|
|
15
15
|
* - Browser download handling
|
|
16
|
-
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { createLogger } from '../../../utils/logger';
|
|
19
|
+
import type { DataRecord } from '../types';
|
|
20
|
+
|
|
21
|
+
/**
|
|
17
22
|
* @example
|
|
18
23
|
* ```tsx
|
|
19
24
|
* // Basic export
|
|
@@ -37,6 +42,73 @@
|
|
|
37
42
|
* ```
|
|
38
43
|
*/
|
|
39
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Column definition for export
|
|
47
|
+
*/
|
|
48
|
+
export interface ExportColumn {
|
|
49
|
+
header?: string;
|
|
50
|
+
id?: string;
|
|
51
|
+
accessorKey?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Escapes a value for CSV format according to RFC 4180
|
|
56
|
+
* - Encloses in quotes if value contains comma, newline, or quote
|
|
57
|
+
* - Escapes existing quotes by doubling them
|
|
58
|
+
* - Prevents CSV injection by sanitizing dangerous characters
|
|
59
|
+
*/
|
|
60
|
+
function escapeCSVValue(value: unknown, sanitizeForSecurity: boolean = true): string {
|
|
61
|
+
if (value === null || value === undefined) {
|
|
62
|
+
return sanitizeForSecurity ? '""' : '';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let stringValue = String(value);
|
|
66
|
+
|
|
67
|
+
if (sanitizeForSecurity) {
|
|
68
|
+
// Sanitize to prevent CSV injection
|
|
69
|
+
// Check for dangerous patterns that could be interpreted as formulas
|
|
70
|
+
// If starts with =, +, -, @, or \t, it could be a formula
|
|
71
|
+
if (/^[=+\-@]/.test(stringValue) || stringValue.startsWith('\t')) {
|
|
72
|
+
// Prefix with single quote to prevent formula interpretation in Excel
|
|
73
|
+
stringValue = "'" + stringValue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Remove control characters except newline (which is handled below)
|
|
77
|
+
stringValue = stringValue.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F]/g, '');
|
|
78
|
+
|
|
79
|
+
// Always quote values for consistency and safety
|
|
80
|
+
const escaped = stringValue.replace(/"/g, '""');
|
|
81
|
+
return `"${escaped}"`;
|
|
82
|
+
} else {
|
|
83
|
+
// Minimal escaping - only escape quotes in quoted strings
|
|
84
|
+
const escaped = stringValue.replace(/"/g, '""');
|
|
85
|
+
return `"${escaped}"`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Formats a value according to locale preferences
|
|
91
|
+
*/
|
|
92
|
+
function formatLocaleValue(value: unknown, locale?: string): string {
|
|
93
|
+
if (value === null || value === undefined) {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (typeof value === 'number') {
|
|
98
|
+
return new Intl.NumberFormat(locale).format(value);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (value instanceof Date) {
|
|
102
|
+
return new Intl.DateTimeFormat(locale).format(value);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (typeof value === 'boolean') {
|
|
106
|
+
return ''; // Don't format booleans in formatLocaleValue, let them pass through as-is
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return String(value);
|
|
110
|
+
}
|
|
111
|
+
|
|
40
112
|
/**
|
|
41
113
|
* Generates CSV content from data without triggering download
|
|
42
114
|
*
|
|
@@ -45,26 +117,42 @@
|
|
|
45
117
|
* @param options - Export options
|
|
46
118
|
* @returns CSV content as string
|
|
47
119
|
*/
|
|
48
|
-
export function generateCSVContent<TData>(
|
|
120
|
+
export function generateCSVContent<TData extends DataRecord>(
|
|
49
121
|
data: TData[],
|
|
50
|
-
columns:
|
|
51
|
-
options: {
|
|
122
|
+
columns: ExportColumn[],
|
|
123
|
+
options: {
|
|
124
|
+
includeHeaders?: boolean;
|
|
125
|
+
locale?: string;
|
|
126
|
+
sanitizeForSecurity?: boolean; // Default: true
|
|
127
|
+
} = {}
|
|
52
128
|
): string {
|
|
53
129
|
if (!data.length) return '';
|
|
54
130
|
|
|
55
|
-
const {
|
|
131
|
+
const {
|
|
132
|
+
includeHeaders = true,
|
|
133
|
+
locale,
|
|
134
|
+
sanitizeForSecurity = true
|
|
135
|
+
} = options;
|
|
56
136
|
|
|
57
137
|
// Create CSV header row
|
|
58
|
-
const headers = columns.map(col =>
|
|
138
|
+
const headers = columns.map(col => {
|
|
139
|
+
const headerValue = col.header || col.id || "Column";
|
|
140
|
+
return escapeCSVValue(headerValue, sanitizeForSecurity);
|
|
141
|
+
});
|
|
59
142
|
|
|
60
143
|
// Format data into CSV rows
|
|
61
144
|
const csvData = data.map(row => {
|
|
62
145
|
return columns.map(col => {
|
|
63
146
|
const key = col.accessorKey || col.id;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
147
|
+
let value = key ? row[key] : undefined;
|
|
148
|
+
|
|
149
|
+
// Format according to locale if provided
|
|
150
|
+
if (locale && (typeof value === 'number' || value instanceof Date || typeof value === 'boolean')) {
|
|
151
|
+
value = formatLocaleValue(value, locale);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Escape the value for CSV
|
|
155
|
+
return escapeCSVValue(value, sanitizeForSecurity);
|
|
68
156
|
}).join(",");
|
|
69
157
|
});
|
|
70
158
|
|
|
@@ -82,6 +170,7 @@ export function generateCSVContent<TData>(
|
|
|
82
170
|
* @param data - Array of data objects to export
|
|
83
171
|
* @param columns - Column definitions for mapping
|
|
84
172
|
* @param filename - Optional filename for download (default: "download.csv")
|
|
173
|
+
* @param options - Optional export configuration
|
|
85
174
|
* @returns Promise that resolves when export is complete, rejects on error
|
|
86
175
|
*
|
|
87
176
|
* @example
|
|
@@ -98,7 +187,7 @@ export function generateCSVContent<TData>(
|
|
|
98
187
|
* ];
|
|
99
188
|
*
|
|
100
189
|
* try {
|
|
101
|
-
* await exportToCSV(users, columns, 'users.csv');
|
|
190
|
+
* await exportToCSV(users, columns, 'users.csv', { locale: 'en-US' });
|
|
102
191
|
* showSuccessToast('Data exported successfully');
|
|
103
192
|
* } catch (error) {
|
|
104
193
|
* showErrorToast('Failed to export data');
|
|
@@ -112,13 +201,22 @@ export function generateCSVContent<TData>(
|
|
|
112
201
|
* - Triggers browser download
|
|
113
202
|
* - Throws error if export fails
|
|
114
203
|
*/
|
|
115
|
-
export function exportToCSV<TData>(
|
|
204
|
+
export function exportToCSV<TData extends DataRecord>(
|
|
116
205
|
data: TData[],
|
|
117
|
-
columns:
|
|
118
|
-
filename: string = "download.csv"
|
|
206
|
+
columns: ExportColumn[],
|
|
207
|
+
filename: string = "download.csv",
|
|
208
|
+
options: {
|
|
209
|
+
locale?: string;
|
|
210
|
+
sanitizeForSecurity?: boolean;
|
|
211
|
+
} = {}
|
|
119
212
|
): Promise<void> {
|
|
213
|
+
const logger = createLogger('ExportUtils');
|
|
120
214
|
return new Promise((resolve, reject) => {
|
|
121
215
|
try {
|
|
216
|
+
if (typeof window === 'undefined') {
|
|
217
|
+
throw new Error('CSV export is only available in browser environments');
|
|
218
|
+
}
|
|
219
|
+
|
|
122
220
|
if (!data || data.length === 0) {
|
|
123
221
|
throw new Error('No data to export');
|
|
124
222
|
}
|
|
@@ -127,7 +225,7 @@ export function exportToCSV<TData>(
|
|
|
127
225
|
throw new Error('No columns defined for export');
|
|
128
226
|
}
|
|
129
227
|
|
|
130
|
-
const csvContent = generateCSVContent(data, columns);
|
|
228
|
+
const csvContent = generateCSVContent(data, columns, options);
|
|
131
229
|
|
|
132
230
|
// Create and trigger download
|
|
133
231
|
const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
|
|
@@ -150,7 +248,7 @@ export function exportToCSV<TData>(
|
|
|
150
248
|
link.click();
|
|
151
249
|
document.body.removeChild(link);
|
|
152
250
|
} catch (error) {
|
|
153
|
-
|
|
251
|
+
logger.error('Failed to export data to CSV:', error);
|
|
154
252
|
reject(error);
|
|
155
253
|
}
|
|
156
254
|
});
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
* This utility can be used across different data tables with different column structures.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { createLogger } from '../../../utils/logger';
|
|
12
|
+
import type { CellValue } from '../types';
|
|
13
|
+
|
|
11
14
|
export interface ColumnMapping {
|
|
12
15
|
[targetField: string]: string[];
|
|
13
16
|
}
|
|
@@ -16,9 +19,20 @@ export interface ImportOptions {
|
|
|
16
19
|
columnMappings?: ColumnMapping;
|
|
17
20
|
dateFormats?: readonly string[];
|
|
18
21
|
dateFormatHints?: Record<string, readonly string[]>; // Field-specific date format hints
|
|
19
|
-
defaultValues?: Record<string,
|
|
22
|
+
defaultValues?: Record<string, CellValue>;
|
|
20
23
|
timezone?: string; // Default timezone for date parsing
|
|
21
24
|
strictDateParsing?: boolean; // Whether to be strict about date format matching
|
|
25
|
+
maxRows?: number; // Maximum number of rows to import (default: 10000)
|
|
26
|
+
maxStringLength?: number; // Maximum length for string values (default: 10000)
|
|
27
|
+
allowControlChars?: boolean; // Whether to allow control characters (default: false)
|
|
28
|
+
schemaValidation?: Record<string, {
|
|
29
|
+
required?: boolean;
|
|
30
|
+
type?: 'string' | 'number' | 'boolean' | 'date';
|
|
31
|
+
min?: number;
|
|
32
|
+
max?: number;
|
|
33
|
+
pattern?: RegExp;
|
|
34
|
+
allowEmpty?: boolean;
|
|
35
|
+
}>; // Schema validation rules
|
|
22
36
|
}
|
|
23
37
|
|
|
24
38
|
// Predefined date format sets for common use cases
|
|
@@ -140,15 +154,117 @@ export const DATE_FORMAT_SETS = {
|
|
|
140
154
|
* });
|
|
141
155
|
* ```
|
|
142
156
|
*/
|
|
143
|
-
export
|
|
144
|
-
|
|
157
|
+
export interface ImportError {
|
|
158
|
+
row: number;
|
|
159
|
+
field: string;
|
|
160
|
+
message: string;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface ImportWarning {
|
|
164
|
+
row: number;
|
|
165
|
+
field: string;
|
|
166
|
+
message: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Sanitizes a string value by removing or escaping dangerous characters
|
|
171
|
+
*/
|
|
172
|
+
function sanitizeString(value: string, options: ImportOptions): string {
|
|
173
|
+
const maxLength = options.maxStringLength || 10000;
|
|
174
|
+
let sanitized = value;
|
|
175
|
+
|
|
176
|
+
// Truncate to max length
|
|
177
|
+
if (sanitized.length > maxLength) {
|
|
178
|
+
sanitized = sanitized.substring(0, maxLength);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Remove control characters if not allowed
|
|
182
|
+
if (!options.allowControlChars) {
|
|
183
|
+
// Keep tabs, newlines, carriage returns
|
|
184
|
+
sanitized = sanitized.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, '');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Remove null bytes
|
|
188
|
+
sanitized = sanitized.replace(/\0/g, '');
|
|
189
|
+
|
|
190
|
+
// Remove SQL injection attempts
|
|
191
|
+
sanitized = sanitized.replace(/['";]/g, (char) => {
|
|
192
|
+
// Escape quotes and semicolons
|
|
193
|
+
return char === '"' ? '"' : char === "'" ? ''' : ';';
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
return sanitized;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Validates a value against schema rules
|
|
201
|
+
*/
|
|
202
|
+
function validateValue(field: string, value: unknown, rules: ImportOptions['schemaValidation']): {
|
|
203
|
+
valid: boolean;
|
|
204
|
+
message?: string;
|
|
205
|
+
} {
|
|
206
|
+
if (!rules || !rules[field]) {
|
|
207
|
+
return { valid: true };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const rule = rules[field];
|
|
211
|
+
|
|
212
|
+
// Check required
|
|
213
|
+
if (rule.required && (value === null || value === undefined || value === '')) {
|
|
214
|
+
return { valid: false, message: `Field '${field}' is required` };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Check empty allowed
|
|
218
|
+
if (!rule.allowEmpty && value === '') {
|
|
219
|
+
return { valid: false, message: `Field '${field}' cannot be empty` };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check type
|
|
223
|
+
if (rule.type) {
|
|
224
|
+
if (rule.type === 'number') {
|
|
225
|
+
if (typeof value !== 'number' && (typeof value !== 'string' || isNaN(Number(value)))) {
|
|
226
|
+
return { valid: false, message: `Field '${field}' must be a number` };
|
|
227
|
+
}
|
|
228
|
+
const numValue = typeof value === 'number' ? value : Number(value);
|
|
229
|
+
|
|
230
|
+
if (rule.min !== undefined && numValue < rule.min) {
|
|
231
|
+
return { valid: false, message: `Field '${field}' must be at least ${rule.min}` };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (rule.max !== undefined && numValue > rule.max) {
|
|
235
|
+
return { valid: false, message: `Field '${field}' must be at most ${rule.max}` };
|
|
236
|
+
}
|
|
237
|
+
} else if (rule.type === 'boolean') {
|
|
238
|
+
if (typeof value !== 'boolean' && value !== 'true' && value !== 'false') {
|
|
239
|
+
return { valid: false, message: `Field '${field}' must be a boolean` };
|
|
240
|
+
}
|
|
241
|
+
} else if (rule.type === 'date') {
|
|
242
|
+
const dateValue = typeof value === 'string' ? new Date(value) : value;
|
|
243
|
+
if (!(dateValue instanceof Date) || isNaN(dateValue.getTime())) {
|
|
244
|
+
return { valid: false, message: `Field '${field}' must be a valid date` };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Check pattern
|
|
250
|
+
if (rule.pattern && typeof value === 'string') {
|
|
251
|
+
if (!rule.pattern.test(value)) {
|
|
252
|
+
return { valid: false, message: `Field '${field}' does not match required pattern` };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return { valid: true };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function flexibleImport<T extends Record<string, unknown>>(
|
|
260
|
+
csvData: Record<string, unknown>[],
|
|
145
261
|
targetFields: string[],
|
|
146
262
|
options: ImportOptions = {}
|
|
147
263
|
): {
|
|
148
264
|
mappedData: T[];
|
|
149
265
|
mappings: Record<string, string | null>;
|
|
150
|
-
errors:
|
|
151
|
-
warnings:
|
|
266
|
+
errors: ImportError[];
|
|
267
|
+
warnings: ImportWarning[];
|
|
152
268
|
stats: {
|
|
153
269
|
totalRows: number;
|
|
154
270
|
successfulRows: number;
|
|
@@ -156,9 +272,34 @@ export function flexibleImport<T = any>(
|
|
|
156
272
|
warningRows: number;
|
|
157
273
|
};
|
|
158
274
|
} {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
275
|
+
const logger = createLogger('FlexibleImport');
|
|
276
|
+
logger.info('Starting flexible import...');
|
|
277
|
+
logger.debug('Available CSV columns:', Object.keys(csvData[0] || {}));
|
|
278
|
+
logger.debug('Target fields:', targetFields);
|
|
279
|
+
|
|
280
|
+
const maxRows = options.maxRows || 10000;
|
|
281
|
+
|
|
282
|
+
// Check row limit
|
|
283
|
+
if (csvData.length > maxRows) {
|
|
284
|
+
const error: ImportError = {
|
|
285
|
+
row: 0,
|
|
286
|
+
field: 'global',
|
|
287
|
+
message: `Import exceeds maximum row limit of ${maxRows}. Found ${csvData.length} rows.`
|
|
288
|
+
};
|
|
289
|
+
logger.error('Import exceeds row limit', { rowCount: csvData.length, limit: maxRows });
|
|
290
|
+
return {
|
|
291
|
+
mappedData: [],
|
|
292
|
+
mappings: {},
|
|
293
|
+
errors: [error],
|
|
294
|
+
warnings: [],
|
|
295
|
+
stats: {
|
|
296
|
+
totalRows: csvData.length,
|
|
297
|
+
successfulRows: 0,
|
|
298
|
+
errorRows: csvData.length,
|
|
299
|
+
warningRows: 0
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
162
303
|
|
|
163
304
|
// Default column mappings
|
|
164
305
|
const defaultMappings: ColumnMapping = {
|
|
@@ -192,11 +333,11 @@ export function flexibleImport<T = any>(
|
|
|
192
333
|
possibleName.toLowerCase().includes(col.toLowerCase())
|
|
193
334
|
);
|
|
194
335
|
if (found) {
|
|
195
|
-
|
|
336
|
+
logger.debug(`Mapped ${targetField} -> ${found}`);
|
|
196
337
|
return found;
|
|
197
338
|
}
|
|
198
339
|
}
|
|
199
|
-
|
|
340
|
+
logger.warn(`No mapping found for ${targetField}`);
|
|
200
341
|
return null;
|
|
201
342
|
};
|
|
202
343
|
|
|
@@ -206,7 +347,7 @@ export function flexibleImport<T = any>(
|
|
|
206
347
|
mappings[field] = findColumn(field);
|
|
207
348
|
});
|
|
208
349
|
|
|
209
|
-
|
|
350
|
+
logger.debug('Column mappings:', mappings);
|
|
210
351
|
|
|
211
352
|
// Enhanced date parsing with multiple format support
|
|
212
353
|
const parseDate = (dateStr: string, fieldName?: string): Date | null => {
|
|
@@ -320,16 +461,19 @@ export function flexibleImport<T = any>(
|
|
|
320
461
|
|
|
321
462
|
|
|
322
463
|
// Parse different data types
|
|
323
|
-
const parseValue = (value:
|
|
464
|
+
const parseValue = (value: unknown, fieldName: string): unknown => {
|
|
324
465
|
if (value === null || value === undefined || value === '') {
|
|
325
466
|
return options.defaultValues?.[fieldName] || '';
|
|
326
467
|
}
|
|
327
468
|
|
|
328
469
|
// Auto-detect and parse different data types
|
|
329
470
|
if (typeof value === 'string') {
|
|
471
|
+
// Sanitize string values
|
|
472
|
+
const sanitized = sanitizeString(value, options);
|
|
473
|
+
|
|
330
474
|
// Try to parse as date first (for fields that might contain dates)
|
|
331
475
|
if (fieldName.toLowerCase().includes('date') || fieldName.toLowerCase().includes('time')) {
|
|
332
|
-
const dateValue = parseDate(
|
|
476
|
+
const dateValue = parseDate(sanitized, fieldName);
|
|
333
477
|
if (dateValue) {
|
|
334
478
|
// Return ISO string for consistent date handling
|
|
335
479
|
return dateValue.toISOString();
|
|
@@ -337,57 +481,83 @@ export function flexibleImport<T = any>(
|
|
|
337
481
|
}
|
|
338
482
|
|
|
339
483
|
// Try to parse as boolean
|
|
340
|
-
if (
|
|
341
|
-
if (
|
|
484
|
+
if (sanitized.toLowerCase() === 'true') return true;
|
|
485
|
+
if (sanitized.toLowerCase() === 'false') return false;
|
|
342
486
|
|
|
343
487
|
// Try to parse as array (comma-separated)
|
|
344
|
-
if (
|
|
345
|
-
return
|
|
488
|
+
if (sanitized.includes(',') && (fieldName.toLowerCase().includes('tag') || fieldName.toLowerCase().includes('category'))) {
|
|
489
|
+
return sanitized.split(',').map((item: string) => item.trim()).filter((item: string) => item);
|
|
346
490
|
}
|
|
347
491
|
|
|
348
492
|
// Try to parse as number (only if not a date field)
|
|
349
493
|
if (!fieldName.toLowerCase().includes('date') && !fieldName.toLowerCase().includes('time')) {
|
|
350
|
-
const numValue = parseFloat(
|
|
494
|
+
const numValue = parseFloat(sanitized);
|
|
351
495
|
if (!isNaN(numValue) && isFinite(numValue)) {
|
|
352
496
|
return numValue;
|
|
353
497
|
}
|
|
354
498
|
}
|
|
499
|
+
|
|
500
|
+
return sanitized;
|
|
355
501
|
}
|
|
356
502
|
|
|
357
503
|
return value;
|
|
358
504
|
};
|
|
359
505
|
|
|
360
|
-
const
|
|
361
|
-
|
|
506
|
+
const validationErrors: ImportError[] = [];
|
|
507
|
+
const validationWarnings: ImportWarning[] = [];
|
|
508
|
+
const mappedData: T[] = [];
|
|
509
|
+
|
|
510
|
+
for (let index = 0; index < csvData.length; index++) {
|
|
511
|
+
const row = csvData[index];
|
|
512
|
+
logger.debug(`Processing row ${index + 1}:`, row);
|
|
362
513
|
|
|
363
|
-
const transformedRow:
|
|
514
|
+
const transformedRow: Record<string, unknown> = {};
|
|
515
|
+
let hasErrors = false;
|
|
364
516
|
|
|
365
517
|
targetFields.forEach(field => {
|
|
366
518
|
const sourceColumn = mappings[field];
|
|
519
|
+
let value: unknown;
|
|
520
|
+
|
|
367
521
|
if (sourceColumn && row[sourceColumn] !== undefined) {
|
|
368
|
-
|
|
522
|
+
value = parseValue(row[sourceColumn], field);
|
|
369
523
|
} else {
|
|
370
|
-
|
|
524
|
+
value = options.defaultValues?.[field] || '';
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
transformedRow[field] = value;
|
|
528
|
+
|
|
529
|
+
// Validate against schema
|
|
530
|
+
const validation = validateValue(field, value, options.schemaValidation);
|
|
531
|
+
if (!validation.valid) {
|
|
532
|
+
validationErrors.push({
|
|
533
|
+
row: index + 1,
|
|
534
|
+
field,
|
|
535
|
+
message: validation.message || `Validation failed for field '${field}'`
|
|
536
|
+
});
|
|
537
|
+
hasErrors = true;
|
|
371
538
|
}
|
|
372
539
|
});
|
|
373
540
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
541
|
+
// Only add row if validation passed (or if no validation rules)
|
|
542
|
+
if (!options.schemaValidation || !hasErrors) {
|
|
543
|
+
mappedData.push(transformedRow as T);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
logger.debug(`Transformed row ${index + 1}:`, transformedRow);
|
|
547
|
+
}
|
|
381
548
|
|
|
549
|
+
// Calculate unique error rows (multiple errors per row still count as 1 error row)
|
|
550
|
+
const errorRowSet = new Set(validationErrors.map(e => e.row));
|
|
551
|
+
|
|
382
552
|
return {
|
|
383
|
-
mappedData,
|
|
553
|
+
mappedData: mappedData,
|
|
384
554
|
mappings,
|
|
385
555
|
errors: validationErrors,
|
|
386
556
|
warnings: validationWarnings,
|
|
387
557
|
stats: {
|
|
388
558
|
totalRows: csvData.length,
|
|
389
559
|
successfulRows: mappedData.length,
|
|
390
|
-
errorRows:
|
|
560
|
+
errorRows: errorRowSet.size,
|
|
391
561
|
warningRows: validationWarnings.length,
|
|
392
562
|
}
|
|
393
563
|
};
|
|
@@ -91,7 +91,7 @@ export function groupHierarchicalData<TData extends HierarchicalDataRow>(
|
|
|
91
91
|
* Optimized depth cache to avoid recalculating depth for the same row
|
|
92
92
|
* This prevents O(n²) behavior when calculating depths for all rows
|
|
93
93
|
*/
|
|
94
|
-
const depthCache = new WeakMap<
|
|
94
|
+
const depthCache = new WeakMap<unknown[], Map<string, number>>();
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* Gets the depth level of a row in the hierarchy
|
|
@@ -2,5 +2,7 @@ export * from './exportUtils';
|
|
|
2
2
|
export * from './rowUtils';
|
|
3
3
|
export * from './hierarchicalUtils';
|
|
4
4
|
export * from './hierarchicalSorting';
|
|
5
|
+
export * from './a11yUtils';
|
|
6
|
+
export * from './paginationUtils';
|
|
5
7
|
// Stubs for legacy error handling (kept for useDataTablePerformance compatibility)
|
|
6
8
|
export * from './errorHandling';
|