Includes room interior with floor, walls, glass you can see through, and all uncommitted production changes that were running live. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
122 lines
4.2 KiB
TypeScript
122 lines
4.2 KiB
TypeScript
"use client";
|
|
|
|
import { Suspense, useCallback } from "react";
|
|
import { useConfiguratorStore } from "@/lib/store";
|
|
import { Scene3D } from "./scene";
|
|
import { Camera } from "lucide-react";
|
|
|
|
function LoadingFallback() {
|
|
return (
|
|
<div className="flex h-full w-full items-center justify-center bg-gradient-to-br from-slate-100 to-slate-200">
|
|
<div className="text-center">
|
|
<div className="mb-4 size-12 animate-spin rounded-full border-4 border-[#1A2E2E] border-t-[#C4D668]" />
|
|
<p className="text-sm font-medium text-[#1A2E2E]">3D Scene laden...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function formatPrice(amount: number): string {
|
|
return new Intl.NumberFormat("nl-NL", {
|
|
style: "currency",
|
|
currency: "EUR",
|
|
minimumFractionDigits: 0,
|
|
maximumFractionDigits: 0,
|
|
}).format(amount);
|
|
}
|
|
|
|
export function DoorVisualizer() {
|
|
const { doorType, gridType, finish, handle, priceBreakdown, setScreenshotDataUrl } =
|
|
useConfiguratorStore();
|
|
|
|
const handleScreenshot = useCallback(() => {
|
|
const canvas = document.querySelector("canvas");
|
|
if (!canvas) return;
|
|
const dataUrl = canvas.toDataURL("image/png");
|
|
setScreenshotDataUrl(dataUrl);
|
|
|
|
// Also trigger download
|
|
const link = document.createElement("a");
|
|
link.download = "proinn-deur-configuratie.png";
|
|
link.href = dataUrl;
|
|
link.click();
|
|
}, [setScreenshotDataUrl]);
|
|
|
|
return (
|
|
<div className="relative h-full w-full overflow-hidden rounded-[2.5rem]">
|
|
{/* Live Preview Badge */}
|
|
<div className="absolute left-8 top-8 z-10">
|
|
<div className="flex items-center gap-2 rounded-full bg-[#1A2E2E] px-4 py-2 text-sm font-semibold text-white shadow-lg">
|
|
<div className="size-2 animate-pulse rounded-full bg-[#C4D668]" />
|
|
3D Voorbeeld
|
|
</div>
|
|
</div>
|
|
|
|
{/* Screenshot Button */}
|
|
<div className="absolute right-8 top-8 z-10">
|
|
<button
|
|
onClick={handleScreenshot}
|
|
className="flex items-center gap-2 rounded-full bg-[#1A2E2E]/80 px-3 py-2 text-xs font-medium text-white shadow-lg backdrop-blur-sm transition-all hover:bg-[#1A2E2E]"
|
|
>
|
|
<Camera className="size-3.5" />
|
|
Screenshot
|
|
</button>
|
|
</div>
|
|
|
|
{/* 3D Scene */}
|
|
<Suspense fallback={<LoadingFallback />}>
|
|
<Scene3D />
|
|
</Suspense>
|
|
|
|
{/* Live Price Badge */}
|
|
<div className="absolute right-8 bottom-24 z-10 lg:bottom-8">
|
|
<div className="rounded-2xl bg-[#1A2E2E] px-5 py-3 text-right shadow-lg">
|
|
<div className="text-[10px] font-medium uppercase tracking-wider text-gray-400">
|
|
Indicatieprijs
|
|
</div>
|
|
<div className="text-xl font-bold text-[#C4D668]">
|
|
{formatPrice(priceBreakdown.totalPrice)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Configuration Info Card */}
|
|
<div className="absolute bottom-8 left-8 z-10">
|
|
<div className="rounded-2xl bg-white/90 p-4 shadow-lg backdrop-blur-sm">
|
|
<div className="grid grid-cols-2 gap-3 text-sm">
|
|
<div>
|
|
<div className="text-xs font-medium text-gray-500">Type</div>
|
|
<div className="font-semibold capitalize text-[#1A2E2E]">
|
|
{doorType}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-xs font-medium text-gray-500">Verdeling</div>
|
|
<div className="font-semibold text-[#1A2E2E]">{gridType}</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-xs font-medium text-gray-500">Afwerking</div>
|
|
<div className="font-semibold capitalize text-[#1A2E2E]">
|
|
{finish}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-xs font-medium text-gray-500">Greep</div>
|
|
<div className="font-semibold capitalize text-[#1A2E2E]">
|
|
{handle}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Controls Hint */}
|
|
<div className="absolute bottom-8 right-8 z-10 hidden lg:hidden">
|
|
<div className="rounded-xl bg-[#1A2E2E]/80 px-3 py-2 text-xs text-white backdrop-blur-sm">
|
|
<p className="font-medium">Drag to rotate - Scroll to zoom</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|