portal pages update for details view
This commit is contained in:
parent
b0856c986f
commit
78a7ff2060
5 changed files with 193 additions and 47 deletions
|
|
@ -1,40 +1,65 @@
|
|||
// app/portal/laser-settings/page.tsx
|
||||
import { redirect } from "next/navigation";
|
||||
import SettingsSwitcher from "@/components/portal/SettingsSwitcher";
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { useSearchParams, useRouter } from "next/navigation";
|
||||
|
||||
// Your existing list/tabs component for the four settings lists:
|
||||
const SettingsSwitcher = dynamic(() => import("@/components/portal/SettingsSwitcher"), { ssr: false });
|
||||
|
||||
export const metadata = { title: "MakerDash • Laser Settings" };
|
||||
|
||||
function pickOne<T>(v: T | T[] | undefined): T | undefined {
|
||||
if (Array.isArray(v)) return v[0];
|
||||
return v;
|
||||
}
|
||||
export default function LaserSettingsPortalPage() {
|
||||
const search = useSearchParams();
|
||||
const router = useRouter();
|
||||
const t = (search.get("t") || "fiber").toLowerCase(); // fiber | uv | co2-galvo | co2-gantry
|
||||
const id = search.get("id");
|
||||
|
||||
export default function LaserSettingsPortalPage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams?: Record<string, string | string[] | undefined>;
|
||||
}) {
|
||||
// Read tab + optional id from query
|
||||
const t = (pickOne(searchParams?.t) || "fiber").toLowerCase();
|
||||
const id = pickOne(searchParams?.id);
|
||||
// Build canonical detail route we already have in /app/settings/*/[id]/
|
||||
const embedSrc = id
|
||||
? t === "fiber"
|
||||
? `/settings/fiber/${encodeURIComponent(id)}`
|
||||
: t === "uv"
|
||||
? `/settings/uv/${encodeURIComponent(id)}`
|
||||
: t === "co2-galvo"
|
||||
? `/settings/co2-galvo/${encodeURIComponent(id)}`
|
||||
: t === "co2-gantry"
|
||||
? `/settings/co2-gantry/${encodeURIComponent(id)}`
|
||||
: null
|
||||
: null;
|
||||
|
||||
// If an id is present, hop directly to the existing detail route
|
||||
if (id) {
|
||||
const slugMap: Record<string, string> = {
|
||||
fiber: "fiber",
|
||||
uv: "uv",
|
||||
"co2-galvo": "co2-galvo",
|
||||
"co2-gantry": "co2-gantry",
|
||||
};
|
||||
const slug = slugMap[t] ?? "fiber";
|
||||
redirect(`/settings/${slug}/${encodeURIComponent(id)}`);
|
||||
}
|
||||
const goList = () => router.replace(`/portal/laser-settings?t=${encodeURIComponent(t)}`);
|
||||
|
||||
// Otherwise show the normal portal view
|
||||
return (
|
||||
<div className="rounded-lg border p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold">Laser Settings</h2>
|
||||
<SettingsSwitcher />
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h2 className="text-xl font-semibold">Laser Settings</h2>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={goList}
|
||||
className={`rounded-md border px-3 py-1 text-sm ${id ? "opacity-80 hover:opacity-100" : "bg-accent text-background"}`}
|
||||
>
|
||||
List
|
||||
</button>
|
||||
<button
|
||||
disabled={!id}
|
||||
className={`rounded-md border px-3 py-1 text-sm ${id ? "bg-accent text-background" : "opacity-40 cursor-not-allowed"}`}
|
||||
>
|
||||
Details{ id ? ` #${id}` : "" }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* List view (existing) */}
|
||||
{!id && <SettingsSwitcher />}
|
||||
|
||||
{/* Detail view (embed canonical page) */}
|
||||
{id && embedSrc && (
|
||||
<iframe
|
||||
key={embedSrc}
|
||||
src={embedSrc}
|
||||
className="mt-4 h-[calc(100vh-260px)] w-full rounded-md border"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,48 @@
|
|||
// app/portal/laser-sources/page.tsx
|
||||
import LasersPage from "@/app/lasers/page";
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { useSearchParams, useRouter } from "next/navigation";
|
||||
|
||||
const LasersList = dynamic(() => import("@/app/lasers/page"), { ssr: false });
|
||||
|
||||
export const metadata = { title: "MakerDash • Laser Sources" };
|
||||
|
||||
export default function LaserSourcesPortalPage() {
|
||||
const search = useSearchParams();
|
||||
const router = useRouter();
|
||||
const id = search.get("id");
|
||||
|
||||
const goList = () => router.replace("/portal/laser-sources");
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold">Laser Sources</h2>
|
||||
<div className="rounded-md border p-4">
|
||||
{/* Render canonical lasers page directly (Server Component OK) */}
|
||||
<LasersPage />
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h2 className="text-xl font-semibold">Laser Sources</h2>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={goList}
|
||||
className={`rounded-md border px-3 py-1 text-sm ${id ? "opacity-80 hover:opacity-100" : "bg-accent text-background"}`}
|
||||
>
|
||||
List
|
||||
</button>
|
||||
<button
|
||||
disabled={!id}
|
||||
className={`rounded-md border px-3 py-1 text-sm ${id ? "bg-accent text-background" : "opacity-40 cursor-not-allowed"}`}
|
||||
>
|
||||
Details{ id ? ` #${id}` : "" }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!id && <LasersList />}
|
||||
|
||||
{id && (
|
||||
<iframe
|
||||
key={id}
|
||||
src={`/lasers/${encodeURIComponent(id)}`}
|
||||
className="mt-4 h-[calc(100vh-260px)] w-full rounded-md border"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,67 @@
|
|||
// app/portal/materials/page.tsx
|
||||
import MaterialsSwitcher from "@/components/portal/MaterialsSwitcher";
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { useSearchParams, useRouter } from "next/navigation";
|
||||
|
||||
const MaterialsList = dynamic(() => import("@/app/materials/materials/page"), { ssr: false });
|
||||
const CoatingsList = dynamic(() => import("@/app/materials/materials-coatings/page"), { ssr: false });
|
||||
|
||||
export const metadata = { title: "MakerDash • Materials" };
|
||||
|
||||
export default function MaterialsPortalPage() {
|
||||
const search = useSearchParams();
|
||||
const router = useRouter();
|
||||
const t = (search.get("t") || "materials").toLowerCase(); // materials | materials-coatings
|
||||
const id = search.get("id");
|
||||
|
||||
const embedSrc = id
|
||||
? t === "materials"
|
||||
? `/materials/materials/${encodeURIComponent(id)}`
|
||||
: `/materials/materials-coatings/${encodeURIComponent(id)}`
|
||||
: null;
|
||||
|
||||
const go = (tab: string) => {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set("t", tab);
|
||||
url.searchParams.delete("id");
|
||||
router.replace(url.pathname + "?" + url.searchParams.toString());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold">Materials</h2>
|
||||
<MaterialsSwitcher />
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h2 className="text-xl font-semibold">Materials</h2>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => go("materials")}
|
||||
className={`rounded-md border px-3 py-1 text-sm ${t === "materials" && !id ? "bg-accent text-background" : "opacity-80 hover:opacity-100"}`}
|
||||
>
|
||||
Materials
|
||||
</button>
|
||||
<button
|
||||
onClick={() => go("materials-coatings")}
|
||||
className={`rounded-md border px-3 py-1 text-sm ${t === "materials-coatings" && !id ? "bg-accent text-background" : "opacity-80 hover:opacity-100"}`}
|
||||
>
|
||||
Coatings
|
||||
</button>
|
||||
<button
|
||||
disabled={!id}
|
||||
className={`rounded-md border px-3 py-1 text-sm ${id ? "bg-accent text-background" : "opacity-40 cursor-not-allowed"}`}
|
||||
>
|
||||
Details{ id ? ` #${id}` : "" }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!id && (t === "materials" ? <MaterialsList /> : <CoatingsList />)}
|
||||
|
||||
{id && embedSrc && (
|
||||
<iframe
|
||||
key={embedSrc}
|
||||
src={embedSrc}
|
||||
className="mt-4 h-[calc(100vh-260px)] w-full rounded-md border"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,48 @@
|
|||
// app/portal/projects/page.tsx
|
||||
import ProjectsSwitcher from "@/components/portal/ProjectsSwitcher";
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { useSearchParams, useRouter } from "next/navigation";
|
||||
|
||||
const ProjectsList = dynamic(() => import("@/app/projects/page"), { ssr: false });
|
||||
|
||||
export const metadata = { title: "MakerDash • Projects" };
|
||||
|
||||
export default function ProjectsPortalPage() {
|
||||
const search = useSearchParams();
|
||||
const router = useRouter();
|
||||
const id = search.get("id");
|
||||
|
||||
const goList = () => router.replace("/portal/projects");
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold">Projects</h2>
|
||||
<ProjectsSwitcher />
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h2 className="text-xl font-semibold">Projects</h2>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={goList}
|
||||
className={`rounded-md border px-3 py-1 text-sm ${id ? "opacity-80 hover:opacity-100" : "bg-accent text-background"}`}
|
||||
>
|
||||
List
|
||||
</button>
|
||||
<button
|
||||
disabled={!id}
|
||||
className={`rounded-md border px-3 py-1 text-sm ${id ? "bg-accent text-background" : "opacity-40 cursor-not-allowed"}`}
|
||||
>
|
||||
Details{ id ? ` #${id}` : "" }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!id && <ProjectsList />}
|
||||
|
||||
{id && (
|
||||
<iframe
|
||||
key={id}
|
||||
src={`/projects/${encodeURIComponent(id)}`}
|
||||
className="mt-4 h-[calc(100vh-260px)] w-full rounded-md border"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,21 +59,21 @@ function legacyMap(pathname: string): MapResult | null {
|
|||
if (pathname.startsWith("/portal")) return null;
|
||||
|
||||
// 1) DETAIL PAGES: map legacy detail URLs straight into the portal with ?id=
|
||||
// NOTE: We intentionally DO NOT remap `/lasers/:id` and `/projects/:id`
|
||||
// so the portal iframes can load those canonical pages without recursion.
|
||||
const detailRules: Array<[RegExp, (m: RegExpExecArray) => MapResult]> = [
|
||||
// Laser settings
|
||||
[/^\/fiber-settings\/([^/]+)\/?$/i, (m) => ({ pathname: "/portal/laser-settings", query: { t: "fiber", id: m[1] } })],
|
||||
[/^\/uv-settings\/([^/]+)\/?$/i, (m) => ({ pathname: "/portal/laser-settings", query: { t: "uv", id: m[1] } })],
|
||||
[/^\/co2-galvo-settings\/([^/]+)\/?$/i, (m) => ({ pathname: "/portal/laser-settings", query: { t: "co2-galvo", id: m[1] } })],
|
||||
[/^\/co2-gantry-settings\/([^/]+)\/?$/i, (m) => ({ pathname: "/portal/laser-settings", query: { t: "co2-gantry", id: m[1] } })],
|
||||
[/^\/co2gantry-settings\/([^/]+)\/?$/i, (m) => ({ pathname: "/portal/laser-settings", query: { t: "co2-gantry", id: m[1] } })], // old alias
|
||||
[/^\/co2gantry-settings\/([^/]+)\/?$/i, (m) => ({ pathname: "/portal/laser-settings", query: { t: "co2-gantry", id: m[1] } })],
|
||||
|
||||
// Materials
|
||||
[/^\/materials\/([^/]+)\/?$/i, (m) => ({ pathname: "/portal/materials", query: { t: "materials", id: m[1] } })],
|
||||
[/^\/materials-coatings\/([^/]+)\/?$/i, (m) => ({ pathname: "/portal/materials", query: { t: "materials-coatings", id: m[1] } })],
|
||||
|
||||
// Lasers / Projects detail
|
||||
[/^\/lasers\/([^/]+)\/?$/i, (m) => ({ pathname: "/portal/laser-sources", query: { id: m[1] } })],
|
||||
[/^\/projects\/([^/]+)\/?$/i, (m) => ({ pathname: "/portal/projects", query: { id: m[1] } })],
|
||||
// (no lasers/projects detail remap here on purpose)
|
||||
];
|
||||
for (const [re, to] of detailRules) {
|
||||
const m = re.exec(pathname);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue