[id] pages render in portal
This commit is contained in:
parent
dcd88b200f
commit
656ad5fe7e
17 changed files with 1028 additions and 1025 deletions
144
app/lasers/[id]/lasers.tsx
Normal file
144
app/lasers/[id]/lasers.tsx
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function LaserSourceDetailsPage() {
|
||||
const { id } = useParams();
|
||||
const [laser, setLaser] = useState(null);
|
||||
const [labels, setLabels] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/items/laser_source/${id}?fields=*`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => setLaser(data.data || null));
|
||||
|
||||
fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/fields/laser_source`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const labelMap = {};
|
||||
(data.data || []).forEach((field) => {
|
||||
if (field.interface === 'select-dropdown' && field.options?.choices) {
|
||||
labelMap[field.field] = {};
|
||||
field.options.choices.forEach((choice) => {
|
||||
labelMap[field.field][choice.value] = choice.text;
|
||||
});
|
||||
}
|
||||
});
|
||||
setLabels(labelMap);
|
||||
});
|
||||
}, [id]);
|
||||
|
||||
if (!laser) return <div className="p-6">Loading...</div>;
|
||||
|
||||
const resolveLabel = (field, value) => {
|
||||
if (!value) return '—';
|
||||
|
||||
const hardcodedLabels = {
|
||||
op: {
|
||||
pm: 'MOPA',
|
||||
pq: 'Q-Switch',
|
||||
},
|
||||
cooling: {
|
||||
aa: 'Air, Active',
|
||||
ap: 'Air, Passive',
|
||||
w: 'Water',
|
||||
},
|
||||
};
|
||||
|
||||
if (hardcodedLabels[field] && hardcodedLabels[field][value]) {
|
||||
return hardcodedLabels[field][value];
|
||||
}
|
||||
|
||||
return labels[field]?.[value] || value;
|
||||
};
|
||||
|
||||
const fieldGroups = [
|
||||
{
|
||||
title: 'General Information',
|
||||
fields: {
|
||||
make: 'Make',
|
||||
model: 'Model',
|
||||
op: 'Pulse Operation Mode',
|
||||
notes: 'Notes',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Optical Specifications',
|
||||
fields: {
|
||||
w: 'Laser Wattage (W)',
|
||||
mj: 'milliJoule Max (mJ)',
|
||||
nm: 'Wavelength (nm)',
|
||||
k_hz: 'Pulse Repetition Rate (kHz)',
|
||||
ns: 'Pulse Width (ns)',
|
||||
d: 'Beam Diameter (mm)',
|
||||
m2: 'M² - Quality',
|
||||
instability: 'Instability',
|
||||
polarization: 'Polarization',
|
||||
band: 'Band (nm)',
|
||||
anti: 'Anti-Reflection Coating',
|
||||
mw: 'Red Dot Wattage (mW)',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Electrical & Timing',
|
||||
fields: {
|
||||
v: 'Operating Voltage (V)',
|
||||
temp_op: 'Operating Temperature (°C)',
|
||||
temp_store: 'Storage Temperature (°C)',
|
||||
l_on: 'l_on',
|
||||
l_off: 'l_off',
|
||||
mj_c: 'mj_c',
|
||||
ns_c: 'ns_c',
|
||||
d_c: 'd_c',
|
||||
on_c: 'on_c',
|
||||
off_c: 'off_c',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Integration & Physical',
|
||||
fields: {
|
||||
cable: 'Cable Length (m)',
|
||||
cooling: 'Cooling Method',
|
||||
weight: 'Weight (kg)',
|
||||
dimensions: 'Dimensions (cm)',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-4xl mx-auto">
|
||||
<h1 className="text-3xl font-bold mb-4">
|
||||
{laser.make || '—'} {laser.model || ''}
|
||||
</h1>
|
||||
|
||||
<div className="space-y-6">
|
||||
{fieldGroups.map(({ title, fields }) => (
|
||||
<section key={title} className="bg-card border border-border rounded-xl p-4">
|
||||
<h2 className="text-xl font-semibold mb-2">{title}</h2>
|
||||
<dl className="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4">
|
||||
{Object.entries(fields).map(([key, label]) => (
|
||||
<div key={key}>
|
||||
<dt className="font-medium text-muted-foreground">{label}</dt>
|
||||
<dd className="text-base break-words">
|
||||
{resolveLabel(key, laser[key])}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-8">
|
||||
<Link href="/lasers" className="text-blue-600 underline">
|
||||
← Back to Laser Sources
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1,144 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function LaserSourceDetailsPage() {
|
||||
const { id } = useParams();
|
||||
const [laser, setLaser] = useState(null);
|
||||
const [labels, setLabels] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/items/laser_source/${id}?fields=*`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => setLaser(data.data || null));
|
||||
|
||||
fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/fields/laser_source`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const labelMap = {};
|
||||
(data.data || []).forEach((field) => {
|
||||
if (field.interface === 'select-dropdown' && field.options?.choices) {
|
||||
labelMap[field.field] = {};
|
||||
field.options.choices.forEach((choice) => {
|
||||
labelMap[field.field][choice.value] = choice.text;
|
||||
});
|
||||
}
|
||||
});
|
||||
setLabels(labelMap);
|
||||
});
|
||||
}, [id]);
|
||||
|
||||
if (!laser) return <div className="p-6">Loading...</div>;
|
||||
|
||||
const resolveLabel = (field, value) => {
|
||||
if (!value) return '—';
|
||||
|
||||
const hardcodedLabels = {
|
||||
op: {
|
||||
pm: 'MOPA',
|
||||
pq: 'Q-Switch',
|
||||
},
|
||||
cooling: {
|
||||
aa: 'Air, Active',
|
||||
ap: 'Air, Passive',
|
||||
w: 'Water',
|
||||
},
|
||||
};
|
||||
|
||||
if (hardcodedLabels[field] && hardcodedLabels[field][value]) {
|
||||
return hardcodedLabels[field][value];
|
||||
}
|
||||
|
||||
return labels[field]?.[value] || value;
|
||||
};
|
||||
|
||||
const fieldGroups = [
|
||||
{
|
||||
title: 'General Information',
|
||||
fields: {
|
||||
make: 'Make',
|
||||
model: 'Model',
|
||||
op: 'Pulse Operation Mode',
|
||||
notes: 'Notes',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Optical Specifications',
|
||||
fields: {
|
||||
w: 'Laser Wattage (W)',
|
||||
mj: 'milliJoule Max (mJ)',
|
||||
nm: 'Wavelength (nm)',
|
||||
k_hz: 'Pulse Repetition Rate (kHz)',
|
||||
ns: 'Pulse Width (ns)',
|
||||
d: 'Beam Diameter (mm)',
|
||||
m2: 'M² - Quality',
|
||||
instability: 'Instability',
|
||||
polarization: 'Polarization',
|
||||
band: 'Band (nm)',
|
||||
anti: 'Anti-Reflection Coating',
|
||||
mw: 'Red Dot Wattage (mW)',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Electrical & Timing',
|
||||
fields: {
|
||||
v: 'Operating Voltage (V)',
|
||||
temp_op: 'Operating Temperature (°C)',
|
||||
temp_store: 'Storage Temperature (°C)',
|
||||
l_on: 'l_on',
|
||||
l_off: 'l_off',
|
||||
mj_c: 'mj_c',
|
||||
ns_c: 'ns_c',
|
||||
d_c: 'd_c',
|
||||
on_c: 'on_c',
|
||||
off_c: 'off_c',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Integration & Physical',
|
||||
fields: {
|
||||
cable: 'Cable Length (m)',
|
||||
cooling: 'Cooling Method',
|
||||
weight: 'Weight (kg)',
|
||||
dimensions: 'Dimensions (cm)',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-4xl mx-auto">
|
||||
<h1 className="text-3xl font-bold mb-4">
|
||||
{laser.make || '—'} {laser.model || ''}
|
||||
</h1>
|
||||
|
||||
<div className="space-y-6">
|
||||
{fieldGroups.map(({ title, fields }) => (
|
||||
<section key={title} className="bg-card border border-border rounded-xl p-4">
|
||||
<h2 className="text-xl font-semibold mb-2">{title}</h2>
|
||||
<dl className="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4">
|
||||
{Object.entries(fields).map(([key, label]) => (
|
||||
<div key={key}>
|
||||
<dt className="font-medium text-muted-foreground">{label}</dt>
|
||||
<dd className="text-base break-words">
|
||||
{resolveLabel(key, laser[key])}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-8">
|
||||
<Link href="/lasers" className="text-blue-600 underline">
|
||||
← Back to Laser Sources
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// app/lasers/[id]/page.tsx
|
||||
import { redirect } from "next/navigation";
|
||||
export default function Page({ params }: { params: { id: string } }) {
|
||||
redirect(`/portal/laser-sources?id=${encodeURIComponent(params.id)}`);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue