diff --git a/app/[[...slug]]/error.tsx b/app/[[...slug]]/error.tsx new file mode 100644 index 0000000..06e8a90 --- /dev/null +++ b/app/[[...slug]]/error.tsx @@ -0,0 +1,16 @@ +'use client'; + +import { Link } from '$shared/ui'; + +/** + * Route-level error boundary — shown when an unhandled error escapes + * the [[...slug]] segment. Mirrors the not-found layout. + */ +export default function ErrorPage() { + return ( +
+

500

+ Back to main +
+ ); +} diff --git a/src/widgets/SectionFactory/ui/SectionErrorBoundary/SectionErrorBoundary.tsx b/src/widgets/SectionFactory/ui/SectionErrorBoundary/SectionErrorBoundary.tsx new file mode 100644 index 0000000..bf72206 --- /dev/null +++ b/src/widgets/SectionFactory/ui/SectionErrorBoundary/SectionErrorBoundary.tsx @@ -0,0 +1,41 @@ +'use client'; + +import type { ErrorInfo, ReactNode } from 'react'; +import { Component } from 'react'; + +type Props = { + /** + * Section content to render + */ + children: ReactNode; +}; + +type State = { + /** + * Whether an error was caught + */ + hasError: boolean; +}; + +/** + * Isolates a single section's render errors so a broken section + * does not crash the rest of the page. + */ +export class SectionErrorBoundary extends Component { + state: State = { hasError: false }; + + static getDerivedStateFromError(): State { + return { hasError: true }; + } + + override componentDidCatch(error: Error, info: ErrorInfo) { + console.error('[SectionErrorBoundary]', error, info.componentStack); + } + + override render() { + if (this.state.hasError) { + return null; + } + return this.props.children; + } +} diff --git a/src/widgets/SectionFactory/ui/SectionFactory/SectionFactory.tsx b/src/widgets/SectionFactory/ui/SectionFactory/SectionFactory.tsx index 9bb4892..8e342f6 100644 --- a/src/widgets/SectionFactory/ui/SectionFactory/SectionFactory.tsx +++ b/src/widgets/SectionFactory/ui/SectionFactory/SectionFactory.tsx @@ -5,6 +5,7 @@ import ExperienceSection from '../../../ExperienceSection/ui/ExperienceSection/E import IntroSection from '../../../IntroSection/ui/IntroSection/IntroSection'; import ProjectsSection from '../../../ProjectsSection/ui/ProjectsSection/ProjectsSection'; import SkillsSection from '../../../SkillsSection/ui/SkillsSection/SkillsSection'; +import { SectionErrorBoundary } from '../SectionErrorBoundary/SectionErrorBoundary'; /** * Props for the SectionFactory widget. @@ -36,5 +37,9 @@ export function SectionFactory({ slug }: SectionFactoryProps) { notFound(); } - return ; + return ( + + + + ); }