Add temporary public utility-only site
This commit is contained in:
parent
4884b5d367
commit
2a9f4df15f
6 changed files with 193 additions and 259 deletions
127
app/components/TemporaryUtilityHub.tsx
Normal file
127
app/components/TemporaryUtilityHub.tsx
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
"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>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import { useSearchParams, useRouter } from "next/navigation";
|
|||
import { cn } from "@/lib/utils";
|
||||
import { TOOLKIT_TABS } from "@/components/utilities/laser-toolkit/registry";
|
||||
|
||||
export default function LaserToolkitSwitcher() {
|
||||
export default function LaserToolkitSwitcher({ basePath = "/portal/utilities" }: { basePath?: string }) {
|
||||
const sp = useSearchParams();
|
||||
const router = useRouter();
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ export default function LaserToolkitSwitcher() {
|
|||
function setTab(k: string) {
|
||||
const q = new URLSearchParams(sp.toString());
|
||||
q.set("lt", k);
|
||||
router.replace(`/portal/utilities?${q.toString()}`, { scroll: false });
|
||||
router.replace(`${basePath}?${q.toString()}`, { scroll: false });
|
||||
}
|
||||
|
||||
if (!active) {
|
||||
|
|
|
|||
|
|
@ -208,8 +208,8 @@ export default function FileBrowserPanel() {
|
|||
|
||||
<div className="select-all text-xs text-muted-foreground">{path || "/"}</div>
|
||||
|
||||
<div className="grid grid-cols-[minmax(560px,1fr)_minmax(420px,42vw)] items-start gap-3">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<div className="grid grid-cols-1 items-start gap-3 xl:grid-cols-[minmax(560px,1fr)_minmax(420px,42vw)]">
|
||||
<div className="overflow-x-auto rounded-md border">
|
||||
<div className="fs-table bg-muted px-2 py-2 fs-cell fs-tight">
|
||||
<div className="px-1">Name</div>
|
||||
<div className="text-center">Type</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue