backend integration started
This commit is contained in:
107
src/components/ErrorBoundary.tsx
Normal file
107
src/components/ErrorBoundary.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Component, type ErrorInfo, type ReactNode } from 'react';
|
||||
|
||||
/**
|
||||
* The last line before a white page.
|
||||
*
|
||||
* React unmounts the entire tree when a render throws and nothing catches it.
|
||||
* With no boundary anywhere in the app, one bad field shape on one card takes
|
||||
* the whole console down to an empty document — no message, no route, nothing
|
||||
* to act on but the browser console. That is exactly how a POS-health response
|
||||
* typed as an array but delivered as an object presented itself: a blank screen
|
||||
* on sign-in, with the cause four layers down.
|
||||
*
|
||||
* So this is not decoration. It converts "the app is broken" into "this screen
|
||||
* is broken, and here is what it said", which is the difference between a bug
|
||||
* report and a guess.
|
||||
*
|
||||
* Deliberately a class: `getDerivedStateFromError` has no hook equivalent.
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
/** Names the area in the message — "the Console page", "the assistant". */
|
||||
area?: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
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) {
|
||||
// Kept as console output rather than sent anywhere: there is no error
|
||||
// reporting endpoint, and inventing one would be a network call nobody
|
||||
// asked for. The component stack is the part that is not in the throw.
|
||||
console.error('[nearle] render failed', error, info.componentStack);
|
||||
}
|
||||
|
||||
private reset = () => {
|
||||
this.setState({ error: null });
|
||||
};
|
||||
|
||||
override render() {
|
||||
const { error } = this.state;
|
||||
if (!error) return this.props.children;
|
||||
|
||||
const area = this.props.area ?? 'this screen';
|
||||
|
||||
return (
|
||||
<div
|
||||
role="alert"
|
||||
style={{
|
||||
margin: '48px auto',
|
||||
maxWidth: 620,
|
||||
padding: '28px 32px',
|
||||
borderRadius: 16,
|
||||
border: '1px solid #F1D3D3',
|
||||
background: '#FFFBFB',
|
||||
fontFamily: 'inherit',
|
||||
}}
|
||||
>
|
||||
<h2 style={{ margin: '0 0 8px', fontSize: 18, fontWeight: 600, color: '#8A1F1F' }}>
|
||||
Something on {area} failed to render
|
||||
</h2>
|
||||
<p style={{ margin: '0 0 16px', fontSize: 14, lineHeight: 1.6, color: '#5C4747' }}>
|
||||
The rest of the console is fine — this is one screen, not the whole app. The message
|
||||
below is what broke, and the full stack is in the browser console.
|
||||
</p>
|
||||
<pre
|
||||
style={{
|
||||
margin: '0 0 20px',
|
||||
padding: '12px 14px',
|
||||
borderRadius: 10,
|
||||
background: '#FFF1F1',
|
||||
color: '#7A2020',
|
||||
fontSize: 13,
|
||||
lineHeight: 1.5,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
}}
|
||||
>
|
||||
{error.message || String(error)}
|
||||
</pre>
|
||||
<button
|
||||
type="button"
|
||||
onClick={this.reset}
|
||||
style={{
|
||||
padding: '9px 18px',
|
||||
borderRadius: 999,
|
||||
border: '1px solid #D9C0C0',
|
||||
background: '#FFFFFF',
|
||||
fontSize: 14,
|
||||
fontWeight: 500,
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user