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")} км +
+ )} +
+ + +

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

+