feat(pwa): переход в штраф из выписки, заметные даты, минус раздел пополнений

Дата в выписке и в списке штрафов встала первой строкой чернильным цветом: в
этих списках ищут «что было 12-го», а бледная подпись под заголовком для этого
не годилась.

Строка штрафа в выписке стала кнопкой в карточку — там фотография и подробности.
Остальные строки не притворяются ссылками.

Раздел «Мои пополнения» удалён вместе с кнопкой: он показывал только наши
платежи и у большинства водителей был пуст (1С проводит пополнения сама), а те
же операции видны в выписке фильтром «Пополнения».

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 17:58:28 +10:00
co-authored by Claude Opus 5
parent 0c7735ef85
commit 3bdd5ce818
8 changed files with 55 additions and 118 deletions
@@ -1,4 +1,6 @@
import { useQuery } from "@tanstack/react-query";
import { useNavigate } from "react-router-dom";
import { ChevronRight } from "lucide-react";
import { getStatement, type StatementKind } from "@/api/driver";
import { formatDate, formatMoney } from "@/lib/format";
import { OperationIcon } from "@/components/operationIcon";
@@ -25,6 +27,7 @@ export function Statement({ from, to, kind, onFrom, onTo, onKind }: {
onTo: (v: string) => void;
onKind: (k: StatementKind) => void;
}) {
const nav = useNavigate();
const { data, isLoading, isError, refetch } = useQuery({
queryKey: ["statement", from, to, kind],
queryFn: () => getStatement(from, to, kind),
@@ -69,22 +72,38 @@ export function Statement({ from, to, kind, onFrom, onTo, onKind }: {
<p className="text-muted text-sm text-center py-8">Нет операций за выбранный период</p>
) : (
<div>
{items.map((op, idx) => (
<div key={`${op.date}-${idx}`}
className="flex items-center gap-3 py-2.5 border-b border-line last:border-0">
<OperationIcon title={op.title} />
<span className="min-w-0 flex-1">
<span className="block text-sm">{op.title}</span>
<span className="block text-muted text-xs">
{formatDate(op.date)}
{op.description ? ` · ${op.description}` : ""}
{items.map((op, idx) => {
const row = (
<>
<OperationIcon title={op.title} />
<span className="min-w-0 flex-1">
{/* Дата — первой строкой и чернильным цветом: в выписке ищут
«что было 12-го», а не название операции, и бледная подпись
под заголовком для этого не годится. */}
<span className="block text-xs font-semibold">{formatDate(op.date)}</span>
<span className="block text-sm">{op.title}</span>
{op.description && (
<span className="block text-muted text-xs truncate">{op.description}</span>
)}
</span>
</span>
<b className={`shrink-0 ${op.amount < 0 ? "text-neg" : "text-pos"}`}>
{formatMoney(op.amount)}
</b>
</div>
))}
<b className={`shrink-0 ${op.amount < 0 ? "text-neg" : "text-pos"}`}>
{formatMoney(op.amount)}
</b>
</>
);
const cls = "w-full flex items-center gap-3 py-2.5 border-b border-line last:border-0 text-left";
// Строка штрафа ведёт в карточку: в выписке видно «Штраф ГИБДД
// −618,75 ₽», а за что именно — только там, вместе с фотографией.
return op.fine_id != null ? (
<button key={`${op.date}-${idx}`} className={cls}
onClick={() => nav(`/fines/${op.fine_id}`)}>
{row}
<ChevronRight size={16} className="text-muted shrink-0" />
</button>
) : (
<div key={`${op.date}-${idx}`} className={cls}>{row}</div>
);
})}
</div>
)}
</div>