// app/api/options/lens/route.ts import { NextRequest, NextResponse } from "next/server"; import { directusFetch } from "@/lib/directus"; /** * Parse "110x110", "110×110", "110 X 110", etc. -> [110, 110] * Returns null if we can't find two numbers. */ function parseFieldSize(s: unknown): [number, number] | null { if (!s) return null; const nums = String(s).match(/\d+(\.\d+)?/g)?.map((n) => Number(n)) ?? []; if (nums.length >= 2 && Number.isFinite(nums[0]) && Number.isFinite(nums[1])) { return [nums[0], nums[1]]; } return null; } /** Pull a clean integer from a value like "F160", "160", "160mm" */ function parseFocalLength(v: unknown): number | null { if (v == null) return null; const m = String(v).match(/\d+(\.\d+)?/); if (!m) return null; const n = Number(m[0]); return Number.isFinite(n) ? Math.round(n) : null; } /** Natural-ish compare for scan lens sizes: sort by width then height */ function sizeCompare(a: string, b: string): number { const as = parseFieldSize(a); const bs = parseFieldSize(b); if (!as && !bs) return String(a).localeCompare(String(b)); if (!as) return 1; if (!bs) return -1; // width, then height if (as[0] !== bs[0]) return as[0] - bs[0]; if (as[1] !== bs[1]) return as[1] - bs[1]; return 0; } export async function GET(req: NextRequest) { try { const { searchParams } = new URL(req.url); const target = searchParams.get("target") || ""; const q = (searchParams.get("q") || "").trim().toLowerCase(); const limit = Number(searchParams.get("limit") || "500"); const isGantry = target === "settings_co2gan"; if (isGantry) { // CO2 Gantry -> FOCUS lenses const url = `/items/laser_focus_lens?fields=id,name&limit=${limit}`; const { data } = await directusFetch<{ data: Array<{ id: string | number; name?: string }> }>(url); let rows = (data ?? []).map((r) => ({ id: String(r.id), label: r.name?.trim() || String(r.id), _key: r.name?.toLowerCase() ?? "", })); if (q) rows = rows.filter((r) => r.label.toLowerCase().includes(q)); rows.sort((a, b) => a.label.localeCompare(b.label)); return NextResponse.json({ data: rows.map(({ id, label }) => ({ id, label })) }); } // Galvo/UV/Fiber -> SCAN lenses with field_size + focal_length const url = `/items/laser_scan_lens?fields=id,field_size,focal_length&limit=${limit}`; const { data } = await directusFetch<{ data: Array<{ id: string | number; field_size?: string; focal_length?: string | number }>; }>(url); let rows = (data ?? []).map((r) => { const sizeTxt = (r.field_size ?? "").toString().trim(); // e.g. "110x110" const fmm = parseFocalLength(r.focal_length); // e.g. 160 // Build label: "110x110mm (F160)" or "110x110mm" if focal length missing const label = sizeTxt ? `${sizeTxt}mm${fmm != null ? ` (F${fmm})` : ""}` : `${r.id}`; return { id: String(r.id), label, _size: sizeTxt, }; }); if (q) { const qq = q.toLowerCase(); rows = rows.filter((r) => r.label.toLowerCase().includes(qq)); } rows.sort((a, b) => { const c = sizeCompare(a._size, b._size); return c !== 0 ? c : a.label.localeCompare(b.label); }); return NextResponse.json({ data: rows.map(({ id, label }) => ({ id, label })) }); } catch (e: any) { console.error("[options/lens] error:", e?.message || e); return NextResponse.json({ error: e?.message || "Internal error" }, { status: 500 }); } }