From 7204290e45a94f2d1beecf9932cd0d53d419ecb4 Mon Sep 17 00:00:00 2001 From: vladtechno Date: Tue, 18 Aug 2026 19:22:16 +1000 Subject: [PATCH] =?UTF-8?q?fix(pwa):=20=D1=88=D1=82=D1=80=D0=B0=D1=84?= =?UTF-8?q?=D1=8B=20=D0=BD=D0=B5=20=D0=BF=D1=80=D1=8F=D1=87=D1=83=D1=82?= =?UTF-8?q?=D1=81=D1=8F=20=D0=B7=D0=B0=20=D0=B2=D0=BA=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D0=BA=D0=BE=D0=B9=20=C2=AB=D0=9D=D0=B5=20=D0=BF=D0=BE=D0=B3?= =?UTF-8?q?=D0=B0=D1=88=D0=B5=D0=BD=D1=8B=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Водитель, оплативший штраф сам, видел пустой экран: список открывался на «Не погашены», а там у него ноль (Морозов, штраф от 16.08 — оплачен им, парк его не выставлял). Выглядело как «приложение потеряло мои штрафы». Теперь вкладка выбирается по данным: есть непогашенные — открываем их, нет — открываем «Все». Заодно один запрос вместо двух: список короткий, фильтруем на месте, и переключение вкладок стало мгновенным. Co-Authored-By: Claude Opus 5 --- .../frontend/src/pages/FinesPage.test.tsx | 18 +++++++++++++----- driver-pwa/frontend/src/pages/FinesPage.tsx | 18 ++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/driver-pwa/frontend/src/pages/FinesPage.test.tsx b/driver-pwa/frontend/src/pages/FinesPage.test.tsx index e965ae5..5397502 100644 --- a/driver-pwa/frontend/src/pages/FinesPage.test.tsx +++ b/driver-pwa/frontend/src/pages/FinesPage.test.tsx @@ -14,10 +14,8 @@ const FINE = { }; vi.mock("@/api/driver", () => ({ - getFines: vi.fn(async (status: string) => ({ - items: status === "open" - ? [FINE] - : [FINE, { ...FINE, id: 13, settled: "paid" as const, amount: 500 }], + getFines: vi.fn(async () => ({ + items: [FINE, { ...FINE, id: 13, settled: "paid" as const, amount: 500 }], open_count: 1, debt: -5156, })), getFine: vi.fn(async () => ({ @@ -50,12 +48,22 @@ function wrap(path = "/fines") { describe("список штрафов", () => { beforeEach(() => vi.clearAllMocks()); + it("нет непогашенных — открываем «Все», а не пустой экран", async () => { + // Водитель оплатил штраф сам: долг нулевой, но штрафы у него есть. Раньше + // список открывался на «Не погашены» и выглядел пустым (Морозов, 18.08.2026). + vi.mocked(driver.getFines).mockResolvedValueOnce({ + items: [{ ...FINE, settled: "paid" as const }], open_count: 0, debt: 0, + } as never); + wrap(); + expect(await screen.findByText("погашен")).toBeInTheDocument(); + expect(screen.queryByText(/Непогашенных штрафов нет/)).not.toBeInTheDocument(); + }); + it("долг берётся из баланса 1С, а не из сумм постановлений", async () => { wrap(); // Парк гасит ГИБДД авансом: если считать по «оплачен/не оплачен» из // постановлений, экран покажет «долгов нет» при −5 156 ₽ на главном. expect(await screen.findByText(/Долг по штрафам −5 156 ₽ · не погашено: 1/)).toBeInTheDocument(); - expect(vi.mocked(driver.getFines).mock.calls[0][0]).toBe("open"); }); it("закрытый в ГИБДД штраф остаётся непогашенным для водителя", async () => { diff --git a/driver-pwa/frontend/src/pages/FinesPage.tsx b/driver-pwa/frontend/src/pages/FinesPage.tsx index eaa6c63..37e0219 100644 --- a/driver-pwa/frontend/src/pages/FinesPage.tsx +++ b/driver-pwa/frontend/src/pages/FinesPage.tsx @@ -22,12 +22,18 @@ import { Spinner } from "@/components/Spinner"; */ export function FinesPage() { const nav = useNavigate(); - const [tab, setTab] = useState<"open" | "all">("open"); + // Запрашиваем ВСЕ и фильтруем на месте: список короткий, зато вкладка + // переключается мгновенно и, главное, известно, есть ли непогашенные ещё до + // того, как выбрана вкладка. const { data, isLoading, isError, refetch } = useQuery({ - queryKey: ["fines", tab], - queryFn: () => getFines(tab), + queryKey: ["fines"], + queryFn: () => getFines("all"), }); - const items = data?.items ?? []; + const [tab, setTab] = useState<"open" | "all" | null>(null); + // Долгов нет — открываем «Все». Иначе водитель с оплаченными штрафами видел + // пустой экран и решал, что приложение их потеряло (Морозов, 18.08.2026). + const active = tab ?? (data && data.open_count > 0 ? "open" : "all"); + const items = (data?.items ?? []).filter((f) => active === "all" || f.settled === "owed"); return (
@@ -47,7 +53,7 @@ export function FinesPage() { key={key} onClick={() => setTab(key)} className={`px-3 py-2 rounded-xl2 text-xs border ${ - tab === key ? "border-line bg-surface font-semibold" : "border-line text-muted" + active === key ? "border-ink bg-surface font-semibold" : "border-line text-muted" }`} > {label} @@ -64,7 +70,7 @@ export function FinesPage() {
) : items.length === 0 ? (

- {tab === "open" ? "Непогашенных штрафов нет" : "Штрафов не найдено"} + {active === "open" ? "Непогашенных штрафов нет" : "Штрафов не найдено"}

) : (