diff --git a/mechanic-pwa/frontend/src/api/inspections.ts b/mechanic-pwa/frontend/src/api/inspections.ts index b933f09..722909c 100644 --- a/mechanic-pwa/frontend/src/api/inspections.ts +++ b/mechanic-pwa/frontend/src/api/inspections.ts @@ -66,6 +66,8 @@ export interface InspectionDetail { photos: PhotoSummary[]; markers: MarkerSummary[]; mileage?: number | null; + /** ФИО механика, выполнившего осмотр (lookup users.full_name по performed_by). */ + inspector_name?: string | null; } export async function createInspection( diff --git a/mechanic-pwa/frontend/src/pages/InspectionReview.tsx b/mechanic-pwa/frontend/src/pages/InspectionReview.tsx index b8ad5fe..b9047da 100644 --- a/mechanic-pwa/frontend/src/pages/InspectionReview.tsx +++ b/mechanic-pwa/frontend/src/pages/InspectionReview.tsx @@ -1,3 +1,4 @@ +import { useState } from "react"; import { useParams, useNavigate } from "react-router-dom"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; @@ -114,6 +115,12 @@ export default function InspectionReview() { staleTime: Infinity, }); + // Lightbox: либо ID нашего фото в MinIO (через AuthImg), либо vendor TT + // image hash (через ttImageUrl). + const [preview, setPreview] = useState< + { kind: "photo"; id: number } | { kind: "tt"; ttId: string } | null + >(null); + if (isLoading) return
Загружаю…
; if (isError || !ins) return
Осмотр не найден.
; @@ -122,6 +129,25 @@ export default function InspectionReview() { const photosById = new Map(); for (const p of ins.photos) photosById.set(p.id, p); + // Все уникальные фото осмотра: основные + photo_id из маркеров (наши локальные + // фото повреждений) + tt_image_id из маркеров (carry-over из Element). Без + // tt_image_id counter недоучитывал импортированные дефекты — видно на + // осмотрах, где почти все маркеры пришли из вендора. + const markerPhotoIds = ins.markers + .map((m: MarkerSummary) => m.photo_id) + .filter((id): id is number => id != null); + const markerTtIds = ins.markers + .map((m: MarkerSummary) => m.tt_image_id) + .filter((id): id is string => !!id); + const uniquePhotoIds = Array.from( + new Set([...ins.photos.map((p: PhotoSummary) => p.id), ...markerPhotoIds]) + ); + const uniqueTtIds = Array.from(new Set(markerTtIds)); + const totalPhotoCount = uniquePhotoIds.length + uniqueTtIds.length; + // Дополнительные фото-карточки в галерее: photo_id маркеров, которых нет в + // основном списке (`ins.photos` — это side-photos осмотра). + const extraMarkerPhotos = markerPhotoIds.filter((id) => !photosById.has(id)); + return (
+ {ins.inspector_name && ( +
+ Механик: + {ins.inspector_name} +
+ )}
Начат: {new Date(ins.started_at).toLocaleString("ru-RU")} @@ -179,14 +211,18 @@ export default function InspectionReview() {

- Фото ({ins.photos.length}) + Фото ({totalPhotoCount})

- {ins.photos.length === 0 ? ( + {totalPhotoCount === 0 ? (
Нет фото.
) : (
{ins.photos.map((p: PhotoSummary) => ( - + setPreview({ kind: "photo", id: p.id })} + > {p.side}
))} + {/* Фото-маркеров, которых нет в основном списке (наши локальные) */} + {extraMarkerPhotos.map((id) => ( + setPreview({ kind: "photo", id })} + > + +
повреждение
+
+ ))} + {/* TT-фото carry-over дефектов из Element */} + {uniqueTtIds.map((ttId) => ( + setPreview({ kind: "tt", ttId })} + > + повреждение (Element) +
из Element
+
+ ))}
)}
@@ -249,7 +316,12 @@ export default function InspectionReview() { )} {m.photo_id ? ( -
+
+ setPreview({ kind: "photo", id: m.photo_id as number }) + } + >
) : m.tt_image_id ? ( -
+
+ setPreview({ kind: "tt", ttId: m.tt_image_id as string }) + } + > Фото из Element Mechanic
)} + + {/* Lightbox preview — click photo to enlarge, click background to close. + Соответствует UX в TtStateDetail (импортированные из Element осмотры). */} + {preview != null && ( +
setPreview(null)} + > +
+ +
+
e.stopPropagation()} + > + {preview.kind === "photo" ? ( + + ) : ( + Просмотр (Element) + )} +
+
+ )}
); }