fix(tradein): error/not-found/loading UI при restore по ?id= (#822) (#825)
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 1m33s
Deploy Trade-In / deploy (push) Successful in 36s

Co-authored-by: bot-frontend <bot-frontend@gendsgn.local>
Co-committed-by: bot-frontend <bot-frontend@gendsgn.local>
This commit is contained in:
bot-frontend 2026-05-30 19:49:47 +00:00 committed by bot-reviewer
parent 1a733302c2
commit 31d24ffbdb

View file

@ -12,6 +12,7 @@ import { useQueryClient } from "@tanstack/react-query";
import "@/components/trade-in/trade-in.css";
import type { AggregatedEstimate, TradeInEstimateInput, HouseType, RepairState } from "@/types/trade-in";
import { useEstimateMutation, useEstimate } from "@/lib/trade-in-api";
import { HTTPError } from "@/lib/api";
import { EstimateForm } from "@/components/trade-in/EstimateForm";
import { Topbar } from "@/components/trade-in/Topbar";
import { SourcesProgress } from "@/components/trade-in/SourcesProgress";
@ -127,6 +128,16 @@ export default function TradeInPage() {
}
: null);
// #822: restore по ?id= — отражаем загрузку/ошибку вместо молчаливого
// empty-state. Backend отдаёт 404 на missing/owner-mismatch/expired.
const restoreActive = urlEstimateId !== null && freshResult === null;
const restoreError = restoreActive && restoredEstimate.isError;
const restoreLoading = restoreActive && restoredEstimate.isPending;
const restoreNotFound =
restoreError &&
restoredEstimate.error instanceof HTTPError &&
restoredEstimate.error.status === 404;
// Надёжный input для «Что-если»: предпочитаем поля самой оценки (есть и при
// restore по ?id=, где resultData.input — stub с нулями), откатываемся на input.
// address/rooms обязательны для payload — берём из estimate, иначе из input.
@ -275,6 +286,36 @@ export default function TradeInPage() {
<ExposureCard estimate={resultData.estimate} />
<OfferCard estimate={resultData.estimate} brandSlug={activeBrandSlug} />
</>
) : restoreError ? (
<article className="card">
<div className="card-body" style={{ padding: "48px 24px", textAlign: "center" }}>
<h3 style={{ fontSize: 16, fontWeight: 600, marginBottom: 6 }}>
{restoreNotFound
? "Отчёт не найден или недоступен"
: "Не удалось загрузить отчёт"}
</h3>
<p style={{ color: "var(--muted)", fontSize: 14, marginBottom: 16 }}>
{restoreNotFound
? "Ссылка устарела, отчёт удалён или у вас нет к нему доступа."
: "Произошла ошибка при загрузке отчёта. Попробуйте ещё раз."}
</p>
<button
type="button"
className="btn btn-primary"
onClick={() => router.replace("/")}
>
Новая оценка
</button>
</div>
</article>
) : restoreLoading ? (
<article className="card">
<div className="card-body" style={{ padding: "48px 24px", textAlign: "center" }}>
<p style={{ color: "var(--muted)", fontSize: 14 }}>
Загрузка отчёта
</p>
</div>
</article>
) : (
<article className="card">
<div className="card-body" style={{ padding: "48px 24px", textAlign: "center" }}>