fixes
Build and Push Docker Image / build (push) Successful in 43s

This commit is contained in:
jason
2026-04-21 21:47:52 -05:00
parent bc3b78aa33
commit 95774c9c21
4 changed files with 153 additions and 6 deletions
+112
View File
@@ -0,0 +1,112 @@
"use client";
// Lightweight wrapper around <StepViewer>. Used anywhere we want a
// "Show 3D" toggle that reveals the in-browser STEP viewer on demand:
//
// - Assembly detail page (admin)
// - Operator scan page (phone)
//
// Keeps the heavy occt / three.js / WASM out of the initial bundle via
// next/dynamic + ssr:false, and only starts loading when the user taps
// the toggle.
//
// Intentionally does NOT capture thumbnails — the part detail page has
// its own richer variant for that. Here we just render the model.
import dynamic from "next/dynamic";
import { useState } from "react";
const StepViewer = dynamic(() => import("@/components/StepViewer"), {
ssr: false,
loading: () => (
<div className="relative w-full h-[320px] rounded-lg border border-slate-200 bg-slate-50 flex items-center justify-center text-sm text-slate-500">
Loading viewer
</div>
),
});
interface Props {
/** FileAsset id whose bytes will be streamed from /api/v1/files/:id/download. */
fileId: string;
/** Human-friendly file name shown in the header. */
fileName: string;
/** Panel heading, e.g. "3D viewer" or "Assembly 3D". */
title?: string;
/** If true, the panel renders already open. Defaults to false — saves the
* ~2 MB WASM download until the operator actually asks for it, which matters
* on a phone. */
defaultOpen?: boolean;
/** Height of the viewer canvas. Defaults to 360 (good for phones). Admin
* pages can bump this to 480. */
height?: number;
/** Extra tailwind classes for the outer <section>. */
className?: string;
}
export default function StepViewerPanel({
fileId,
fileName,
title = "3D viewer",
defaultOpen = false,
height = 360,
className,
}: Props) {
const [open, setOpen] = useState(defaultOpen);
const [edges, setEdges] = useState(true);
const url = `/api/v1/files/${fileId}/download`;
return (
<section className={className}>
<div className="flex items-center justify-between gap-3 mb-2">
<h2 className="text-sm font-semibold text-slate-900">{title}</h2>
<div className="flex items-center gap-3">
{open ? (
<label className="flex items-center gap-1.5 text-xs text-slate-600">
<input
type="checkbox"
checked={edges}
onChange={(e) => setEdges(e.target.checked)}
/>
Show edges
</label>
) : null}
<button
type="button"
onClick={() => setOpen((v) => !v)}
className={`rounded-md text-sm font-medium px-3 py-1.5 ${
open
? "bg-white border border-slate-300 text-slate-700"
: "bg-slate-900 text-white"
}`}
>
{open ? "Hide" : "Show 3D"}
</button>
</div>
</div>
{open ? (
<div className="rounded-xl border border-slate-200 bg-white p-3">
<div className="mb-2 text-[11px] text-slate-500 truncate" title={fileName}>
Viewing: <code className="font-mono text-slate-700">{fileName}</code>
</div>
<div style={{ height }} className="relative w-full rounded-lg border border-slate-200 bg-slate-50 overflow-hidden">
<StepViewer
key={`${fileId}-${edges ? "e" : "ne"}`}
url={url}
showEdges={edges}
className="absolute inset-0"
/>
</div>
<p className="mt-2 text-[11px] text-slate-500">
Drag to rotate · pinch / scroll to zoom · two-finger drag to pan. Rendered in
your browser the STEP file isn&apos;t downloaded to your device.
</p>
</div>
) : (
<p className="text-xs text-slate-500">
Interactive 3D preview. Tap <span className="font-medium">Show 3D</span> to load
(~2 MB of viewer code fetched on first use).
</p>
)}
</section>
);
}