diff --git a/driver-pwa/frontend/src/pages/BalancePage.test.tsx b/driver-pwa/frontend/src/pages/BalancePage.test.tsx index b6b69b7..cbc2685 100644 --- a/driver-pwa/frontend/src/pages/BalancePage.test.tsx +++ b/driver-pwa/frontend/src/pages/BalancePage.test.tsx @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from "vitest"; -import { render, screen } from "@testing-library/react"; +import { render, screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { MemoryRouter } from "react-router-dom"; +import { MemoryRouter, Route, Routes } from "react-router-dom"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; // Ответ как у настоящего водителя: статьи 1С, итог отдельным числом. @@ -34,7 +34,12 @@ function wrap() { const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } }); return render( - + + + } /> + экран пополнения

} /> +
+
); } @@ -63,22 +68,39 @@ describe("главный экран", () => { expect(calls[calls.length - 1][2]).toBe("fines"); }); - it("категории живут в шторке «Пополнить», а не на экране", async () => { + it("аренда и штрафы видны сразу, остальные статьи — нет", async () => { wrap(); await screen.findByText("−8 976,25 ₽"); - expect(screen.queryByText("Долг аренда")).not.toBeInTheDocument(); - - await userEvent.click(screen.getByRole("button", { name: /Пополнить/ })); - expect(await screen.findByRole("dialog", { name: "Куда пополнить" })).toBeInTheDocument(); + // Эти две водитель проверяет каждый день — они на экране. expect(screen.getByText("Долг аренда")).toBeInTheDocument(); - // «Страховка» платежом не закрывается — в списке выбора ей делать нечего. + expect(screen.getByText("Долг по штрафам")).toBeInTheDocument(); + expect(screen.getByText("−3 050 ₽")).toBeInTheDocument(); + // Редкие статьи на экран не лезут: для них есть шторка. expect(screen.queryByText("Страховка")).not.toBeInTheDocument(); }); + it("нажатие на строку долга ведёт сразу к оплате этой статьи", async () => { + wrap(); + await userEvent.click(await screen.findByRole("button", { name: /Долг по штрафам/ })); + expect(await screen.findByText("экран пополнения")).toBeInTheDocument(); + }); + + it("категории для выбора живут в шторке «Пополнить»", async () => { + wrap(); + await screen.findByText("−8 976,25 ₽"); + await userEvent.click(screen.getByRole("button", { name: /^Пополнить$/ })); + const sheet = await screen.findByRole("dialog", { name: "Куда пополнить" }); + const inSheet = within(sheet); + expect(inSheet.getByText("Долг аренда")).toBeInTheDocument(); + expect(inSheet.getByText("Долг по штрафам")).toBeInTheDocument(); + // «Страховка» платежом не закрывается — в списке выбора ей делать нечего. + expect(inSheet.queryByText("Страховка")).not.toBeInTheDocument(); + }); + it("шторка закрывается тапом мимо", async () => { wrap(); await screen.findByText("−8 976,25 ₽"); - await userEvent.click(screen.getByRole("button", { name: /Пополнить/ })); + await userEvent.click(screen.getByRole("button", { name: /^Пополнить$/ })); await userEvent.click(await screen.findByRole("button", { name: "Закрыть" })); expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); }); diff --git a/driver-pwa/frontend/src/pages/BalancePage.tsx b/driver-pwa/frontend/src/pages/BalancePage.tsx index 5e62e94..b8b8c31 100644 --- a/driver-pwa/frontend/src/pages/BalancePage.tsx +++ b/driver-pwa/frontend/src/pages/BalancePage.tsx @@ -10,6 +10,11 @@ import { Sheet } from "@/components/Sheet"; import { Spinner } from "@/components/Spinner"; import { Statement } from "@/components/Statement"; +// Две статьи, которые водитель проверяет каждый день: за них он платит и из-за +// них ему звонят. Остальные (депозит, повреждения, прочее) — редкие, им хватает +// шторки «Пополнить», иначе экран снова превращается в столбик из шести строк. +const MAIN_BUCKETS = ["Долг аренда", "Долг по штрафам"]; + /** * Главный экран из двух половин: сверху деньги, снизу — за что они списаны. * @@ -32,6 +37,9 @@ export function BalancePage() { const pending = data?.pending_topup ?? 0; const noData = !isLoading && !isError && accounts.length === 0; const payable = accounts.filter((a) => a.payable); + const main = MAIN_BUCKETS.map((b) => accounts.find((a) => a.bucket === b)).filter( + (a): a is NonNullable => a != null, + ); function pick(bucket?: string) { setSheet(false); @@ -54,10 +62,29 @@ export function BalancePage() { {formatMoney(total)} )} + {main.length > 0 && ( +
+ {main.map((a) => ( + // Нажатие ведёт сразу к оплате этой статьи: чаще всего человек за + // тем и открыл приложение, а лишний заход в шторку — лишний шаг. + + ))} +
+ )} {pending > 0 && ( // Платёж принят нами, но 1С его ещё не провела. В сумму не входит: сложить // оба источника значило бы показать пополнение дважды после проведения. -
+
Зачисляется {formatMoney(pending)}