Files
stalendeuren/bron/RequestForm.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

417 lines
12 KiB
JavaScript

import React, { useEffect, useState } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import TechInformation from "./TechInformation";
const RequestForm = ({ techInformation, attachDesign, glContext, sceneContext, cameraContext }) => {
const navigate = useNavigate();
const [formData, setFormData] = useState({
aanhef: "Dhr",
voornaam: "",
tussenvoegsel: "",
achternaam: "",
straatnaam: "",
huisnummer: "",
postcode: "",
woonplaats: "",
land: "Nederland",
emailadres: "",
telefoonnummer: "",
comment: "",
file: null,
});
useEffect(() => {
setFormData({...formData, file: attachDesign});
}, [attachDesign])
const [isSubmitting, setIsSubmitting] = useState(false);
const [isClearSubmitting, setIsClearSubmitting] = useState(false);
const [formErrors, setFormErrors] = useState({});
const handleChange = (e) => {
const { name, value, files } = e.target;
if (name === "file") {
setFormData({
...formData,
[name]: files[0],
});
} else {
setFormData({
...formData,
[name]: value,
});
}
};
const validate = () => {
const errors = {};
Object.keys(formData).forEach((key) => {
if (["comment", "tussenvoegsel", "file"].includes(key)) return;
if (!formData[key]) {
errors[key] = "Dit veld is verplicht";
}
});
return errors;
};
const dataURLToBlob = (dataURL) => {
const byteString = atob(dataURL.split(',')[1]);
const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
const arrayBuffer = new ArrayBuffer(byteString.length);
const uint8Array = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i++) {
uint8Array[i] = byteString.charCodeAt(i);
}
return new Blob([arrayBuffer], { type: mimeString });
};
const handleSubmit = async (isReset = false) => {
const errors = validate();
if (Object.keys(errors).length === 0) {
setIsSubmitting(true);
setIsClearSubmitting(isReset);
try {
glContext.render(sceneContext, cameraContext);
const image = glContext.domElement.toDataURL('image/png');
const imageBlob = dataURLToBlob(image);
const formDataToSubmit = new FormData();
Object.keys(formData).forEach((key) => {
formDataToSubmit.append(key, formData[key]);
});
formDataToSubmit.append('constructImage', imageBlob, 'constructImage.png');
formDataToSubmit.append(
"techInformation",
JSON.stringify(techInformation)
);
formDataToSubmit.append(
"platform", "clooz"
);
const response = await axios.post(
"https://api-lumbronch.agreatidea.studio/api/request-a-quote",
formDataToSubmit,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
navigate("/request-confirmation");
if (isReset) {
setFormData({
aanhef: "Dhr",
voornaam: "",
tussenvoegsel: "",
achternaam: "",
straatnaam: "",
huisnummer: "",
postcode: "",
woonplaats: "",
land: "Nederland",
emailadres: "",
telefoonnummer: "",
comment: "",
file: null,
});
}
// Handle successful form submission
} catch (error) {
console.error(error);
// Handle error in form submission
} finally {
setIsSubmitting(false);
}
} else {
setFormErrors(errors);
}
};
const formStyle = {
maxWidth: "400px",
margin: "0 auto",
backgroundColor: "#2D3748",
padding: "20px",
borderRadius: "8px",
};
const inputStyle = {
width: "100%",
padding: "10px",
marginBottom: "10px",
borderRadius: "4px",
backgroundColor: "#fff",
color: "#333",
border: "none",
boxSizing: "border-box",
};
const fileInputStyle = {
display: "none",
};
const labelStyle = {
color: "#fff",
fontSize: "14px",
fontWeight: "bold",
display: "block",
};
const errorStyle = {
color: "red",
fontSize: "12px",
margin: 0,
};
const buttonStyle = {
backgroundColor: "#48BB78",
color: "#FFF",
fontSize: "16px",
padding: "10px 20px",
borderRadius: "100px",
border: "none",
cursor: "pointer",
};
const linkButtonStyle = {
fontSize: "14px",
color: "#FFF",
cursor: "pointer",
display: "block",
};
const fileUploadLabelStyle = {
display: "flex",
alignItems: "center",
gap: "10px",
cursor: "pointer",
backgroundColor: "#48BB78",
padding: "8px 15px",
borderRadius: "50px",
color: "#FFF",
fontSize: "14px",
textAlign: "center",
};
return (
<form onSubmit={handleSubmit}>
<TechInformation techInformation={techInformation} />
<div style={formStyle}>
<h2 style={{ color: "#FFF", marginBottom: "20px", fontSize: "16px" }}>
Vul uw gegevens in
</h2>
<div style={{ marginBottom: "10px" }}>
<select
name="aanhef"
value={formData.aanhef}
onChange={handleChange}
style={{
...inputStyle,
border: formErrors.aanhef ? "1px solid red" : "none",
}}
required
>
<option value="Dhr">Dhr.</option>
<option value="Mevr">Mevr.</option>
</select>
{formErrors.aanhef && <p style={errorStyle}>{formErrors.aanhef}</p>}
</div>
{[
"voornaam",
"tussenvoegsel",
"achternaam",
"straatnaam",
"huisnummer",
"postcode",
"woonplaats",
"emailadres",
"telefoonnummer",
].map((field) => (
<div
key={field}
style={{
marginBottom: "10px",
display: "flex",
flexDirection: "column",
}}
>
<input
type="text"
name={field}
value={formData[field]}
onChange={handleChange}
placeholder={field.charAt(0).toUpperCase() + field.slice(1)}
style={{
...inputStyle,
border: formErrors[field] ? "1px solid red" : "none",
}}
required
/>
{formErrors[field] && <p style={errorStyle}>{formErrors[field]}</p>}
</div>
))}
<div style={{ marginBottom: "10px" }}>
<select
name="land"
value={formData.land}
onChange={handleChange}
style={{
...inputStyle,
border: formErrors.land ? "1px solid red" : "none",
}}
required
>
<option value="Nederland">Nederland</option>
<option value="Spanje">Spanje</option>
<option value="Duitsland">Duitsland</option>
<option value="België">België</option>
{/* Add more options as needed */}
</select>
{formErrors.land && <p style={errorStyle}>{formErrors.land}</p>}
</div>
<div key="comment" style={{ marginBottom: "10px", display: "flex" }}>
<textarea
rows={3}
name="comment"
value={formData.comment}
onChange={handleChange}
placeholder="Opmerkingen"
style={{
...inputStyle,
border: formErrors.comment ? "1px solid red" : "none",
}}
/>
{formErrors.comment && <p style={errorStyle}>{formErrors.comment}</p>}
</div>
<div style={{ color: "red", marginBottom: "20px" }}>
* Vul alle velden correct in.
</div>
<div
style={{
marginBottom: "10px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<label htmlFor="file" style={labelStyle}>
Afbeelding bijvoegen:
</label>
<label
htmlFor="file"
style={{
...fileUploadLabelStyle,
backgroundColor: formData.file ? "#48BB78" : "red",
}}
>
<img src="/images/upload.png" width={20} />
Kies bestand
</label>
<input
type="file"
name="file"
id="file"
onChange={handleChange}
style={{
...fileInputStyle,
border: formErrors.file ? "1px solid red" : "none",
}}
/>
{formErrors.file && <p style={errorStyle}>{formErrors.file}</p>}
</div>
<div
style={{
display: "flex",
justifyContent: "end",
alignItems: "center",
}}
>
<button
type="button"
onClick={() => handleSubmit(false)}
style={{ ...buttonStyle, marginTop: "10px", minHeight: "44px", minWidth: "187px" }}
disabled={isSubmitting}
className={`relative ${
isSubmitting ? "opacity-50 cursor-not-allowed" : ""
}`}
>
{isSubmitting && !isClearSubmitting ? (
<div className="absolute inset-0 flex justify-center items-center">
<svg
className="animate-spin h-5 w-5 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
></path>
</svg>
</div>
) : (
"Offerte aanvragen"
)}
</button>
</div>
<button
type="button"
onClick={() => handleSubmit(true)}
style={{ ...buttonStyle, marginTop: "20px", width: "100%", minHeight: "44px" }}
disabled={isSubmitting}
className={`relative ${
isSubmitting ? "opacity-50 cursor-not-allowed" : ""
}`}
>
{isSubmitting && isClearSubmitting ? (
<div className="absolute inset-0 flex justify-center items-center">
<svg
className="animate-spin h-5 w-5 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
></path>
</svg>
</div>
) : (
"Versturen en nog een offerte aanvragen"
)}
</button>
</div>
</form>
);
};
export default RequestForm;