Files
ttrpg_manager/src/components/ui/ErrorBoundary.tsx
T
NilsBriggen 1a9e5e2c18 Build MVP: campaigns, characters, combat, dice, compendium
- Rules abstraction (5e + pf2e) behind one interface, fully tested
- Pure combat engine: turn-order safe across add/remove/reorder/init-change
- Dexie storage with real cascade deletes + Zod validation on write
- Seedable dice engine with notation parser
- Lazy SRD compendium (code-split), bestiary -> combat
- Strict TS, 54 unit tests, Playwright e2e smoke (all green)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 00:09:42 +02:00

51 lines
1.6 KiB
TypeScript

import { Component, type ErrorInfo, type ReactNode } from 'react';
import { Button } from './Button';
interface Props {
children: ReactNode;
/** reset key — when it changes, the boundary clears its error */
resetKey?: string | number;
}
interface State {
error: Error | null;
}
/** Catches render errors so one broken screen never white-screens the whole app. */
export class ErrorBoundary extends Component<Props, State> {
override state: State = { error: null };
static getDerivedStateFromError(error: Error): State {
return { error };
}
override componentDidCatch(error: Error, info: ErrorInfo): void {
console.error('Render error caught by boundary:', error, info.componentStack);
}
override componentDidUpdate(prev: Props): void {
if (prev.resetKey !== this.props.resetKey && this.state.error) {
this.setState({ error: null });
}
}
override render(): ReactNode {
if (this.state.error) {
return (
<div className="m-6 rounded-lg border border-danger/40 bg-danger/10 p-6">
<h2 className="text-lg font-semibold text-danger">Something went wrong</h2>
<p className="mt-1 text-sm text-muted">
This screen hit an error. Your data is safe try again or reload.
</p>
<pre className="mt-3 max-h-40 overflow-auto rounded bg-surface p-3 text-xs text-muted">
{this.state.error.message}
</pre>
<Button className="mt-4" variant="secondary" onClick={() => this.setState({ error: null })}>
Try again
</Button>
</div>
);
}
return this.props.children;
}
}