feat: Latest production version with interior scene and glass
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>
This commit is contained in:
@@ -1,22 +1,21 @@
|
||||
"use client";
|
||||
|
||||
import { useConfiguratorStore, type DoorType } from "@/lib/store";
|
||||
import { useConfiguratorStore, type DoorType, type GridType } from "@/lib/store";
|
||||
import { useFormContext } from "@/components/offerte/form-context";
|
||||
import { Check } from "lucide-react";
|
||||
|
||||
// Door type visual icons (inline SVGs)
|
||||
// ============================================
|
||||
// DOOR TYPE ICONS (SVG)
|
||||
// ============================================
|
||||
|
||||
function TaatsIcon({ selected }: { selected: boolean }) {
|
||||
const stroke = selected ? "#C4D668" : "#1A2E2E";
|
||||
const fill = selected ? "#C4D668" : "none";
|
||||
return (
|
||||
<svg viewBox="0 0 64 80" className="h-20 w-16">
|
||||
{/* Door frame */}
|
||||
<rect x="8" y="4" width="48" height="72" rx="2" fill="none" stroke={stroke} strokeWidth="2" />
|
||||
{/* Glass */}
|
||||
<rect x="14" y="10" width="36" height="60" rx="1" fill={selected ? "#C4D668" : "#e5e7eb"} opacity="0.2" />
|
||||
{/* Pivot point (center) */}
|
||||
<circle cx="32" cy="40" r="3" fill={fill} stroke={stroke} strokeWidth="1.5" />
|
||||
{/* Rotation arrow */}
|
||||
<path d="M 44 20 A 16 16 0 0 1 44 60" fill="none" stroke={stroke} strokeWidth="1.5" strokeDasharray="3 2" />
|
||||
<polygon points="44,58 48,54 40,54" fill={stroke} />
|
||||
</svg>
|
||||
@@ -28,14 +27,10 @@ function ScharnierIcon({ selected }: { selected: boolean }) {
|
||||
const fill = selected ? "#C4D668" : "none";
|
||||
return (
|
||||
<svg viewBox="0 0 64 80" className="h-20 w-16">
|
||||
{/* Door frame */}
|
||||
<rect x="8" y="4" width="48" height="72" rx="2" fill="none" stroke={stroke} strokeWidth="2" />
|
||||
{/* Glass */}
|
||||
<rect x="14" y="10" width="36" height="60" rx="1" fill={selected ? "#C4D668" : "#e5e7eb"} opacity="0.2" />
|
||||
{/* Hinge dots on left side */}
|
||||
<circle cx="10" cy="20" r="2.5" fill={fill} stroke={stroke} strokeWidth="1.5" />
|
||||
<circle cx="10" cy="60" r="2.5" fill={fill} stroke={stroke} strokeWidth="1.5" />
|
||||
{/* Rotation arrow from hinge side */}
|
||||
<path d="M 56 18 A 24 24 0 0 1 56 62" fill="none" stroke={stroke} strokeWidth="1.5" strokeDasharray="3 2" />
|
||||
<polygon points="56,60 60,56 52,56" fill={stroke} />
|
||||
</svg>
|
||||
@@ -46,11 +41,8 @@ function PaneelIcon({ selected }: { selected: boolean }) {
|
||||
const stroke = selected ? "#C4D668" : "#1A2E2E";
|
||||
return (
|
||||
<svg viewBox="0 0 64 80" className="h-20 w-16">
|
||||
{/* Door frame */}
|
||||
<rect x="8" y="4" width="48" height="72" rx="2" fill="none" stroke={stroke} strokeWidth="2" />
|
||||
{/* Glass */}
|
||||
<rect x="14" y="10" width="36" height="60" rx="1" fill={selected ? "#C4D668" : "#e5e7eb"} opacity="0.2" />
|
||||
{/* Fixed indicator - lock symbol */}
|
||||
<rect x="26" y="34" width="12" height="12" rx="2" fill="none" stroke={stroke} strokeWidth="1.5" />
|
||||
<circle cx="32" cy="34" r="5" fill="none" stroke={stroke} strokeWidth="1.5" />
|
||||
</svg>
|
||||
@@ -63,70 +55,149 @@ const doorTypeIcons: Record<DoorType, (props: { selected: boolean }) => React.Re
|
||||
paneel: PaneelIcon,
|
||||
};
|
||||
|
||||
// Grid type visual illustrations (CSS-based rectangles with dividers)
|
||||
function GridIllustration({ dividers, selected }: { dividers: number; selected: boolean }) {
|
||||
const borderColor = selected ? "border-[#C4D668]" : "border-[#1A2E2E]/40";
|
||||
const dividerBg = selected ? "bg-[#C4D668]" : "bg-[#1A2E2E]/30";
|
||||
const glassBg = selected ? "bg-[#C4D668]/10" : "bg-gray-100";
|
||||
// ============================================
|
||||
// GRID PATTERN SVG ILLUSTRATIONS
|
||||
// ============================================
|
||||
|
||||
return (
|
||||
<div className={`flex h-20 w-14 flex-col overflow-hidden rounded border-2 ${borderColor}`}>
|
||||
{dividers === 0 && (
|
||||
<div className={`flex-1 ${glassBg}`} />
|
||||
)}
|
||||
{dividers > 0 &&
|
||||
Array.from({ length: dividers + 1 }).map((_, i) => (
|
||||
<div key={i} className="flex flex-1 flex-col">
|
||||
{i > 0 && <div className={`h-[2px] shrink-0 ${dividerBg}`} />}
|
||||
<div className={`flex-1 ${glassBg}`} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
function GridSVG({ pattern, selected }: { pattern: GridType; selected: boolean }) {
|
||||
const stroke = selected ? "#C4D668" : "#1A2E2E";
|
||||
const glass = selected ? "#C4D668" : "#e5e7eb";
|
||||
const opacity = selected ? 0.15 : 0.3;
|
||||
const sw = 1.5;
|
||||
|
||||
// Frame: outer rect with inner glass area
|
||||
const frame = (children: React.ReactNode) => (
|
||||
<svg viewBox="0 0 40 60" className="h-14 w-10">
|
||||
<rect x="2" y="2" width="36" height="56" rx="1" fill="none" stroke={stroke} strokeWidth={sw} />
|
||||
<rect x="5" y="5" width="30" height="50" fill={glass} opacity={opacity} />
|
||||
{children}
|
||||
</svg>
|
||||
);
|
||||
|
||||
switch (pattern) {
|
||||
case "geen":
|
||||
return frame(null);
|
||||
|
||||
case "2-vlak":
|
||||
return frame(
|
||||
<line x1="5" y1="30" x2="35" y2="30" stroke={stroke} strokeWidth={sw} />
|
||||
);
|
||||
|
||||
case "3-vlak":
|
||||
return frame(
|
||||
<>
|
||||
<line x1="5" y1="22" x2="35" y2="22" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="5" y1="38" x2="35" y2="38" stroke={stroke} strokeWidth={sw} />
|
||||
</>
|
||||
);
|
||||
|
||||
case "4-vlak":
|
||||
return frame(
|
||||
<>
|
||||
<line x1="5" y1="18" x2="35" y2="18" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="5" y1="30" x2="35" y2="30" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="5" y1="42" x2="35" y2="42" stroke={stroke} strokeWidth={sw} />
|
||||
</>
|
||||
);
|
||||
|
||||
case "6-vlak":
|
||||
return frame(
|
||||
<>
|
||||
<line x1="5" y1="22" x2="35" y2="22" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="5" y1="38" x2="35" y2="38" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="20" y1="5" x2="20" y2="55" stroke={stroke} strokeWidth={sw} />
|
||||
</>
|
||||
);
|
||||
|
||||
case "8-vlak":
|
||||
return frame(
|
||||
<>
|
||||
<line x1="5" y1="18" x2="35" y2="18" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="5" y1="30" x2="35" y2="30" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="5" y1="42" x2="35" y2="42" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="20" y1="5" x2="20" y2="55" stroke={stroke} strokeWidth={sw} />
|
||||
</>
|
||||
);
|
||||
|
||||
case "kruis":
|
||||
return frame(
|
||||
<>
|
||||
<line x1="5" y1="30" x2="35" y2="30" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="20" y1="5" x2="20" y2="55" stroke={stroke} strokeWidth={sw} />
|
||||
</>
|
||||
);
|
||||
|
||||
case "ongelijk-3":
|
||||
return frame(
|
||||
<>
|
||||
<line x1="5" y1="24" x2="35" y2="24" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="5" y1="40" x2="35" y2="40" stroke={stroke} strokeWidth={sw} />
|
||||
</>
|
||||
);
|
||||
|
||||
case "boerderij":
|
||||
return frame(
|
||||
<>
|
||||
<line x1="5" y1="20" x2="35" y2="20" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="20" y1="20" x2="20" y2="55" stroke={stroke} strokeWidth={sw} />
|
||||
</>
|
||||
);
|
||||
|
||||
case "herenhuis":
|
||||
return frame(
|
||||
<>
|
||||
<line x1="5" y1="18" x2="35" y2="18" stroke={stroke} strokeWidth={sw} />
|
||||
<line x1="5" y1="40" x2="35" y2="40" stroke={stroke} strokeWidth={sw} />
|
||||
</>
|
||||
);
|
||||
|
||||
default:
|
||||
return frame(null);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// DATA
|
||||
// ============================================
|
||||
|
||||
const doorTypes: Array<{
|
||||
value: DoorType;
|
||||
label: string;
|
||||
description: string;
|
||||
}> = [
|
||||
{
|
||||
value: "taats",
|
||||
label: "Taatsdeur",
|
||||
description: "Pivoterende deur",
|
||||
},
|
||||
{
|
||||
value: "scharnier",
|
||||
label: "Scharnierdeur",
|
||||
description: "Zijscharnieren",
|
||||
},
|
||||
{
|
||||
value: "paneel",
|
||||
label: "Vast Paneel",
|
||||
description: "Geen beweging",
|
||||
},
|
||||
{ value: "taats", label: "Taatsdeur", description: "Pivoterende deur" },
|
||||
{ value: "scharnier", label: "Scharnierdeur", description: "Zijscharnieren" },
|
||||
{ value: "paneel", label: "Vast Paneel", description: "Geen beweging" },
|
||||
];
|
||||
|
||||
const gridTypes: Array<{
|
||||
value: "3-vlak" | "4-vlak" | "geen";
|
||||
value: GridType;
|
||||
label: string;
|
||||
description: string;
|
||||
dividers: number;
|
||||
}> = [
|
||||
{ value: "geen", label: "Geen", description: "Volledig vlak", dividers: 0 },
|
||||
{ value: "3-vlak", label: "3-vlaks", description: "2 balken", dividers: 2 },
|
||||
{ value: "4-vlak", label: "4-vlaks", description: "3 balken", dividers: 3 },
|
||||
{ value: "geen", label: "Geen", description: "Volledig vlak" },
|
||||
{ value: "2-vlak", label: "2-vlaks", description: "1 balk" },
|
||||
{ value: "3-vlak", label: "3-vlaks", description: "2 balken" },
|
||||
{ value: "4-vlak", label: "4-vlaks", description: "3 balken" },
|
||||
{ value: "kruis", label: "Kruis", description: "1H + 1V" },
|
||||
{ value: "6-vlak", label: "6-vlaks", description: "2H + 1V" },
|
||||
{ value: "8-vlak", label: "8-vlaks", description: "3H + 1V" },
|
||||
{ value: "ongelijk-3", label: "Ongelijk", description: "3 ongelijk" },
|
||||
{ value: "boerderij", label: "Boerderij", description: "2+2 onder" },
|
||||
{ value: "herenhuis", label: "Herenhuis", description: "3 horizontaal" },
|
||||
];
|
||||
|
||||
// ============================================
|
||||
// MAIN COMPONENT
|
||||
// ============================================
|
||||
|
||||
export function StepProduct() {
|
||||
const { nextStep } = useFormContext();
|
||||
const { doorType, gridType, setDoorType, setGridType } =
|
||||
useConfiguratorStore();
|
||||
const { doorType, gridType, setDoorType, setGridType } = useConfiguratorStore();
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Door Type Selection - Visual Tiles */}
|
||||
{/* Door Type Selection */}
|
||||
<div>
|
||||
<h2 className="mb-2 text-lg font-bold text-[#1A2E2E]">Kies uw deurtype</h2>
|
||||
<p className="mb-4 text-sm text-gray-600">
|
||||
@@ -153,11 +224,7 @@ export function StepProduct() {
|
||||
<IconComponent selected={selected} />
|
||||
</div>
|
||||
<h3 className="text-sm font-bold">{type.label}</h3>
|
||||
<p
|
||||
className={`mt-1 text-xs ${
|
||||
selected ? "text-white/70" : "text-gray-500"
|
||||
}`}
|
||||
>
|
||||
<p className={`mt-1 text-xs ${selected ? "text-white/70" : "text-gray-500"}`}>
|
||||
{type.description}
|
||||
</p>
|
||||
{selected && (
|
||||
@@ -171,14 +238,14 @@ export function StepProduct() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Grid Type Selection - Visual Tiles */}
|
||||
{/* Grid Type Selection - 10 patterns */}
|
||||
<div>
|
||||
<h2 className="mb-2 text-lg font-bold text-[#1A2E2E]">Verdeling</h2>
|
||||
<p className="mb-4 text-sm text-gray-600">
|
||||
Kies het aantal horizontale vlakken.
|
||||
Kies het patroon van de glasverdeling.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="grid grid-cols-5 gap-2">
|
||||
{gridTypes.map((type) => {
|
||||
const selected = gridType === type.value;
|
||||
|
||||
@@ -187,26 +254,22 @@ export function StepProduct() {
|
||||
key={type.value}
|
||||
type="button"
|
||||
onClick={() => setGridType(type.value)}
|
||||
className={`group relative flex flex-col items-center rounded-xl border-2 px-2 py-4 transition-all ${
|
||||
className={`group relative flex flex-col items-center rounded-xl border-2 px-1 py-3 transition-all ${
|
||||
selected
|
||||
? "border-[#C4D668] bg-[#1A2E2E] text-white ring-2 ring-[#C4D668] shadow-lg shadow-[#C4D668]/20"
|
||||
: "border-gray-200 bg-white text-gray-900 hover:border-[#1A2E2E]/20 hover:shadow-md"
|
||||
}`}
|
||||
>
|
||||
<div className="mb-3 flex items-center justify-center">
|
||||
<GridIllustration dividers={type.dividers} selected={selected} />
|
||||
<div className="mb-2 flex items-center justify-center">
|
||||
<GridSVG pattern={type.value} selected={selected} />
|
||||
</div>
|
||||
<h3 className="text-sm font-bold">{type.label}</h3>
|
||||
<p
|
||||
className={`mt-1 text-xs ${
|
||||
selected ? "text-white/70" : "text-gray-500"
|
||||
}`}
|
||||
>
|
||||
<h3 className="text-[10px] font-bold leading-tight">{type.label}</h3>
|
||||
<p className={`text-[9px] leading-tight ${selected ? "text-white/70" : "text-gray-500"}`}>
|
||||
{type.description}
|
||||
</p>
|
||||
{selected && (
|
||||
<div className="absolute right-2 top-2 flex size-5 items-center justify-center rounded-full bg-[#C4D668]">
|
||||
<Check className="size-3 text-[#1A2E2E]" />
|
||||
<div className="absolute right-1 top-1 flex size-4 items-center justify-center rounded-full bg-[#C4D668]">
|
||||
<Check className="size-2.5 text-[#1A2E2E]" />
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user