diff --git a/driver-pwa/frontend/src/components/Statement.tsx b/driver-pwa/frontend/src/components/Statement.tsx
index 90f6999..3a108b0 100644
--- a/driver-pwa/frontend/src/components/Statement.tsx
+++ b/driver-pwa/frontend/src/components/Statement.tsx
@@ -1,6 +1,7 @@
import { useQuery } from "@tanstack/react-query";
import { getStatement, type StatementKind } from "@/api/driver";
import { formatDate, formatMoney } from "@/lib/format";
+import { OperationIcon } from "@/components/operationIcon";
import { Spinner } from "@/components/Spinner";
const FILTERS: { key: StatementKind; label: string }[] = [
@@ -70,8 +71,9 @@ export function Statement({ from, to, kind, onFrom, onTo, onKind }: {
{items.map((op, idx) => (
-
+ className="flex items-center gap-3 py-2.5 border-b border-line last:border-0">
+
+
{op.title}
{formatDate(op.date)}
diff --git a/driver-pwa/frontend/src/components/operationIcon.test.ts b/driver-pwa/frontend/src/components/operationIcon.test.ts
new file mode 100644
index 0000000..f4978dd
--- /dev/null
+++ b/driver-pwa/frontend/src/components/operationIcon.test.ts
@@ -0,0 +1,25 @@
+import { describe, it, expect } from "vitest";
+import { operationIcon } from "./operationIcon";
+
+/** Названия — настоящие теги 1С из выписки (finance_ledger_entries.tag). */
+describe("значок операции", () => {
+ it("разные виды операций различаются", () => {
+ const names = [
+ "Аренда автомобиля",
+ "Пени",
+ "Штраф ГИБДД",
+ "Пополнение баланса через мобильное приложение",
+ "ДТП",
+ "Перевод с депозита на баланс",
+ ].map((t) => operationIcon(t));
+ expect(new Set(names).size).toBe(names.length);
+ });
+
+ it("штраф ГИБДД и штраф компании — один значок: для водителя это одно и то же", () => {
+ expect(operationIcon("Штраф ГИБДД")).toBe(operationIcon("Штраф компании"));
+ });
+
+ it("незнакомый тег получает нейтральный значок, а не пустоту", () => {
+ expect(operationIcon("Списание за что-то новое")).toBeTruthy();
+ });
+});
diff --git a/driver-pwa/frontend/src/components/operationIcon.tsx b/driver-pwa/frontend/src/components/operationIcon.tsx
new file mode 100644
index 0000000..c9be2c7
--- /dev/null
+++ b/driver-pwa/frontend/src/components/operationIcon.tsx
@@ -0,0 +1,53 @@
+import {
+ Banknote, Car, Flag, Gavel, Gift, HandCoins, Percent, PiggyBank, Receipt,
+ RefreshCcw, ShieldAlert, TriangleAlert, Wrench, type LucideIcon,
+} from "lucide-react";
+
+/**
+ * Значок операции в выписке — по тегу 1С, а не по знаку суммы.
+ *
+ * Список сливается в стену текста: за неделю у активного водителя два десятка
+ * строк, и половина из них — одинаковая по виду «аренда автомобиля». Значок
+ * даёт зацепку для глаза, чтобы взгляд находил нужное, не читая каждую строку.
+ *
+ * Общий компонент, а не иконка на месте применения: тот же приём, что в складе
+ * CRM (modules/warehouse/categoryIcons.tsx). Разложены по местам — и размеры с
+ * толщиной линии разъезжаются при первой же правке.
+ *
+ * Монохромные намеренно: цвет в этом списке уже занят и означает направление
+ * денег (красное — списали, зелёное — пришло). Цветной значок спорил бы с ним.
+ */
+const BY_TAG: [RegExp, LucideIcon][] = [
+ [/аренда/i, Car],
+ [/пени/i, Percent],
+ [/штраф/i, Gavel],
+ [/дтп|повреждени/i, TriangleAlert],
+ [/депозит/i, PiggyBank],
+ [/пополнение|оплата/i, Banknote],
+ [/ремонт|запчаст/i, Wrench],
+ [/компенсац/i, HandCoins],
+ [/акци|бонус|подар/i, Gift],
+ [/коррекц|сторно|перерасч/i, RefreshCcw],
+ [/страхов/i, ShieldAlert],
+ [/остаток|opening/i, Flag],
+];
+
+export function operationIcon(title: string): LucideIcon {
+ for (const [re, icon] of BY_TAG) {
+ if (re.test(title)) return icon;
+ }
+ return Receipt;
+}
+
+/** Значок в рамке — как в списке склада: одинаковый размер у всех строк. */
+export function OperationIcon({ title }: { title: string }) {
+ const Icon = operationIcon(title);
+ return (
+
+
+
+ );
+}