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>
417 lines
12 KiB
JavaScript
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;
|