@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
|
@@ -1,66 +1,21 @@
|
|
|
1
|
-
import React, { useState, useEffect,
|
|
2
|
-
import { FileText, ExternalLink } from 'lucide-react';
|
|
1
|
+
import React, { useState, useEffect, useContext } from 'react';
|
|
3
2
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
4
3
|
import type { Database } from '../../types/database';
|
|
5
4
|
import { FileReference, FileCategory } from '../../types/file-reference';
|
|
6
|
-
import { usePublicFileDisplay } from '
|
|
7
|
-
import { useFileDisplay } from '
|
|
5
|
+
import { usePublicFileDisplay } from './usePublicFileDisplay';
|
|
6
|
+
import { useFileDisplay } from './useFileDisplay';
|
|
8
7
|
import { useFileUrl } from '../../hooks/useFileUrl';
|
|
9
8
|
import { PublicPageContext } from '../PublicLayout/PublicPageContext';
|
|
10
9
|
import { useIsPublicPage } from '../PublicLayout/usePublicPageContext';
|
|
11
10
|
import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner';
|
|
11
|
+
import { FileDisplayContent } from './FileDisplayContent';
|
|
12
|
+
import { getFallbackClasses } from './fallbackUtils';
|
|
15
13
|
import { logger } from '../../utils/core/logger';
|
|
16
14
|
|
|
17
|
-
/**
|
|
18
|
-
* Size classes for fallback display
|
|
19
|
-
*/
|
|
20
|
-
const fallbackSizeClasses = {
|
|
21
|
-
xs: 'size-4 text-xs',
|
|
22
|
-
sm: 'size-6 text-sm',
|
|
23
|
-
md: 'size-8 text-base',
|
|
24
|
-
lg: 'size-12 text-lg',
|
|
25
|
-
xl: 'size-16 text-xl',
|
|
26
|
-
'2xl': 'size-20 text-2xl'
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Base classes for fallback display
|
|
31
|
-
*/
|
|
32
|
-
const fallbackBaseClasses = 'size-full grid place-items-center text-center text-sec-600 font-semibold';
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Helper function to compute fallback classes for a given size
|
|
36
|
-
*/
|
|
37
|
-
function getFallbackClasses(size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' = 'md'): string {
|
|
38
|
-
const sizeClass = fallbackSizeClasses[size];
|
|
39
|
-
return `${fallbackBaseClasses} ${sizeClass}`.trim();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Default fallback text generator - extracts initials from file name
|
|
44
|
-
*/
|
|
45
|
-
function defaultGenerateFallbackText(fileName?: string): string {
|
|
46
|
-
if (!fileName) return 'FL';
|
|
47
|
-
|
|
48
|
-
// Extract initials from file name (without extension)
|
|
49
|
-
const baseName = fileName.replace(/\.[^/.]+$/, '');
|
|
50
|
-
const words = baseName.split(/[\s\-_]+/);
|
|
51
|
-
|
|
52
|
-
if (words.length === 0) return 'FL';
|
|
53
|
-
|
|
54
|
-
return words
|
|
55
|
-
.map(word => word.charAt(0).toUpperCase())
|
|
56
|
-
.join('')
|
|
57
|
-
.substring(0, 3); // Max 3 characters
|
|
58
|
-
}
|
|
59
|
-
|
|
60
15
|
export interface FileDisplayProps {
|
|
61
16
|
table_name: string;
|
|
62
17
|
record_id: string;
|
|
63
|
-
/**
|
|
18
|
+
/**
|
|
64
19
|
* Optional organisation ID. When not provided (undefined), the component will automatically
|
|
65
20
|
* search for files in both user-scoped (organisation_id = null) and organisation-scoped contexts.
|
|
66
21
|
* If both types of files exist, organisation-scoped files are preferred.
|
|
@@ -89,9 +44,9 @@ export interface FileDisplayProps {
|
|
|
89
44
|
fallbackSourceText?: string;
|
|
90
45
|
/** Size variant for fallback display (only applies when showFallback is true) */
|
|
91
46
|
fallbackSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
92
|
-
/**
|
|
93
|
-
* Enable children rendering in displayOnly mode. When true, uses standard display path
|
|
94
|
-
* (with children support) even if showDelete is false. The delete button will only appear
|
|
47
|
+
/**
|
|
48
|
+
* Enable children rendering in displayOnly mode. When true, uses standard display path
|
|
49
|
+
* (with children support) even if showDelete is false. The delete button will only appear
|
|
95
50
|
* if showDelete is also true.
|
|
96
51
|
*/
|
|
97
52
|
enableChildren?: boolean;
|
|
@@ -99,540 +54,6 @@ export interface FileDisplayProps {
|
|
|
99
54
|
showMetadata?: boolean;
|
|
100
55
|
}
|
|
101
56
|
|
|
102
|
-
// Shared rendering logic for file display
|
|
103
|
-
interface FileDisplayContentProps {
|
|
104
|
-
isLoading: boolean;
|
|
105
|
-
error: string | Error | null;
|
|
106
|
-
fileUrl: string | null;
|
|
107
|
-
fileReference: FileReference | null;
|
|
108
|
-
fileReferences: FileReference[];
|
|
109
|
-
fileUrls: Map<string, string>;
|
|
110
|
-
fileCount: number;
|
|
111
|
-
category: FileCategory | undefined;
|
|
112
|
-
displayOnly: boolean;
|
|
113
|
-
showDelete: boolean;
|
|
114
|
-
className: string;
|
|
115
|
-
imgClassName?: string;
|
|
116
|
-
children?: React.ReactNode;
|
|
117
|
-
onDelete?: () => Promise<void>;
|
|
118
|
-
clearError?: () => void;
|
|
119
|
-
organisation_id: string | undefined;
|
|
120
|
-
loadingComponent?: React.ComponentType;
|
|
121
|
-
errorComponent?: React.ComponentType<{ error: Error | string | null; retry?: () => void }>;
|
|
122
|
-
showFallback?: boolean;
|
|
123
|
-
generateFallbackText?: (fileName?: string) => string;
|
|
124
|
-
fallbackText?: string;
|
|
125
|
-
fallbackSourceText?: string;
|
|
126
|
-
fallbackSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
127
|
-
enableChildren?: boolean;
|
|
128
|
-
showMetadata?: boolean;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const FileDisplayContent = React.memo(function FileDisplayContent({
|
|
132
|
-
isLoading,
|
|
133
|
-
error,
|
|
134
|
-
fileUrl,
|
|
135
|
-
fileReference,
|
|
136
|
-
fileReferences,
|
|
137
|
-
fileUrls,
|
|
138
|
-
fileCount,
|
|
139
|
-
category,
|
|
140
|
-
displayOnly,
|
|
141
|
-
showDelete,
|
|
142
|
-
className,
|
|
143
|
-
imgClassName,
|
|
144
|
-
children,
|
|
145
|
-
onDelete,
|
|
146
|
-
clearError,
|
|
147
|
-
organisation_id: _organisation_id,
|
|
148
|
-
loadingComponent: LoadingComponent,
|
|
149
|
-
errorComponent: ErrorComponent,
|
|
150
|
-
showFallback = false,
|
|
151
|
-
generateFallbackText = defaultGenerateFallbackText,
|
|
152
|
-
fallbackText,
|
|
153
|
-
fallbackSourceText,
|
|
154
|
-
fallbackSize = 'md',
|
|
155
|
-
enableChildren = false,
|
|
156
|
-
showMetadata = true
|
|
157
|
-
}: FileDisplayContentProps) {
|
|
158
|
-
const [imageError, setImageError] = useState(false);
|
|
159
|
-
const [internalFileUrls, setInternalFileUrls] = useState<Map<string, string>>(new Map(fileUrls));
|
|
160
|
-
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
161
|
-
const fileReferencesRef = useRef<FileReference[]>([]);
|
|
162
|
-
const imgRef = useRef<HTMLImageElement | null>(null);
|
|
163
|
-
const currentSrcRef = useRef<string | null>(null);
|
|
164
|
-
const isImageLoadingRef = useRef(false);
|
|
165
|
-
|
|
166
|
-
// Stabilize fileUrl to prevent unnecessary image reloads
|
|
167
|
-
// This prevents NS_BINDING_ABORTED errors when the component re-renders
|
|
168
|
-
const stableFileUrl = useMemo(() => fileUrl, [fileUrl]);
|
|
169
|
-
|
|
170
|
-
// Track when image starts loading to prevent cancellation
|
|
171
|
-
const handleImageLoadStart = useCallback(() => {
|
|
172
|
-
isImageLoadingRef.current = true;
|
|
173
|
-
if (stableFileUrl) {
|
|
174
|
-
currentSrcRef.current = stableFileUrl;
|
|
175
|
-
}
|
|
176
|
-
}, [stableFileUrl]);
|
|
177
|
-
|
|
178
|
-
// Track when image finishes loading
|
|
179
|
-
const handleImageLoad = useCallback(() => {
|
|
180
|
-
isImageLoadingRef.current = false;
|
|
181
|
-
if (stableFileUrl) {
|
|
182
|
-
currentSrcRef.current = stableFileUrl;
|
|
183
|
-
}
|
|
184
|
-
}, [stableFileUrl]);
|
|
185
|
-
|
|
186
|
-
// Compute fallback text
|
|
187
|
-
const computedFallbackText = useMemo(() => {
|
|
188
|
-
if (fallbackText) return fallbackText;
|
|
189
|
-
// Use fallbackSourceText if provided, otherwise fall back to filename
|
|
190
|
-
const sourceText = fallbackSourceText ?? fileReference?.file_metadata?.fileName;
|
|
191
|
-
return generateFallbackText(sourceText);
|
|
192
|
-
}, [fallbackText, fallbackSourceText, fileReference, generateFallbackText]);
|
|
193
|
-
|
|
194
|
-
// Compute fallback classes
|
|
195
|
-
const fallbackClasses = useMemo(() => {
|
|
196
|
-
return getFallbackClasses(fallbackSize);
|
|
197
|
-
}, [fallbackSize]);
|
|
198
|
-
|
|
199
|
-
// Sync fileUrls prop with internal state
|
|
200
|
-
// Track file references to detect when they change and sync URLs
|
|
201
|
-
useEffect(() => {
|
|
202
|
-
const currentIds = fileReferences.map(f => f.id).join(',');
|
|
203
|
-
const prevIds = fileReferencesRef.current.map(f => f.id).join(',');
|
|
204
|
-
|
|
205
|
-
if (currentIds !== prevIds) {
|
|
206
|
-
fileReferencesRef.current = fileReferences;
|
|
207
|
-
// Reset internal URLs when file references change, then immediately sync from props
|
|
208
|
-
setInternalFileUrls(new Map(fileUrls));
|
|
209
|
-
} else {
|
|
210
|
-
// If file references haven't changed, just sync URLs
|
|
211
|
-
setInternalFileUrls(new Map(fileUrls));
|
|
212
|
-
}
|
|
213
|
-
}, [fileReferences, fileUrls]);
|
|
214
|
-
|
|
215
|
-
const handleDeleteClick = () => {
|
|
216
|
-
setDeleteDialogOpen(true);
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const handleDeleteConfirm = async () => {
|
|
220
|
-
setDeleteDialogOpen(false);
|
|
221
|
-
try {
|
|
222
|
-
if (onDelete) {
|
|
223
|
-
await onDelete();
|
|
224
|
-
}
|
|
225
|
-
setImageError(false);
|
|
226
|
-
} catch (_error) {
|
|
227
|
-
// Error handling is delegated to onDelete callback
|
|
228
|
-
setImageError(false);
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
const handleImageError = () => {
|
|
233
|
-
setImageError(true);
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
const getFileIcon = (fileType: string) => {
|
|
237
|
-
if (fileType.startsWith('image/')) return '🖼️';
|
|
238
|
-
if (fileType.startsWith('video/')) return '🎥';
|
|
239
|
-
if (fileType.startsWith('audio/')) return '🎵';
|
|
240
|
-
if (fileType.includes('pdf')) return '📄';
|
|
241
|
-
if (fileType.includes('word')) return '📝';
|
|
242
|
-
if (fileType.includes('excel') || fileType.includes('spreadsheet')) return '📊';
|
|
243
|
-
if (fileType.includes('powerpoint') || fileType.includes('presentation')) return '📊';
|
|
244
|
-
return '📁';
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
const formatFileSize = (bytes: number) => {
|
|
248
|
-
if (bytes === 0) return '0 Bytes';
|
|
249
|
-
const k = 1024;
|
|
250
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
251
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
252
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
// Check for errors first - errors should be shown even if fileCount is 0
|
|
256
|
-
if (error) {
|
|
257
|
-
if (ErrorComponent) {
|
|
258
|
-
return <ErrorComponent error={error} retry={clearError} />;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Show fallback if enabled
|
|
262
|
-
if (showFallback) {
|
|
263
|
-
return (
|
|
264
|
-
<figure className={className} title="File unavailable">
|
|
265
|
-
<p className={fallbackClasses}>
|
|
266
|
-
{computedFallbackText}
|
|
267
|
-
</p>
|
|
268
|
-
</figure>
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
return (
|
|
273
|
-
<figure className={className} title="Error">
|
|
274
|
-
<p className={getFallbackClasses(fallbackSize || 'md')}>
|
|
275
|
-
Error loading file: {error instanceof Error ? error.message : String(error)}
|
|
276
|
-
</p>
|
|
277
|
-
{clearError && (
|
|
278
|
-
<Button
|
|
279
|
-
onClick={clearError}
|
|
280
|
-
className="mt-2"
|
|
281
|
-
aria-label="Retry loading file"
|
|
282
|
-
>
|
|
283
|
-
Try again
|
|
284
|
-
</Button>
|
|
285
|
-
)}
|
|
286
|
-
</figure>
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Show fallback immediately if enabled and we have no files (even during loading)
|
|
291
|
-
// This provides better UX by showing fallback UI instead of a spinner when we know there are no files
|
|
292
|
-
if (fileCount === 0 && !isLoading) {
|
|
293
|
-
// Show fallback if enabled
|
|
294
|
-
if (showFallback) {
|
|
295
|
-
return (
|
|
296
|
-
<figure className={className} title="No file">
|
|
297
|
-
<p className={fallbackClasses}>
|
|
298
|
-
{computedFallbackText}
|
|
299
|
-
</p>
|
|
300
|
-
{children}
|
|
301
|
-
</figure>
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return (
|
|
306
|
-
<figure className={`text-sec-500 text-center p-4 ${className}`}>
|
|
307
|
-
<p>No files found</p>
|
|
308
|
-
{children}
|
|
309
|
-
</figure>
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// During loading, show fallback if enabled (better UX than spinner for empty states)
|
|
314
|
-
if (isLoading && showFallback && fileCount === 0) {
|
|
315
|
-
return (
|
|
316
|
-
<figure className={className} title="Loading...">
|
|
317
|
-
<p className={fallbackClasses}>
|
|
318
|
-
{computedFallbackText}
|
|
319
|
-
</p>
|
|
320
|
-
{children}
|
|
321
|
-
</figure>
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (isLoading) {
|
|
326
|
-
if (LoadingComponent) {
|
|
327
|
-
return <LoadingComponent />;
|
|
328
|
-
}
|
|
329
|
-
return (
|
|
330
|
-
<figure className={className} title="Loading">
|
|
331
|
-
<div className="flex items-center justify-center p-4">
|
|
332
|
-
<LoadingSpinner />
|
|
333
|
-
</div>
|
|
334
|
-
</figure>
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Single file display (when category or displayOnly is specified)
|
|
339
|
-
if ((category || displayOnly) && fileReference) {
|
|
340
|
-
const isImage = fileReference.file_metadata.fileType?.startsWith('image/');
|
|
341
|
-
|
|
342
|
-
// Simplified image-only display when displayOnly is true and it's an image
|
|
343
|
-
// Only use simplified path if enableChildren is not explicitly enabled
|
|
344
|
-
if (displayOnly && isImage && !showDelete && !enableChildren) {
|
|
345
|
-
// Show fallback if image error occurred and fallback is enabled
|
|
346
|
-
if (imageError && showFallback) {
|
|
347
|
-
return (
|
|
348
|
-
<figure className={className} title={fileReference.file_metadata.fileName || 'File'}>
|
|
349
|
-
<p className={fallbackClasses}>
|
|
350
|
-
{computedFallbackText}
|
|
351
|
-
</p>
|
|
352
|
-
</figure>
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Show loading skeleton if URL is not available yet
|
|
357
|
-
if (!stableFileUrl) {
|
|
358
|
-
return (
|
|
359
|
-
<figure className={className || "max-w-full h-48"} title="Loading">
|
|
360
|
-
<p className={fallbackClasses}>
|
|
361
|
-
<LoadingSpinner />
|
|
362
|
-
</p>
|
|
363
|
-
</figure>
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return (
|
|
368
|
-
<figure className={className || ""}>
|
|
369
|
-
<img
|
|
370
|
-
ref={imgRef}
|
|
371
|
-
key={fileReference.id}
|
|
372
|
-
src={stableFileUrl || undefined}
|
|
373
|
-
alt={fileReference.file_metadata.fileName || 'File'}
|
|
374
|
-
className={imgClassName || "object-cover size-full"}
|
|
375
|
-
onError={handleImageError}
|
|
376
|
-
onLoadStart={handleImageLoadStart}
|
|
377
|
-
onLoad={handleImageLoad}
|
|
378
|
-
loading="lazy"
|
|
379
|
-
/>
|
|
380
|
-
</figure>
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Document link display when displayOnly is true and file is not an image
|
|
385
|
-
// Render non-image files as clickable links that open in a new tab
|
|
386
|
-
if (displayOnly && !isImage && stableFileUrl && fileReference && !showDelete) {
|
|
387
|
-
const fileName = fileReference.file_metadata?.fileName || 'Document';
|
|
388
|
-
const ariaLabel = `Open ${fileName} in new tab`;
|
|
389
|
-
|
|
390
|
-
return (
|
|
391
|
-
<figure className={className}>
|
|
392
|
-
<a
|
|
393
|
-
href={stableFileUrl || undefined}
|
|
394
|
-
target="_blank"
|
|
395
|
-
rel="noopener noreferrer"
|
|
396
|
-
aria-label={ariaLabel}
|
|
397
|
-
className="flex items-center gap-2 p-3 bg-sec-50 border border-sec-200 rounded-lg hover:bg-sec-100 transition-colors text-main-600 hover:text-main-700 focus:outline-none focus:ring-2 focus:ring-main-500 focus:ring-offset-2"
|
|
398
|
-
>
|
|
399
|
-
<FileText className="size-5 shrink-0" aria-hidden="true" />
|
|
400
|
-
<span className="flex-1 font-medium truncate">
|
|
401
|
-
{fileName}
|
|
402
|
-
</span>
|
|
403
|
-
<ExternalLink className="size-5 shrink-0" aria-hidden="true" />
|
|
404
|
-
</a>
|
|
405
|
-
{showMetadata && (
|
|
406
|
-
<figcaption>
|
|
407
|
-
<p>{fileName}</p>
|
|
408
|
-
{fileReference.file_metadata.fileSize && (
|
|
409
|
-
<p>{formatFileSize(fileReference.file_metadata.fileSize)} • {fileReference.file_metadata.fileType}</p>
|
|
410
|
-
)}
|
|
411
|
-
</figcaption>
|
|
412
|
-
)}
|
|
413
|
-
</figure>
|
|
414
|
-
);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// Standard single file display with wrapper
|
|
418
|
-
// For displayOnly mode, if fallback is enabled and there's no URL or image error, show fallback instead of folder icon
|
|
419
|
-
if (displayOnly && showFallback && (!stableFileUrl || imageError || !isImage)) {
|
|
420
|
-
return (
|
|
421
|
-
<figure className={className} title={fileReference.file_metadata.fileName || 'File'}>
|
|
422
|
-
<p className={fallbackClasses}>
|
|
423
|
-
{computedFallbackText}
|
|
424
|
-
</p>
|
|
425
|
-
</figure>
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
return (
|
|
430
|
-
<figure className={`relative ${className}`}>
|
|
431
|
-
{isImage && stableFileUrl && !imageError ? (
|
|
432
|
-
<>
|
|
433
|
-
<img
|
|
434
|
-
key={fileReference.id}
|
|
435
|
-
src={stableFileUrl}
|
|
436
|
-
alt={fileReference.file_metadata.fileName || 'File'}
|
|
437
|
-
className={imgClassName || "object-cover size-full"}
|
|
438
|
-
onError={handleImageError}
|
|
439
|
-
loading="lazy"
|
|
440
|
-
/>
|
|
441
|
-
{showDelete && (
|
|
442
|
-
<>
|
|
443
|
-
<Button
|
|
444
|
-
variant="destructive"
|
|
445
|
-
size="icon"
|
|
446
|
-
onClick={handleDeleteClick}
|
|
447
|
-
className="absolute top-2 right-2"
|
|
448
|
-
title="Delete file"
|
|
449
|
-
aria-label="Delete file"
|
|
450
|
-
>
|
|
451
|
-
×
|
|
452
|
-
</Button>
|
|
453
|
-
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
|
454
|
-
<DialogContent size="sm" title="Confirm Delete">
|
|
455
|
-
<DialogHeader>
|
|
456
|
-
<h2>Confirm Delete</h2>
|
|
457
|
-
</DialogHeader>
|
|
458
|
-
<DialogBody>
|
|
459
|
-
<p>Are you sure you want to delete this file? This action cannot be undone.</p>
|
|
460
|
-
</DialogBody>
|
|
461
|
-
<DialogFooter>
|
|
462
|
-
<Button variant="outline" onClick={() => setDeleteDialogOpen(false)}>
|
|
463
|
-
Cancel
|
|
464
|
-
</Button>
|
|
465
|
-
<Button variant="destructive" onClick={handleDeleteConfirm}>
|
|
466
|
-
Delete
|
|
467
|
-
</Button>
|
|
468
|
-
</DialogFooter>
|
|
469
|
-
</DialogContent>
|
|
470
|
-
</Dialog>
|
|
471
|
-
</>
|
|
472
|
-
)}
|
|
473
|
-
{children}
|
|
474
|
-
{showMetadata && (
|
|
475
|
-
<figcaption>
|
|
476
|
-
<p>{fileReference.file_metadata.fileName || 'Unknown file'}</p>
|
|
477
|
-
{(fileReference.file_metadata.fileSize || fileReference.file_metadata.fileType) && (
|
|
478
|
-
<p>
|
|
479
|
-
{fileReference.file_metadata.fileSize && formatFileSize(fileReference.file_metadata.fileSize)}
|
|
480
|
-
{fileReference.file_metadata.fileSize && fileReference.file_metadata.fileType && ' • '}
|
|
481
|
-
{fileReference.file_metadata.fileType}
|
|
482
|
-
</p>
|
|
483
|
-
)}
|
|
484
|
-
</figcaption>
|
|
485
|
-
)}
|
|
486
|
-
</>
|
|
487
|
-
) : isImage && imageError && showFallback ? (
|
|
488
|
-
// Show fallback when image fails to load and fallback is enabled
|
|
489
|
-
<>
|
|
490
|
-
<p className={fallbackClasses} title={fileReference.file_metadata.fileName || 'File'}>
|
|
491
|
-
{computedFallbackText}
|
|
492
|
-
</p>
|
|
493
|
-
{children}
|
|
494
|
-
{showMetadata && (
|
|
495
|
-
<figcaption>
|
|
496
|
-
<p>{fileReference.file_metadata.fileName || 'Unknown file'}</p>
|
|
497
|
-
{(fileReference.file_metadata.fileSize || fileReference.file_metadata.fileType) && (
|
|
498
|
-
<p>
|
|
499
|
-
{fileReference.file_metadata.fileSize && formatFileSize(fileReference.file_metadata.fileSize)}
|
|
500
|
-
{fileReference.file_metadata.fileSize && fileReference.file_metadata.fileType && ' • '}
|
|
501
|
-
{fileReference.file_metadata.fileType}
|
|
502
|
-
</p>
|
|
503
|
-
)}
|
|
504
|
-
</figcaption>
|
|
505
|
-
)}
|
|
506
|
-
</>
|
|
507
|
-
) : (
|
|
508
|
-
<>
|
|
509
|
-
<div className="flex items-center space-x-3 p-3 bg-sec-50 rounded-lg border border-sec-200">
|
|
510
|
-
<span className="text-2xl">
|
|
511
|
-
{getFileIcon(fileReference.file_metadata.fileType || '')}
|
|
512
|
-
</span>
|
|
513
|
-
{showDelete && (
|
|
514
|
-
<>
|
|
515
|
-
<Button
|
|
516
|
-
variant="destructive"
|
|
517
|
-
size="icon"
|
|
518
|
-
onClick={handleDeleteClick}
|
|
519
|
-
className="ml-auto"
|
|
520
|
-
title="Delete file"
|
|
521
|
-
aria-label="Delete file"
|
|
522
|
-
>
|
|
523
|
-
×
|
|
524
|
-
</Button>
|
|
525
|
-
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
|
526
|
-
<DialogContent size="sm" title="Confirm Delete">
|
|
527
|
-
<DialogHeader>
|
|
528
|
-
<h2>Confirm Delete</h2>
|
|
529
|
-
</DialogHeader>
|
|
530
|
-
<DialogBody>
|
|
531
|
-
<p>Are you sure you want to delete this file? This action cannot be undone.</p>
|
|
532
|
-
</DialogBody>
|
|
533
|
-
<DialogFooter>
|
|
534
|
-
<Button variant="outline" onClick={() => setDeleteDialogOpen(false)}>
|
|
535
|
-
Cancel
|
|
536
|
-
</Button>
|
|
537
|
-
<Button variant="destructive" onClick={handleDeleteConfirm}>
|
|
538
|
-
Delete
|
|
539
|
-
</Button>
|
|
540
|
-
</DialogFooter>
|
|
541
|
-
</DialogContent>
|
|
542
|
-
</Dialog>
|
|
543
|
-
</>
|
|
544
|
-
)}
|
|
545
|
-
</div>
|
|
546
|
-
{children}
|
|
547
|
-
{showMetadata && (
|
|
548
|
-
<figcaption>
|
|
549
|
-
<p>{fileReference.file_metadata.fileName || 'Unknown file'}</p>
|
|
550
|
-
{(fileReference.file_metadata.fileSize || fileReference.file_metadata.fileType) && (
|
|
551
|
-
<p>
|
|
552
|
-
{fileReference.file_metadata.fileSize && formatFileSize(fileReference.file_metadata.fileSize)}
|
|
553
|
-
{fileReference.file_metadata.fileSize && fileReference.file_metadata.fileType && ' • '}
|
|
554
|
-
{fileReference.file_metadata.fileType}
|
|
555
|
-
</p>
|
|
556
|
-
)}
|
|
557
|
-
</figcaption>
|
|
558
|
-
)}
|
|
559
|
-
</>
|
|
560
|
-
)}
|
|
561
|
-
</figure>
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// Multiple files display
|
|
566
|
-
return (
|
|
567
|
-
<figure className={`space-y-2 ${className}`}>
|
|
568
|
-
{fileReferences.map((fileRef) => {
|
|
569
|
-
const isImage = fileRef.file_metadata.fileType?.startsWith('image/');
|
|
570
|
-
const fileUrl = internalFileUrls.get(fileRef.id) || null;
|
|
571
|
-
const canDownload = !isImage && fileUrl;
|
|
572
|
-
|
|
573
|
-
return (
|
|
574
|
-
<figure key={fileRef.id} className="flex items-center space-x-3 p-3 bg-sec-50 rounded-lg border border-sec-200">
|
|
575
|
-
{isImage && fileUrl ? (
|
|
576
|
-
<img
|
|
577
|
-
key={fileRef.id}
|
|
578
|
-
src={fileUrl || undefined}
|
|
579
|
-
alt={fileRef.file_metadata.fileName || 'File'}
|
|
580
|
-
className={imgClassName || "object-cover size-full"}
|
|
581
|
-
onError={handleImageError}
|
|
582
|
-
loading="lazy"
|
|
583
|
-
/>
|
|
584
|
-
) : (
|
|
585
|
-
<span className="text-2xl">
|
|
586
|
-
{getFileIcon(fileRef.file_metadata.fileType || '')}
|
|
587
|
-
</span>
|
|
588
|
-
)}
|
|
589
|
-
{showMetadata && (
|
|
590
|
-
<figcaption className="flex-1 min-w-0">
|
|
591
|
-
<p className="font-medium text-sec-900 truncate">
|
|
592
|
-
{fileRef.file_metadata.fileName || 'Unknown file'}
|
|
593
|
-
</p>
|
|
594
|
-
{(fileRef.file_metadata.fileSize || fileRef.file_metadata.fileType || fileRef.file_metadata.category) && (
|
|
595
|
-
<p className="text-sm text-sec-500">
|
|
596
|
-
{fileRef.file_metadata.fileSize && formatFileSize(fileRef.file_metadata.fileSize)}
|
|
597
|
-
{fileRef.file_metadata.fileSize && fileRef.file_metadata.fileType && ' • '}
|
|
598
|
-
{fileRef.file_metadata.fileType}
|
|
599
|
-
{(fileRef.file_metadata.fileSize || fileRef.file_metadata.fileType) && fileRef.file_metadata.category && ' • '}
|
|
600
|
-
{fileRef.file_metadata.category}
|
|
601
|
-
</p>
|
|
602
|
-
)}
|
|
603
|
-
</figcaption>
|
|
604
|
-
)}
|
|
605
|
-
<div className="flex items-center space-x-2">
|
|
606
|
-
{canDownload && (
|
|
607
|
-
<a
|
|
608
|
-
href={fileRef.file_path}
|
|
609
|
-
download={fileRef.file_metadata.fileName || 'download'}
|
|
610
|
-
className="text-main-500 hover:text-main-700 p-1"
|
|
611
|
-
title="Download file"
|
|
612
|
-
>
|
|
613
|
-
↓
|
|
614
|
-
</a>
|
|
615
|
-
)}
|
|
616
|
-
{showDelete && onDelete && (
|
|
617
|
-
<Button
|
|
618
|
-
variant="destructive"
|
|
619
|
-
size="icon"
|
|
620
|
-
onClick={handleDeleteClick}
|
|
621
|
-
title="Delete file"
|
|
622
|
-
aria-label="Delete file"
|
|
623
|
-
>
|
|
624
|
-
×
|
|
625
|
-
</Button>
|
|
626
|
-
)}
|
|
627
|
-
</div>
|
|
628
|
-
</figure>
|
|
629
|
-
);
|
|
630
|
-
})}
|
|
631
|
-
{children}
|
|
632
|
-
</figure>
|
|
633
|
-
);
|
|
634
|
-
});
|
|
635
|
-
|
|
636
57
|
/**
|
|
637
58
|
* Internal component for public page context
|
|
638
59
|
* Uses PublicPageContext to get Supabase client
|
|
@@ -657,14 +78,9 @@ function FileDisplayPublic({
|
|
|
657
78
|
enableChildren,
|
|
658
79
|
showMetadata
|
|
659
80
|
}: FileDisplayProps) {
|
|
660
|
-
// Call all hooks unconditionally at the top level
|
|
661
|
-
// Hooks must be called in the same order on every render
|
|
662
81
|
const publicPageContext = useContext(PublicPageContext);
|
|
663
82
|
const supabase = publicPageContext?.supabase ?? null;
|
|
664
83
|
|
|
665
|
-
// Call hook unconditionally - if supabase is null, the hook will handle it
|
|
666
|
-
// Use a dummy supabase client if null to satisfy type requirements
|
|
667
|
-
// The hook should handle null gracefully, but TypeScript requires a valid type
|
|
668
84
|
const {
|
|
669
85
|
fileUrl,
|
|
670
86
|
fileReference,
|
|
@@ -678,12 +94,10 @@ function FileDisplayPublic({
|
|
|
678
94
|
record_id,
|
|
679
95
|
organisation_id,
|
|
680
96
|
category,
|
|
681
|
-
{ supabase: supabase as unknown as SupabaseClient<Database> }
|
|
97
|
+
{ supabase: supabase as unknown as SupabaseClient<Database> }
|
|
682
98
|
);
|
|
683
99
|
|
|
684
|
-
// Early return after all hooks have been called
|
|
685
100
|
if (!supabase) {
|
|
686
|
-
// If fallback is enabled, show fallback UI instead of error
|
|
687
101
|
if (showFallback) {
|
|
688
102
|
return (
|
|
689
103
|
<FileDisplayContent
|
|
@@ -714,8 +128,6 @@ function FileDisplayPublic({
|
|
|
714
128
|
/>
|
|
715
129
|
);
|
|
716
130
|
}
|
|
717
|
-
|
|
718
|
-
// Only show error if fallback is not enabled
|
|
719
131
|
return (
|
|
720
132
|
<figure className={className} title="Error">
|
|
721
133
|
<p className={getFallbackClasses(fallbackSize || 'md')}>
|
|
@@ -725,7 +137,6 @@ function FileDisplayPublic({
|
|
|
725
137
|
);
|
|
726
138
|
}
|
|
727
139
|
|
|
728
|
-
// Log errors for debugging public file display issues
|
|
729
140
|
if (error) {
|
|
730
141
|
logger.error('FileDisplayPublic', 'Error fetching file', {
|
|
731
142
|
table_name,
|
|
@@ -737,28 +148,23 @@ function FileDisplayPublic({
|
|
|
737
148
|
});
|
|
738
149
|
}
|
|
739
150
|
|
|
740
|
-
// Public context doesn't support delete operations
|
|
741
151
|
const handleDelete = async () => {
|
|
742
152
|
// Delete operations are not available in public context for security reasons
|
|
743
153
|
};
|
|
744
154
|
|
|
745
|
-
// Handle displayOnly mode: select first file (prefer images) from all files
|
|
746
155
|
let finalFileReference = fileReference;
|
|
747
156
|
let finalFileUrl = fileUrl;
|
|
748
157
|
let finalFileReferences = fileReferences;
|
|
749
158
|
let finalFileCount = fileCount;
|
|
750
159
|
|
|
751
160
|
if (displayOnly && !category && fileReferences.length > 0) {
|
|
752
|
-
|
|
753
|
-
const imageFiles = fileReferences.filter(f =>
|
|
161
|
+
const imageFiles = fileReferences.filter(f =>
|
|
754
162
|
f.file_metadata.fileType?.startsWith('image/')
|
|
755
163
|
);
|
|
756
164
|
const targetFile = imageFiles.length > 0 ? imageFiles[0] : fileReferences[0];
|
|
757
165
|
finalFileReference = targetFile;
|
|
758
166
|
finalFileReferences = [targetFile];
|
|
759
167
|
finalFileCount = 1;
|
|
760
|
-
|
|
761
|
-
// Get URL for target file from fileUrls map
|
|
762
168
|
finalFileUrl = fileUrls.get(targetFile.id) || null;
|
|
763
169
|
}
|
|
764
170
|
|
|
@@ -773,7 +179,7 @@ function FileDisplayPublic({
|
|
|
773
179
|
fileCount={finalFileCount}
|
|
774
180
|
category={category}
|
|
775
181
|
displayOnly={displayOnly}
|
|
776
|
-
showDelete={false}
|
|
182
|
+
showDelete={false}
|
|
777
183
|
className={className}
|
|
778
184
|
imgClassName={imgClassName}
|
|
779
185
|
children={children}
|
|
@@ -817,11 +223,9 @@ function FileDisplayAuthenticated({
|
|
|
817
223
|
showMetadata
|
|
818
224
|
}: FileDisplayProps) {
|
|
819
225
|
const { supabase } = useUnifiedAuth();
|
|
226
|
+
const [displayOnlyFileReference, setDisplayOnlyFileReference] =
|
|
227
|
+
useState<FileReference | null>(null);
|
|
820
228
|
|
|
821
|
-
// Consolidated state for displayOnly mode - must be before early return
|
|
822
|
-
const [displayOnlyFileReference, setDisplayOnlyFileReference] = useState<FileReference | null>(null);
|
|
823
|
-
|
|
824
|
-
// Call hooks before any early returns - hooks must handle null supabase gracefully
|
|
825
229
|
const {
|
|
826
230
|
fileUrl,
|
|
827
231
|
fileReference,
|
|
@@ -831,16 +235,13 @@ function FileDisplayAuthenticated({
|
|
|
831
235
|
isLoading,
|
|
832
236
|
error,
|
|
833
237
|
refetch
|
|
834
|
-
} = useFileDisplay(
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
// Use fileUrls map if available, otherwise use useFileUrl hook
|
|
843
|
-
const displayOnlyFileUrlFromMap = displayOnlyFileReference ? fileUrls.get(displayOnlyFileReference.id) : null;
|
|
238
|
+
} = useFileDisplay(table_name, record_id, organisation_id, category, {
|
|
239
|
+
supabase: supabase || null
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const displayOnlyFileUrlFromMap = displayOnlyFileReference
|
|
243
|
+
? fileUrls.get(displayOnlyFileReference.id)
|
|
244
|
+
: null;
|
|
844
245
|
const displayOnlyFileUrlHook = useFileUrl(
|
|
845
246
|
displayOnlyFileReference && !displayOnlyFileUrlFromMap ? displayOnlyFileReference : null,
|
|
846
247
|
{
|
|
@@ -851,23 +252,18 @@ function FileDisplayAuthenticated({
|
|
|
851
252
|
);
|
|
852
253
|
const displayOnlyFileUrl = displayOnlyFileUrlFromMap || displayOnlyFileUrlHook.url;
|
|
853
254
|
|
|
854
|
-
// Handle displayOnly mode: select first file (prefer images) from all files
|
|
855
255
|
useEffect(() => {
|
|
856
256
|
if (displayOnly && !category && fileReferences.length > 0) {
|
|
857
|
-
|
|
858
|
-
const imageFiles = fileReferences.filter(f =>
|
|
257
|
+
const imageFiles = fileReferences.filter(f =>
|
|
859
258
|
f.file_metadata.fileType?.startsWith('image/')
|
|
860
259
|
);
|
|
861
260
|
const targetFile = imageFiles.length > 0 ? imageFiles[0] : fileReferences[0];
|
|
862
261
|
setDisplayOnlyFileReference(targetFile);
|
|
863
|
-
// URL generation is handled by useFileUrl hook or fileUrls map
|
|
864
262
|
} else {
|
|
865
|
-
// Clear displayOnly files when not in displayOnly mode or when category is specified
|
|
866
263
|
setDisplayOnlyFileReference(null);
|
|
867
264
|
}
|
|
868
265
|
}, [displayOnly, category, fileReferences, fileUrls]);
|
|
869
266
|
|
|
870
|
-
// Early return check after all hooks are called
|
|
871
267
|
if (!supabase) {
|
|
872
268
|
return (
|
|
873
269
|
<figure className={className} title="Error">
|
|
@@ -878,12 +274,10 @@ function FileDisplayAuthenticated({
|
|
|
878
274
|
);
|
|
879
275
|
}
|
|
880
276
|
|
|
881
|
-
// Delete operation - implementation pending
|
|
882
277
|
const handleDelete = async () => {
|
|
883
278
|
// TODO: Implement delete via FileReferenceService when delete functionality is needed
|
|
884
279
|
};
|
|
885
280
|
|
|
886
|
-
// Determine final file reference and URL based on mode
|
|
887
281
|
let finalFileReference = fileReference;
|
|
888
282
|
let finalFileUrl = fileUrl;
|
|
889
283
|
let finalFileReferences = fileReferences;
|
|
@@ -892,12 +286,10 @@ function FileDisplayAuthenticated({
|
|
|
892
286
|
let finalError = error;
|
|
893
287
|
|
|
894
288
|
if (displayOnly && !category) {
|
|
895
|
-
// DisplayOnly mode: use state-managed file reference and URL
|
|
896
289
|
finalFileReference = displayOnlyFileReference;
|
|
897
290
|
finalFileUrl = displayOnlyFileUrl;
|
|
898
291
|
finalFileReferences = displayOnlyFileReference ? [displayOnlyFileReference] : [];
|
|
899
292
|
finalFileCount = displayOnlyFileReference ? 1 : 0;
|
|
900
|
-
// Include URL loading state in overall loading state
|
|
901
293
|
if (!displayOnlyFileUrlFromMap) {
|
|
902
294
|
finalIsLoading = isLoading || displayOnlyFileUrlHook.isLoading;
|
|
903
295
|
finalError = error || displayOnlyFileUrlHook.error;
|
|
@@ -935,30 +327,10 @@ function FileDisplayAuthenticated({
|
|
|
935
327
|
);
|
|
936
328
|
}
|
|
937
329
|
|
|
938
|
-
/**
|
|
939
|
-
* Component for displaying file references with context-awareness
|
|
940
|
-
*
|
|
941
|
-
* This component is context-aware and automatically detects whether it's being used
|
|
942
|
-
* in a public or authenticated context. It fetches and displays files from storage.
|
|
943
|
-
*
|
|
944
|
-
* The component automatically detects context and uses:
|
|
945
|
-
* - PublicPageProvider context for public pages
|
|
946
|
-
* - UnifiedAuthProvider context for authenticated pages
|
|
947
|
-
*
|
|
948
|
-
* @param props - File display configuration
|
|
949
|
-
* @param props.displayOnly - Display only a single file instead of all files. Uses first file (prefers images) from all files, without category filtering. When true:
|
|
950
|
-
* - **Image files**: Renders a simplified image-only display without metadata or wrapper divs
|
|
951
|
-
* - **Non-image files** (PDFs, Word docs, Excel files, etc.): Renders as clickable links that open in a new tab with document icon, filename, and external link icon. Links include proper security attributes (`rel="noopener noreferrer"`) and accessibility features (ARIA labels, keyboard navigation, visible focus states)
|
|
952
|
-
* - If `showDelete={true}`, uses standard wrapper behavior instead of simplified display
|
|
953
|
-
* @param props.category - Optional category filter. When specified, only displays files matching this category and uses single file display variant.
|
|
954
|
-
* @returns React element with file display
|
|
955
|
-
*/
|
|
956
330
|
/**
|
|
957
331
|
* File display component.
|
|
958
332
|
* Renders files from the file reference system with support for previews, downloads, and public/private access.
|
|
959
|
-
*
|
|
960
|
-
* @param props - File display configuration
|
|
961
|
-
* @returns The rendered file display
|
|
333
|
+
* Context-aware: uses PublicPageProvider for public pages and UnifiedAuthProvider for authenticated pages.
|
|
962
334
|
*/
|
|
963
335
|
export function FileDisplay({
|
|
964
336
|
table_name,
|
|
@@ -980,10 +352,8 @@ export function FileDisplay({
|
|
|
980
352
|
enableChildren,
|
|
981
353
|
showMetadata
|
|
982
354
|
}: FileDisplayProps) {
|
|
983
|
-
// Check which context we're in and route to the appropriate component
|
|
984
355
|
const isPublicPage = useIsPublicPage();
|
|
985
|
-
|
|
986
|
-
// If we're in a public page context, use the public component
|
|
356
|
+
|
|
987
357
|
if (isPublicPage) {
|
|
988
358
|
return (
|
|
989
359
|
<FileDisplayPublic
|
|
@@ -1009,8 +379,6 @@ export function FileDisplay({
|
|
|
1009
379
|
);
|
|
1010
380
|
}
|
|
1011
381
|
|
|
1012
|
-
// Otherwise, use the authenticated component
|
|
1013
|
-
// It will show an error if not in UnifiedAuthProvider
|
|
1014
382
|
return (
|
|
1015
383
|
<FileDisplayAuthenticated
|
|
1016
384
|
table_name={table_name}
|