From d208838bb39f1ad3349caa6654758877c3b31e2f Mon Sep 17 00:00:00 2001 From: vladtechno Date: Wed, 20 May 2026 13:34:10 +1000 Subject: [PATCH] =?UTF-8?q?inspection-review:=20=D0=BA=D0=B0=D1=80=D1=82?= =?UTF-8?q?=D0=BE=D1=87=D0=BA=D0=B0=20=D0=BC=D0=B0=D1=88=D0=B8=D0=BD=D1=8B?= =?UTF-8?q?=20+=20=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8=D1=80=D1=83?= =?UTF-8?q?=D0=B5=D0=BC=D1=8B=D0=B5=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=82=D0=BA?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Сверху на /inspections/ добавлена кликабельная карточка машины: гос.номер крупно + марка/модель/год + VIN. Использует новое поле ins.vehicle, которое backend теперь возвращает в InspectionDetail (см. PremiumCRM коммит a9c94d0). Клик ведёт на /vehicles/. 2. Блок «Заметки механика» — textarea с локальным draft'ом и кнопкой «Сохранить» (видна только если draft отличается от ins.notes). Под капотом — PATCH /api/v1/mechanic/inspections/ с {notes}. TG-импортированные осмотры из «Подготовка авто» уже приезжают с заполненными notes (caption сообщения становится телом заметки), и теперь механик может дополнить/исправить их прямо в карточке. 3. Бонус: добавлен показ пробега (mileage) в первой info-карте. --- mechanic-pwa/frontend/src/api/inspections.ts | 13 +++ .../frontend/src/pages/InspectionReview.tsx | 82 ++++++++++++++++++- 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/mechanic-pwa/frontend/src/api/inspections.ts b/mechanic-pwa/frontend/src/api/inspections.ts index 722909c..1b07c2b 100644 --- a/mechanic-pwa/frontend/src/api/inspections.ts +++ b/mechanic-pwa/frontend/src/api/inspections.ts @@ -51,9 +51,22 @@ export interface MarkerSummary { created_at: string; } +export interface VehicleBrief { + id: number; + license_plate: string; + vin?: string | null; + make?: string | null; + model?: string | null; + year?: number | null; +} + export interface InspectionDetail { id: number; vehicle_id: number; + /** Сводка по машине (гос.номер / марка / модель / год). Backend attaches её + в get_inspection_by_id_with_relations, чтобы карточка осмотра могла + показать машину без второго запроса. */ + vehicle?: VehicleBrief | null; type: InspectionType; performed_by: number; driver_id?: number | null; diff --git a/mechanic-pwa/frontend/src/pages/InspectionReview.tsx b/mechanic-pwa/frontend/src/pages/InspectionReview.tsx index 920f5e2..30ebb1a 100644 --- a/mechanic-pwa/frontend/src/pages/InspectionReview.tsx +++ b/mechanic-pwa/frontend/src/pages/InspectionReview.tsx @@ -1,10 +1,11 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useParams, useNavigate } from "react-router-dom"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { getInspection, deleteInspection, + patchInspection, type MarkerSummary, type PhotoSummary, } from "@/api/inspections"; @@ -121,10 +122,28 @@ export default function InspectionReview() { { kind: "photo"; id: number } | { kind: "tt"; ttId: string } | null >(null); + // Локальный draft заметок — синхронизируется с ins.notes при загрузке/обновлении. + // Кнопка «Сохранить» появляется только если draft отличается от сохранённого. + const [notesDraft, setNotesDraft] = useState(""); + useEffect(() => { + if (ins?.notes !== undefined) setNotesDraft(ins.notes ?? ""); + }, [ins?.notes]); + + const notesMutation = useMutation({ + mutationFn: (next: string) => patchInspection(insId, { notes: next }), + onSuccess: (updated) => { + toast.success("Заметка сохранена"); + queryClient.setQueryData(["inspection", insId], updated); + }, + onError: () => toast.error("Не удалось сохранить заметку"), + }); + if (isLoading) return
Загружаю…
; if (isError || !ins) return
Осмотр не найден.
; + const notesDirty = (notesDraft ?? "") !== (ins.notes ?? ""); + // Lookup photo by id для определения annotated_key. const photosById = new Map(); for (const p of ins.photos) photosById.set(p.id, p); @@ -159,6 +178,28 @@ export default function InspectionReview() {

Осмотр #{ins.id}

+ {ins.vehicle && ( + navigate(`/vehicles/${ins.vehicle?.id}`)} + > +
+ {ins.vehicle.license_plate} +
+
+ {[ins.vehicle.make, ins.vehicle.model, ins.vehicle.year] + .filter(Boolean) + .join(" · ") || "—"} +
+ {ins.vehicle.vin && ( +
+ VIN: + {ins.vehicle.vin} +
+ )} +
+ )} +
Тип: @@ -184,10 +225,43 @@ export default function InspectionReview() { {new Date(ins.finished_at).toLocaleString("ru-RU")}
)} - {ins.notes && ( + {ins.mileage != null && (
- Заметки: - {ins.notes} + Пробег: + {ins.mileage.toLocaleString("ru-RU")} км +
+ )} +
+ + +

+ Заметки механика +

+