Critical demo-blocker: POST /analyze returns 202 {status:fetching} for parcels
whose geometry isn't cached yet (#93 on-demand НСПД fetch). useParcelAnalyzeQuery
treated 202 as success → the stub (no score/competitors) reached render → Section 3/4
threw TypeError → white screen (no error boundary). Repro confirmed on prod.
- useParcelAnalyzeQuery: 202-aware — poll /fetch-status (2s, 2-min cap), re-POST
on ready (status-checked, no stub on 202 race), throw HTTPError on
not_in_nspd/failed/invalid_format; horizon preserved in both POSTs; retry: false
- error boundaries: app/site-finder/analysis/[cad]/error.tsx + app/error.tsx —
no white screen ever; calm RU message in prod, error detail in dev
- Section3/Section4: guard competitors/score against partial payloads
- CadInput: drop hardcoded default cad (empty + placeholder), raw hex → tokens
Map parcel-select already fixed by 3cea915. Part of EPIC #958.
85 lines
2.3 KiB
TypeScript
85 lines
2.3 KiB
TypeScript
"use client";
|
||
|
||
import Link from "next/link";
|
||
|
||
/**
|
||
* App-level error boundary — global net of last resort (#1001, 958-B5).
|
||
*
|
||
* Catches render errors not handled by a nested route `error.tsx`, so no route
|
||
* in the app can fall through to a blank white screen.
|
||
*/
|
||
export default function Error({
|
||
error,
|
||
reset,
|
||
}: {
|
||
error: Error & { digest?: string };
|
||
reset: () => void;
|
||
}) {
|
||
return (
|
||
<main style={{ padding: "24px 20px", maxWidth: 1400, margin: "0 auto" }}>
|
||
<div
|
||
style={{
|
||
marginTop: 24,
|
||
background: "var(--bg-card, #FFFFFF)",
|
||
border: "1px solid var(--border-card, #E6E8EC)",
|
||
borderRadius: 12,
|
||
padding: "24px",
|
||
}}
|
||
>
|
||
<h1
|
||
style={{
|
||
margin: 0,
|
||
fontSize: 18,
|
||
fontWeight: 600,
|
||
color: "var(--fg-primary, #111111)",
|
||
}}
|
||
>
|
||
Что-то пошло не так
|
||
</h1>
|
||
<p
|
||
style={{
|
||
margin: "12px 0 0",
|
||
fontSize: 13,
|
||
color: "var(--fg-secondary, #5B6066)",
|
||
}}
|
||
>
|
||
{process.env.NODE_ENV === "production"
|
||
? "Попробуйте обновить страницу или вернуться на главную."
|
||
: error.message || "Произошла непредвиденная ошибка."}
|
||
</p>
|
||
<div style={{ display: "flex", gap: 12, marginTop: 24 }}>
|
||
<button
|
||
type="button"
|
||
onClick={reset}
|
||
style={{
|
||
padding: "8px 18px",
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
background: "var(--accent, #1D4ED8)",
|
||
color: "var(--fg-on-dark, #E2E8F0)",
|
||
border: "none",
|
||
borderRadius: 8,
|
||
cursor: "pointer",
|
||
}}
|
||
>
|
||
Повторить
|
||
</button>
|
||
<Link
|
||
href="/"
|
||
style={{
|
||
display: "inline-flex",
|
||
alignItems: "center",
|
||
padding: "8px 18px",
|
||
fontSize: 14,
|
||
fontWeight: 500,
|
||
color: "var(--accent, #1D4ED8)",
|
||
textDecoration: "none",
|
||
}}
|
||
>
|
||
← На главную
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|