// app/api/options/lens/route.ts import { NextResponse } from "next/server"; import { directusFetch } from "@/lib/directus"; type Target = | "settings_fiber" | "settings_uv" | "settings_co2gal" | "settings_co2gan"; /** Map target -> Directus collection */ function collectionForTarget(t?: string) { switch ((t ?? "") as Target) { case "settings_fiber": case "settings_uv": case "settings_co2gal": return "laser_scan_lens" as const; // has field_size + focal_length case "settings_co2gan": return "laser_focus_lens" as const; // has name (no focal_length) default: return null; } } /** Parse "110x110", "110×110", "110 x 110", or single "110" => {w,h} */ function parseFieldSize(raw: unknown): { w: number; h: number } | null { if (raw == null) return null; const s = String(raw).trim(); const m = s.match(/(\d+(?:\.\d+)?)(?:\s*[x×]\s*(\d+(?:\.\d+)?))?/i); if (!m) return null; const w = Number(m[1]); const h = m[2] ? Number(m[2]) : w; if (!Number.isFinite(w) || !Number.isFinite(h)) return null; return { w, h }; } const fmtNum = (n: number) => Number.isInteger(n) ? String(n) : String(n).replace(/\.0+$/, ""); const dimsText = (d: { w: number; h: number }) => `${fmtNum(d.w)}x${fmtNum(d.h)}`; export async function GET(req: Request) { try { const { searchParams } = new URL(req.url); const target = searchParams.get("target") || undefined; const q = (searchParams.get("q") || "").toLowerCase().trim(); const limit = Number(searchParams.get("limit") || "500"); const coll = collectionForTarget(target); if (!coll) return NextResponse.json({ data: [] }); if (coll === "laser_scan_lens") { // fiber / uv / co2gal → scan lenses have field_size + focal_length const { data } = await directusFetch<{ data: any[] }>( `/items/${coll}?fields=id,field_size,focal_length&limit=${encodeURIComponent( String(limit) )}` ); const rows = (data ?? []).map((r) => { const dim = parseFieldSize(r.field_size); const fnumRaw = r.focal_length; const label = dim && fnumRaw != null && fnumRaw !== "" ? `${dimsText(dim)} (F${fmtNum(Number(fnumRaw))})` : dim ? dimsText(dim) : String(r.field_size ?? r.id); const area = dim && Number.isFinite(dim.w) && Number.isFinite(dim.h) ? dim.w * dim.h : Number.POSITIVE_INFINITY; return { id: String(r.id), label, _sort: { area, w: dim?.w ?? Number.POSITIVE_INFINITY, h: dim?.h ?? Number.POSITIVE_INFINITY }, _search: `${r.field_size ?? ""} ${r.focal_length ?? ""} ${label}`.toLowerCase(), }; }); const filtered = q ? rows.filter((r) => r._search.includes(q)) : rows; filtered.sort((a, b) => { if (a._sort.area !== b._sort.area) return a._sort.area - b._sort.area; if (a._sort.w !== b._sort.w) return a._sort.w - b._sort.w; if (a._sort.h !== b._sort.h) return a._sort.h - b._sort.h; return a.label.localeCompare(b.label); }); return NextResponse.json({ data: filtered.map(({ id, label }) => ({ id, label })), }); } // CO2 Gantry → focus lenses only have "name" const { data } = await directusFetch<{ data: any[] }>( `/items/${coll}?fields=id,name&limit=${encodeURIComponent(String(limit))}` ); const rows = (data ?? []).map((r) => ({ id: String(r.id), label: String(r.name ?? r.id), _search: String(r.name ?? r.id).toLowerCase(), })); const filtered = q ? rows.filter((r) => r._search.includes(q)) : rows; filtered.sort((a, b) => a.label.localeCompare(b.label)); return NextResponse.json({ data: filtered.map(({ id, label }) => ({ id, label })), }); } catch (err: any) { console.error("[options/lens] error:", err?.message || err); return NextResponse.json({ data: [] }, { status: 200 }); } }