@jmruthers/pace-core 0.5.193 → 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 +62 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +299 -0
- package/cursor-rules/01-standards-compliance.mdc +244 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +222 -0
- package/cursor-rules/04-testing-standards.mdc +268 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +309 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +119 -0
- package/cursor-rules/README.md +192 -0
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
- package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
- package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
- package/dist/{UnifiedAuthProvider-RGJTDE2C.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-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
- package/dist/chunk-2UOI2FG5.js.map +1 -0
- package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
- package/dist/chunk-3XC4CPTD.js.map +1 -0
- package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
- package/dist/chunk-6J4GEEJR.js.map +1 -0
- package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
- 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-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
- 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-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
- package/dist/chunk-MMZ7JXPU.js.map +1 -0
- package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
- package/dist/chunk-NECFR5MM.js.map +1 -0
- package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
- package/dist/chunk-SFZUDBL5.js.map +1 -0
- package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
- package/dist/chunk-XWQCNGTQ.js.map +1 -0
- package/dist/components.d.ts +6 -6
- package/dist/components.js +15 -12
- 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 +59 -126
- package/dist/hooks.js +19 -28
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +63 -16
- package/dist/index.js +23 -24
- 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 +146 -115
- 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-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +15 -15
- 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 +263 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +6 -28
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -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 +40 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +12 -6
- 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} +737 -691
- 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 +86 -0
- 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/install-cursor-rules.cjs +236 -0
- 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__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- 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 +16 -4
- package/src/components/Button/Button.tsx +27 -4
- package/src/components/Calendar/Calendar.tsx +9 -3
- 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.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +40 -6
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
- 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 +200 -561
- 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 +9 -1
- 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 +9 -1
- 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 +62 -852
- 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/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- 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 +14 -2
- 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/useKeyboardNavigation.ts +2 -2
- 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/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +8 -7
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/EventSelector/EventSelector.tsx +4 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
- 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 +15 -12
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +31 -26
- package/src/components/Header/Header.tsx +22 -11
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +36 -34
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +12 -8
- package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
- 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.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceAppLayout/test-setup.tsx +1 -2
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +95 -438
- 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 +5 -6
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +5 -1
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +22 -19
- package/src/components/index.ts +2 -2
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +5 -1
- package/src/hooks/public/usePublicEventLogo.ts +5 -1
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +5 -1
- 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/useDataTableState.ts +8 -18
- 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 +16 -2
- package/src/hooks/useFocusTrap.ts +7 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +4 -1
- 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 +8 -1
- 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 +3 -3
- package/src/index.ts +2 -1
- package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- 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 +58 -22
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
- 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/EnhancedNavigationMenu.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.tsx +5 -2
- package/src/rbac/components/PagePermissionGuard.tsx +158 -18
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +6 -2
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +21 -6
- 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 +212 -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/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js +0 -628
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js.map +0 -1
- package/dist/chunk-7FLMSG37.js 2.map +0 -1
- package/dist/chunk-7FLMSG37.js.map +0 -1
- package/dist/chunk-BC4IJKSL.js.map +0 -1
- package/dist/chunk-E3SPN4VZ.js +0 -12917
- package/dist/chunk-E3SPN4VZ.js.map +0 -1
- package/dist/chunk-E66EQZE6 5.js +0 -37
- package/dist/chunk-E66EQZE6.js 2.map +0 -1
- package/dist/chunk-HWIIPPNI.js.map +0 -1
- package/dist/chunk-I7PSE6JW 5.js +0 -191
- package/dist/chunk-I7PSE6JW.js 2.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-IIELH4DL.js.map +0 -1
- package/dist/chunk-KNC55RTG.js 5.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js 5.map +0 -1
- package/dist/chunk-LFNCN2SP.js +0 -412
- package/dist/chunk-LFNCN2SP.js 2.map +0 -1
- package/dist/chunk-LFNCN2SP.js.map +0 -1
- package/dist/chunk-LMC26NLJ 2.js +0 -84
- package/dist/chunk-NOAYCWCX.js +0 -4993
- package/dist/chunk-NOAYCWCX.js.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js.map +0 -1
- package/dist/chunk-QXHPKYJV 3.js +0 -113
- package/dist/chunk-R77UEZ4E.js +0 -68
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
- package/dist/chunk-XNXXZ43G.js.map +0 -1
- package/dist/chunk-ZSAAAMVR 6.js +0 -25
- package/dist/components.js 5.map +0 -1
- package/dist/styles/index 2.js +0 -12
- package/dist/styles/index.js 5.map +0 -1
- package/dist/theming/runtime 5.js +0 -19
- package/dist/theming/runtime.js 5.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 -666
- /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-RGJTDE2C.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/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
|
@@ -25,10 +25,35 @@
|
|
|
25
25
|
* <MyComponent />
|
|
26
26
|
* </ErrorBoundary>
|
|
27
27
|
*
|
|
28
|
+
* // Using global error handler (recommended for applications)
|
|
29
|
+
* import { ErrorBoundaryProvider, ErrorBoundary } from '@jmruthers/pace-core';
|
|
30
|
+
*
|
|
31
|
+
* // Setup once in main.tsx
|
|
32
|
+
* <ErrorBoundaryProvider
|
|
33
|
+
* defaultErrorHandler={(error, errorInfo, errorId, componentName) => {
|
|
34
|
+
* if (import.meta.env.DEV) {
|
|
35
|
+
* console.error(`[${componentName}] ErrorBoundary caught error:`, error, errorInfo);
|
|
36
|
+
* }
|
|
37
|
+
* errorTracking.captureException(error, {
|
|
38
|
+
* componentStack: errorInfo.componentStack,
|
|
39
|
+
* errorBoundary: true,
|
|
40
|
+
* errorId,
|
|
41
|
+
* componentName,
|
|
42
|
+
* });
|
|
43
|
+
* }}
|
|
44
|
+
* >
|
|
45
|
+
* <App />
|
|
46
|
+
* </ErrorBoundaryProvider>
|
|
47
|
+
*
|
|
48
|
+
* // Then use ErrorBoundary without onError prop - uses global handler
|
|
49
|
+
* <ErrorBoundary componentName="AppRoot">
|
|
50
|
+
* <AppContent />
|
|
51
|
+
* </ErrorBoundary>
|
|
52
|
+
*
|
|
28
53
|
* // Error boundary with custom fallback
|
|
29
54
|
* <ErrorBoundary
|
|
30
55
|
* componentName="UserProfile"
|
|
31
|
-
* fallback={<
|
|
56
|
+
* fallback={<section>Something went wrong loading the profile.</section>}
|
|
32
57
|
* onError={(error, errorInfo, errorId) => {
|
|
33
58
|
* console.log('Error caught:', errorId, error);
|
|
34
59
|
* }}
|
|
@@ -58,6 +83,16 @@
|
|
|
58
83
|
* </ErrorBoundary>
|
|
59
84
|
* <Footer />
|
|
60
85
|
* </ErrorBoundary>
|
|
86
|
+
*
|
|
87
|
+
* // Per-instance onError overrides global handler
|
|
88
|
+
* <ErrorBoundary
|
|
89
|
+
* componentName="SpecialCase"
|
|
90
|
+
* onError={(error, errorInfo, errorId) => {
|
|
91
|
+
* // Custom handling for this specific case - global handler won't be called
|
|
92
|
+
* }}
|
|
93
|
+
* >
|
|
94
|
+
* <SpecialComponent />
|
|
95
|
+
* </ErrorBoundary>
|
|
61
96
|
* ```
|
|
62
97
|
*
|
|
63
98
|
* @accessibility
|
|
@@ -75,7 +110,7 @@
|
|
|
75
110
|
* - Configurable error reporting
|
|
76
111
|
*
|
|
77
112
|
* @dependencies
|
|
78
|
-
* - React
|
|
113
|
+
* - React 19+ - Component lifecycle
|
|
79
114
|
* - Performance monitoring utilities
|
|
80
115
|
* - Tailwind CSS - Styling
|
|
81
116
|
*/
|
|
@@ -120,6 +155,8 @@ export interface ErrorBoundaryProps {
|
|
|
120
155
|
enableRetry?: boolean;
|
|
121
156
|
/** Whether to enable error reporting */
|
|
122
157
|
enableReporting?: boolean;
|
|
158
|
+
/** Internal: Global error handler from context (not part of public API) */
|
|
159
|
+
_globalErrorHandler?: (error: Error, errorInfo: React.ErrorInfo, errorId: string, componentName: string) => void;
|
|
123
160
|
}
|
|
124
161
|
|
|
125
162
|
/**
|
|
@@ -162,15 +199,16 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
162
199
|
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
|
163
200
|
const { componentName = 'Unknown Component', onError, enableReporting = true } = this.props;
|
|
164
201
|
const errorId = this.state.errorId!;
|
|
202
|
+
const componentNameForHandler = componentName || 'Unknown Component';
|
|
165
203
|
|
|
166
204
|
this.setState({ errorInfo });
|
|
167
205
|
|
|
168
206
|
// Enhanced logging with component name and error ID
|
|
169
|
-
logger.error('ErrorBoundary', `[${
|
|
207
|
+
logger.error('ErrorBoundary', `[${componentNameForHandler}] Caught error ${errorId}:`, error, errorInfo);
|
|
170
208
|
|
|
171
209
|
// Performance monitoring - track error occurrence
|
|
172
210
|
performanceBudgetMonitor.measure('ERROR_BOUNDARY_TRIGGER', 1, {
|
|
173
|
-
componentName,
|
|
211
|
+
componentName: componentNameForHandler,
|
|
174
212
|
errorId,
|
|
175
213
|
errorMessage: error.message,
|
|
176
214
|
stack: error.stack?.substring(0, 200), // Truncated stack trace
|
|
@@ -178,12 +216,14 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
178
216
|
|
|
179
217
|
// Report error if enabled
|
|
180
218
|
if (enableReporting) {
|
|
181
|
-
this.reportError(errorId,
|
|
219
|
+
this.reportError(errorId, componentNameForHandler);
|
|
182
220
|
}
|
|
183
221
|
|
|
184
|
-
// Call
|
|
222
|
+
// Call error handler: prefer prop onError, fall back to global handler from props
|
|
185
223
|
if (onError) {
|
|
186
224
|
onError(error, errorInfo, errorId);
|
|
225
|
+
} else if (this.props._globalErrorHandler) {
|
|
226
|
+
this.props._globalErrorHandler(error, errorInfo, errorId, componentNameForHandler);
|
|
187
227
|
}
|
|
188
228
|
}
|
|
189
229
|
|
|
@@ -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;
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
* - Button component - Retry functionality
|
|
81
81
|
* - Alert component - Error display
|
|
82
82
|
* - LoadingSpinner - Loading states
|
|
83
|
-
* - React
|
|
83
|
+
* - React 19+ - Hooks and effects
|
|
84
84
|
* - Tailwind CSS - Styling
|
|
85
85
|
*/
|
|
86
86
|
|
|
@@ -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;
|
|
@@ -49,7 +49,7 @@ vi.mock('../../utils/storage/helpers', () => ({
|
|
|
49
49
|
}));
|
|
50
50
|
|
|
51
51
|
// Helper to render with UnifiedAuthProvider
|
|
52
|
-
const renderWithUnifiedAuth = (ui: React.ReactElement) => {
|
|
52
|
+
const renderWithUnifiedAuth = (ui: React.ReactElement<any>) => {
|
|
53
53
|
return renderWithProviders(
|
|
54
54
|
<UnifiedAuthProvider
|
|
55
55
|
supabaseClient={supabase}
|
|
@@ -64,7 +64,7 @@ const renderWithUnifiedAuth = (ui: React.ReactElement) => {
|
|
|
64
64
|
);
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
const renderWithPublicContext = (ui: React.ReactElement
|
|
67
|
+
const renderWithPublicContext = (ui: React.ReactElement<any>, value: any = null) => {
|
|
68
68
|
const Context = publicPageContextStore.value ?? React.createContext(null);
|
|
69
69
|
|
|
70
70
|
return renderWithProviders(
|
|
@@ -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
|
*
|
|
@@ -81,12 +81,15 @@
|
|
|
81
81
|
* - Clear link identification
|
|
82
82
|
*
|
|
83
83
|
* @dependencies
|
|
84
|
-
* - React
|
|
84
|
+
* - React 19+ - Component framework
|
|
85
85
|
* - Tailwind CSS - Styling
|
|
86
86
|
*/
|
|
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
|