source and lens routing bugs fixed
This commit is contained in:
parent
793f848bfe
commit
d8f613b161
3 changed files with 139 additions and 82 deletions
|
|
@ -1,56 +1,45 @@
|
|||
// app/api/options/[collection]/route.ts
|
||||
// app/app/api/options/[collection]/route.ts
|
||||
import { NextResponse } from "next/server";
|
||||
import { directusFetch } from "@/lib/directus";
|
||||
|
||||
/**
|
||||
* Generic option lists resolved by collection name.
|
||||
* NOTE: laser_source and lens have their own dedicated routes.
|
||||
*/
|
||||
const MAP: Record<
|
||||
string,
|
||||
{ path: string; fields: string; label: (x: any) => string }
|
||||
> = {
|
||||
material: { path: "/items/material", fields: "id,name", label: (x) => x.name },
|
||||
material_coating: { path: "/items/material_coating", fields: "id,name", label: (x) => x.name },
|
||||
material_color: { path: "/items/material_color", fields: "id,name", label: (x) => x.name },
|
||||
material_opacity: { path: "/items/material_opacity", fields: "id,opacity", label: (x) => String(x.opacity) },
|
||||
laser_software: { path: "/items/laser_software", fields: "id,name", label: (x) => x.name },
|
||||
type MapEntry = { path: string; fields: string; label: (x: any) => string };
|
||||
|
||||
const MAP: Record<string, MapEntry> = {
|
||||
material: { path: "/items/material", fields: "id,name", label: (x) => String(x.name ?? x.id) },
|
||||
material_coating: { path: "/items/material_coating", fields: "id,name", label: (x) => String(x.name ?? x.id) },
|
||||
material_color: { path: "/items/material_color", fields: "id,name", label: (x) => String(x.name ?? x.id) },
|
||||
material_opacity: { path: "/items/material_opacity", fields: "id,opacity", label: (x) => String(x.opacity ?? x.id) },
|
||||
laser_software: { path: "/items/laser_software", fields: "id,name", label: (x) => String(x.name ?? x.id) },
|
||||
// laser_source and lens are handled by dedicated routes
|
||||
};
|
||||
|
||||
export async function GET(req: Request, ctx: any) {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const q = searchParams.get("q")?.trim() || "";
|
||||
const limit = Number(searchParams.get("limit") || "400");
|
||||
|
||||
// Use a loose type for the 2nd arg to avoid Next 15's strict route signature check
|
||||
const key = String(ctx?.params?.collection ?? "");
|
||||
|
||||
const cfg = MAP[key];
|
||||
if (!cfg) {
|
||||
// Unknown collection → return an empty list (avoids noisy 4xx)
|
||||
return NextResponse.json({ data: [] });
|
||||
}
|
||||
|
||||
// Build Directus URL (dummy base to use URL.searchParams ergonomically)
|
||||
const u = new URL("http://x" + cfg.path);
|
||||
u.searchParams.set("fields", cfg.fields);
|
||||
u.searchParams.set("limit", String(limit));
|
||||
if (q) u.searchParams.set("search", q);
|
||||
|
||||
export async function GET(req: Request, ctx: { params: { collection: string } }) {
|
||||
try {
|
||||
const { data } = await directusFetch<{ data: any[] }>(
|
||||
u.pathname + "?" + u.searchParams.toString()
|
||||
);
|
||||
const { searchParams } = new URL(req.url);
|
||||
const key = ctx.params.collection;
|
||||
const q = (searchParams.get("q") || "").trim().toLowerCase();
|
||||
const limit = Number(searchParams.get("limit") || "500");
|
||||
|
||||
const out = (data || [])
|
||||
.map((it) => ({ id: String(it.id), label: cfg.label(it) ?? String(it.id) }))
|
||||
.sort((a, b) => a.label.localeCompare(b.label));
|
||||
const cfg = MAP[key];
|
||||
if (!cfg) {
|
||||
return NextResponse.json({ error: "unsupported collection" }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ data: out });
|
||||
const url = `${cfg.path}?fields=${encodeURIComponent(cfg.fields)}&limit=${limit}`;
|
||||
const { data } = await directusFetch<{ data: any[] }>(url);
|
||||
const list = Array.isArray(data) ? data : [];
|
||||
|
||||
const mapped = list.map((x) => ({
|
||||
id: String(x.id),
|
||||
label: cfg.label(x),
|
||||
_s: Object.values(x).join(" ").toLowerCase(),
|
||||
}));
|
||||
|
||||
const filtered = q ? mapped.filter((m) => m._s.includes(q)) : mapped;
|
||||
filtered.sort((a, b) => a.label.localeCompare(b.label));
|
||||
|
||||
return NextResponse.json({ data: filtered.map(({ id, label }) => ({ id, label })) });
|
||||
} catch (e: any) {
|
||||
return NextResponse.json(
|
||||
{ error: e?.message || "Directus fetch failed" },
|
||||
{ status: 500 }
|
||||
);
|
||||
return NextResponse.json({ error: e?.message || "Failed to load options" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
65
app/api/options/laser_source/route.ts
Normal file
65
app/api/options/laser_source/route.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// app/app/api/options/laser_source/route.ts
|
||||
import { NextResponse } from "next/server";
|
||||
import { directusFetch } from "@/lib/directus";
|
||||
|
||||
// Parse "nm" that may be stored as a string (e.g., "1064", "1064nm", "1,064")
|
||||
function parseNm(v: any): number | null {
|
||||
const s = String(v ?? "").replace(/[^0-9.]/g, "");
|
||||
if (!s) return null;
|
||||
const n = Number(s);
|
||||
return Number.isFinite(n) ? n : null;
|
||||
}
|
||||
|
||||
// target → wavelength range (nm)
|
||||
function nmRangeForTarget(t?: string): [number, number] | null {
|
||||
switch (t) {
|
||||
case "settings_fiber": return [1000, 1100];
|
||||
case "settings_uv": return [300, 400];
|
||||
case "settings_co2gan":
|
||||
case "settings_co2gal": return [10000, 11000];
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(req: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const target = searchParams.get("target") || undefined;
|
||||
const q = (searchParams.get("q") || "").trim().toLowerCase();
|
||||
const limit = Number(searchParams.get("limit") || "500");
|
||||
|
||||
const range = nmRangeForTarget(target);
|
||||
if (!range) {
|
||||
return NextResponse.json({ error: "missing/invalid target" }, { status: 400 });
|
||||
}
|
||||
const [lo, hi] = range;
|
||||
|
||||
// Only request fields we can read. laser_source uses submission_id as PK.
|
||||
const url = `/items/laser_source?fields=submission_id,make,model,nm&limit=${limit}`;
|
||||
const { data } = await directusFetch<{ data: any[] }>(url);
|
||||
const list = Array.isArray(data) ? data : [];
|
||||
|
||||
// Filter by nm and optional text query
|
||||
const filtered = list.filter((x) => {
|
||||
const nm = parseNm(x.nm);
|
||||
if (nm === null || nm < lo || nm > hi) return false;
|
||||
if (!q) return true;
|
||||
const label = [x.make, x.model].filter(Boolean).join(" ").toLowerCase();
|
||||
return label.includes(q);
|
||||
});
|
||||
|
||||
// Build labels and sort by make, then model
|
||||
const out = filtered
|
||||
.map((x) => ({
|
||||
id: String(x.submission_id), // critical: use submission_id, not id
|
||||
label: [x.make, x.model].filter(Boolean).join(" ") || String(x.submission_id),
|
||||
sortKey: [x.make ?? "", x.model ?? ""].join(" ").toLowerCase(),
|
||||
}))
|
||||
.sort((a, b) => a.sortKey.localeCompare(b.sortKey))
|
||||
.map(({ id, label }) => ({ id, label }));
|
||||
|
||||
return NextResponse.json({ data: out });
|
||||
} catch (e: any) {
|
||||
return NextResponse.json({ error: e?.message || "Failed to load laser_source" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +1,57 @@
|
|||
// app/api/options/lens/route.ts
|
||||
// app/app/api/options/lens/route.ts
|
||||
import { NextResponse } from "next/server";
|
||||
import { directusFetch } from "@/lib/directus";
|
||||
|
||||
/**
|
||||
* For fiber, co2-galvo, uv → f-theta scan lenses
|
||||
* For co2-gantry → focusing lenses
|
||||
*/
|
||||
function collectionForTarget(target?: string): { coll: string } | null {
|
||||
switch (target) {
|
||||
case "settings_fiber":
|
||||
case "settings_co2gal":
|
||||
case "settings_uv":
|
||||
return { coll: "laser_scan_lens" };
|
||||
case "settings_co2gan":
|
||||
return { coll: "laser_focusing_lens" };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function bestLensLabel(it: any): string {
|
||||
// Try common fields, then derive something readable
|
||||
if (it.name) return String(it.name);
|
||||
if (it.model) return String(it.model);
|
||||
if (it.focal_length_mm) return `F${it.focal_length_mm} mm`;
|
||||
return String(it.submission_id ?? it.id ?? "lens");
|
||||
// Decide which collection to read based on target
|
||||
function lensCollectionForTarget(target?: string) {
|
||||
if (target === "settings_co2gan") return "laser_focus_lens"; // gantry
|
||||
// fiber, uv, co2gal → scan lenses
|
||||
return "laser_scan_lens";
|
||||
}
|
||||
|
||||
export async function GET(req: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const q = searchParams.get("q")?.trim() || "";
|
||||
const limit = Number(searchParams.get("limit") || "500");
|
||||
const target = searchParams.get("target") || undefined;
|
||||
const q = (searchParams.get("q") || "").trim().toLowerCase();
|
||||
const limit = Number(searchParams.get("limit") || "500");
|
||||
|
||||
const cfg = collectionForTarget(target);
|
||||
if (!cfg) return NextResponse.json({ error: "missing/invalid target" }, { status: 400 });
|
||||
const coll = lensCollectionForTarget(target);
|
||||
|
||||
const url = `/items/${cfg.coll}?limit=${limit}${q ? `&search=${encodeURIComponent(q)}` : ""}`;
|
||||
// Request both possible PK fields, plus name & focal_length for labels
|
||||
const url = `/items/${coll}?fields=submission_id,id,name,focal_length&limit=${limit}`;
|
||||
const { data } = await directusFetch<{ data: any[] }>(url);
|
||||
const list = Array.isArray(data) ? data : [];
|
||||
|
||||
const out = (data || [])
|
||||
.map((it) => ({
|
||||
id: String(it.submission_id ?? it.id),
|
||||
label: bestLensLabel(it),
|
||||
}))
|
||||
.sort((a, b) => a.label.localeCompare(b.label));
|
||||
const mapped = list.map((x) => {
|
||||
const id = String(x.submission_id ?? x.id);
|
||||
const label =
|
||||
(x.name && String(x.name)) ||
|
||||
(x.focal_length != null ? `F${x.focal_length} mm` : id);
|
||||
const key =
|
||||
(x.focal_length != null ? String(x.focal_length).padStart(6, "0") : "") +
|
||||
" " +
|
||||
label.toLowerCase();
|
||||
return { id, label, key, focal: Number(x.focal_length ?? NaN) };
|
||||
});
|
||||
|
||||
return NextResponse.json({ data: out });
|
||||
// Optional text filter
|
||||
const filtered = q
|
||||
? mapped.filter((m) => m.label.toLowerCase().includes(q))
|
||||
: mapped;
|
||||
|
||||
// Prefer numeric focal_length ordering, fallback to label
|
||||
filtered.sort((a, b) => {
|
||||
const aNum = Number.isFinite(a.focal);
|
||||
const bNum = Number.isFinite(b.focal);
|
||||
if (aNum && bNum) return a.focal - b.focal;
|
||||
if (aNum) return -1;
|
||||
if (bNum) return 1;
|
||||
return a.key.localeCompare(b.key);
|
||||
});
|
||||
|
||||
return NextResponse.json({ data: filtered.map(({ id, label }) => ({ id, label })) });
|
||||
} catch (e: any) {
|
||||
return NextResponse.json({ error: e?.message || "lens error" }, { status: 500 });
|
||||
return NextResponse.json({ error: e?.message || "Failed to load lens options" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue