vehicle list: цветной чип статуса (На линии / ДТП / Ремонт / Бронь)

VehicleSummary.status (cars_v2.element_status) рендерится как цветной
pill справа от гос.номера. Палитра по семантике:
  emerald — На линии (норма)
  amber   — Ремонтируется / Ждет ремонта
  red     — ДТП
  blue    — Бронь / Выкуп / На продаже / Подготовка на продажу / Ждет
            доставки до офиса (admin-операции)
  muted   — Простой и неизвестные статусы

Чип появляется в Home (списке машин) и в шапке VehicleCard. Backend
сторона — PremiumCRM 92d4317.
This commit is contained in:
2026-05-20 15:17:15 +10:00
parent 577ec37a2f
commit 98806d1e92
3 changed files with 63 additions and 2 deletions
@@ -8,6 +8,10 @@ export interface VehicleSummary {
model?: string | null; model?: string | null;
year?: number | null; year?: number | null;
last_inspection_at?: string | null; last_inspection_at?: string | null;
/** Текущий статус машины из 1C Element (cars_v2.element_status):
'На линии' / 'Простой' / 'Ремонтируется' / 'Ждет ремонта' / 'ДТП' /
'Бронь' / 'Выкуп' / 'На продаже' / etc. NULL если не задан. */
status?: string | null;
} }
export interface VehicleDetail extends VehicleSummary { export interface VehicleDetail extends VehicleSummary {
+35
View File
@@ -10,6 +10,38 @@ const ACTION_TITLES: Record<string, string> = {
repair: "Ремонт", repair: "Ремонт",
}; };
// Группа цветов по семантике статуса. Палитра подсчитана из Tailwind с
// прозрачностью, чтобы чип не перебивал гос.номер. Tone:
// ok — на линии (зелёный)
// idle — простой (серый)
// repair — в ремонте / ждёт ремонта (янтарный)
// alert — ДТП (красный)
// admin — выкуп / на продаже / бронь / подготовка к продаже / ждёт доставки
// sold — продана (не отображается — отфильтрована из выдачи)
const STATUS_TONE: Record<string, string> = {
"На линии": "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-300",
"Простой": "bg-muted text-muted-foreground",
"Ремонтируется": "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300",
"Ждет ремонта": "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300",
"ДТП": "bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300",
"Бронь": "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
"Выкуп": "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
"На продаже": "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
"Подготовка на продажу": "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
"Ждет доставки до офиса": "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
};
function StatusChip({ status }: { status: string }) {
const tone =
STATUS_TONE[status] ??
"bg-muted text-muted-foreground";
return (
<span className={`inline-flex items-center text-xs font-medium px-2 py-0.5 rounded-full ${tone}`}>
{status}
</span>
);
}
export default function Home() { export default function Home() {
const [q, setQ] = useState(""); const [q, setQ] = useState("");
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
@@ -63,7 +95,10 @@ export default function Home() {
navigate(`/vehicles/${v.id}${action ? `?action=${action}` : ""}`) navigate(`/vehicles/${v.id}${action ? `?action=${action}` : ""}`)
} }
> >
<div className="flex items-center gap-2 flex-wrap">
<div className="font-semibold text-lg">{v.license_plate}</div> <div className="font-semibold text-lg">{v.license_plate}</div>
{v.status && <StatusChip status={v.status} />}
</div>
<div className="text-sm text-muted-foreground"> <div className="text-sm text-muted-foreground">
{[v.make, v.model, v.year].filter(Boolean).join(" ") || "—"} {[v.make, v.model, v.year].filter(Boolean).join(" ") || "—"}
</div> </div>
@@ -110,7 +110,29 @@ export default function VehicleCard() {
</button> </button>
<Card className="p-4"> <Card className="p-4">
<div className="flex items-center gap-2 flex-wrap">
<div className="text-xl font-semibold">{v.license_plate}</div> <div className="text-xl font-semibold">{v.license_plate}</div>
{v.status && (
<span
className={`inline-flex items-center text-xs font-medium px-2 py-0.5 rounded-full ${
{
"На линии": "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-300",
"Простой": "bg-muted text-muted-foreground",
"Ремонтируется": "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300",
"Ждет ремонта": "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300",
"ДТП": "bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300",
"Бронь": "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
"Выкуп": "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
"На продаже": "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
"Подготовка на продажу": "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
"Ждет доставки до офиса": "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
}[v.status] ?? "bg-muted text-muted-foreground"
}`}
>
{v.status}
</span>
)}
</div>
<div className="text-sm text-muted-foreground"> <div className="text-sm text-muted-foreground">
{[v.make, v.model, v.year].filter(Boolean).join(" ") || "—"} {[v.make, v.model, v.year].filter(Boolean).join(" ") || "—"}
</div> </div>