feat(driver-pwa): balance screen (hero total + accounts)
Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
import { describe, it, expect, vi } from "vitest";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { MemoryRouter } from "react-router-dom";
|
||||||
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
vi.mock("@/api/driver", () => ({
|
||||||
|
getBalance: vi.fn(async () => ({
|
||||||
|
accounts: [
|
||||||
|
{ bucket: "Долг аренда", balance: -3200 },
|
||||||
|
{ bucket: "Депозит", balance: 5000 },
|
||||||
|
],
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { BalancePage } from "./BalancePage";
|
||||||
|
|
||||||
|
function wrap() {
|
||||||
|
const qc = new QueryClient();
|
||||||
|
return render(
|
||||||
|
<QueryClientProvider client={qc}>
|
||||||
|
<MemoryRouter><BalancePage /></MemoryRouter>
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("BalancePage", () => {
|
||||||
|
it("shows hero total (sum of negatives) and account rows", async () => {
|
||||||
|
wrap();
|
||||||
|
expect(await screen.findByText("Долг аренда")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Депозит")).toBeInTheDocument();
|
||||||
|
// Appears in both the hero total and the single debt account row.
|
||||||
|
expect(screen.getAllByText("−3 200 ₽").length).toBeGreaterThan(0);
|
||||||
|
expect(screen.getByRole("button", { name: /Пополнить/i })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1 +1,55 @@
|
|||||||
export function BalancePage() { return null; }
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { History, LogOut } from "lucide-react";
|
||||||
|
import { getBalance } from "@/api/driver";
|
||||||
|
import { formatMoney } from "@/lib/format";
|
||||||
|
import { useAuth } from "@/store/auth";
|
||||||
|
import { AppHeader } from "@/components/AppHeader";
|
||||||
|
import { Spinner } from "@/components/Spinner";
|
||||||
|
|
||||||
|
export function BalancePage() {
|
||||||
|
const nav = useNavigate();
|
||||||
|
const clear = useAuth((s) => s.clear);
|
||||||
|
const { data, isLoading } = useQuery({ queryKey: ["balance"], queryFn: getBalance });
|
||||||
|
|
||||||
|
const accounts = data?.accounts ?? [];
|
||||||
|
const totalDebt = accounts.filter((a) => a.balance < 0).reduce((s, a) => s + a.balance, 0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AppHeader initials="" />
|
||||||
|
<div className="card p-4 mb-4 text-center">
|
||||||
|
<p className="text-muted text-xs mb-1">Общий долг</p>
|
||||||
|
<p className={`text-2xl font-extrabold ${totalDebt < 0 ? "text-neg" : ""}`}>{formatMoney(totalDebt)}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="flex justify-center py-10"><Spinner /></div>
|
||||||
|
) : (
|
||||||
|
<div className="mb-4">
|
||||||
|
{accounts.map((a) => (
|
||||||
|
<button
|
||||||
|
key={a.bucket}
|
||||||
|
onClick={() => nav("/topup", { state: { bucket: a.bucket } })}
|
||||||
|
className="w-full flex justify-between items-center py-3 border-b border-line last:border-0 text-left"
|
||||||
|
>
|
||||||
|
<span className="text-sm">{a.bucket}</span>
|
||||||
|
<b className={a.balance < 0 ? "text-neg" : "text-pos"}>{formatMoney(a.balance)}</b>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button className="btn-primary" onClick={() => nav("/topup")}>Пополнить</button>
|
||||||
|
|
||||||
|
<div className="flex justify-center gap-6 mt-6 text-muted text-xs">
|
||||||
|
<button className="flex items-center gap-1.5" onClick={() => nav("/history")}>
|
||||||
|
<History size={15} /> История
|
||||||
|
</button>
|
||||||
|
<button className="flex items-center gap-1.5" onClick={() => { clear(); nav("/login"); }}>
|
||||||
|
<LogOut size={15} /> Выйти
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user