Files
stalendeuren/bron/endpoint.js
Ubuntu 3d788740cb 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>
2026-03-01 14:50:31 +00:00

654 lines
23 KiB
JavaScript

import { Canvas, useThree } from "@react-three/fiber";
import axios from "axios";
// import { EffectComposer } from "@react-three/postprocessing";
import DoorHole from "./logic/DoorHole";
import { Suspense, useState, useEffect } from "react";
import Preloader from "./logic/preloader";
import Door from "./logic/UI/door";
import Design from "./logic/UI/design";
import Information from "./logic/UI/information";
import * as THREE from "three";
import { MyContext } from "./logic/data/contextapi";
import Extra from "./logic/UI/extra";
import Samenstling from "./logic/UI/samenstling";
import Arrow from "./logic/UI/arrow";
import { DirectionalLightHelper, CameraHelper } from "three";
import React, { useRef } from "react";
export default function Endpoint() {
const [json, setJson] = useState({ Data: [] });
const [step, setStep] = useState("door");
const [type, settype] = useState("Taatsdeur");
const [door, setdoor] = useState("3panel");
const [frameSize, setFrameSize] = useState(0.125);
const [glassType, setGlassType] = useState(0x111111);
const [frameType, setFrameType] = useState(
"./images/doortypes/RAL 9005 fijn structuur.png"
);
const [width, setWidth] = useState(90); // State for width
const [height, setHeight] = useState(250); // State for height
const [holeWidth, setHoleWidth] = useState(90);
const [doorConfig, setDoorConfig] = useState("enkele");
const [sidePannel, setSidePannel] = useState("geen");
const [sidePannelConfig, setSidePannelConfig] = useState("eigen maat");
const [sidePannelSize, setSidePannelSize] = useState(width);
const [stalenType, setStalenType] = useState("tussen");
const [stalenPart, setStalenPart] = useState(1);
const [handle, setHandle] = useState(1);
const [maxWidth, setMaxWidth] = useState(105);
const [prwType, setPrwType] = useState("Enkele");
const [prwImage, setPrwImage] = useState(
"./images/doortypes/enkele deur.png"
);
const [extraOptions, setExtraOptions] = useState([
"Meetservice",
"Montage service",
]);
const [count, setCount] = useState(1);
const [coverSheetSteel, setCoverSheetSteel] = useState(false);
const [coverLearn, setCoverLearn] = useState(false);
const [coverWoodliness, setCoverWoodliness] = useState(false);
const [coverMirrors, setCoverMirrors] = useState(false);
const [coverCylinderLockKey, setCoverCylinderLockKey] = useState(false);
const [coverDontKnow, setCoverDontKnow] = useState(false);
const [handleSheetSteel, setHandleSheetSteel] = useState(false);
const [handleLearn, setHandleLearn] = useState(false);
const [handleWoodliness, setHandleWoodliness] = useState(false);
const [handleMirrors, setHandleMirrors] = useState(false);
const [handleCylinderLockKey, setHandleCylinderLockKey] = useState(false);
const [standardBlack, setStandardBlack] = useState(false);
const [alternativeRALColour, setAlternativeRALColour] = useState(false);
const [metallicCoating, setMetallicCoating] = useState(false);
const [bright, setBright] = useState(false);
const [canaleOrCrepi, setCanaleOrCrepi] = useState(false);
const [fireResistant, setFireResistant] = useState(false);
const [outdoorPlacement, setOutdoorPlacement] = useState(false);
const [maxHeight, setMaxHeight] = useState(0);
const [contentHeight, setContentHeight] = useState("calc(100vh - 320px)");
const [open, setOpen] = useState(false);
const [inprogress, setInprogress] = useState(false);
const [bubbles, setBubbles] = useState();
const [attachDesign, setAttachDesign] = useState(null);
const [colorPickerOpened, setColorPickerOpened] = useState(false);
const [customFrameType, setCustomFrameType] = useState({name: "", color: ""});
const [compositionImage, setCompositionImage] = useState("https://config.livingsteel.nl/images/doortypes/3%20panel.png");
const [glContext, setGlContext] = useState(null);
const [sceneContext, setSceneContext] = useState(null); // Store the Scene context
const [cameraContext, setCameraContext] = useState(null); // Store the Camera context
// Calculate the maximum height based on the available screen height
useEffect(() => {
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
const isAndroid = /android/i.test(userAgent);
const isIOS = /iPad|iPhone|iPod/.test(userAgent) && !window.MSStream;
const calculateMaxHeight = () => {
let availableHeight;
if (window.innerWidth > 800) {
const screenHeight = window.innerHeight;
// Adjust maxHeight to leave space for other elements like headers or footers
availableHeight = screenHeight - 100; // Adjust this value as needed
} else {
setContentHeight(
`calc(100vh - 25vh - ${isAndroid ? "270px" : "310px"})`
);
const screenHeight = window.innerHeight * 0.65;
// Adjust maxHeight to leave space for other elements like headers or footers 30
availableHeight = screenHeight + 210; // Adjust this value as needed
}
setMaxHeight(availableHeight);
};
fetchBubbles();
calculateMaxHeight(); // Call once initially
window.addEventListener("resize", calculateMaxHeight); // Recalculate on window resize
return () => {
window.removeEventListener("resize", calculateMaxHeight); // Cleanup
};
}, []);
const fetchBubbles = async () => {
try {
const response = await axios.get(
"https://api.config-fencing.com/api/get-bubbles"
);
setBubbles(response.data.bubbles);
} catch (error) {
console.error("Error fetching bubbles:", error);
}
};
// Create a ref for the SpotLight
const spotLightRef = useRef();
return (
<>
<MyContext.Provider
value={{
json,
setJson,
setStep,
sidePannel,
setSidePannel,
type,
settype,
door,
setdoor,
width,
setWidth,
height,
setHeight,
holeWidth,
setHoleWidth,
doorConfig,
setDoorConfig,
prwType,
setPrwType,
prwImage,
setPrwImage,
count,
setCount,
frameSize,
setFrameSize,
glassType,
setGlassType,
frameType,
setFrameType,
sidePannelConfig,
setSidePannelConfig,
sidePannelSize,
setSidePannelSize,
stalenType,
setStalenType,
stalenPart,
setStalenPart,
handle,
setHandle,
maxWidth,
setMaxWidth,
coverSheetSteel,
setCoverSheetSteel,
coverLearn,
setCoverLearn,
coverWoodliness,
setCoverWoodliness,
coverMirrors,
setCoverMirrors,
coverCylinderLockKey,
setCoverCylinderLockKey,
coverDontKnow,
setCoverDontKnow,
handleSheetSteel,
setHandleSheetSteel,
handleLearn,
setHandleLearn,
handleWoodliness,
setHandleWoodliness,
handleMirrors,
setHandleMirrors,
handleCylinderLockKey,
setHandleCylinderLockKey,
standardBlack,
setStandardBlack,
alternativeRALColour,
setAlternativeRALColour,
metallicCoating,
setMetallicCoating,
bright,
setBright,
canaleOrCrepi,
setCanaleOrCrepi,
fireResistant,
setFireResistant,
outdoorPlacement,
setOutdoorPlacement,
setExtraOptions,
extraOptions,
open,
setOpen,
inprogress,
setInprogress,
bubbles,
setBubbles,
attachDesign,
setAttachDesign,
colorPickerOpened,
setColorPickerOpened,
customFrameType,
setCustomFrameType,
compositionImage,
setCompositionImage,
glContext,
sceneContext,
cameraContext,
}}
>
<div style={{ width: "100vw", height: "100svh", overflow: "hidden" }}>
<div className="body-can-2" style={{}}>
<div className="body-can-2-1" style={{}}>
<div className="body-can-2-1-1" style={{}}>
<Suspense fallback={<Preloader />}>
<Canvas
frameloop="demand"
gl={{
antialias: true,
alpha: true,
toneMapping: THREE.SRGBColorSpace,
}}
camera={{ fov: 53 }}
onCreated={({ gl, scene, camera }) => {
setGlContext(gl);
setSceneContext(scene);
setCameraContext(camera);
}}
// shadows // Enable shadows
>
{/* Ambient light for basic illumination */}
<DoorHole />
</Canvas>
</Suspense>
</div>
<div className="body-can-2-1-2" style={{ overflowY: "hidden" }}>
<div
className="quot"
style={{
backgroundColor: "#bdc79d",
display: "flex",
alignItems: "center",
padding: "10px",
}}
>
{/* <span
style={{
color: "white",
fontWeight: "bold",
marginLeft: "30px",
}}
>
Prijs incl. BTW
</span> */}
{/* <div
style={{
backgroundColor: "white",
borderRadius: "20px",
width: "auto",
padding: "5px",
margin: "0 0 0 10px",
}}
>
<span
className="body-txt"
style={{}}
onClick={() => {
setStep("door");
}}
>
&euro; 2200,00
</span>
</div> */}
</div>
<div
className={`step-content ${
step === "door" ? "expand" : "collapsed"
}`}
style={{
maxHeight: `${maxHeight}px`,
}}
>
<div
style={{
position: "relative",
cursor: "pointer",
marginTop: "auto",
}}
onClick={() => {
setStep("door");
}}
>
<button
className={`body-btn ${
step === "door" ? "expand" : "collapsed"
}`}
style={{}}
>
1
</button>
<span
className={`body-txt ${
step === "door" ? "expand" : "collapsed"
}`}
style={{ opacity: step === "door" ? 1 : 0.4 }}
onClick={() => {
setStep("door");
}}
>
DEURMAAT & TYPE
</span>
<div
style={{
position: "absolute",
top: "5px",
right: "20px",
}}
>
<input
type="radio"
checked={step === "door"}
readOnly
onChange={() => setStep("door")}
className={`radio ${
step === "door" ? "expand" : "collapsed"
}`}
/>
{/* <Arrow direction={step === 'door' ? 'down' : 'up'} /> */}
</div>
</div>
<div
style={{
overflowY: "auto",
padding: step == "door" ? "10px" : 0,
height: step == "door" ? contentHeight : 0,
opacity: step == "door" ? 1 : 0,
transition:
"height 0.3s ease-in-out, opacity 0.5s ease-in-out",
}}
>
<Door />
</div>
</div>
<div
className={`step-content ${
step === "samenstling" ? "expand" : "collapsed"
}`}
style={{
maxHeight: `${maxHeight}px`,
}}
>
<div
style={{
position: "relative",
cursor: "pointer",
}}
onClick={() => {
setStep("samenstling");
}}
>
<button
className={`body-btn ${
step === "samenstling" ? "expand" : "collapsed"
}`}
>
2
</button>
<span
className={`body-txt ${
step === "samenstling" ? "expand" : "collapsed"
}`}
style={{ opacity: step === "samenstling" ? 1 : 0.4 }}
onClick={() => {
setStep("samenstling");
}}
>
Samenstelling
</span>
<div
style={{
position: "absolute",
top: "5px",
right: "20px",
}}
>
<input
type="radio"
checked={step === "samenstling"}
readOnly
onChange={() => setStep("samenstling")}
className={`radio ${
step === "samenstling" ? "expand" : "collapsed"
}`}
/>
</div>
</div>
<div
style={{
overflowY: "auto",
padding: step == "samenstling" ? "10px" : 0,
height: step == "samenstling" ? contentHeight : 0,
opacity: step == "samenstling" ? 1 : 0,
transition:
"height 0.3s ease-in-out, opacity 0.5s ease-in-out",
}}
>
<Samenstling />
</div>
</div>
<div
className={`step-content ${
step === "design" ? "expand" : "collapsed"
}`}
style={{
maxHeight: `${maxHeight}px`,
}}
>
<div
style={{
position: "relative",
cursor: "pointer",
}}
onClick={() => {
setStep("design");
}}
>
<button
className={`body-btn ${
step === "design" ? "expand" : "collapsed"
}`}
style={{}}
>
3
</button>
<span
className={`body-txt ${
step === "design" ? "expand" : "collapsed"
}`}
style={{ opacity: step === "design" ? 1 : 0.4 }}
onClick={() => {
setStep("design");
}}
>
Design, Kleur & GLAS
</span>
<div
style={{
position: "absolute",
top: "5px",
right: "20px",
}}
>
<input
type="radio"
checked={step === "design"}
readOnly
onChange={() => setStep("design")}
className={`radio ${
step === "design" ? "expand" : "collapsed"
}`}
/>
</div>
</div>
<div
style={{
backgroundColor: "#fff",
overflowY: "auto",
padding: step == "design" ? "10px" : 0,
height: step == "design" ? contentHeight : 0,
opacity: step == "design" ? 1 : 0,
transition:
"height 0.3s ease-in-out, opacity 0.5s ease-in-out",
}}
>
<Design />
</div>
</div>
<div
className={`step-content ${
step === "extra" ? "expand" : "collapsed"
}`}
style={{
maxHeight: `${maxHeight}px`,
}}
>
<div
style={{
position: "relative",
cursor: "pointer",
}}
onClick={() => {
setStep("extra");
}}
>
<button
className={`body-btn ${
step === "extra" ? "expand" : "collapsed"
}`}
>
4
</button>
<span
className={`body-txt ${
step === "extra" ? "expand" : "collapsed"
}`}
style={{ opacity: step === "extra" ? 1 : 0.4 }}
onClick={() => {
setStep("extra");
}}
>
Extra Opties
</span>
<div
style={{
position: "absolute",
top: "5px",
right: "20px",
}}
>
<input
type="radio"
checked={step === "extra"}
readOnly
onChange={() => setStep("extra")}
className={`radio ${
step === "extra" ? "expand" : "collapsed"
}`}
/>
{/* <Arrow direction={step === 'extra' ? 'down' : 'up'} /> */}
</div>
</div>
<div
style={{
overflowY: "auto",
padding: step == "extra" ? "10px" : 0,
height: step == "extra" ? contentHeight : 0,
opacity: step == "extra" ? 1 : 0,
transition:
"height 0.3s ease-in-out, opacity 0.5s ease-in-out",
}}
>
<Extra />
</div>
</div>
<div
className={`pd step-content ${
step === "information" ? "expand" : "collapsed"
}`}
style={{
maxHeight: `${maxHeight}px`,
}}
>
<div
style={{
position: "relative",
cursor: "pointer",
}}
onClick={() => {
setStep("information");
}}
>
<button
className={`body-btn ${
step === "information" ? "expand" : "collapsed"
}`}
style={{}}
>
5
</button>
<span
className={`body-txt ${
step === "information" ? "expand" : "collapsed"
}`}
style={{ opacity: step === "information" ? 1 : 0.4 }}
onClick={() => {
setStep("information");
}}
>
Order informatie
</span>
<div
style={{
position: "absolute",
top: "5px",
right: "20px",
}}
>
<input
type="radio"
checked={step === "information"}
readOnly
onChange={() => setStep("information")}
className={`radio ${
step === "information" ? "expand" : "collapsed"
}`}
/>
{/* <Arrow
direction={step === 'information' ? 'down' : 'up'}
/> */}
</div>
</div>
<div
style={{
backgroundColor: "#fff",
overflowY: "auto",
padding: step == "information" ? "10px" : 0,
height: step == "information" ? contentHeight : 0,
opacity: step == "information" ? 1 : 0,
transition:
"height 0.3s ease-in-out, opacity 0.5s ease-in-out",
}}
>
<Information />
</div>
</div>
</div>
</div>
</div>
</div>
</MyContext.Provider>
</>
);
}