@jmruthers/pace-core 0.6.1 → 0.6.2
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 +43 -10
- package/cursor-rules/00-pace-core-compliance.mdc +18 -91
- package/cursor-rules/01-standards-compliance.mdc +16 -47
- package/cursor-rules/02-project-structure.mdc +4 -4
- package/cursor-rules/03-solid-principles.mdc +45 -164
- package/cursor-rules/04-testing-standards.mdc +22 -69
- package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
- package/cursor-rules/06-code-quality.mdc +42 -125
- package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +18 -0
- package/cursor-rules/README.md +2 -1
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
- package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
- package/dist/{DataTable-DQ7RSOHE.js → DataTable-TPTKCX4D.js} +10 -9
- package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +356 -111
- package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
- package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
- package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
- package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
- package/dist/chunk-24UVZUZG.js.map +1 -0
- package/dist/{chunk-4N5C5XZU.js → chunk-2UOI2FG5.js} +4 -4
- package/dist/chunk-2UOI2FG5.js.map +1 -0
- package/dist/{chunk-T33XF5ZC.js → chunk-3XC4CPTD.js} +4317 -3963
- package/dist/chunk-3XC4CPTD.js.map +1 -0
- package/dist/{chunk-4ZC4GX36.js → chunk-6J4GEEJR.js} +172 -45
- package/dist/chunk-6J4GEEJR.js.map +1 -0
- package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-BYFSK72L.js → chunk-EHMR7VYL.js} +4 -4
- package/dist/chunk-EHMR7VYL.js.map +1 -0
- package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
- package/dist/chunk-F2IMUDXZ.js.map +1 -0
- package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
- package/dist/chunk-FFQEQTNW.js.map +1 -0
- package/dist/chunk-FMUCXFII.js +76 -0
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-3XTALGJF.js → chunk-MMZ7JXPU.js} +60 -223
- package/dist/chunk-MMZ7JXPU.js.map +1 -0
- package/dist/{chunk-GLK6VM3F.js → chunk-NECFR5MM.js} +254 -170
- package/dist/chunk-NECFR5MM.js.map +1 -0
- package/dist/{chunk-JBKQ3SAO.js → chunk-SFZUDBL5.js} +40 -4
- package/dist/chunk-SFZUDBL5.js.map +1 -0
- package/dist/{chunk-XM25TVIE.js → chunk-XWQCNGTQ.js} +724 -363
- package/dist/chunk-XWQCNGTQ.js.map +1 -0
- package/dist/components.d.ts +5 -5
- package/dist/components.js +14 -11
- package/dist/components.js.map +1 -1
- package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
- package/dist/hooks.d.ts +55 -122
- package/dist/hooks.js +8 -12
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +60 -13
- package/dist/index.js +19 -19
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +21 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +145 -114
- package/dist/rbac/index.js +8 -11
- package/dist/theming/runtime.d.ts +1 -13
- package/dist/theming/runtime.js +1 -1
- package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
- package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
- package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
- package/dist/types.d.ts +2 -2
- package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +31 -1
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +14 -14
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +7 -1
- package/docs/api/classes/ColumnFactory.md +8 -8
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +18 -15
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +4 -4
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +9 -2
- package/docs/api/interfaces/ButtonProps.md +7 -4
- package/docs/api/interfaces/CalendarProps.md +8 -5
- package/docs/api/interfaces/CardProps.md +8 -5
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +24 -21
- package/docs/api/interfaces/DataTableColumn.md +31 -31
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
- package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
- package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +8 -8
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +26 -23
- package/docs/api/interfaces/FooterProps.md +10 -8
- package/docs/api/interfaces/FormFieldProps.md +10 -10
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +7 -4
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +14 -11
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +11 -11
- package/docs/api/interfaces/NavigationMenuProps.md +15 -15
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
- package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
- package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
- package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
- package/docs/api/interfaces/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +3 -3
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +4 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +14 -11
- package/docs/api/interfaces/UserMenuProps.md +8 -6
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +575 -634
- package/docs/architecture/database-schema-requirements.md +161 -0
- package/docs/core-concepts/rbac-system.md +3 -3
- package/docs/documentation-index.md +2 -4
- package/docs/getting-started/cursor-rules.md +2 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +2 -24
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/database-changes-december-2025.md +3 -3
- package/docs/rbac/event-based-apps.md +1 -1
- package/docs/rbac/getting-started.md +1 -1
- package/docs/rbac/quick-start.md +1 -1
- package/docs/standards/README.md +1 -0
- package/package.json +2 -1
- package/scripts/audit/core/checks/accessibility.cjs +197 -0
- package/scripts/audit/core/checks/api-usage.cjs +191 -0
- package/scripts/audit/core/checks/bundle.cjs +142 -0
- package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +714 -687
- package/scripts/audit/core/checks/config.cjs +54 -0
- package/scripts/audit/core/checks/coverage.cjs +84 -0
- package/scripts/audit/core/checks/dependencies.cjs +454 -0
- package/scripts/audit/core/checks/documentation.cjs +203 -0
- package/scripts/audit/core/checks/environment.cjs +128 -0
- package/scripts/audit/core/checks/error-handling.cjs +299 -0
- package/scripts/audit/core/checks/forms.cjs +172 -0
- package/scripts/audit/core/checks/heuristics.cjs +68 -0
- package/scripts/audit/core/checks/hooks.cjs +334 -0
- package/scripts/audit/core/checks/imports.cjs +244 -0
- package/scripts/audit/core/checks/performance.cjs +325 -0
- package/scripts/audit/core/checks/routes.cjs +117 -0
- package/scripts/audit/core/checks/state.cjs +130 -0
- package/scripts/audit/core/checks/structure.cjs +65 -0
- package/scripts/audit/core/checks/style.cjs +584 -0
- package/scripts/audit/core/checks/testing.cjs +122 -0
- package/scripts/audit/core/checks/typescript.cjs +61 -0
- package/scripts/audit/core/scanner.cjs +199 -0
- package/scripts/audit/core/utils.cjs +137 -0
- package/scripts/audit/index.cjs +223 -0
- package/scripts/audit/reporters/console.cjs +151 -0
- package/scripts/audit/reporters/json.cjs +54 -0
- package/scripts/audit/reporters/markdown.cjs +124 -0
- package/scripts/audit-consuming-app.cjs +61 -936
- package/scripts/build-docs/build-decision.js +240 -0
- package/scripts/build-docs/cache-utils.js +105 -0
- package/scripts/build-docs/content-normalization.js +150 -0
- package/scripts/build-docs/file-utils.js +105 -0
- package/scripts/build-docs/git-utils.js +86 -0
- package/scripts/build-docs/hash-utils.js +116 -0
- package/scripts/build-docs/typedoc-runner.js +220 -0
- package/scripts/build-docs-incremental.js +77 -913
- package/scripts/utils/command-runner.js +16 -11
- package/scripts/validate-formats.js +61 -56
- package/scripts/validate-master.js +74 -69
- package/scripts/validate-pre-publish.js +70 -65
- package/src/__tests__/hooks/usePermissions.test.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +12 -18
- package/src/components/Alert/Alert.tsx +5 -7
- package/src/components/Avatar/Avatar.test.tsx +4 -4
- package/src/components/Badge/Badge.tsx +14 -0
- package/src/components/Button/Button.tsx +22 -0
- package/src/components/Calendar/Calendar.tsx +8 -2
- package/src/components/Card/Card.tsx +4 -0
- package/src/components/Checkbox/Checkbox.test.tsx +12 -12
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/DataTable/DataTable.tsx +38 -4
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
- package/src/components/DataTable/components/DataTableBody.tsx +8 -0
- package/src/components/DataTable/components/DataTableCore.tsx +196 -554
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
- package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
- package/src/components/DataTable/components/DataTableModals.tsx +8 -0
- package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
- package/src/components/DataTable/components/EditFields.tsx +307 -0
- package/src/components/DataTable/components/EditableRow.tsx +8 -0
- package/src/components/DataTable/components/EmptyState.tsx +10 -0
- package/src/components/DataTable/components/FilterRow.tsx +12 -0
- package/src/components/DataTable/components/GroupHeader.tsx +12 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
- package/src/components/DataTable/components/ImportModal.tsx +7 -0
- package/src/components/DataTable/components/LoadingState.tsx +6 -0
- package/src/components/DataTable/components/PaginationControls.tsx +16 -1
- package/src/components/DataTable/components/RowComponent.tsx +391 -0
- package/src/components/DataTable/components/UnifiedTableBody.tsx +61 -849
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
- package/src/components/DataTable/components/cellValueUtils.ts +40 -0
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
- package/src/components/DataTable/context/DataTableContext.tsx +50 -0
- package/src/components/DataTable/core/ColumnFactory.ts +31 -0
- package/src/components/DataTable/core/DataTableContext.tsx +32 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
- package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
- package/src/components/DataTable/styles.ts +6 -6
- package/src/components/DataTable/types.ts +6 -10
- package/src/components/DataTable/utils/a11yUtils.ts +7 -0
- package/src/components/DataTable/utils/debugTools.ts +18 -113
- package/src/components/DataTable/utils/errorHandling.ts +12 -0
- package/src/components/DataTable/utils/exportUtils.ts +9 -0
- package/src/components/DataTable/utils/flexibleImport.ts +12 -48
- package/src/components/DataTable/utils/paginationUtils.ts +8 -0
- package/src/components/DataTable/utils/performanceUtils.ts +5 -1
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/EventSelector/EventSelector.tsx +3 -0
- package/src/components/FileDisplay/FileDisplay.tsx +32 -18
- package/src/components/FileUpload/FileUpload.tsx +22 -2
- package/src/components/Footer/Footer.test.tsx +16 -16
- package/src/components/Footer/Footer.tsx +14 -11
- package/src/components/Form/Form.tsx +1 -0
- package/src/components/Header/Header.tsx +21 -10
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +8 -4
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoginForm/LoginForm.tsx +4 -0
- package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
- package/src/components/NavigationMenu/types.ts +56 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -2
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +32 -11
- package/src/components/PaceAppLayout/test-setup.tsx +1 -2
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
- package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +80 -434
- package/src/components/Select/context.ts +23 -0
- package/src/components/Select/hooks/useSelectEvents.ts +87 -0
- package/src/components/Select/hooks/useSelectSearch.ts +91 -0
- package/src/components/Select/hooks/useSelectState.ts +104 -0
- package/src/components/Select/index.ts +9 -1
- package/src/components/Select/types.ts +123 -0
- package/src/components/Select/utils/text.ts +26 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Toast/Toast.tsx +4 -0
- package/src/components/Tooltip/Tooltip.tsx +2 -2
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +21 -18
- package/src/components/index.ts +2 -2
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +4 -0
- package/src/hooks/public/usePublicEventLogo.ts +4 -0
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +4 -0
- package/src/hooks/services/useAuth.ts +32 -0
- package/src/hooks/services/useCurrentEvent.ts +6 -0
- package/src/hooks/services/useCurrentOrganisation.ts +6 -0
- package/src/hooks/useDebounce.ts +9 -0
- package/src/hooks/useEventTheme.ts +6 -0
- package/src/hooks/useFileDisplay.ts +4 -0
- package/src/hooks/useFileReference.ts +25 -7
- package/src/hooks/useFileUrl.ts +11 -1
- package/src/hooks/useFocusManagement.ts +14 -0
- package/src/hooks/useFocusTrap.ts +3 -0
- package/src/hooks/useInactivityTracker.ts +3 -0
- package/src/hooks/useKeyboardShortcuts.ts +4 -0
- package/src/hooks/useOrganisationPermissions.ts +4 -0
- package/src/hooks/useOrganisationSecurity.ts +4 -0
- package/src/hooks/usePerformanceMonitor.ts +4 -0
- package/src/hooks/usePermissionCache.ts +7 -0
- package/src/hooks/useQueryCache.ts +12 -1
- package/src/hooks/useSessionRestoration.ts +4 -0
- package/src/hooks/useStorage.ts +4 -0
- package/src/hooks/useToast.ts +1 -1
- package/src/index.ts +2 -1
- package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
- package/src/providers/services/AuthServiceProvider.tsx +18 -0
- package/src/providers/services/EventServiceProvider.tsx +18 -0
- package/src/providers/services/InactivityServiceProvider.tsx +18 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +36 -0
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +2 -2
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
- package/src/rbac/adapters.tsx +14 -5
- package/src/rbac/api.ts +100 -67
- package/src/rbac/components/NavigationProvider.tsx +4 -1
- package/src/rbac/components/PagePermissionGuard.tsx +157 -17
- package/src/rbac/components/RoleBasedRouter.tsx +5 -1
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +20 -5
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
- package/src/rbac/engine.ts +38 -14
- package/src/rbac/hooks/permissions/index.ts +7 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
- package/src/rbac/hooks/permissions/useCan.ts +347 -0
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
- package/src/rbac/hooks/useCan.test.ts +71 -64
- package/src/rbac/hooks/usePermissions.ts +14 -995
- package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
- package/src/rbac/hooks/useResourcePermissions.ts +14 -4
- package/src/rbac/hooks/useSecureSupabase.ts +33 -13
- package/src/rbac/permissions.ts +0 -30
- package/src/rbac/secureClient.ts +200 -61
- package/src/rbac/types.ts +8 -0
- package/src/theming/__tests__/parseEventColours.test.ts +6 -9
- package/src/theming/parseEventColours.ts +5 -19
- package/src/types/vitest-globals.d.ts +51 -26
- package/src/utils/__mocks__/supabaseMock.ts +1 -3
- package/src/utils/__tests__/formatting.unit.test.ts +4 -4
- package/src/utils/__tests__/index.unit.test.ts +2 -2
- package/src/utils/audit/audit.ts +0 -3
- package/src/utils/core/cn.ts +1 -1
- package/src/utils/file-reference/index.ts +53 -1
- package/src/utils/formatting/formatting.ts +8 -18
- package/src/utils/index.ts +0 -1
- package/dist/chunk-3QRJFVBR.js.map +0 -1
- package/dist/chunk-3XTALGJF.js.map +0 -1
- package/dist/chunk-4N5C5XZU.js.map +0 -1
- package/dist/chunk-4ZC4GX36.js.map +0 -1
- package/dist/chunk-BYFSK72L.js.map +0 -1
- package/dist/chunk-EXUD6RNJ.js +0 -451
- package/dist/chunk-EXUD6RNJ.js.map +0 -1
- package/dist/chunk-GLK6VM3F.js.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-JBKQ3SAO.js.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-LXQLPRQ2.js.map +0 -1
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-T33XF5ZC.js.map +0 -1
- package/dist/chunk-XM25TVIE.js.map +0 -1
- package/docs/api/classes/ErrorBoundary.md +0 -144
- package/docs/migration/quick-migration-guide.md +0 -356
- package/docs/migration/service-architecture.md +0 -281
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
- package/src/hooks/useSecureDataAccess.test.ts +0 -559
- package/src/hooks/useSecureDataAccess.ts +0 -681
- /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-TPTKCX4D.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
- /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
- /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
- /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
- /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
- /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Error Boundary Context
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/ErrorBoundary
|
|
5
|
+
* @since 0.6.2
|
|
6
|
+
*
|
|
7
|
+
* Context provider for global error handler configuration in ErrorBoundary components.
|
|
8
|
+
* Allows applications to register a default error handler once, which is then used by
|
|
9
|
+
* all ErrorBoundary instances unless overridden.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* // Setup once in main.tsx
|
|
14
|
+
* <ErrorBoundaryProvider
|
|
15
|
+
* defaultErrorHandler={(error, errorInfo, errorId, componentName) => {
|
|
16
|
+
* if (import.meta.env.DEV) {
|
|
17
|
+
* console.error(`[${componentName}] ErrorBoundary caught error:`, error, errorInfo);
|
|
18
|
+
* }
|
|
19
|
+
* errorTracking.captureException(error, {
|
|
20
|
+
* componentStack: errorInfo.componentStack,
|
|
21
|
+
* errorBoundary: true,
|
|
22
|
+
* errorId,
|
|
23
|
+
* componentName,
|
|
24
|
+
* });
|
|
25
|
+
* }}
|
|
26
|
+
* >
|
|
27
|
+
* <App />
|
|
28
|
+
* </ErrorBoundaryProvider>
|
|
29
|
+
*
|
|
30
|
+
* // Usage - no onError needed, uses global handler
|
|
31
|
+
* <ErrorBoundary componentName="AppRoot">
|
|
32
|
+
* <AppContent />
|
|
33
|
+
* </ErrorBoundary>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import React, { createContext, useContext, ReactNode } from 'react';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Type definition for the global error handler function
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export type GlobalErrorHandler = (
|
|
44
|
+
error: Error,
|
|
45
|
+
errorInfo: React.ErrorInfo,
|
|
46
|
+
errorId: string,
|
|
47
|
+
componentName: string
|
|
48
|
+
) => void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Context value type for ErrorBoundary configuration
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
interface ErrorBoundaryContextType {
|
|
55
|
+
/** Global error handler that will be used by all ErrorBoundary instances */
|
|
56
|
+
defaultErrorHandler?: GlobalErrorHandler;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Context for ErrorBoundary global configuration
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
const ErrorBoundaryContext = createContext<ErrorBoundaryContextType | undefined>(undefined);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Props for ErrorBoundaryProvider component
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
export interface ErrorBoundaryProviderProps {
|
|
70
|
+
/** Children to wrap with the provider */
|
|
71
|
+
children: ReactNode;
|
|
72
|
+
/** Global error handler that will be used by all ErrorBoundary instances unless overridden */
|
|
73
|
+
defaultErrorHandler?: GlobalErrorHandler;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Provider component for ErrorBoundary global error handler configuration
|
|
78
|
+
*
|
|
79
|
+
* Wrap your application with this provider to set a default error handler that will
|
|
80
|
+
* be used by all ErrorBoundary instances unless they provide their own onError prop.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* <ErrorBoundaryProvider
|
|
85
|
+
* defaultErrorHandler={(error, errorInfo, errorId, componentName) => {
|
|
86
|
+
* errorTracking.captureException(error, {
|
|
87
|
+
* componentStack: errorInfo.componentStack,
|
|
88
|
+
* errorBoundary: true,
|
|
89
|
+
* errorId,
|
|
90
|
+
* componentName,
|
|
91
|
+
* });
|
|
92
|
+
* }}
|
|
93
|
+
* >
|
|
94
|
+
* <App />
|
|
95
|
+
* </ErrorBoundaryProvider>
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @public
|
|
99
|
+
*/
|
|
100
|
+
/**
|
|
101
|
+
* Error boundary provider component.
|
|
102
|
+
* Provides global error handling configuration to child ErrorBoundary components.
|
|
103
|
+
*
|
|
104
|
+
* @param props - Error boundary provider configuration
|
|
105
|
+
* @returns The error boundary provider
|
|
106
|
+
*/
|
|
107
|
+
export function ErrorBoundaryProvider({
|
|
108
|
+
children,
|
|
109
|
+
defaultErrorHandler,
|
|
110
|
+
}: ErrorBoundaryProviderProps) {
|
|
111
|
+
const contextValue: ErrorBoundaryContextType = {
|
|
112
|
+
defaultErrorHandler,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<ErrorBoundaryContext.Provider value={contextValue}>
|
|
117
|
+
{children}
|
|
118
|
+
</ErrorBoundaryContext.Provider>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Hook to access ErrorBoundary context
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
export function useErrorBoundaryContext(): ErrorBoundaryContextType | undefined {
|
|
127
|
+
return useContext(ErrorBoundaryContext);
|
|
128
|
+
}
|
|
129
|
+
|
|
@@ -2,7 +2,32 @@
|
|
|
2
2
|
* @file Error Boundary exports
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
// Re-export ErrorBoundary with automatic context support
|
|
6
|
+
import { ErrorBoundary as ErrorBoundaryClass } from './ErrorBoundary';
|
|
7
|
+
import { useErrorBoundaryContext } from './ErrorBoundaryContext';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import type { ErrorBoundaryProps } from './ErrorBoundary';
|
|
10
|
+
|
|
11
|
+
// Export types and provider
|
|
6
12
|
export type { ErrorBoundaryProps, ErrorBoundaryState } from './ErrorBoundary';
|
|
7
|
-
|
|
13
|
+
export { ErrorBoundaryProvider } from './ErrorBoundaryContext';
|
|
14
|
+
export type { ErrorBoundaryProviderProps, GlobalErrorHandler } from './ErrorBoundaryContext';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ErrorBoundary component with automatic context support
|
|
18
|
+
* This wrapper automatically uses the global error handler from ErrorBoundaryProvider
|
|
19
|
+
* if no onError prop is provided.
|
|
20
|
+
*/
|
|
21
|
+
export const ErrorBoundary = React.forwardRef<
|
|
22
|
+
ErrorBoundaryClass,
|
|
23
|
+
Omit<ErrorBoundaryProps, '_globalErrorHandler'>
|
|
24
|
+
>((props, ref) => {
|
|
25
|
+
const context = useErrorBoundaryContext();
|
|
26
|
+
const globalErrorHandler = context?.defaultErrorHandler;
|
|
27
|
+
|
|
28
|
+
return React.createElement(ErrorBoundaryClass, { ...props, ref, _globalErrorHandler: globalErrorHandler });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
ErrorBoundary.displayName = 'ErrorBoundary';
|
|
32
|
+
|
|
8
33
|
export default ErrorBoundary;
|
|
@@ -95,6 +95,9 @@ import { useEffect, useMemo, useRef } from 'react';
|
|
|
95
95
|
import { cn } from '../../utils/core/cn';
|
|
96
96
|
import { logger } from '../../utils/core/logger';
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Props for the EventSelector component.
|
|
100
|
+
*/
|
|
98
101
|
export interface EventSelectorProps {
|
|
99
102
|
/** Placeholder text for the dropdown */
|
|
100
103
|
placeholder?: string;
|
|
@@ -192,10 +192,15 @@ function FileDisplayContent({
|
|
|
192
192
|
|
|
193
193
|
const handleDeleteConfirm = async () => {
|
|
194
194
|
setDeleteDialogOpen(false);
|
|
195
|
-
|
|
196
|
-
|
|
195
|
+
try {
|
|
196
|
+
if (onDelete) {
|
|
197
|
+
await onDelete();
|
|
198
|
+
}
|
|
199
|
+
setImageError(false);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
// Error handling is delegated to onDelete callback
|
|
202
|
+
setImageError(false);
|
|
197
203
|
}
|
|
198
|
-
setImageError(false);
|
|
199
204
|
};
|
|
200
205
|
|
|
201
206
|
const handleImageError = () => {
|
|
@@ -773,16 +778,10 @@ function FileDisplayAuthenticated({
|
|
|
773
778
|
}: FileDisplayProps) {
|
|
774
779
|
const { supabase } = useUnifiedAuth();
|
|
775
780
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
<figure className={className} title="Error">
|
|
779
|
-
<p className={getFallbackClasses(fallbackSize || 'md')}>
|
|
780
|
-
Supabase client not available in authenticated context
|
|
781
|
-
</p>
|
|
782
|
-
</figure>
|
|
783
|
-
);
|
|
784
|
-
}
|
|
781
|
+
// Consolidated state for displayOnly mode - must be before early return
|
|
782
|
+
const [displayOnlyFileReference, setDisplayOnlyFileReference] = useState<FileReference | null>(null);
|
|
785
783
|
|
|
784
|
+
// Call hooks before any early returns - hooks must handle null supabase gracefully
|
|
786
785
|
const {
|
|
787
786
|
fileUrl,
|
|
788
787
|
fileReference,
|
|
@@ -797,20 +796,17 @@ function FileDisplayAuthenticated({
|
|
|
797
796
|
record_id,
|
|
798
797
|
organisation_id,
|
|
799
798
|
category,
|
|
800
|
-
{ supabase }
|
|
799
|
+
{ supabase: supabase || null }
|
|
801
800
|
);
|
|
802
|
-
|
|
803
|
-
// Consolidated state for displayOnly mode
|
|
804
|
-
const [displayOnlyFileReference, setDisplayOnlyFileReference] = useState<FileReference | null>(null);
|
|
805
801
|
|
|
806
802
|
// Use fileUrls map if available, otherwise use useFileUrl hook
|
|
807
803
|
const displayOnlyFileUrlFromMap = displayOnlyFileReference ? fileUrls.get(displayOnlyFileReference.id) : null;
|
|
808
804
|
const displayOnlyFileUrlHook = useFileUrl(
|
|
809
805
|
displayOnlyFileReference && !displayOnlyFileUrlFromMap ? displayOnlyFileReference : null,
|
|
810
806
|
{
|
|
811
|
-
supabase,
|
|
807
|
+
supabase: supabase || null,
|
|
812
808
|
organisation_id,
|
|
813
|
-
autoLoad: !displayOnlyFileUrlFromMap && !!displayOnlyFileReference
|
|
809
|
+
autoLoad: !displayOnlyFileUrlFromMap && !!displayOnlyFileReference && !!supabase
|
|
814
810
|
}
|
|
815
811
|
);
|
|
816
812
|
const displayOnlyFileUrl = displayOnlyFileUrlFromMap || displayOnlyFileUrlHook.url;
|
|
@@ -831,6 +827,17 @@ function FileDisplayAuthenticated({
|
|
|
831
827
|
}
|
|
832
828
|
}, [displayOnly, category, fileReferences, fileUrls]);
|
|
833
829
|
|
|
830
|
+
// Early return check after all hooks are called
|
|
831
|
+
if (!supabase) {
|
|
832
|
+
return (
|
|
833
|
+
<figure className={className} title="Error">
|
|
834
|
+
<p className={getFallbackClasses(fallbackSize || 'md')}>
|
|
835
|
+
Supabase client not available in authenticated context
|
|
836
|
+
</p>
|
|
837
|
+
</figure>
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
|
|
834
841
|
// Delete operation - implementation pending
|
|
835
842
|
const handleDelete = async () => {
|
|
836
843
|
// TODO: Implement delete via FileReferenceService when delete functionality is needed
|
|
@@ -906,6 +913,13 @@ function FileDisplayAuthenticated({
|
|
|
906
913
|
* @param props.category - Optional category filter. When specified, only displays files matching this category and uses single file display variant.
|
|
907
914
|
* @returns React element with file display
|
|
908
915
|
*/
|
|
916
|
+
/**
|
|
917
|
+
* File display component.
|
|
918
|
+
* Renders files from the file reference system with support for previews, downloads, and public/private access.
|
|
919
|
+
*
|
|
920
|
+
* @param props - File display configuration
|
|
921
|
+
* @returns The rendered file display
|
|
922
|
+
*/
|
|
909
923
|
export function FileDisplay({
|
|
910
924
|
table_name,
|
|
911
925
|
record_id,
|
|
@@ -15,6 +15,10 @@ import { getCurrentAppName } from '../../utils/app/appNameResolver';
|
|
|
15
15
|
import { getAppId } from '../../utils/app/appIdResolver';
|
|
16
16
|
import { assertAppId } from '../../types/core';
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Props for the FileUpload component.
|
|
20
|
+
* Configures file upload behavior including storage location, validation, and callbacks.
|
|
21
|
+
*/
|
|
18
22
|
export interface FileUploadProps {
|
|
19
23
|
supabase: SupabaseClient;
|
|
20
24
|
table_name: string;
|
|
@@ -71,6 +75,15 @@ export function FileUpload({
|
|
|
71
75
|
onProgress,
|
|
72
76
|
children
|
|
73
77
|
}: FileUploadProps) {
|
|
78
|
+
// Validate pageContext early - required prop
|
|
79
|
+
if (!pageContext) {
|
|
80
|
+
const errorMsg = 'pageContext is required for FileUpload component. This is used for permission checks.';
|
|
81
|
+
if (import.meta.env.MODE === 'development') {
|
|
82
|
+
console.error('[FileUpload]', errorMsg);
|
|
83
|
+
}
|
|
84
|
+
throw new Error(errorMsg);
|
|
85
|
+
}
|
|
86
|
+
|
|
74
87
|
const [isDragging, setIsDragging] = useState(false);
|
|
75
88
|
const [uploadStates, setUploadStates] = useState<Map<string, FileUploadState>>(new Map());
|
|
76
89
|
const [resolvedAppId, setResolvedAppId] = useState<string | null>(app_id || null);
|
|
@@ -286,6 +299,12 @@ export function FileUpload({
|
|
|
286
299
|
throw new Error(errorMsg);
|
|
287
300
|
}
|
|
288
301
|
|
|
302
|
+
// Validate pageContext before upload
|
|
303
|
+
if (!pageContext) {
|
|
304
|
+
const errorMsg = 'pageContext is required for file upload. This is used for permission checks.';
|
|
305
|
+
throw new Error(errorMsg);
|
|
306
|
+
}
|
|
307
|
+
|
|
289
308
|
const result = await uploadFile({
|
|
290
309
|
table_name,
|
|
291
310
|
record_id,
|
|
@@ -294,7 +313,7 @@ export function FileUpload({
|
|
|
294
313
|
app_id: resolvedAppId ? assertAppId(resolvedAppId) : assertAppId(''),
|
|
295
314
|
category,
|
|
296
315
|
folder,
|
|
297
|
-
pageContext,
|
|
316
|
+
pageContext: pageContext,
|
|
298
317
|
event_id,
|
|
299
318
|
is_public: isPublic
|
|
300
319
|
}, file);
|
|
@@ -393,7 +412,7 @@ export function FileUpload({
|
|
|
393
412
|
onUploadError?.(errorMessage, file);
|
|
394
413
|
}
|
|
395
414
|
}
|
|
396
|
-
}, [uploadFile, table_name, record_id, organisation_id, resolvedAppId, category, folder, isPublic, maxSize, onUploadSuccess, onUploadError, onProgress, validateFile, generatePreview, showPreview, appIdError]);
|
|
415
|
+
}, [uploadFile, table_name, record_id, organisation_id, resolvedAppId, category, folder, isPublic, maxSize, onUploadSuccess, onUploadError, onProgress, validateFile, generatePreview, showPreview, appIdError, pageContext]);
|
|
397
416
|
|
|
398
417
|
const handleDragOver = useCallback((e: React.DragEvent) => {
|
|
399
418
|
e.preventDefault();
|
|
@@ -475,6 +494,7 @@ export function FileUpload({
|
|
|
475
494
|
className="hidden"
|
|
476
495
|
disabled={isDisabled}
|
|
477
496
|
data-testid="file-input"
|
|
497
|
+
aria-label={accept ? `Upload file${multiple ? 's' : ''} (${accept})` : `Upload file${multiple ? 's' : ''}`}
|
|
478
498
|
/>
|
|
479
499
|
<div className="text-sec-600">
|
|
480
500
|
{isResolvingAppId ? (
|
|
@@ -82,7 +82,7 @@ describe('Footer Component', () => {
|
|
|
82
82
|
it('renders with children content', () => {
|
|
83
83
|
renderWithProviders(
|
|
84
84
|
<Footer>
|
|
85
|
-
<
|
|
85
|
+
<section data-testid="custom-content">Custom footer content</section>
|
|
86
86
|
</Footer>
|
|
87
87
|
);
|
|
88
88
|
|
|
@@ -163,7 +163,7 @@ describe('Footer Component', () => {
|
|
|
163
163
|
it('renders content in proper semantic structure', () => {
|
|
164
164
|
renderWithProviders(
|
|
165
165
|
<Footer>
|
|
166
|
-
<
|
|
166
|
+
<section>Custom content</section>
|
|
167
167
|
</Footer>
|
|
168
168
|
);
|
|
169
169
|
|
|
@@ -175,7 +175,7 @@ describe('Footer Component', () => {
|
|
|
175
175
|
it('renders logo before children content', () => {
|
|
176
176
|
renderWithProviders(
|
|
177
177
|
<Footer logo="/logo.png">
|
|
178
|
-
<
|
|
178
|
+
<section data-testid="children">Children content</section>
|
|
179
179
|
</Footer>
|
|
180
180
|
);
|
|
181
181
|
|
|
@@ -189,7 +189,7 @@ describe('Footer Component', () => {
|
|
|
189
189
|
it('renders copyright text after children content', () => {
|
|
190
190
|
renderWithProviders(
|
|
191
191
|
<Footer>
|
|
192
|
-
<
|
|
192
|
+
<section data-testid="children">Children content</section>
|
|
193
193
|
</Footer>
|
|
194
194
|
);
|
|
195
195
|
|
|
@@ -380,7 +380,7 @@ describe('Footer Component', () => {
|
|
|
380
380
|
logo="/test-logo.png"
|
|
381
381
|
copyright="Custom copyright text"
|
|
382
382
|
>
|
|
383
|
-
<
|
|
383
|
+
<section>Test children</section>
|
|
384
384
|
</Footer>
|
|
385
385
|
);
|
|
386
386
|
|
|
@@ -398,27 +398,27 @@ describe('Footer Component', () => {
|
|
|
398
398
|
it('works with complex children content', () => {
|
|
399
399
|
renderWithProviders(
|
|
400
400
|
<Footer>
|
|
401
|
-
<
|
|
402
|
-
<
|
|
401
|
+
<section className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
|
|
402
|
+
<section>
|
|
403
403
|
<h3 className="font-semibold mb-2">About Us</h3>
|
|
404
404
|
<p className="text-sm text-muted-foreground">
|
|
405
405
|
We provide innovative solutions for modern businesses.
|
|
406
406
|
</p>
|
|
407
|
-
</
|
|
408
|
-
<
|
|
407
|
+
</section>
|
|
408
|
+
<section>
|
|
409
409
|
<h3 className="font-semibold mb-2">Contact</h3>
|
|
410
410
|
<p className="text-sm text-muted-foreground">
|
|
411
411
|
Email: info@company.com
|
|
412
412
|
</p>
|
|
413
|
-
</
|
|
414
|
-
<
|
|
413
|
+
</section>
|
|
414
|
+
<section>
|
|
415
415
|
<h3 className="font-semibold mb-2">Follow Us</h3>
|
|
416
|
-
<
|
|
416
|
+
<nav className="flex gap-2">
|
|
417
417
|
<a href="#" className="text-muted-foreground hover:text-foreground">Twitter</a>
|
|
418
418
|
<a href="#" className="text-muted-foreground hover:text-foreground">LinkedIn</a>
|
|
419
|
-
</
|
|
420
|
-
</
|
|
421
|
-
</
|
|
419
|
+
</nav>
|
|
420
|
+
</section>
|
|
421
|
+
</section>
|
|
422
422
|
</Footer>
|
|
423
423
|
);
|
|
424
424
|
|
|
@@ -463,7 +463,7 @@ describe('Footer Component', () => {
|
|
|
463
463
|
logo="/logo.png"
|
|
464
464
|
className="custom-footer"
|
|
465
465
|
>
|
|
466
|
-
<
|
|
466
|
+
<section data-testid="custom-content">Custom footer content</section>
|
|
467
467
|
</Footer>
|
|
468
468
|
);
|
|
469
469
|
|
|
@@ -47,28 +47,28 @@
|
|
|
47
47
|
*
|
|
48
48
|
* // Footer with custom content
|
|
49
49
|
* <Footer companyName="My Company">
|
|
50
|
-
* <
|
|
51
|
-
* <
|
|
50
|
+
* <section className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
|
|
51
|
+
* <section>
|
|
52
52
|
* <h3 className="font-semibold mb-2">About Us</h3>
|
|
53
53
|
* <p className="text-sm text-muted-foreground">
|
|
54
54
|
* We provide innovative solutions for modern businesses.
|
|
55
55
|
* </p>
|
|
56
|
-
* </
|
|
57
|
-
* <
|
|
56
|
+
* </section>
|
|
57
|
+
* <section>
|
|
58
58
|
* <h3 className="font-semibold mb-2">Contact</h3>
|
|
59
59
|
* <p className="text-sm text-muted-foreground">
|
|
60
60
|
* Email: info@mycompany.com<br />
|
|
61
61
|
* Phone: (555) 123-4567
|
|
62
62
|
* </p>
|
|
63
|
-
* </
|
|
64
|
-
* <
|
|
63
|
+
* </section>
|
|
64
|
+
* <section>
|
|
65
65
|
* <h3 className="font-semibold mb-2">Follow Us</h3>
|
|
66
|
-
* <
|
|
66
|
+
* <nav className="flex gap-2">
|
|
67
67
|
* <a href="#" className="text-muted-foreground hover:text-foreground">Twitter</a>
|
|
68
68
|
* <a href="#" className="text-muted-foreground hover:text-foreground">LinkedIn</a>
|
|
69
|
-
* </
|
|
70
|
-
* </
|
|
71
|
-
* </
|
|
69
|
+
* </nav>
|
|
70
|
+
* </section>
|
|
71
|
+
* </section>
|
|
72
72
|
* </Footer>
|
|
73
73
|
* ```
|
|
74
74
|
*
|
|
@@ -87,6 +87,9 @@
|
|
|
87
87
|
import React from 'react';
|
|
88
88
|
import { cn } from '../../utils/core/cn';
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Props for the Footer component.
|
|
92
|
+
*/
|
|
90
93
|
export interface FooterProps {
|
|
91
94
|
/**
|
|
92
95
|
* Company or organization name
|
|
@@ -138,7 +141,7 @@ export interface FooterProps {
|
|
|
138
141
|
*
|
|
139
142
|
* // With children
|
|
140
143
|
* <Footer>
|
|
141
|
-
* <
|
|
144
|
+
* <section>Custom footer content</section>
|
|
142
145
|
* </Footer>
|
|
143
146
|
*
|
|
144
147
|
* // With logo and copyright
|
|
@@ -359,6 +359,7 @@ export function FormField<
|
|
|
359
359
|
type={type}
|
|
360
360
|
placeholder={placeholder}
|
|
361
361
|
data-testid={testId}
|
|
362
|
+
aria-label={label || placeholder || name}
|
|
362
363
|
className={cn(
|
|
363
364
|
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
364
365
|
fieldError && "border-destructive focus-visible:ring-destructive"
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
* ]}
|
|
43
43
|
* currentPath="/dashboard"
|
|
44
44
|
* actions={
|
|
45
|
-
*
|
|
45
|
+
* <>
|
|
46
46
|
* <Button variant="outline">Export</Button>
|
|
47
47
|
* <Button>New Item</Button>
|
|
48
|
-
*
|
|
48
|
+
* </>
|
|
49
49
|
* }
|
|
50
50
|
* user={currentUser}
|
|
51
51
|
* onSignOut={handleSignOut}
|
|
@@ -199,10 +199,10 @@ export interface HeaderProps {
|
|
|
199
199
|
*
|
|
200
200
|
* function HeaderWithActions() {
|
|
201
201
|
* const customActions = (
|
|
202
|
-
*
|
|
202
|
+
* <>
|
|
203
203
|
* <Button variant="outline" size="sm">Export</Button>
|
|
204
204
|
* <Button size="sm">New Item</Button>
|
|
205
|
-
*
|
|
205
|
+
* </>
|
|
206
206
|
* );
|
|
207
207
|
*
|
|
208
208
|
* return (
|
|
@@ -238,6 +238,13 @@ export interface HeaderProps {
|
|
|
238
238
|
*
|
|
239
239
|
* @since 0.1.0
|
|
240
240
|
*/
|
|
241
|
+
/**
|
|
242
|
+
* Header component for application layouts.
|
|
243
|
+
* Provides navigation, user menu, organisation/event selectors, and customizable branding.
|
|
244
|
+
*
|
|
245
|
+
* @param props - Header configuration
|
|
246
|
+
* @returns The rendered header
|
|
247
|
+
*/
|
|
241
248
|
export function Header({
|
|
242
249
|
logoUrl,
|
|
243
250
|
logoAlt = 'Logo',
|
|
@@ -278,7 +285,7 @@ export function Header({
|
|
|
278
285
|
"w-full border-b border-main-200 h-16 shadow-sm bg-main-100 ",
|
|
279
286
|
className
|
|
280
287
|
)} role="banner">
|
|
281
|
-
<nav className="px-4 w-[min(var(--app-width),100%)] mx-auto
|
|
288
|
+
<nav className="px-4 w-[min(var(--app-width),100%)] mx-auto grid grid-cols-[auto_1fr_auto_auto_auto_auto] items-center gap-4 h-full">
|
|
282
289
|
{/* Logo */}
|
|
283
290
|
{logo ? (
|
|
284
291
|
logoHref ? (
|
|
@@ -333,10 +340,7 @@ export function Header({
|
|
|
333
340
|
itemsPreFiltered={true}
|
|
334
341
|
/>
|
|
335
342
|
)}
|
|
336
|
-
|
|
337
343
|
|
|
338
|
-
{/* Right side: Organisation Selector, Event Selector, Actions, and User Menu */}
|
|
339
|
-
<div className="flex items-center gap-4 ml-auto">
|
|
340
344
|
{/* Organisation Selector - Only show if user has organisations */}
|
|
341
345
|
{showOrgSelector ? (
|
|
342
346
|
<OrganisationSelectorConditional />
|
|
@@ -346,7 +350,15 @@ export function Header({
|
|
|
346
350
|
{showEventSelector ? (
|
|
347
351
|
<EventSelector
|
|
348
352
|
placeholder="Select event"
|
|
349
|
-
className=
|
|
353
|
+
className={cn(
|
|
354
|
+
"w-96",
|
|
355
|
+
// If both org selector and actions exist, EventSelector uses 1 column
|
|
356
|
+
// If only one exists, EventSelector spans 2 columns
|
|
357
|
+
// If neither exists, EventSelector spans 3 columns
|
|
358
|
+
showOrgSelector && actions ? "col-span-1" :
|
|
359
|
+
showOrgSelector || actions ? "col-span-2" :
|
|
360
|
+
"col-span-3"
|
|
361
|
+
)}
|
|
350
362
|
data-testid="event-selector"
|
|
351
363
|
/>
|
|
352
364
|
) : null}
|
|
@@ -367,7 +379,6 @@ export function Header({
|
|
|
367
379
|
/>
|
|
368
380
|
)
|
|
369
381
|
)}
|
|
370
|
-
</div>
|
|
371
382
|
|
|
372
383
|
</nav>
|
|
373
384
|
</header>
|
|
@@ -420,14 +420,14 @@ describe('InputGroup Component', () => {
|
|
|
420
420
|
// Accessibility tests
|
|
421
421
|
describe('Accessibility', () => {
|
|
422
422
|
it('forwards ref correctly', () => {
|
|
423
|
-
const ref = React.createRef<
|
|
423
|
+
const ref = React.createRef<HTMLFieldSetElement>();
|
|
424
424
|
renderWithProviders(
|
|
425
425
|
<InputGroup ref={ref}>
|
|
426
426
|
<Input placeholder="Input 1" />
|
|
427
427
|
</InputGroup>
|
|
428
428
|
);
|
|
429
429
|
|
|
430
|
-
expect(ref.current).toBeInstanceOf(
|
|
430
|
+
expect(ref.current).toBeInstanceOf(HTMLFieldSetElement);
|
|
431
431
|
});
|
|
432
432
|
|
|
433
433
|
it('maintains proper focus order', async () => {
|
|
@@ -55,6 +55,10 @@ import { cn } from '../../utils/core/cn';
|
|
|
55
55
|
// BASE INPUT COMPONENT
|
|
56
56
|
// ============================================================================
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Props for the Input component.
|
|
60
|
+
* Extends standard input HTML attributes.
|
|
61
|
+
*/
|
|
58
62
|
export interface InputProps
|
|
59
63
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
60
64
|
/**
|
|
@@ -136,7 +140,7 @@ Input.displayName = 'Input';
|
|
|
136
140
|
// INPUT GROUP COMPONENT
|
|
137
141
|
// ============================================================================
|
|
138
142
|
|
|
139
|
-
export interface InputGroupProps extends React.HTMLAttributes<
|
|
143
|
+
export interface InputGroupProps extends React.HTMLAttributes<HTMLFieldSetElement> {
|
|
140
144
|
/** Child elements to be rendered in the group */
|
|
141
145
|
children: React.ReactNode;
|
|
142
146
|
/** Layout orientation of the input group */
|
|
@@ -165,7 +169,7 @@ export interface InputGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
165
169
|
* </InputGroup>
|
|
166
170
|
* ```
|
|
167
171
|
*/
|
|
168
|
-
export const InputGroup = React.forwardRef<
|
|
172
|
+
export const InputGroup = React.forwardRef<HTMLFieldSetElement, InputGroupProps>(
|
|
169
173
|
({ className, children, orientation = 'vertical', spacing = 'md', ...props }, ref) => {
|
|
170
174
|
const spacingClasses = {
|
|
171
175
|
sm: orientation === 'horizontal' ? 'space-x-2' : 'space-y-2',
|
|
@@ -174,7 +178,7 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
|
|
|
174
178
|
};
|
|
175
179
|
|
|
176
180
|
return (
|
|
177
|
-
<
|
|
181
|
+
<fieldset
|
|
178
182
|
ref={ref}
|
|
179
183
|
className={cn(
|
|
180
184
|
'flex',
|
|
@@ -185,7 +189,7 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
|
|
|
185
189
|
{...props}
|
|
186
190
|
>
|
|
187
191
|
{children}
|
|
188
|
-
</
|
|
192
|
+
</fieldset>
|
|
189
193
|
);
|
|
190
194
|
}
|
|
191
195
|
);
|
|
@@ -382,10 +382,10 @@ describe('LoadingSpinner Component', () => {
|
|
|
382
382
|
|
|
383
383
|
it('works within a card or container', () => {
|
|
384
384
|
renderWithProviders(
|
|
385
|
-
<
|
|
385
|
+
<section className="card">
|
|
386
386
|
<h2>Loading Content</h2>
|
|
387
387
|
<LoadingSpinner size="lg" />
|
|
388
|
-
</
|
|
388
|
+
</section>
|
|
389
389
|
);
|
|
390
390
|
|
|
391
391
|
const spinner = screen.getByRole('status');
|
|
@@ -397,11 +397,11 @@ describe('LoadingSpinner Component', () => {
|
|
|
397
397
|
|
|
398
398
|
it('works with multiple instances', () => {
|
|
399
399
|
renderWithProviders(
|
|
400
|
-
|
|
400
|
+
<>
|
|
401
401
|
<LoadingSpinner size="sm" />
|
|
402
402
|
<LoadingSpinner size="md" />
|
|
403
403
|
<LoadingSpinner size="lg" />
|
|
404
|
-
|
|
404
|
+
</>
|
|
405
405
|
);
|
|
406
406
|
|
|
407
407
|
const spinners = screen.getAllByRole('status');
|
|
@@ -106,6 +106,10 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }
|
|
|
106
106
|
import { Alert, AlertDescription } from '../Alert/Alert';
|
|
107
107
|
import { cn } from '../../utils/core/cn';
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Props for the LoginForm component.
|
|
111
|
+
* Configures login form behavior, validation, and callbacks.
|
|
112
|
+
*/
|
|
109
113
|
export interface LoginFormProps {
|
|
110
114
|
/** Callback invoked when the form is submitted */
|
|
111
115
|
onSignIn: (data: { email: string; password: string }) => Promise<void>;
|