le-app/app/components/TemporaryUtilityHub.tsx

135 lines
5.1 KiB
TypeScript
Raw Permalink Normal View History

2026-07-10 12:18:27 -04:00
"use client";
import dynamic from "next/dynamic";
import { useMemo } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { cn } from "@/lib/utils";
const LaserToolkit = dynamic(
() => import("@/components/portal/LaserToolkitSwitcher"),
{ ssr: false }
);
const FileBrowser = dynamic(
() => import("@/components/utilities/files/FileBrowserPanel"),
{ ssr: false }
);
const BackgroundRemover = dynamic(
() => import("@/components/utilities/BackgroundRemoverPanel"),
{ ssr: false }
);
const TOOLS = [
{
key: "laser-toolkit",
label: "Laser Toolkit",
description: "Calculators for beam size, overlap, hatch spacing, job time, and more.",
icon: "/images/utils/toolkit.png",
component: LaserToolkit,
},
{
key: "files",
label: "File Server",
description: "Browse, preview, and download files from the public library.",
icon: "/images/utils/fs.png",
component: FileBrowser,
},
{
key: "background-remover",
label: "Background Remover",
description: "Remove image backgrounds using our self-hosted processing service.",
icon: "/images/utils/bgrm.png",
component: BackgroundRemover,
},
] as const;
export default function TemporaryUtilityHub() {
const router = useRouter();
const searchParams = useSearchParams();
const activeKey = searchParams.get("tool") || TOOLS[0].key;
const active = useMemo(
() => TOOLS.find((tool) => tool.key === activeKey) || TOOLS[0],
[activeKey]
);
const ActiveTool = active.component;
function selectTool(key: string) {
const query = new URLSearchParams(searchParams.toString());
query.set("tool", key);
if (key !== "laser-toolkit") query.delete("lt");
router.replace(`/?${query.toString()}`, { scroll: false });
}
return (
<main className="mx-auto min-h-screen max-w-7xl px-4 py-6 sm:px-6 sm:py-10">
<header className="mb-8 border-b pb-6">
<p className="mb-2 text-sm font-medium uppercase tracking-[0.2em] text-muted-foreground">
Laser Everything
</p>
<h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">Community Utilities</h1>
<p className="mt-3 max-w-3xl text-sm leading-6 text-muted-foreground sm:text-base">
Our database and member features are temporarily offline while we change backend
systems. These free utilities remain available without an account.
</p>
2026-07-10 12:23:47 -04:00
<p className="mt-4 max-w-3xl rounded-lg border border-amber-500/30 bg-amber-500/10 p-4 text-sm leading-6 text-amber-100">
Directus was the system behind our member features, but it has moved to a corporate
licensing model that we cannot afford for a free community project. We are moving the
site to another free and open source CMS. Until that work is finished, user accounts,
community libraries, and databases will be unavailable. We are working to bring
everything back as soon as we can. Thank you for your patience.
</p>
2026-07-10 12:18:27 -04:00
</header>
<nav aria-label="Utilities" className="mb-8 grid gap-3 md:grid-cols-3">
{TOOLS.map((tool) => {
const selected = tool.key === active.key;
return (
<button
key={tool.key}
type="button"
onClick={() => selectTool(tool.key)}
aria-pressed={selected}
className={cn(
"flex min-h-24 items-start gap-4 rounded-xl border p-4 text-left transition",
selected
? "border-primary bg-primary text-primary-foreground shadow-sm"
: "bg-card hover:border-muted-foreground/50 hover:bg-muted/50"
)}
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={tool.icon} alt="" className="h-10 w-10 rounded-lg object-cover" />
<span>
<span className="block font-semibold">{tool.label}</span>
<span
className={cn(
"mt-1 block text-sm leading-5",
selected ? "text-primary-foreground/80" : "text-muted-foreground"
)}
>
{tool.description}
</span>
</span>
</button>
);
})}
</nav>
<section aria-labelledby="active-tool-title" className="rounded-xl border bg-card p-3 sm:p-5">
<div className="mb-5 flex items-center gap-3 border-b pb-4">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={active.icon} alt="" className="h-8 w-8 rounded-md object-cover" />
<div>
<h2 id="active-tool-title" className="text-xl font-semibold">{active.label}</h2>
<p className="text-sm text-muted-foreground">{active.description}</p>
</div>
</div>
<ActiveTool basePath="/" />
</section>
<footer className="mt-8 text-center text-xs leading-5 text-muted-foreground">
No account is required. Database-backed profiles, projects, settings, and submissions
are intentionally unavailable on this temporary site.
</footer>
</main>
);
}