@jmruthers/pace-core 0.6.10 → 0.6.11
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 +21 -0
- package/audit-tool/00-dependencies.cjs +46 -13
- package/audit-tool/audits/01-pace-core-compliance.cjs +96 -21
- package/audit-tool/audits/02-project-structure.cjs +13 -3
- package/audit-tool/audits/03-architecture.cjs +78 -4
- package/audit-tool/audits/04-code-quality.cjs +9 -2
- package/audit-tool/audits/05-styling.cjs +19 -7
- package/audit-tool/audits/06-security-rbac.cjs +105 -14
- package/audit-tool/audits/07-api-tech-stack.cjs +31 -15
- package/audit-tool/audits/08-testing-documentation.cjs +11 -3
- package/audit-tool/audits/09-operations.cjs +19 -7
- package/audit-tool/index.cjs +22 -11
- package/audit-tool/utils/report-utils.cjs +4 -0
- package/cursor-rules/01-pace-core-compliance.mdc +1 -0
- package/cursor-rules/02-project-structure.mdc +1 -0
- package/cursor-rules/03-architecture.mdc +3 -1
- package/cursor-rules/04-code-quality.mdc +1 -0
- package/cursor-rules/05-styling.mdc +41 -7
- package/cursor-rules/06-security-rbac.mdc +2 -1
- package/cursor-rules/07-api-tech-stack.mdc +1 -0
- package/cursor-rules/08-testing-documentation.mdc +1 -0
- package/cursor-rules/09-operations.mdc +1 -0
- package/dist/{DataTable-SAXFG4XI.js → DataTable-EFYP2QLE.js} +10 -7
- package/dist/{InactivityServiceProvider-DHryoh6K.d.ts → InactivityServiceProvider-BbxwwDz1.d.ts} +10 -1
- package/dist/{UnifiedAuthProvider-CiBAl9-s.d.ts → UnifiedAuthProvider-Bkt_tzdS.d.ts} +56 -24
- package/dist/{api-F47QJ7FX.js → api-BZR2CYXL.js} +3 -2
- package/dist/api-result-USV1Czr-.d.ts +51 -0
- package/dist/{audit-Z6ZZBWLU.js → audit-HI2DHUVU.js} +2 -1
- package/dist/{auth-BZOJqrdd.d.ts → auth-JvdRVaud.d.ts} +1 -1
- package/dist/{chunk-KSNLMI7N.js → chunk-2DL2WSOE.js} +1 -155
- package/dist/{chunk-MPY44PWB.js → chunk-2OEVOGGR.js} +4648 -3560
- package/dist/chunk-44CNXN4P.js +15 -0
- package/dist/{chunk-Y4PF6HIM.js → chunk-4R3T5ENU.js} +867 -786
- package/dist/{chunk-LNHFAF4X.js → chunk-7A6IMHH2.js} +289 -247
- package/dist/chunk-CU2BU2MQ.js +2 -0
- package/dist/{chunk-JJEYZ3DX.js → chunk-D6BMFMQZ.js} +37 -2
- package/dist/{chunk-BCTXBU6U.js → chunk-ENLXB7GP.js} +88 -71
- package/dist/{chunk-FBZ7U3ID.js → chunk-J2KQK6DG.js} +937 -987
- package/dist/{chunk-TFIPNIPE.js → chunk-KJXRL3XE.js} +3300 -2245
- package/dist/{chunk-3GWSPISD.js → chunk-L5LFKKLJ.js} +1 -1
- package/dist/{chunk-X5EAU5G7.js → chunk-PCSHBLPB.js} +132 -114
- package/dist/{chunk-NIU6DPQV.js → chunk-QRYSEPHB.js} +2 -0
- package/dist/{chunk-KYURMOQM.js → chunk-V7FTM2LU.js} +423 -320
- package/dist/chunk-WY6Y7KC3.js +264 -0
- package/dist/{chunk-FN52B75D.js → chunk-XOJME5T7.js} +176 -15
- package/dist/{chunk-7YDC7LMU.js → chunk-XPFVT3GN.js} +71 -66
- package/dist/{chunk-66R6RLUZ.js → chunk-YFTFFJIV.js} +3 -3
- package/dist/{chunk-W46INAVW.js → chunk-YYTWKVHO.js} +688 -570
- package/dist/components.d.ts +8 -7
- package/dist/components.js +17 -15
- package/dist/{database.generated-DT8JTZiP.d.ts → database.generated-qkdoiVrJ.d.ts} +45 -10
- package/dist/eslint-rules/index.cjs +3 -0
- package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +74 -0
- package/dist/{event-WTAQuGcq.d.ts → event-BfCox3N2.d.ts} +36 -10
- package/dist/{file-reference-BavO2eQj.d.ts → file-reference-DU1hcawx.d.ts} +29 -13
- package/dist/hooks.d.ts +22 -9
- package/dist/hooks.js +34 -25
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +66 -177
- package/dist/index.js +316 -340
- package/dist/pagination-BW1mqywp.d.ts +201 -0
- package/dist/providers.d.ts +6 -5
- package/dist/providers.js +5 -3
- package/dist/rbac/index.d.ts +123 -138
- package/dist/rbac/index.js +10 -8
- package/dist/theming/runtime.d.ts +19 -2
- package/dist/theming/runtime.js +1 -1
- package/dist/{timezone-K-ptz3HO.d.ts → timezone-BTWWXKVY.d.ts} +1 -1
- package/dist/types.d.ts +17 -10
- package/dist/types.js +1 -0
- package/dist/{usePublicPageContext-vxBlEHO9.d.ts → usePublicPageContext-B91dGYW1.d.ts} +433 -356
- package/dist/{usePublicRouteParams-G3Ks53mk.d.ts → usePublicRouteParams-BgV6VhMi.d.ts} +73 -4
- package/dist/utils.d.ts +163 -145
- package/dist/utils.js +42 -25
- package/docs/api/modules.md +782 -643
- package/docs/api-reference/rpc-functions.md +12 -3
- package/docs/core-concepts/rbac-system.md +8 -0
- package/docs/getting-started/cursor-rules.md +17 -20
- package/docs/getting-started/dependencies.md +1 -1
- package/docs/getting-started/setup.md +235 -0
- package/docs/implementation-guides/authentication.md +27 -0
- package/docs/implementation-guides/data-tables.md +176 -3
- package/docs/migration/ApiResult-migration.md +25 -0
- package/docs/rbac/api-reference.md +33 -31
- package/docs/standards/0-standards-overview.md +50 -15
- package/docs/standards/1-pace-core-compliance-standards.md +62 -57
- package/docs/standards/2-project-structure-standards.md +33 -16
- package/docs/standards/3-architecture-standards.md +41 -1
- package/docs/standards/4-code-quality-standards.md +26 -6
- package/docs/standards/5-styling-standards.md +35 -1
- package/docs/standards/6-security-rbac-standards.md +66 -0
- package/docs/standards/7-api-tech-stack-standards.md +25 -14
- package/docs/standards/8-testing-documentation-standards.md +31 -0
- package/docs/standards/9-operations-standards.md +19 -0
- package/docs/standards/README.md +20 -201
- package/docs/testing/test-setup-for-consumers.md +2 -0
- package/docs/troubleshooting/common-issues.md +17 -1
- package/docs/troubleshooting/organisation-context-setup.md +8 -0
- package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
- package/eslint-config-pace-core.cjs +20 -0
- package/package.json +14 -20
- package/scripts/{build-docs-incremental.js → build-docs.js} +3 -2
- package/scripts/setup.cjs +536 -0
- package/scripts/validate.cjs +480 -0
- package/src/__tests__/helpers/{__tests__/component-test-utils.test.tsx → component-test-utils.test.tsx} +3 -3
- package/src/__tests__/helpers/{__tests__/optimized-test-setup.test.ts → optimized-test-setup.test.ts} +2 -2
- package/src/__tests__/helpers/{__tests__/supabaseMock.test.ts → supabaseMock.test.ts} +2 -2
- package/src/__tests__/helpers/{__tests__/test-providers.test.tsx → test-providers.test.tsx} +1 -1
- package/src/__tests__/helpers/test-providers.tsx +37 -39
- package/src/__tests__/helpers/{__tests__/test-utils.test.tsx → test-utils.test.tsx} +4 -3
- package/src/__tests__/helpers/{__tests__/timer-utils.test.ts → timer-utils.test.ts} +2 -2
- package/src/assets/app-icons/index.test.ts +304 -0
- package/src/components/AddressField/AddressField.test.tsx +1 -1
- package/src/components/AddressField/AddressField.tsx +238 -212
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/Card/Card.test.tsx +172 -17
- package/src/components/Card/Card.tsx +19 -10
- package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
- package/src/components/ContextSelector/{__tests__/ContextSelector.test.tsx → ContextSelector.test.tsx} +6 -6
- package/src/components/ContextSelector/ContextSelector.tsx +66 -280
- package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
- package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
- package/src/components/DataTable/AUDIT_REPORT.md +59 -44
- package/src/components/DataTable/{__tests__/DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx} +6 -6
- package/src/components/DataTable/{__tests__/DataTable.default-state.test.tsx → DataTable.default-state.test.tsx} +5 -5
- package/src/components/DataTable/{__tests__/DataTable.export.test.tsx → DataTable.export.test.tsx} +10 -10
- package/src/components/DataTable/{__tests__/DataTable.grouping-aggregation.test.tsx → DataTable.grouping-aggregation.test.tsx} +6 -6
- package/src/components/DataTable/{__tests__/DataTable.hooks.test.tsx → DataTable.hooks.test.tsx} +6 -6
- package/src/components/DataTable/{__tests__/DataTable.select-label-display.test.tsx → DataTable.select-label-display.test.tsx} +6 -6
- package/src/components/DataTable/DataTable.test.tsx +787 -416
- package/src/components/DataTable/DataTable.tsx +12 -12
- package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
- package/src/components/DataTable/{__tests__/DataTableCore.test-setup.ts → DataTableCore.test-setup.ts} +10 -9
- package/src/components/DataTable/{__tests__/DataTableCore.test.tsx → DataTableCore.test.tsx} +8 -8
- package/src/components/DataTable/{__tests__/README.md → README.md} +17 -7
- package/src/components/DataTable/TESTING.md +101 -0
- package/src/components/DataTable/{__tests__/a11y.basic.test.tsx → a11y.basic.test.tsx} +34 -34
- package/src/components/DataTable/components/DataTableCore.tsx +104 -864
- package/src/components/DataTable/components/{__tests__/GroupingDropdown.test.tsx → GroupingDropdown.test.tsx} +17 -8
- package/src/components/DataTable/components/GroupingDropdown.tsx +2 -2
- package/src/components/DataTable/components/ImportModal.tsx +61 -559
- package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/context/{__tests__/DataTableContext.test.tsx → DataTableContext.test.tsx} +2 -2
- package/src/components/DataTable/context/DataTableContext.tsx +7 -6
- package/src/components/DataTable/core/{__tests__/ColumnFactory.test.ts → ColumnFactory.test.ts} +2 -2
- package/src/components/DataTable/hooks/{__tests__/useColumnOrderPersistence.test.ts → useColumnOrderPersistence.test.ts} +2 -2
- package/src/components/DataTable/hooks/{__tests__/useColumnVisibilityPersistence.test.ts → useColumnVisibilityPersistence.test.ts} +2 -2
- package/src/components/DataTable/hooks/{__tests__/useDataTableConfiguration.test.ts → useDataTableConfiguration.test.ts} +3 -3
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +14 -2
- package/src/components/DataTable/hooks/{__tests__/useDataTableDataPipeline.test.ts → useDataTableDataPipeline.test.ts} +6 -6
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
- package/src/components/DataTable/hooks/{__tests__/useDataTablePermissions.test.ts → useDataTablePermissions.test.ts} +11 -11
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +79 -247
- package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
- package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
- package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
- package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
- package/src/components/DataTable/hooks/{__tests__/useDataTableState.test.ts → useDataTableState.test.ts} +47 -5
- package/src/components/DataTable/hooks/useDataTableState.ts +145 -94
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
- package/src/components/DataTable/hooks/{__tests__/useEffectiveColumnOrder.test.ts → useEffectiveColumnOrder.test.ts} +2 -2
- package/src/components/DataTable/hooks/{__tests__/useHierarchicalState.test.ts → useHierarchicalState.test.ts} +2 -2
- package/src/components/DataTable/{components/hooks → hooks}/useImportModalFocus.test.ts +3 -3
- package/src/components/DataTable/{components/hooks → hooks}/useImportModalFocus.ts +2 -2
- package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
- package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
- package/src/components/DataTable/hooks/{__tests__/useKeyboardNavigation.test.ts → useKeyboardNavigation.test.ts} +3 -3
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +309 -269
- package/src/components/DataTable/{components/hooks → hooks}/usePermissionTracking.test.ts +3 -3
- package/src/components/DataTable/{components/hooks → hooks}/usePermissionTracking.ts +3 -3
- package/src/components/DataTable/hooks/{__tests__/useServerSideDataEffect.test.ts → useServerSideDataEffect.test.ts} +2 -2
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +14 -3
- package/src/components/DataTable/hooks/{__tests__/useTableColumns.test.ts → useTableColumns.test.ts} +2 -2
- package/src/components/DataTable/hooks/{__tests__/useTableHandlers.test.ts → useTableHandlers.test.ts} +25 -4
- package/src/components/DataTable/hooks/useTableHandlers.ts +5 -2
- package/src/components/DataTable/index.ts +18 -17
- package/src/components/DataTable/{__tests__/keyboard.test.tsx → keyboard.test.tsx} +41 -63
- package/src/components/DataTable/{__tests__/mocks → mocks}/MockRBACProvider.tsx +1 -1
- package/src/components/DataTable/{__tests__/pagination.modes.test.tsx → pagination.modes.test.tsx} +6 -6
- package/src/components/DataTable/{__tests__/ssr.strict-mode.test.tsx → ssr.strict-mode.test.tsx} +2 -2
- package/src/components/DataTable/{__tests__/styles.test.ts → styles.test.ts} +1 -4
- package/src/components/DataTable/styles.ts +0 -1
- package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
- package/src/components/DataTable/{__tests__/test-utils → test-utils}/dataFactories.ts +2 -2
- package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
- package/src/components/DataTable/{__tests__/test-utils/sharedTestUtils.tsx → test-utils/sharedTestUtils.ts} +97 -66
- package/src/components/DataTable/{__tests__/test-utils.ts → test-utils.ts} +1 -1
- package/src/components/DataTable/types/actions.ts +71 -0
- package/src/components/DataTable/types/base.ts +39 -0
- package/src/components/DataTable/types/columns.ts +125 -0
- package/src/components/DataTable/types/export.ts +32 -0
- package/src/components/DataTable/types/features.ts +81 -0
- package/src/components/DataTable/types/hierarchical.ts +44 -0
- package/src/components/DataTable/types/index.ts +43 -0
- package/src/components/DataTable/types/pagination.ts +85 -0
- package/src/components/DataTable/types/performance.ts +47 -0
- package/src/components/DataTable/types/props.ts +62 -0
- package/src/components/DataTable/types/rbac.ts +45 -0
- package/src/components/DataTable/{components/__tests__ → ui/layout}/DataTableCore.test.tsx +430 -28
- package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
- package/src/components/DataTable/{components/__tests__ → ui/layout}/DataTableErrorBoundary.test.tsx +4 -4
- package/src/components/DataTable/{components → ui/layout}/DataTableErrorBoundary.tsx +7 -7
- package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
- package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
- package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
- package/src/components/DataTable/{components → ui/modals}/DataTableModals.tsx +36 -28
- package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
- package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
- package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
- package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
- package/src/components/DataTable/{components/__tests__ → ui/shared}/AccessDeniedPage.test.tsx +2 -2
- package/src/components/DataTable/{components → ui/shared}/AccessDeniedPage.tsx +2 -2
- package/src/components/DataTable/{components/__tests__ → ui/shared}/ActionButtons.test.tsx +6 -4
- package/src/components/DataTable/{components → ui/shared}/ActionButtons.tsx +4 -4
- package/src/components/DataTable/{components/__tests__ → ui/shared}/ColumnFilter.test.tsx +29 -16
- package/src/components/DataTable/{components → ui/shared}/ColumnFilter.tsx +4 -4
- package/src/components/DataTable/{components/__tests__ → ui/shared}/PaginationControls.test.tsx +38 -16
- package/src/components/DataTable/{components → ui/shared}/PaginationControls.tsx +21 -15
- package/src/components/DataTable/{components/__tests__ → ui/shared}/SortIndicator.test.tsx +2 -2
- package/src/components/DataTable/{components → ui/shared}/SortIndicator.tsx +1 -1
- package/src/components/DataTable/{components/__tests__ → ui/table}/EditFields.test.tsx +3 -3
- package/src/components/DataTable/{components → ui/table}/EditFields.tsx +138 -69
- package/src/components/DataTable/{components/__tests__ → ui/table}/EditableRow.test.tsx +36 -27
- package/src/components/DataTable/{components → ui/table}/EditableRow.tsx +86 -104
- package/src/components/DataTable/{components/__tests__ → ui/table}/EmptyState.test.tsx +2 -62
- package/src/components/DataTable/{components → ui/table}/EmptyState.tsx +7 -15
- package/src/components/DataTable/{components/__tests__ → ui/table}/FilterRow.test.tsx +5 -4
- package/src/components/DataTable/{components → ui/table}/FilterRow.tsx +3 -3
- package/src/components/DataTable/{components/__tests__ → ui/table}/LoadingState.test.tsx +6 -10
- package/src/components/DataTable/{components → ui/table}/LoadingState.tsx +4 -4
- package/src/components/DataTable/{components/__tests__ → ui/table}/RowComponent.test.tsx +412 -17
- package/src/components/DataTable/{components → ui/table}/RowComponent.tsx +183 -177
- package/src/components/DataTable/{components/__tests__ → ui/table}/UnifiedTableBody.test.tsx +425 -16
- package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
- package/src/components/DataTable/{components/__tests__ → ui/table}/cellValueUtils.test.ts +2 -2
- package/src/components/DataTable/{components → ui/table}/cellValueUtils.ts +1 -1
- package/src/components/DataTable/{components/__tests__ → ui/toolbar}/BulkOperationsDropdown.test.tsx +12 -5
- package/src/components/DataTable/{components → ui/toolbar}/BulkOperationsDropdown.tsx +3 -3
- package/src/components/DataTable/{components/__tests__ → ui/toolbar}/ColumnVisibilityDropdown.test.tsx +7 -4
- package/src/components/DataTable/{components → ui/toolbar}/ColumnVisibilityDropdown.tsx +7 -7
- package/src/components/DataTable/{components/__tests__ → ui/toolbar}/DataTableToolbar.test.tsx +4 -4
- package/src/components/DataTable/{components → ui/toolbar}/DataTableToolbar.tsx +4 -4
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
- package/src/components/DataTable/utils/{__tests__/a11yUtils.test.ts → a11yUtils.test.ts} +2 -2
- package/src/components/DataTable/utils/{__tests__/aggregationUtils.test.ts → aggregationUtils.test.ts} +3 -3
- package/src/components/DataTable/utils/{__tests__/columnUtils.test.ts → columnUtils.test.ts} +2 -2
- package/src/components/DataTable/utils/csvParse.test.ts +74 -0
- package/src/components/DataTable/utils/csvParse.ts +65 -0
- package/src/components/DataTable/utils/{__tests__/errorHandling.test.ts → errorHandling.test.ts} +2 -2
- package/src/components/DataTable/utils/{__tests__/exportUtils.test.ts → exportUtils.test.ts} +3 -3
- package/src/components/DataTable/utils/{__tests__/flexibleImport.test.ts → flexibleImport.test.ts} +2 -2
- package/src/components/DataTable/utils/flexibleImport.ts +3 -186
- package/src/components/DataTable/utils/{__tests__/hierarchicalSorting.test.ts → hierarchicalSorting.test.ts} +3 -3
- package/src/components/DataTable/utils/{__tests__/hierarchicalUtils.test.ts → hierarchicalUtils.test.ts} +3 -3
- package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
- package/src/components/DataTable/utils/importDateParser.ts +114 -0
- package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
- package/src/components/DataTable/utils/importValueParser.ts +91 -0
- package/src/components/DataTable/utils/{__tests__/paginationUtils.test.ts → paginationUtils.test.ts} +2 -2
- package/src/components/DataTable/utils/paginationUtils.ts +6 -3
- package/src/components/DataTable/utils/{__tests__/performanceUtils.test.ts → performanceUtils.test.ts} +3 -3
- package/src/components/DataTable/utils/{__tests__/rowUtils.test.ts → rowUtils.test.ts} +3 -3
- package/src/components/DataTable/utils/{__tests__/selectFieldUtils.test.ts → selectFieldUtils.test.ts} +66 -3
- package/src/components/DataTable/utils/selectFieldUtils.ts +97 -60
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +1 -1
- package/src/components/DateTimeField/DateTimeField.test.tsx +1 -1
- package/src/components/Dialog/Dialog.test-utils.ts +49 -0
- package/src/components/Dialog/Dialog.test.tsx +896 -89
- package/src/components/Dialog/Dialog.tsx +174 -882
- package/src/components/Dialog/dialogLock.test.ts +238 -0
- package/src/components/Dialog/dialogLock.ts +98 -0
- package/src/components/Dialog/index.ts +2 -0
- package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
- package/src/components/Dialog/useDialogDimensions.ts +140 -0
- package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
- package/src/components/Dialog/useDialogLifecycle.ts +135 -0
- package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
- package/src/components/Dialog/useDialogPersistence.ts +357 -0
- package/src/components/FileDisplay/FileDisplay.test.tsx +40 -40
- package/src/components/FileDisplay/FileDisplay.tsx +24 -656
- package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
- package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
- package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
- package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
- package/src/components/FileDisplay/fallbackUtils.ts +44 -0
- package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
- package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
- package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
- package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
- package/src/{hooks/__tests__ → components/FileDisplay}/useFileDisplay.test.ts +40 -42
- package/src/components/FileDisplay/useFileDisplay.ts +515 -0
- package/src/{hooks/__tests__ → components/FileDisplay}/useFileDisplay.unit.test.ts +406 -77
- package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
- package/src/{hooks/public → components/FileDisplay}/usePublicFileDisplay.test.ts +94 -88
- package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
- package/src/components/FileUpload/FileUpload.test.tsx +16 -10
- package/src/components/FileUpload/FileUpload.tsx +107 -525
- package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
- package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
- package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
- package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
- package/src/components/FileUpload/useFileUploadManager.ts +454 -0
- package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
- package/src/components/FileUpload/useResolvedAppId.ts +77 -0
- package/src/components/Footer/Footer.test.tsx +6 -292
- package/src/components/Footer/Footer.tsx +8 -125
- package/src/components/Form/Form.test.tsx +44 -27
- package/src/components/Form/Form.tsx +64 -287
- package/src/components/Form/useFormPersistence.ts +257 -0
- package/src/components/Header/Header.test.tsx +17 -18
- package/src/components/Header/Header.tsx +10 -1
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Label/Label.test.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +1 -1
- package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +1029 -26
- package/src/components/NavigationMenu/NavigationMenu.tsx +61 -361
- package/src/components/NavigationMenu/index.ts +6 -1
- package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
- package/src/components/NavigationMenu/{__tests__/useNavigationFiltering.test.ts → useNavigationFiltering.test.ts} +68 -53
- package/src/components/NavigationMenu/useNavigationFiltering.ts +197 -296
- package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
- package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +77 -62
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +3 -3
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +16 -19
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +529 -5
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +280 -756
- package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
- package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
- package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +31 -25
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +31 -122
- package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
- package/src/components/Progress/Progress.tsx +1 -2
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -235
- package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
- package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
- package/src/components/PublicLayout/PublicLayout.test.tsx +217 -36
- package/src/components/PublicLayout/PublicPageLayout.tsx +132 -73
- package/src/components/PublicLayout/PublicPageProvider.tsx +5 -1
- package/src/components/Select/Select.test.tsx +1 -1
- package/src/components/Select/Select.tsx +28 -18
- package/src/components/Select/{__tests__/context.test.tsx → context.test.tsx} +3 -3
- package/src/components/Select/{utils/__tests__/text.test.tsx → text.test.tsx} +2 -2
- package/src/components/Select/{utils/text.ts → text.ts} +1 -1
- package/src/components/Select/{hooks/__tests__/useSelectEvents.test.ts → useSelectEvents.test.ts} +5 -5
- package/src/components/Select/{hooks/useSelectEvents.ts → useSelectEvents.ts} +2 -2
- package/src/components/Select/{hooks/__tests__/useSelectSearch.test.tsx → useSelectSearch.test.tsx} +7 -7
- package/src/components/Select/{hooks/useSelectSearch.ts → useSelectSearch.ts} +2 -2
- package/src/components/Select/{hooks/__tests__/useSelectState.test.ts → useSelectState.test.ts} +16 -2
- package/src/components/Select/{hooks/useSelectState.ts → useSelectState.ts} +3 -3
- package/src/components/Table/Table.test.tsx +348 -0
- package/src/components/Tabs/Tabs.test.tsx +270 -0
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Toast/Toast.test.tsx +420 -0
- package/src/components/{__tests__/index.test.ts → index.test.ts} +2 -2
- package/src/constants/{__tests__/performance.test.ts → performance.test.ts} +2 -2
- package/src/hooks/{__tests__/ServiceHooks.test.tsx → ServiceHooks.test.tsx} +8 -8
- package/src/hooks/{__tests__/hooks.integration.test.tsx → hooks.integration.test.tsx} +11 -11
- package/src/hooks/index.ts +7 -4
- package/src/hooks/{__tests__/index.unit.test.ts → index.unit.test.ts} +2 -2
- package/src/hooks/public/usePublicEvent.test.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.test.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.test.ts +1 -1
- package/src/hooks/services/useAuth.ts +9 -7
- package/src/hooks/useAddressAutocomplete.test.ts +22 -22
- package/src/hooks/useAddressAutocomplete.ts +90 -75
- package/src/hooks/{__tests__/useAppConfig.unit.test.ts → useAppConfig.unit.test.ts} +328 -22
- package/src/hooks/{__tests__/useComponentPerformance.unit.test.tsx → useComponentPerformance.unit.test.tsx} +27 -41
- package/src/hooks/useDataTablePerformance.ts +100 -120
- package/src/hooks/{__tests__/useDataTablePerformance.unit.test.ts → useDataTablePerformance.unit.test.ts} +5 -5
- package/src/hooks/{__tests__/useDataTableState.test.ts → useDataTableState.test.ts} +2 -2
- package/src/hooks/{__tests__/useDebounce.unit.test.ts → useDebounce.unit.test.ts} +2 -2
- package/src/hooks/useEventTheme.test.ts +4 -1
- package/src/hooks/useEventTheme.ts +49 -21
- package/src/hooks/useEvents.ts +41 -1
- package/src/hooks/{__tests__/useEvents.unit.test.ts → useEvents.unit.test.ts} +5 -5
- package/src/hooks/useFileReference.test.ts +44 -41
- package/src/hooks/useFileReference.ts +182 -173
- package/src/hooks/useFileUrl.ts +1 -1
- package/src/hooks/{__tests__/useFileUrl.unit.test.ts → useFileUrl.unit.test.ts} +26 -36
- package/src/hooks/{__tests__/useFileUrlCache.test.ts → useFileUrlCache.test.ts} +8 -8
- package/src/hooks/useFileUrlCache.ts +1 -1
- package/src/hooks/{__tests__/useFocusManagement.unit.test.ts → useFocusManagement.unit.test.ts} +2 -2
- package/src/hooks/{__tests__/useFocusTrap.unit.test.tsx → useFocusTrap.unit.test.tsx} +2 -2
- package/src/hooks/{__tests__/useFormDialog.test.ts → useFormDialog.test.ts} +2 -2
- package/src/hooks/useInactivityTracker.ts +138 -131
- package/src/hooks/{__tests__/useInactivityTracker.unit.test.ts → useInactivityTracker.unit.test.ts} +3 -3
- package/src/hooks/{__tests__/useIsMobile.unit.test.ts → useIsMobile.unit.test.ts} +2 -2
- package/src/hooks/useIsPrint.ts +62 -0
- package/src/hooks/useIsPrint.unit.test.ts +545 -0
- package/src/hooks/{__tests__/useKeyboardShortcuts.unit.test.ts → useKeyboardShortcuts.unit.test.ts} +2 -2
- package/src/hooks/{__tests__/useOrganisationPermissions.unit.test.tsx → useOrganisationPermissions.unit.test.tsx} +4 -4
- package/src/hooks/useOrganisationSecurity.test.ts +3 -3
- package/src/hooks/useOrganisationSecurity.ts +190 -201
- package/src/hooks/{__tests__/useOrganisationSecurity.unit.test.tsx → useOrganisationSecurity.unit.test.tsx} +61 -63
- package/src/hooks/{__tests__/useOrganisations.unit.test.ts → useOrganisations.unit.test.ts} +5 -5
- package/src/hooks/{__tests__/usePerformanceMonitor.unit.test.ts → usePerformanceMonitor.unit.test.ts} +13 -14
- package/src/hooks/{__tests__/usePermissionCache.test.ts → usePermissionCache.test.ts} +26 -27
- package/src/hooks/usePermissionCache.ts +276 -271
- package/src/hooks/{__tests__/usePreventTabReload.test.ts → usePreventTabReload.test.ts} +2 -2
- package/src/hooks/{__tests__/usePublicEvent.simple.test.ts → usePublicEvent.simple.test.ts} +4 -4
- package/src/hooks/{__tests__/usePublicEvent.test.ts → usePublicEvent.test.ts} +4 -4
- package/src/hooks/{__tests__/usePublicEvent.unit.test.ts → usePublicEvent.unit.test.ts} +4 -4
- package/src/hooks/{__tests__/usePublicFileDisplay.test.ts → usePublicFileDisplay.test.ts} +12 -12
- package/src/hooks/{__tests__/usePublicRouteParams.unit.test.ts → usePublicRouteParams.unit.test.ts} +3 -3
- package/src/hooks/{__tests__/useQueryCache.test.ts → useQueryCache.test.ts} +2 -2
- package/src/hooks/useQueryCache.ts +0 -2
- package/src/hooks/{__tests__/useRBAC.unit.test.ts → useRBAC.unit.test.ts} +55 -38
- package/src/hooks/{__tests__/useSessionDraft.test.ts → useSessionDraft.test.ts} +2 -2
- package/src/hooks/{__tests__/useSessionRestoration.unit.test.tsx → useSessionRestoration.unit.test.tsx} +10 -19
- package/src/hooks/useStorage.ts +21 -16
- package/src/hooks/{__tests__/useStorage.unit.test.ts → useStorage.unit.test.ts} +38 -75
- package/src/hooks/{__tests__/useToast.test.ts → useToast.test.ts} +2 -2
- package/src/hooks/{__tests__/useToast.unit.test.tsx → useToast.unit.test.tsx} +2 -2
- package/src/hooks/{__tests__/useZodForm.unit.test.tsx → useZodForm.unit.test.tsx} +2 -2
- package/src/icons/{__tests__/index.test.ts → index.test.ts} +2 -2
- package/src/icons/index.ts +2 -0
- package/src/{__tests__/index.test.ts → index.test.ts} +3 -7
- package/src/index.ts +15 -7
- package/src/providers/{__tests__/AuthProvider.test.tsx → AuthProvider.test.tsx} +3 -3
- package/src/providers/{__tests__/EventProvider.test.tsx → EventProvider.test.tsx} +3 -3
- package/src/providers/InactivityProvider.test-helper.tsx +40 -0
- package/src/providers/{__tests__/InactivityProvider.test.tsx → InactivityProvider.test.tsx} +14 -21
- package/src/providers/{__tests__/ProviderLifecycle.test.tsx → ProviderLifecycle.test.tsx} +4 -4
- package/src/providers/{__tests__/UnifiedAuthProvider.test.tsx → UnifiedAuthProvider.test.tsx} +1 -1
- package/src/providers/{__tests__/index.test.ts → index.test.ts} +2 -2
- package/src/providers/services/{__tests__/AuthServiceProvider.integration.test.tsx → AuthServiceProvider.integration.test.tsx} +4 -4
- package/src/providers/services/{__tests__/AuthServiceProvider.test.tsx → AuthServiceProvider.test.tsx} +7 -7
- package/src/providers/services/{__tests__/EventServiceProvider.test.tsx → EventServiceProvider.test.tsx} +7 -7
- package/src/providers/services/{__tests__/InactivityServiceProvider.test.tsx → InactivityServiceProvider.test.tsx} +5 -5
- package/src/providers/services/{__tests__/OrganisationServiceProvider.test.tsx → OrganisationServiceProvider.test.tsx} +6 -6
- package/src/providers/services/UnifiedAuthContext.ts +30 -27
- package/src/providers/services/{__tests__/UnifiedAuthProvider.advanced.test.tsx → UnifiedAuthProvider.advanced.test.tsx} +8 -9
- package/src/providers/services/{__tests__/UnifiedAuthProvider.appId.test.tsx → UnifiedAuthProvider.appId.test.tsx} +25 -25
- package/src/providers/services/{__tests__/UnifiedAuthProvider.integration.test.tsx → UnifiedAuthProvider.integration.test.tsx} +14 -11
- package/src/providers/services/UnifiedAuthProvider.tsx +115 -360
- package/src/providers/services/{__tests__/contexts.test.tsx → contexts.test.tsx} +6 -6
- package/src/providers/services/{__tests__/useUnifiedAuth.test.tsx → useUnifiedAuth.test.tsx} +6 -6
- package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
- package/src/providers/useInactivity.test-helper.ts +27 -0
- package/src/rbac/{__tests__/adapters.comprehensive.test.tsx → adapters.comprehensive.test.tsx} +24 -24
- package/src/rbac/adapters.test.tsx +22 -22
- package/src/rbac/adapters.tsx +29 -29
- package/src/rbac/api.test.ts +973 -42
- package/src/rbac/api.ts +228 -253
- package/src/rbac/{__tests__/audit-batched.test.ts → audit-batched.test.ts} +6 -6
- package/src/rbac/audit.ts +4 -1
- package/src/rbac/{__tests__/auth-rbac-security.integration.test.tsx → auth-rbac-security.integration.test.tsx} +1 -1
- package/src/rbac/{__tests__/auth-rbac.e2e.test.tsx → auth-rbac.e2e.test.tsx} +27 -34
- package/src/rbac/cache-invalidation.test.ts +715 -0
- package/src/rbac/components/{__tests__/AccessDenied.test.tsx → AccessDenied.test.tsx} +3 -3
- package/src/rbac/components/{__tests__/NavigationGuard.test.tsx → NavigationGuard.test.tsx} +13 -11
- package/src/{__tests__/rbac/PagePermissionGuard.test.tsx → rbac/components/PagePermissionGuard.guard.test.tsx} +33 -19
- package/src/rbac/components/{__tests__/PagePermissionGuard.performance.test.tsx → PagePermissionGuard.performance.test.tsx} +30 -9
- package/src/rbac/components/{__tests__/PagePermissionGuard.race-condition.test.tsx → PagePermissionGuard.race-condition.test.tsx} +7 -7
- package/src/rbac/components/{__tests__/PagePermissionGuard.test.tsx → PagePermissionGuard.test.tsx} +10 -10
- package/src/rbac/components/PagePermissionGuard.tsx +177 -372
- package/src/rbac/components/{__tests__/PagePermissionGuard.verification.test.tsx → PagePermissionGuard.verification.test.tsx} +7 -7
- package/src/rbac/config.ts +58 -18
- package/src/rbac/{__tests__/engine.comprehensive.test.ts → engine.comprehensive.test.ts} +3 -3
- package/src/rbac/engine.test.ts +494 -0
- package/src/rbac/errors.ts +89 -55
- package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
- package/src/rbac/hooks/permissions/{__tests__/useAccessLevel.test.ts → useAccessLevel.test.ts} +40 -40
- package/src/rbac/hooks/permissions/useAccessLevel.ts +16 -6
- package/src/rbac/hooks/permissions/{__tests__/useCan.test.ts → useCan.test.ts} +41 -41
- package/src/rbac/hooks/permissions/useCan.ts +170 -252
- package/src/rbac/hooks/permissions/{__tests__/useMultiplePermissions.test.ts → useMultiplePermissions.test.ts} +49 -49
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +6 -2
- package/src/rbac/hooks/permissions/{__tests__/usePermissions.test.ts → usePermissions.test.ts} +10 -12
- package/src/rbac/hooks/permissions/usePermissions.ts +36 -65
- package/src/rbac/hooks/useCan.test.ts +42 -42
- package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
- package/src/rbac/hooks/usePageGuardScope.ts +117 -0
- package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
- package/src/rbac/hooks/{__tests__/usePermissions.integration.test.ts → usePermissions.integration.test.ts} +9 -9
- package/src/{__tests__/hooks/usePermissions.test.ts → rbac/hooks/usePermissions.stability.test.ts} +18 -18
- package/src/rbac/hooks/usePermissions.test.ts +54 -54
- package/src/rbac/hooks/useRBAC.test.ts +313 -217
- package/src/rbac/hooks/useRBAC.ts +145 -81
- package/src/rbac/hooks/useResourcePermissions.test.ts +25 -25
- package/src/rbac/hooks/useResourcePermissions.ts +68 -134
- package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
- package/src/rbac/hooks/useRoleManagement.test.ts +27 -112
- package/src/rbac/hooks/useRoleManagement.ts +153 -585
- package/src/rbac/hooks/{__tests__/useSecureSupabase.test.ts → useSecureSupabase.test.ts} +17 -17
- package/src/rbac/hooks/useSecureSupabase.ts +10 -2
- package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
- package/src/rbac/{__tests__/performance.test.ts → performance.test.ts} +1 -1
- package/src/rbac/{__tests__/rbac-core.test.tsx → rbac-core.test.tsx} +3 -3
- package/src/rbac/{__tests__/rbac-engine-core-logic.test.ts → rbac-engine-core-logic.test.ts} +2 -2
- package/src/rbac/{__tests__/rbac-engine-simplified.test.ts → rbac-engine-simplified.test.ts} +3 -3
- package/src/rbac/{__tests__/rbac-functions.test.ts → rbac-functions.test.ts} +57 -0
- package/src/rbac/{__tests__/rbac-role-isolation.test.ts → rbac-role-isolation.test.ts} +2 -2
- package/src/rbac/request-deduplication.test.ts +14 -9
- package/src/rbac/request-deduplication.ts +5 -4
- package/src/rbac/{__tests__/scenarios.user-role.test.tsx → scenarios.user-role.test.tsx} +23 -23
- package/src/rbac/secureClient.test.ts +514 -83
- package/src/rbac/secureClient.ts +8 -2
- package/src/rbac/security.test.ts +323 -0
- package/src/rbac/types/roleManagement.ts +66 -0
- package/src/rbac/utils/{__tests__/clientSecurity.test.ts → clientSecurity.test.ts} +4 -4
- package/src/rbac/utils/{__tests__/contextValidator.test.ts → contextValidator.test.ts} +4 -4
- package/src/rbac/utils/contextValidator.ts +5 -1
- package/src/rbac/utils/{__tests__/deep-equal.test.ts → deep-equal.test.ts} +1 -1
- package/src/rbac/utils/{__tests__/eventContext.test.ts → eventContext.test.ts} +36 -21
- package/src/rbac/utils/eventContext.ts +37 -33
- package/src/rbac/utils/fetchPermissionMap.ts +13 -0
- package/src/rbac/utils/permissionMapHelpers.ts +34 -0
- package/src/rbac/utils/roleManagementRpc.ts +303 -0
- package/src/services/{__tests__/AuthService.edge-cases.test.ts → AuthService.edge-cases.test.ts} +19 -19
- package/src/services/{__tests__/AuthService.restoreSession.test.ts → AuthService.restoreSession.test.ts} +2 -2
- package/src/services/{__tests__/AuthService.test.ts → AuthService.test.ts} +89 -55
- package/src/services/AuthService.ts +184 -205
- package/src/services/{__tests__/BaseService.edge-cases.test.ts → BaseService.edge-cases.test.ts} +3 -3
- package/src/services/{__tests__/BaseService.test.ts → BaseService.test.ts} +2 -2
- package/src/services/{__tests__/EventService.edge-cases.test.ts → EventService.edge-cases.test.ts} +27 -24
- package/src/services/{__tests__/EventService.eventColours.test.ts → EventService.eventColours.test.ts} +1 -1
- package/src/services/{__tests__/EventService.test.ts → EventService.test.ts} +256 -24
- package/src/services/EventService.ts +242 -312
- package/src/services/{__tests__/InactivityService.edge-cases.test.ts → InactivityService.edge-cases.test.ts} +3 -3
- package/src/services/{__tests__/InactivityService.lifecycle.test.ts → InactivityService.lifecycle.test.ts} +2 -2
- package/src/services/{__tests__/InactivityService.test.ts → InactivityService.test.ts} +179 -4
- package/src/services/InactivityService.ts +172 -213
- package/src/services/{__tests__/OrganisationService.edge-cases.test.ts → OrganisationService.edge-cases.test.ts} +5 -5
- package/src/services/{__tests__/OrganisationService.pagination.test.ts → OrganisationService.pagination.test.ts} +4 -4
- package/src/services/{__tests__/OrganisationService.test.ts → OrganisationService.test.ts} +410 -7
- package/src/services/OrganisationService.ts +184 -238
- package/src/services/base/BaseService.test.ts +1 -1
- package/src/services/interfaces/{__tests__/IAuthService.test.ts → IAuthService.test.ts} +21 -27
- package/src/services/interfaces/IAuthService.ts +10 -9
- package/src/services/interfaces/{__tests__/IEventService.test.ts → IEventService.test.ts} +4 -4
- package/src/services/interfaces/{__tests__/IInactivityService.test.ts → IInactivityService.test.ts} +3 -3
- package/src/services/interfaces/{__tests__/IOrganisationService.test.ts → IOrganisationService.test.ts} +3 -3
- package/src/styles/core.css +243 -12
- package/src/theming/{__tests__/parseEventColours.test.ts → parseEventColours.test.ts} +1 -1
- package/src/theming/{__tests__/runtime.test.ts → runtime.test.ts} +8 -17
- package/src/theming/runtime.ts +71 -2
- package/src/types/api-result.ts +53 -0
- package/src/types/{__tests__/core.test.ts → core.test.ts} +2 -2
- package/src/types/{__tests__/database-generated.test.ts → database-generated.test.ts} +3 -3
- package/src/types/database.generated.ts +45 -10
- package/src/types/event.ts +38 -18
- package/src/types/{__tests__/file-reference.test.ts → file-reference.test.ts} +13 -13
- package/src/types/file-reference.ts +37 -12
- package/src/types/{__tests__/guards.test.ts → guards.test.ts} +2 -2
- package/src/types/{__tests__/index.test.ts → index.test.ts} +2 -2
- package/src/types/index.ts +3 -0
- package/src/types/{__tests__/organisation.roles.test.ts → organisation.roles.test.ts} +1 -1
- package/src/types/{__tests__/organisation.test.ts → organisation.test.ts} +3 -31
- package/src/types/organisation.ts +15 -15
- package/src/types/supabase.ts +13 -4
- package/src/types/{__tests__/theme.test.ts → theme.test.ts} +1 -1
- package/src/types/{__tests__/type-validation.test.ts → type-validation.test.ts} +1 -1
- package/src/types/{__tests__/validation.test.ts → validation.test.ts} +2 -2
- package/src/utils/app/appIdResolver.test.ts +98 -71
- package/src/utils/app/appIdResolver.ts +31 -20
- package/src/utils/{__tests__/appConfig.unit.test.ts → appConfig.unit.test.ts} +1 -1
- package/src/utils/{__tests__/audit.unit.test.ts → audit.unit.test.ts} +1 -1
- package/src/utils/{__tests__/auth-utils.unit.test.ts → auth-utils.unit.test.ts} +16 -17
- package/src/utils/{__tests__/bundleAnalysis.unit.test.ts → bundleAnalysis.unit.test.ts} +35 -35
- package/src/utils/{__tests__/cn.unit.test.ts → cn.unit.test.ts} +1 -1
- package/src/utils/context/organisationContext.test.ts +105 -91
- package/src/utils/context/organisationContext.ts +29 -40
- package/src/utils/core/{__tests__/cn.test.ts → cn.test.ts} +3 -3
- package/src/utils/core/{__tests__/debugLogger.test.ts → debugLogger.test.ts} +2 -2
- package/src/utils/core/{__tests__/logger.test.ts → logger.test.ts} +2 -2
- package/src/utils/core/mergeRefs.ts +24 -0
- package/src/utils/{__tests__/debugLogger.test.ts → debugLogger.test.ts} +1 -1
- package/src/utils/{__tests__/deviceFingerprint.unit.test.ts → deviceFingerprint.unit.test.ts} +1 -1
- package/src/utils/dynamic/createLazyComponent.tsx +9 -1
- package/src/utils/dynamic/{__tests__/dynamicUtils.test.ts → dynamicUtils.test.ts} +2 -2
- package/src/utils/dynamic/{__tests__/lazyLoad.test.tsx → lazyLoad.test.tsx} +2 -2
- package/src/utils/{__tests__/dynamicUtils.unit.test.ts → dynamicUtils.unit.test.ts} +1 -1
- package/src/utils/file-reference/{__tests__/file-reference.test.ts → file-reference.test.ts} +214 -289
- package/src/utils/file-reference/index.ts +330 -347
- package/src/utils/{__tests__/formatDate.unit.test.ts → formatDate.unit.test.ts} +2 -2
- package/src/utils/formatting/formatDateTimeTimezone.test.ts +1 -1
- package/src/utils/formatting/formatNumber.test.ts +1 -1
- package/src/utils/{__tests__/formatting.unit.test.ts → formatting.unit.test.ts} +1 -1
- package/src/utils/google-places/googlePlacesUtils.test.ts +70 -48
- package/src/utils/google-places/googlePlacesUtils.ts +67 -99
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +25 -22
- package/src/utils/google-places/loadGoogleMapsScript.ts +138 -117
- package/src/utils/{__tests__/index.unit.test.ts → index.unit.test.ts} +1 -1
- package/src/utils/{__tests__/lazyLoad.unit.test.tsx → lazyLoad.unit.test.tsx} +13 -14
- package/src/utils/location/location.test.ts +1 -1
- package/src/utils/{__tests__/logger.unit.test.ts → logger.unit.test.ts} +1 -1
- package/src/utils/{__tests__/organisationContext.unit.test.ts → organisationContext.unit.test.ts} +37 -48
- package/src/utils/performance/{__tests__/bundleAnalysis.test.ts → bundleAnalysis.test.ts} +2 -2
- package/src/utils/performance/{__tests__/performanceBenchmark.test.ts → performanceBenchmark.test.ts} +2 -2
- package/src/utils/performance/{__tests__/performanceBudgets.test.ts → performanceBudgets.test.ts} +2 -2
- package/src/utils/{__tests__/performanceBenchmark.test.ts → performanceBenchmark.test.ts} +2 -2
- package/src/utils/{__tests__/performanceBudgets.unit.test.ts → performanceBudgets.unit.test.ts} +2 -2
- package/src/utils/{__tests__/permissionTypes.unit.test.ts → permissionTypes.unit.test.ts} +1 -1
- package/src/utils/{__tests__/permissionUtils.unit.test.ts → permissionUtils.unit.test.ts} +1 -1
- package/src/utils/permissions/{__tests__/permissionTypes.test.ts → permissionTypes.test.ts} +2 -2
- package/src/utils/persistence/{__tests__/keyDerivation.test.ts → keyDerivation.test.ts} +2 -2
- package/src/utils/persistence/{__tests__/sensitiveFieldDetection.test.ts → sensitiveFieldDetection.test.ts} +2 -2
- package/src/utils/{__tests__/request-deduplication.test.ts → request-deduplication.test.ts} +2 -2
- package/src/utils/{__tests__/sanitization.unit.test.ts → sanitization.unit.test.ts} +1 -1
- package/src/utils/{__tests__/schemaUtils.unit.test.ts → schemaUtils.unit.test.ts} +1 -1
- package/src/utils/{__tests__/secureDataAccess.unit.test.ts → secureDataAccess.unit.test.ts} +2 -2
- package/src/utils/{__tests__/secureErrors.unit.test.ts → secureErrors.unit.test.ts} +4 -4
- package/src/utils/{__tests__/secureStorage.unit.test.ts → secureStorage.unit.test.ts} +1 -1
- package/src/utils/security/auth-utils.ts +34 -23
- package/src/utils/security/secureDataAccess.ts +241 -281
- package/src/utils/security/secureErrors.test.ts +1 -1
- package/src/utils/security/secureStorage.test.ts +1 -1
- package/src/utils/security/security.test.ts +25 -17
- package/src/utils/security/security.ts +15 -18
- package/src/utils/security/securityMonitor.test.ts +1 -1
- package/src/utils/{__tests__/security.unit.test.ts → security.unit.test.ts} +21 -15
- package/src/utils/{__tests__/securityMonitor.unit.test.ts → securityMonitor.unit.test.ts} +1 -1
- package/src/utils/{__tests__/sessionTracking.unit.test.ts → sessionTracking.unit.test.ts} +12 -12
- package/src/utils/storage/{__tests__/config.unit.test.ts → config.unit.test.ts} +2 -2
- package/src/utils/storage/helpers.test.ts +88 -102
- package/src/utils/storage/helpers.ts +173 -251
- package/src/utils/storage/{__tests__/index.unit.test.ts → index.unit.test.ts} +3 -3
- package/src/utils/storage/types.ts +7 -0
- package/src/utils/supabase/createBaseClient.test.ts +1 -1
- package/src/utils/timezone/timezone.test.ts +1 -1
- package/src/utils/{__tests__/timezone.test.ts → timezone.test.ts} +2 -2
- package/src/utils/validation/{__tests__/common.test.ts → common.test.ts} +2 -2
- package/src/utils/validation/{__tests__/csrf.test.ts → csrf.test.ts} +56 -28
- package/src/utils/validation/csrf.ts +42 -41
- package/src/utils/validation/{__tests__/htmlSanitization.unit.test.ts → htmlSanitization.unit.test.ts} +2 -2
- package/src/utils/validation/{__tests__/passwordSchema.test.ts → passwordSchema.test.ts} +2 -2
- package/src/utils/validation/{__tests__/schema.test.ts → schema.test.ts} +2 -2
- package/src/utils/validation/{__tests__/sqlInjectionProtection.test.ts → sqlInjectionProtection.test.ts} +2 -2
- package/src/utils/validation/{__tests__/user.test.ts → user.test.ts} +2 -2
- package/src/utils/validation/{__tests__/validation.test.ts → validation.test.ts} +2 -2
- package/src/utils/validation/{__tests__/validationUtils.test.ts → validationUtils.test.ts} +2 -2
- package/src/utils/{__tests__/validation.unit.test.ts → validation.unit.test.ts} +1 -1
- package/src/utils/{__tests__/validationUtils.unit.test.ts → validationUtils.unit.test.ts} +5 -2
- package/dist/UnifiedAuthProvider-BBD2PS3Q.js +0 -7
- package/dist/chunk-KPYQWGFQ.js +0 -183
- package/dist/types-D05dCGma.d.ts +0 -521
- package/scripts/eslint-audit.cjs +0 -222
- package/scripts/generate-docs.js +0 -157
- package/scripts/install-cursor-rules.cjs +0 -255
- package/scripts/install-eslint-config.cjs +0 -349
- package/scripts/setup-build-cache.js +0 -73
- package/scripts/validate-pre-publish.js +0 -145
- package/src/__tests__/integration/UserProfile.test.tsx +0 -124
- package/src/__tests__/public-recipe-view.test.ts +0 -228
- package/src/__tests__/rls-policies.test.ts +0 -472
- package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
- package/src/components/DataTable/components/DataTableLayout.tsx +0 -584
- package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -395
- package/src/components/DataTable/components/__tests__/DataTableLayout.test.tsx +0 -467
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -358
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -957
- package/src/components/DataTable/core/ActionManager.ts +0 -235
- package/src/components/DataTable/core/ColumnManager.ts +0 -204
- package/src/components/DataTable/core/DataManager.ts +0 -190
- package/src/components/DataTable/core/LocalDataAdapter.ts +0 -274
- package/src/components/DataTable/core/PluginRegistry.ts +0 -229
- package/src/components/DataTable/core/StateManager.ts +0 -312
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -235
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -141
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -178
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -133
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -142
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -158
- package/src/components/DataTable/core/interfaces.ts +0 -338
- package/src/components/DataTable/types.ts +0 -764
- package/src/hooks/public/usePublicFileDisplay.ts +0 -534
- package/src/hooks/useFileDisplay.ts +0 -748
- package/src/providers/OrganisationProvider.test.tsx +0 -40
- package/src/providers/OrganisationProvider.tsx +0 -92
- package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
- package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -616
- package/src/providers/__tests__/OrganisationProvider.wrapper.test.tsx +0 -591
- package/src/rbac/__tests__/cache-invalidation.test.ts +0 -393
- /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
- /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
- /package/src/hooks/{__tests__/useApiFetch.unit.test.ts → useApiFetch.unit.test.ts} +0 -0
- /package/src/providers/{__tests__/README.md → README.md} +0 -0
- /package/src/rbac/{__tests__/index.test.ts → index.test.ts} +0 -0
- /package/src/rbac/{__tests__/rbac-integration.test.ts → rbac-integration.test.ts} +0 -0
- /package/src/types/{__tests__/README.md → README.md} +0 -0
|
@@ -124,11 +124,11 @@
|
|
|
124
124
|
import React, { useEffect, useState, useContext } from 'react';
|
|
125
125
|
import { useNavigate, useLocation } from 'react-router-dom';
|
|
126
126
|
import { UnifiedAuthContext } from '../../providers/services/UnifiedAuthProvider';
|
|
127
|
-
import { isSuperAdmin } from '../../rbac/api';
|
|
128
127
|
import { LoginForm } from '../LoginForm';
|
|
129
128
|
import { clearPalette } from '../../theming/runtime';
|
|
130
129
|
import { EventServiceContext } from '../../providers/services/EventServiceContext';
|
|
131
130
|
import { logger } from '../../utils/core/logger';
|
|
131
|
+
import { useLoginAppAccess } from './useLoginAppAccess';
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
134
|
* Props for the PaceLoginPage component.
|
|
@@ -179,8 +179,6 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
179
179
|
const navigate = useNavigate();
|
|
180
180
|
const location = useLocation();
|
|
181
181
|
const [isSigningIn, setIsSigningIn] = useState(false);
|
|
182
|
-
const [accessError, setAccessError] = useState<string | null>(null);
|
|
183
|
-
const [isCheckingAccess, setIsCheckingAccess] = useState(false);
|
|
184
182
|
|
|
185
183
|
// Get event service context (may not be available if outside EventServiceProvider)
|
|
186
184
|
// Using useContext directly allows graceful handling when provider is not available
|
|
@@ -196,6 +194,18 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
196
194
|
const user = authContext?.user ?? null;
|
|
197
195
|
const supabase = authContext?.supabase ?? null;
|
|
198
196
|
|
|
197
|
+
// RBAC app access check (data fetching in hook, not in component)
|
|
198
|
+
const { isCheckingAccess, accessError, clearAccessError } = useLoginAppAccess({
|
|
199
|
+
requireAppAccess,
|
|
200
|
+
isAuthenticated,
|
|
201
|
+
isLoading,
|
|
202
|
+
user,
|
|
203
|
+
supabase,
|
|
204
|
+
appName,
|
|
205
|
+
onSuccessRedirectPath,
|
|
206
|
+
navigate,
|
|
207
|
+
});
|
|
208
|
+
|
|
199
209
|
// Clear any active event theme when login page mounts
|
|
200
210
|
// This ensures the login screen always uses default colors
|
|
201
211
|
useEffect(() => {
|
|
@@ -233,135 +243,34 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
233
243
|
return () => clearTimeout(timeoutId);
|
|
234
244
|
}, [eventService]);
|
|
235
245
|
|
|
236
|
-
//
|
|
246
|
+
// When requireAppAccess is false: navigate only after auth context has updated.
|
|
247
|
+
// Navigating immediately after signIn() can race with React state updates, so ProtectedRoute
|
|
248
|
+
// may still see isAuthenticated false and redirect back to /login. Navigating when
|
|
249
|
+
// isAuthenticated/user become true ensures the next render of protected routes sees the session.
|
|
250
|
+
const isOnLoginPage = location.pathname === '/login' || location.pathname.startsWith('/login');
|
|
237
251
|
useEffect(() => {
|
|
238
|
-
if (
|
|
239
|
-
|
|
252
|
+
if (requireAppAccess || !isOnLoginPage || !isAuthenticated || !user) return;
|
|
253
|
+
try {
|
|
254
|
+
navigate(onSuccessRedirectPath, { replace: true });
|
|
255
|
+
} catch (navError) {
|
|
256
|
+
logger.error('PaceLoginPage', 'Navigation error after sign-in:', navError);
|
|
240
257
|
}
|
|
241
|
-
|
|
242
|
-
const checkAccess = async () => {
|
|
243
|
-
setIsCheckingAccess(true);
|
|
244
|
-
setAccessError(null);
|
|
245
|
-
|
|
246
|
-
try {
|
|
247
|
-
const userId = user.id;
|
|
248
|
-
logger.debug('PaceLoginPage', 'Checking app access using RBAC:', { appName, userId });
|
|
249
|
-
|
|
250
|
-
// Step 1: Check if user is super admin (they have unrestricted access)
|
|
251
|
-
const superAdminCheck = await isSuperAdmin(userId);
|
|
252
|
-
|
|
253
|
-
if (superAdminCheck) {
|
|
254
|
-
logger.debug('PaceLoginPage', 'User is super admin, granting access');
|
|
255
|
-
setIsCheckingAccess(false);
|
|
256
|
-
navigate(onSuccessRedirectPath, { replace: true });
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Step 2: Get the app ID
|
|
261
|
-
const { data: appData, error: appError } = await supabase
|
|
262
|
-
.from('rbac_apps')
|
|
263
|
-
.select('id, name, is_active')
|
|
264
|
-
.eq('name', appName)
|
|
265
|
-
.eq('is_active', true)
|
|
266
|
-
.single();
|
|
267
|
-
|
|
268
|
-
if (appError || !appData) {
|
|
269
|
-
logger.error('PaceLoginPage', 'App not found:', { appName, error: appError });
|
|
270
|
-
setAccessError(`Application "${appName}" is not configured. Please contact your administrator.`);
|
|
271
|
-
setIsCheckingAccess(false);
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Step 3: Get all pages for this app
|
|
276
|
-
const { data: pagesData, error: pagesError } = await supabase
|
|
277
|
-
.from('rbac_app_pages')
|
|
278
|
-
.select('id, page_name')
|
|
279
|
-
.eq('app_id', appData.id);
|
|
280
|
-
|
|
281
|
-
if (pagesError || !pagesData || pagesData.length === 0) {
|
|
282
|
-
setAccessError(`You do not have permission to access ${appName}. This application is currently unavailable. Please contact your administrator if you believe you should have access.`);
|
|
283
|
-
setIsCheckingAccess(false);
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Step 4: Get user's first organisation
|
|
288
|
-
const { data: orgRow } = await supabase
|
|
289
|
-
.from('rbac_organisation_roles')
|
|
290
|
-
.select('organisation_id')
|
|
291
|
-
.eq('user_id', userId)
|
|
292
|
-
.eq('status', 'active')
|
|
293
|
-
.is('revoked_at', null)
|
|
294
|
-
.limit(1)
|
|
295
|
-
.maybeSingle();
|
|
296
|
-
|
|
297
|
-
const organisationId = orgRow?.organisation_id;
|
|
298
|
-
|
|
299
|
-
if (!organisationId) {
|
|
300
|
-
setAccessError(`You do not have permission to access ${appName}. You are not assigned to any organisation. Please contact your administrator.`);
|
|
301
|
-
setIsCheckingAccess(false);
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Step 5: Check if user has ANY read permission on ANY page in this app
|
|
306
|
-
// We check each page and see if user has the 'read' operation allowed
|
|
307
|
-
let hasAnyAccess = false;
|
|
308
|
-
for (const page of pagesData) {
|
|
309
|
-
const { data: hasPermission, error: permError } = await supabase
|
|
310
|
-
.rpc('rbac_check_permission_simplified', {
|
|
311
|
-
p_user_id: userId,
|
|
312
|
-
p_permission: `read:page.${page.page_name}`, // Permission format: operation:resource
|
|
313
|
-
p_organisation_id: organisationId,
|
|
314
|
-
p_event_id: null,
|
|
315
|
-
p_app_id: appData.id,
|
|
316
|
-
p_page_id: page.page_name // Page name to resolve to UUID
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
if (!permError && hasPermission === true) {
|
|
320
|
-
hasAnyAccess = true;
|
|
321
|
-
break;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (hasAnyAccess) {
|
|
326
|
-
setIsCheckingAccess(false);
|
|
327
|
-
navigate(onSuccessRedirectPath, { replace: true });
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// No access - deny
|
|
332
|
-
setAccessError(`You do not have permission to access ${appName}. This application is restricted to authorized users only. Please contact your administrator if you believe you should have access.`);
|
|
333
|
-
setIsCheckingAccess(false);
|
|
334
|
-
} catch (error) {
|
|
335
|
-
logger.error('PaceLoginPage', 'Error checking app access:', error);
|
|
336
|
-
setAccessError('An error occurred while checking your permissions. Please try again or contact support.');
|
|
337
|
-
setIsCheckingAccess(false);
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
checkAccess();
|
|
342
|
-
}, [isAuthenticated, isLoading, user, supabase, appName, requireAppAccess, navigate, onSuccessRedirectPath]);
|
|
258
|
+
}, [requireAppAccess, isOnLoginPage, isAuthenticated, user, navigate, onSuccessRedirectPath]);
|
|
343
259
|
|
|
344
260
|
const handleSubmit = async (data: { email: string; password: string }) => {
|
|
345
261
|
setIsSigningIn(true);
|
|
346
|
-
|
|
347
|
-
|
|
262
|
+
clearAccessError();
|
|
263
|
+
|
|
348
264
|
try {
|
|
349
|
-
const
|
|
265
|
+
const result = await signIn(data.email, data.password);
|
|
350
266
|
|
|
351
|
-
if (
|
|
267
|
+
if ('ok' in result && !result.ok) {
|
|
352
268
|
// Throw error so LoginForm can catch and display it
|
|
353
|
-
throw error;
|
|
269
|
+
throw new Error(result.error.message);
|
|
354
270
|
}
|
|
355
271
|
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
if (!requireAppAccess) {
|
|
359
|
-
try {
|
|
360
|
-
navigate(onSuccessRedirectPath, { replace: true });
|
|
361
|
-
} catch (navError) {
|
|
362
|
-
logger.error('PaceLoginPage', 'Navigation error after sign-in:', navError);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
272
|
+
// When requireAppAccess is true, useLoginAppAccess handles navigation.
|
|
273
|
+
// When requireAppAccess is false, the useEffect above navigates once isAuthenticated/user update.
|
|
365
274
|
} finally {
|
|
366
275
|
setIsSigningIn(false);
|
|
367
276
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useLoginAppAccess
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/PaceLoginPage/useLoginAppAccess
|
|
5
|
+
*
|
|
6
|
+
* Hook that checks RBAC-based app access after login. Encapsulates data fetching
|
|
7
|
+
* (Supabase tables and RPC) so the login page component stays UI-only.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useEffect, useState } from 'react';
|
|
11
|
+
import type { NavigateFunction } from 'react-router-dom';
|
|
12
|
+
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
13
|
+
import { isSuperAdmin } from '../../rbac/api';
|
|
14
|
+
import { logger } from '../../utils/core/logger';
|
|
15
|
+
|
|
16
|
+
export interface UseLoginAppAccessParams {
|
|
17
|
+
requireAppAccess: boolean;
|
|
18
|
+
isAuthenticated: boolean;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
user: { id: string } | null;
|
|
21
|
+
supabase: SupabaseClient | null;
|
|
22
|
+
appName: string;
|
|
23
|
+
onSuccessRedirectPath: string;
|
|
24
|
+
navigate: NavigateFunction;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface UseLoginAppAccessResult {
|
|
28
|
+
isCheckingAccess: boolean;
|
|
29
|
+
accessError: string | null;
|
|
30
|
+
clearAccessError: () => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Checks whether the authenticated user has permission to access the app (RBAC).
|
|
35
|
+
* If requireAppAccess is true and user is authenticated, runs the check and
|
|
36
|
+
* navigates on success or sets accessError on failure.
|
|
37
|
+
*/
|
|
38
|
+
export function useLoginAppAccess({
|
|
39
|
+
requireAppAccess,
|
|
40
|
+
isAuthenticated,
|
|
41
|
+
isLoading,
|
|
42
|
+
user,
|
|
43
|
+
supabase,
|
|
44
|
+
appName,
|
|
45
|
+
onSuccessRedirectPath,
|
|
46
|
+
navigate,
|
|
47
|
+
}: UseLoginAppAccessParams): UseLoginAppAccessResult {
|
|
48
|
+
const [isCheckingAccess, setIsCheckingAccess] = useState(false);
|
|
49
|
+
const [accessError, setAccessError] = useState<string | null>(null);
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!requireAppAccess || !isAuthenticated || isLoading || !user || !supabase) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const checkAccess = async () => {
|
|
57
|
+
setIsCheckingAccess(true);
|
|
58
|
+
setAccessError(null);
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const userId = user.id;
|
|
62
|
+
logger.debug('PaceLoginPage', 'Checking app access using RBAC:', { appName, userId });
|
|
63
|
+
|
|
64
|
+
const superResult = await isSuperAdmin(userId);
|
|
65
|
+
if (superResult.ok && superResult.data) {
|
|
66
|
+
logger.debug('PaceLoginPage', 'User is super admin, granting access');
|
|
67
|
+
setIsCheckingAccess(false);
|
|
68
|
+
navigate(onSuccessRedirectPath, { replace: true });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { data: appData, error: appError } = await supabase
|
|
73
|
+
.from('rbac_apps')
|
|
74
|
+
.select('id, name, is_active')
|
|
75
|
+
.eq('name', appName)
|
|
76
|
+
.eq('is_active', true)
|
|
77
|
+
.single();
|
|
78
|
+
|
|
79
|
+
if (appError || !appData) {
|
|
80
|
+
logger.error('PaceLoginPage', 'App not found:', { appName, error: appError });
|
|
81
|
+
setAccessError(`Application "${appName}" is not configured. Please contact your administrator.`);
|
|
82
|
+
setIsCheckingAccess(false);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const { data: pagesData, error: pagesError } = await supabase
|
|
87
|
+
.from('rbac_app_pages')
|
|
88
|
+
.select('id, page_name')
|
|
89
|
+
.eq('app_id', appData.id);
|
|
90
|
+
|
|
91
|
+
if (pagesError || !pagesData || pagesData.length === 0) {
|
|
92
|
+
setAccessError(`You do not have permission to access ${appName}. This application is currently unavailable. Please contact your administrator if you believe you should have access.`);
|
|
93
|
+
setIsCheckingAccess(false);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const { data: orgRow } = await supabase
|
|
98
|
+
.from('rbac_organisation_roles')
|
|
99
|
+
.select('organisation_id')
|
|
100
|
+
.eq('user_id', userId)
|
|
101
|
+
.eq('status', 'active')
|
|
102
|
+
.is('revoked_at', null)
|
|
103
|
+
.limit(1)
|
|
104
|
+
.maybeSingle();
|
|
105
|
+
|
|
106
|
+
const organisationId = orgRow?.organisation_id;
|
|
107
|
+
|
|
108
|
+
if (!organisationId) {
|
|
109
|
+
setAccessError(`You do not have permission to access ${appName}. You are not assigned to any organisation. Please contact your administrator.`);
|
|
110
|
+
setIsCheckingAccess(false);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let hasAnyAccess = false;
|
|
115
|
+
for (const page of pagesData) {
|
|
116
|
+
const { data: hasPermission, error: permError } = await supabase
|
|
117
|
+
.rpc('rbac_check_permission_simplified', {
|
|
118
|
+
p_user_id: userId,
|
|
119
|
+
p_permission: `read:page.${page.page_name}`,
|
|
120
|
+
p_organisation_id: organisationId,
|
|
121
|
+
p_event_id: null,
|
|
122
|
+
p_app_id: appData.id,
|
|
123
|
+
p_page_id: page.page_name,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (!permError && hasPermission === true) {
|
|
127
|
+
hasAnyAccess = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (hasAnyAccess) {
|
|
133
|
+
setIsCheckingAccess(false);
|
|
134
|
+
navigate(onSuccessRedirectPath, { replace: true });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
setAccessError(`You do not have permission to access ${appName}. This application is restricted to authorized users only. Please contact your administrator if you believe you should have access.`);
|
|
139
|
+
setIsCheckingAccess(false);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
logger.error('PaceLoginPage', 'Error checking app access:', error);
|
|
142
|
+
setAccessError('An error occurred while checking your permissions. Please try again or contact support.');
|
|
143
|
+
setIsCheckingAccess(false);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
checkAccess();
|
|
148
|
+
}, [isAuthenticated, isLoading, user, supabase, appName, requireAppAccess, navigate, onSuccessRedirectPath]);
|
|
149
|
+
|
|
150
|
+
const clearAccessError = () => setAccessError(null);
|
|
151
|
+
|
|
152
|
+
return { isCheckingAccess, accessError, clearAccessError };
|
|
153
|
+
}
|
|
@@ -97,8 +97,7 @@ const Progress = React.forwardRef<
|
|
|
97
97
|
<progress
|
|
98
98
|
ref={ref}
|
|
99
99
|
className={cn(
|
|
100
|
-
'appearance-none border-0 h-2 w-full rounded-full overflow-hidden transition-all
|
|
101
|
-
!isIndeterminate && 'bg-sec-600/50',
|
|
100
|
+
'appearance-none border-0 h-2 w-full rounded-full overflow-hidden transition-all',
|
|
102
101
|
className
|
|
103
102
|
)}
|
|
104
103
|
{...(isIndeterminate ? {} : { value })}
|
|
@@ -68,16 +68,12 @@
|
|
|
68
68
|
* - LoadingSpinner - Loading state UI
|
|
69
69
|
*/
|
|
70
70
|
|
|
71
|
-
import React
|
|
71
|
+
import React from 'react';
|
|
72
72
|
import { Navigate, Outlet } from 'react-router-dom';
|
|
73
|
-
import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
74
|
-
import { useSessionRestoration } from '../../hooks/useSessionRestoration';
|
|
75
|
-
import { useEvents } from '../../hooks/useEvents';
|
|
76
|
-
import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner';
|
|
77
73
|
import { SessionRestorationLoader } from '../SessionRestorationLoader';
|
|
74
|
+
import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner';
|
|
78
75
|
import { Alert, AlertDescription, AlertTitle } from '../Alert/Alert';
|
|
79
|
-
import {
|
|
80
|
-
import { usePreventTabReload } from '../../hooks/usePreventTabReload';
|
|
76
|
+
import { useProtectedRouteState } from './useProtectedRouteState';
|
|
81
77
|
|
|
82
78
|
/**
|
|
83
79
|
* Props for the ProtectedRoute component.
|
|
@@ -130,251 +126,49 @@ export function ProtectedRoute({
|
|
|
130
126
|
requireEvent = false,
|
|
131
127
|
noEventsFallback,
|
|
132
128
|
loadingFallback,
|
|
133
|
-
loginPath = '/login'
|
|
129
|
+
loginPath = '/login',
|
|
134
130
|
}: ProtectedRouteProps) {
|
|
135
|
-
const {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const eventsContext = useEvents();
|
|
140
|
-
const selectedEvent = requireEvent ? eventsContext.selectedEvent : null;
|
|
141
|
-
const events = requireEvent ? (eventsContext.events || []) : [];
|
|
142
|
-
const eventLoading = requireEvent ? (eventsContext.isLoading || false) : false;
|
|
143
|
-
|
|
144
|
-
const sessionRestoration = useSessionRestoration();
|
|
145
|
-
|
|
146
|
-
// Prevent full page reloads when switching tabs (handles bfcache and visibility changes)
|
|
147
|
-
usePreventTabReload({ enabled: true, gracePeriodMs: 2000 });
|
|
148
|
-
|
|
149
|
-
// Track if user was previously authenticated to prevent redirects during session refresh
|
|
150
|
-
const wasAuthenticatedRef = useRef(false);
|
|
151
|
-
const [shouldRedirect, setShouldRedirect] = useState(false);
|
|
152
|
-
const tabJustBecameVisibleRef = useRef(false);
|
|
153
|
-
|
|
154
|
-
// Track authentication state to detect when user was previously logged in
|
|
155
|
-
useEffect(() => {
|
|
156
|
-
if (isAuthenticated) {
|
|
157
|
-
wasAuthenticatedRef.current = true;
|
|
158
|
-
setShouldRedirect(false);
|
|
159
|
-
tabJustBecameVisibleRef.current = false; // Clear visibility flag when authenticated
|
|
160
|
-
}
|
|
161
|
-
}, [isAuthenticated]);
|
|
162
|
-
|
|
163
|
-
// Handle tab visibility changes - prevent immediate redirects when tab becomes visible
|
|
164
|
-
// This prevents the page from refreshing when switching back to the tab
|
|
165
|
-
useEffect(() => {
|
|
166
|
-
if (typeof document === 'undefined') return;
|
|
167
|
-
|
|
168
|
-
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
169
|
-
let wasHidden = document.hidden;
|
|
170
|
-
|
|
171
|
-
const handleVisibilityChange = () => {
|
|
172
|
-
const isNowVisible = !document.hidden;
|
|
173
|
-
|
|
174
|
-
// When tab becomes visible, immediately prevent redirects and give session refresh time
|
|
175
|
-
if (isNowVisible && wasHidden) {
|
|
176
|
-
// Tab just became visible - immediately prevent redirects
|
|
177
|
-
if (!isAuthenticated && wasAuthenticatedRef.current) {
|
|
178
|
-
tabJustBecameVisibleRef.current = true;
|
|
179
|
-
setShouldRedirect(false); // Immediately clear redirect flag
|
|
180
|
-
|
|
181
|
-
// Clear any existing timeout
|
|
182
|
-
if (timeoutId) {
|
|
183
|
-
clearTimeout(timeoutId);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Wait a bit to see if session refresh completes
|
|
187
|
-
timeoutId = setTimeout(() => {
|
|
188
|
-
// Only allow redirect if still not authenticated after delay
|
|
189
|
-
tabJustBecameVisibleRef.current = false;
|
|
190
|
-
// Use a function to get the latest state
|
|
191
|
-
setShouldRedirect((prev) => {
|
|
192
|
-
// Only set to true if we're still not authenticated
|
|
193
|
-
// This will be checked again in the render logic
|
|
194
|
-
return prev;
|
|
195
|
-
});
|
|
196
|
-
}, 2000); // 2 second grace period for session refresh
|
|
197
|
-
}
|
|
198
|
-
} else if (!isNowVisible) {
|
|
199
|
-
// Tab became hidden - clear the visibility flag
|
|
200
|
-
tabJustBecameVisibleRef.current = false;
|
|
201
|
-
if (timeoutId) {
|
|
202
|
-
clearTimeout(timeoutId);
|
|
203
|
-
timeoutId = null;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
wasHidden = !isNowVisible;
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
// Check initial state - if tab is visible and user appears logged out, give grace period
|
|
211
|
-
if (!document.hidden && !isAuthenticated && wasAuthenticatedRef.current) {
|
|
212
|
-
tabJustBecameVisibleRef.current = true;
|
|
213
|
-
setShouldRedirect(false);
|
|
214
|
-
timeoutId = setTimeout(() => {
|
|
215
|
-
tabJustBecameVisibleRef.current = false;
|
|
216
|
-
}, 2000);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
220
|
-
return () => {
|
|
221
|
-
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
222
|
-
if (timeoutId) {
|
|
223
|
-
clearTimeout(timeoutId);
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
}, [isAuthenticated]);
|
|
131
|
+
const { phase, loginPath: resolvedLoginPath } = useProtectedRouteState({
|
|
132
|
+
requireEvent,
|
|
133
|
+
loginPath,
|
|
134
|
+
});
|
|
227
135
|
|
|
228
|
-
|
|
229
|
-
useEffect(() => {
|
|
230
|
-
if (isAuthenticated) {
|
|
231
|
-
setShouldRedirect(false);
|
|
232
|
-
tabJustBecameVisibleRef.current = false;
|
|
233
|
-
}
|
|
234
|
-
}, [isAuthenticated]);
|
|
235
|
-
|
|
236
|
-
const isRestoringSession = useMemo(() => {
|
|
237
|
-
return sessionRestoration.isRestoring &&
|
|
238
|
-
!sessionRestoration.restorationComplete &&
|
|
239
|
-
!sessionRestoration.restorationError &&
|
|
240
|
-
!sessionRestoration.hasTimedOut;
|
|
241
|
-
}, [
|
|
242
|
-
sessionRestoration.isRestoring,
|
|
243
|
-
sessionRestoration.restorationComplete,
|
|
244
|
-
sessionRestoration.restorationError,
|
|
245
|
-
sessionRestoration.hasTimedOut
|
|
246
|
-
]);
|
|
247
|
-
|
|
248
|
-
// Show session restoration loader during restoration
|
|
249
|
-
if (isRestoringSession) {
|
|
136
|
+
if (phase === 'session-restoring') {
|
|
250
137
|
return <SessionRestorationLoader />;
|
|
251
138
|
}
|
|
252
139
|
|
|
253
|
-
|
|
254
|
-
// This must come before auth loading check to avoid blocking when only events are loading
|
|
255
|
-
if (requireEvent && eventLoading) {
|
|
140
|
+
if (phase === 'event-loading') {
|
|
256
141
|
return <Outlet />;
|
|
257
142
|
}
|
|
258
143
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return loadingFallback || (
|
|
263
|
-
<main className="grid place-items-center size-full">
|
|
264
|
-
<LoadingSpinner />
|
|
265
|
-
</main>
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Redirect to login if not authenticated
|
|
270
|
-
// Priority order:
|
|
271
|
-
// 1. If session restoration has timed out or errored → redirect immediately (even if loading)
|
|
272
|
-
// 2. If session restoration hasn't completed yet (and hasn't failed) → show loading (wait for restoration)
|
|
273
|
-
// 3. If session restoration is in progress or still loading → show loading (wait for restoration)
|
|
274
|
-
// 4. If user was never authenticated → redirect immediately (only after restoration/loading complete)
|
|
275
|
-
// 5. If tab just became visible → show loading (prevent redirect during grace period)
|
|
276
|
-
// 6. If we've confirmed they should redirect (after visibility change grace period) → redirect
|
|
277
|
-
// 7. Otherwise, if loading → show loading spinner (session might be refreshing)
|
|
278
|
-
// 8. Otherwise → redirect (user is not authenticated and not loading)
|
|
279
|
-
if (!isAuthenticated) {
|
|
280
|
-
// Session restoration timeout/error always redirects immediately
|
|
281
|
-
if (sessionRestoration.hasTimedOut || sessionRestoration.restorationError) {
|
|
282
|
-
logger.warn('ProtectedRoute', 'Session restoration failed, redirecting to login', {
|
|
283
|
-
timedOut: sessionRestoration.hasTimedOut,
|
|
284
|
-
error: sessionRestoration.restorationError?.message
|
|
285
|
-
});
|
|
286
|
-
return <Navigate to={loginPath} replace />;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// CRITICAL FIX: Check if session restoration hasn't completed yet
|
|
290
|
-
// This covers the case where restoration hasn't started (200ms delay in AuthServiceProvider)
|
|
291
|
-
// or is in progress. We wait if restoration hasn't completed AND hasn't failed.
|
|
292
|
-
// This prevents redirecting to login during page refresh before session restoration completes.
|
|
293
|
-
const restorationNotComplete = !sessionRestoration.restorationComplete &&
|
|
294
|
-
!sessionRestoration.hasTimedOut &&
|
|
295
|
-
!sessionRestoration.restorationError;
|
|
296
|
-
|
|
297
|
-
if (restorationNotComplete || sessionRestoration.isRestoring || isLoading) {
|
|
298
|
-
return loadingFallback || (
|
|
144
|
+
if (phase === 'auth-loading') {
|
|
145
|
+
return (
|
|
146
|
+
loadingFallback ?? (
|
|
299
147
|
<main className="grid place-items-center size-full">
|
|
300
148
|
<LoadingSpinner />
|
|
301
149
|
</main>
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
// User was never authenticated → redirect immediately (only after restoration/loading complete)
|
|
306
|
-
if (!wasAuthenticatedRef.current) {
|
|
307
|
-
return <Navigate to={loginPath} replace />;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Tab just became visible - show loading to prevent redirect during grace period
|
|
311
|
-
// Also check document visibility state directly as a fallback
|
|
312
|
-
const isTabVisible = typeof document !== 'undefined' && !document.hidden;
|
|
313
|
-
if (tabJustBecameVisibleRef.current || (isTabVisible && wasAuthenticatedRef.current && isLoading)) {
|
|
314
|
-
return loadingFallback || (
|
|
315
|
-
<main className="grid place-items-center size-full">
|
|
316
|
-
<LoadingSpinner />
|
|
317
|
-
</main>
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// We've confirmed redirect after grace period → redirect
|
|
322
|
-
if (shouldRedirect) {
|
|
323
|
-
return <Navigate to={loginPath} replace />;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// User was authenticated before but now appears logged out
|
|
327
|
-
// Show loading state while we wait for session refresh (unless we're not loading)
|
|
328
|
-
if (isLoading) {
|
|
329
|
-
return loadingFallback || (
|
|
330
|
-
<main className="grid place-items-center size-full">
|
|
331
|
-
<LoadingSpinner />
|
|
332
|
-
</main>
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Not loading and not authenticated → redirect
|
|
337
|
-
return <Navigate to={loginPath} replace />;
|
|
150
|
+
)
|
|
151
|
+
);
|
|
338
152
|
}
|
|
339
153
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return <Outlet />;
|
|
154
|
+
if (phase === 'redirect') {
|
|
155
|
+
return <Navigate to={resolvedLoginPath} replace />;
|
|
343
156
|
}
|
|
344
157
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
<AlertDescription>
|
|
358
|
-
You don't have access to any events. Please contact your administrator if you believe this is an error.
|
|
359
|
-
</AlertDescription>
|
|
360
|
-
</Alert>
|
|
361
|
-
</main>
|
|
158
|
+
if (phase === 'no-events') {
|
|
159
|
+
return (
|
|
160
|
+
noEventsFallback ?? (
|
|
161
|
+
<main className="grid place-items-center text-center min-h-screen p-8">
|
|
162
|
+
<Alert variant="destructive" className="max-w-md">
|
|
163
|
+
<AlertTitle>No Events Available</AlertTitle>
|
|
164
|
+
<AlertDescription>
|
|
165
|
+
You don't have access to any events. Please contact your administrator if you believe this is an error.
|
|
166
|
+
</AlertDescription>
|
|
167
|
+
</Alert>
|
|
168
|
+
</main>
|
|
169
|
+
)
|
|
362
170
|
);
|
|
363
171
|
}
|
|
364
172
|
|
|
365
|
-
// KEY FIX: Allow rendering when events exist but none selected
|
|
366
|
-
// This allows the event selector (typically in PaceAppLayout header) to be visible
|
|
367
|
-
// Individual pages should handle "no selected event" state gracefully
|
|
368
|
-
// Auto-selection will handle selecting the next event, or user can manually select
|
|
369
|
-
|
|
370
|
-
// If no event selected but events exist, allow rendering
|
|
371
|
-
// The event selector will be visible and user can select, or auto-selection will kick in
|
|
372
|
-
if (!selectedEvent) {
|
|
373
|
-
// Log for debugging - this is expected behavior, not an error
|
|
374
|
-
return <Outlet />;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Event is selected - allow rendering
|
|
378
173
|
return <Outlet />;
|
|
379
174
|
}
|
|
380
|
-
|