diff --git a/components/details/CO2GalvoDetail.tsx b/components/details/CO2GalvoDetail.tsx index ba442242..88724af4 100644 --- a/components/details/CO2GalvoDetail.tsx +++ b/components/details/CO2GalvoDetail.tsx @@ -5,6 +5,9 @@ import { useEffect, useMemo, useState } from "react"; import { useSearchParams, useRouter } from "next/navigation"; import SettingsSubmit from "@/components/forms/SettingsSubmit"; +/** ───────────────────────────────────────────────────────────── + * Types + * ──────────────────────────────────────────────────────────── */ type Rec = { submission_id: string | number; setting_title?: string | null; @@ -13,7 +16,6 @@ type Rec = { photo?: { id?: string } | string | number | null; screen?: { id?: string } | string | number | null; - // ids & readable fields mat?: { id?: string | number; name?: string | null } | null; mat_coat?: { id?: string | number; name?: string | null } | null; mat_color?: { id?: string | number; name?: string | null } | null; @@ -33,40 +35,27 @@ type Rec = { owner?: { id?: string | number; username?: string | null } | string | number | null; uploader?: string | null; - last_modified_date?: string | null; }; +/** ───────────────────────────────────────────────────────────── + * Small helpers (no hooks below) + * ──────────────────────────────────────────────────────────── */ async function readJson(res: Response) { const text = await res.text(); try { return text ? JSON.parse(text) : null; } catch { throw new Error(`Unexpected response (HTTP ${res.status})`); } } +const ownerLabel = (o: Rec["owner"]) => +!o ? "—" : (typeof o === "string" || typeof o === "number") ? String(o) : (o.username || String(o.id ?? "—")); +const isMine = (owner: Rec["owner"], meId: string | null) => +!!meId && !!owner && ((typeof owner === "string" || typeof owner === "number") ? String(owner) === meId : (owner.id != null && String(owner.id) === meId)); +const resolveFileId = (v: Rec["photo"]): string | null => +v == null ? null : (typeof v === "string" || typeof v === "number") ? String(v) : v.id ? String(v.id) : null; +const assetSrc = (id?: string | null) => (id ? `/api/dx/assets/${id}` : ""); -function ownerLabel(o: Rec["owner"]) { - if (!o) return "—"; - if (typeof o === "string" || typeof o === "number") return String(o); - return o.username || String(o.id ?? "—"); -} - -function isMine(owner: Rec["owner"], meId: string | null) { - if (!meId || !owner) return false; - if (typeof owner === "string" || typeof owner === "number") return String(owner) === meId; - return owner.id != null && String(owner.id) === meId; -} - -// --- Image helpers ----------------------------------------------------------- -function resolveFileId(v: Rec["photo"]): string | null { - if (v == null) return null; - if (typeof v === "string" || typeof v === "number") return String(v); - if (typeof v === "object" && v.id) return String(v.id); - // some Directus shapes may nest deeper; add more cases if needed - return null; -} -function assetSrc(id?: string | null) { - return id ? `/api/dx/assets/${id}` : ""; -} -// ---------------------------------------------------------------------------- - +/** ───────────────────────────────────────────────────────────── + * Component + * ──────────────────────────────────────────────────────────── */ export default function CO2GalvoDetail({ id, mode, @@ -80,8 +69,10 @@ export default function CO2GalvoDetail({ onBack?: () => void; showOwnerEdit?: boolean; }) { + // ── Hooks (top-level only; no conditional usage) ─────────── const sp = useSearchParams(); const router = useRouter(); + const editParam = sp.get("edit") === "1"; const editMode = mode ? mode === "edit" : editParam; @@ -89,8 +80,10 @@ export default function CO2GalvoDetail({ const [loading, setLoading] = useState(true); const [err, setErr] = useState(null); - // current user id (for owner-only edit button) const [meId, setMeId] = useState(null); + const [lightbox, setLightbox] = useState<{ src: string; alt: string } | null>(null); + + // current user id useEffect(() => { let dead = false; (async () => { @@ -98,67 +91,34 @@ export default function CO2GalvoDetail({ const r = await fetch(`/api/dx/users/me?fields=id`, { cache: "no-store", credentials: "include" }); if (!r.ok) return; const j = await readJson(r); - const id = j?.data?.id ?? j?.id ?? null; - if (!dead) setMeId(id ? String(id) : null); + const mid = j?.data?.id ?? j?.id ?? null; + if (!dead) setMeId(mid ? String(mid) : null); } catch { /* ignore */ } })(); return () => { dead = true; }; }, []); - // load record (with human-readable fields) + // load record useEffect(() => { if (!id) return; let dead = false; - (async () => { try { setLoading(true); setErr(null); const fields = [ - "submission_id", - "setting_title", - "setting_notes", - "photo.id", - "screen.id", - - "mat.id", - "mat.name", - "mat_coat.id", - "mat_coat.name", - "mat_color.id", - "mat_color.name", - "mat_opacity.id", - "mat_opacity.opacity", - "mat_thickness", - - "source.submission_id", - "source.make", - "source.model", - "source.nm", - - "lens.id", - "lens.field_size", - "lens.focal_length", - - "focus", - "laser_soft", - "repeat_all", - - "fill_settings", - "line_settings", - "raster_settings", - - "owner.id", - "owner.username", - "uploader", - "last_modified_date", + "submission_id","setting_title","setting_notes", + "photo.id","screen.id", + "mat.id","mat.name","mat_coat.id","mat_coat.name","mat_color.id","mat_color.name","mat_opacity.id","mat_opacity.opacity","mat_thickness", + "source.submission_id","source.make","source.model","source.nm", + "lens.id","lens.field_size","lens.focal_length", + "focus","laser_soft","repeat_all", + "fill_settings","line_settings","raster_settings", + "owner.id","owner.username","uploader","last_modified_date", ].join(","); - const url = `/api/dx/items/settings_co2gal?fields=${encodeURIComponent(fields)}&filter[submission_id][_eq]=${encodeURIComponent( - String(id) - )}&limit=1`; - + const url = `/api/dx/items/settings_co2gal?fields=${encodeURIComponent(fields)}&filter[submission_id][_eq]=${encodeURIComponent(String(id))}&limit=1`; const r = await fetch(url, { cache: "no-store", credentials: "include" }); if (!r.ok) { const j = await readJson(r).catch(() => null); @@ -174,18 +134,15 @@ export default function CO2GalvoDetail({ if (!dead) setLoading(false); } })(); - return () => { dead = true; }; }, [id]); + // derive edit initial values (safe when rec is null) const initialValues = useMemo(() => { if (!rec) return null; - const toId = (v: any) => (v == null ? null : typeof v === "object" ? v.id ?? v.submission_id ?? null : v); - const photoId = resolveFileId(rec.photo); const screenId = resolveFileId(rec.screen); - const matId = toId(rec.mat); const coatId = toId(rec.mat_coat); const colorId = toId(rec.mat_color); @@ -214,17 +171,19 @@ export default function CO2GalvoDetail({ }; }, [rec]); + // helpers function clearEditParam() { const params = new URLSearchParams(sp.toString()); params.delete("edit"); router.replace(`?${params.toString()}`); } + // ── Render guards ─────────────────────────────────────────── if (loading) return

Loading setting…

; if (err) return
{err}
; if (!rec) return

Setting not found.

; - // EDIT + // ── EDIT MODE ─────────────────────────────────────────────── if (editMode && initialValues) { return (
@@ -239,34 +198,12 @@ export default function CO2GalvoDetail({ ); } - // VIEW + // ── VIEW MODE ─────────────────────────────────────────────── const ownerDisplay = ownerLabel(rec.owner); const canEdit = showOwnerEdit && isMine(rec.owner, meId); - const photoSrc = assetSrc(resolveFileId(rec.photo)); const screenSrc = assetSrc(resolveFileId(rec.screen)); - // simple lightbox state - const [lightbox, setLightbox] = useState<{ src: string; alt: string } | null>(null); - - function Thumb({ src, alt }: { src: string; alt: string }) { - if (!src) return null; - return ( -
- -
{alt}
-
- ); - } - return (
@@ -275,6 +212,7 @@ export default function CO2GalvoDetail({
+ {/* Left meta */}
Owner: {ownerDisplay}
@@ -313,9 +251,24 @@ export default function CO2GalvoDetail({ )}
+ {/* Right images */}
- - + {photoSrc && ( +
+ +
Result
+
+ )} + {screenSrc && ( +
+ +
Settings Screenshot
+
+ )}
@@ -426,7 +379,7 @@ export default function CO2GalvoDetail({ )} - {/* Lightbox overlay */} + {/* Lightbox */} {lightbox && (