231 lines
8.5 KiB
TypeScript
231 lines
8.5 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect } from 'react'
|
|
import Image from 'next/image'
|
|
import ReCAPTCHA from "react-google-recaptcha"
|
|
import axios from "axios"
|
|
import styles from './BookTable.module.css'
|
|
|
|
interface FormErrors {
|
|
name?: string;
|
|
phone?: string;
|
|
date?: string;
|
|
captcha?: string;
|
|
}
|
|
|
|
export default function BookTable() {
|
|
const [email, setEmail] = useState("");
|
|
|
|
useEffect(() => {
|
|
const user = "info";
|
|
const domain = "antalya-restaurant.com";
|
|
setEmail(`${user}@${domain}`);
|
|
}, []);
|
|
|
|
const [formData, setFormData] = useState({
|
|
name: "",
|
|
phone: "",
|
|
date: "",
|
|
message: "",
|
|
});
|
|
|
|
const [formErrors, setFormErrors] = useState<FormErrors>({});
|
|
const [captchaToken, setCaptchaToken] = useState<string | null>(null);
|
|
const [alert, setAlert] = useState({ show: false, type: "", message: "" });
|
|
const [charCount, setCharCount] = useState(0);
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
const { name, value } = e.target;
|
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
|
|
|
if (name === "message") {
|
|
setCharCount(value.length);
|
|
}
|
|
};
|
|
|
|
const handleCaptchaChange = (token: string | null) => {
|
|
setCaptchaToken(token);
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
|
|
const errors: FormErrors = {};
|
|
if (!formData.name.trim()) errors.name = "Name is required.";
|
|
if (!formData.phone.trim()) errors.phone = "Phone is required.";
|
|
if (!formData.date.trim()) errors.date = "Date is required.";
|
|
if (!captchaToken) errors.captcha = "Please verify the CAPTCHA.";
|
|
|
|
setFormErrors(errors);
|
|
if (Object.keys(errors).length > 0) return;
|
|
|
|
const emailData = {
|
|
name: formData.name,
|
|
phone: formData.phone,
|
|
email: email,
|
|
subject: `Table Reservation - ${formData.name} on ${formData.date}`,
|
|
message: `
|
|
<strong>Reservation Details:</strong><br/>
|
|
Name: ${formData.name}<br/>
|
|
Phone: ${formData.phone}<br/>
|
|
Date: ${formData.date}<br/><br/>
|
|
<strong>Message:</strong><br/>
|
|
${formData.message || "None"}
|
|
`,
|
|
to: email,
|
|
senderName: "Antalya Restaurant - Table Reservation",
|
|
recaptchaToken: captchaToken,
|
|
};
|
|
|
|
try {
|
|
const res = await axios.post(
|
|
"https://mailserver.metatronnest.com/send",
|
|
emailData,
|
|
{ headers: { "Content-Type": "application/json" } }
|
|
);
|
|
|
|
setAlert({
|
|
show: true,
|
|
type: "success",
|
|
message: res?.data?.message || "Reservation request sent successfully! We'll contact you soon.",
|
|
});
|
|
|
|
setFormData({
|
|
name: "",
|
|
phone: "",
|
|
date: "",
|
|
message: "",
|
|
});
|
|
setCaptchaToken(null);
|
|
setFormErrors({});
|
|
setCharCount(0);
|
|
} catch (error) {
|
|
setAlert({
|
|
show: true,
|
|
type: "danger",
|
|
message: "Failed to send reservation request. Please try again later.",
|
|
});
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (alert.show) {
|
|
const timer = setTimeout(() => {
|
|
setAlert((prev) => ({ ...prev, show: false }));
|
|
}, 5000);
|
|
return () => clearTimeout(timer);
|
|
}
|
|
}, [alert.show]);
|
|
|
|
return (
|
|
<section className={styles.section} id="book">
|
|
<div className={styles.imageContainer}>
|
|
<Image
|
|
src="/images/restaurant-interior.png"
|
|
alt="Luxury Restaurant Interior"
|
|
fill
|
|
style={{ objectFit: 'cover' }}
|
|
priority
|
|
/>
|
|
</div>
|
|
|
|
<div className={styles.formContainer}>
|
|
<div className={styles.content}>
|
|
<h2 className={styles.title}>Book A Table</h2>
|
|
|
|
{alert.show && (
|
|
<div className={`${styles.alert} ${styles[`alert${alert.type.charAt(0).toUpperCase() + alert.type.slice(1)}`]}`}>
|
|
{alert.message}
|
|
</div>
|
|
)}
|
|
|
|
<form
|
|
id="reservation-form"
|
|
onSubmit={handleSubmit}
|
|
className={styles.form}
|
|
>
|
|
{/* Name Input with Placeholder */}
|
|
<div className={styles.inputGroup}>
|
|
<label htmlFor="name" className={styles.label}>Name *</label>
|
|
<input
|
|
type="text"
|
|
id="name"
|
|
name="name"
|
|
required
|
|
className={styles.input}
|
|
placeholder="Your Full Name"
|
|
value={formData.name}
|
|
onChange={handleChange}
|
|
/>
|
|
{formErrors.name && <small className={styles.errorText}>{formErrors.name}</small>}
|
|
</div>
|
|
|
|
{/* Phone Input with Placeholder */}
|
|
<div className={styles.inputGroup}>
|
|
<label htmlFor="phone" className={styles.label}>Phone *</label>
|
|
<input
|
|
type="tel"
|
|
id="phone"
|
|
name="phone"
|
|
required
|
|
className={styles.input}
|
|
placeholder="(519) 000-0000"
|
|
value={formData.phone}
|
|
onChange={handleChange}
|
|
/>
|
|
{formErrors.phone && <small className={styles.errorText}>{formErrors.phone}</small>}
|
|
</div>
|
|
|
|
<div className={styles.inputGroup}>
|
|
<label htmlFor="date" className={styles.label}>Date *</label>
|
|
<input
|
|
type="date"
|
|
id="date"
|
|
name="date"
|
|
required
|
|
className={styles.input}
|
|
value={formData.date}
|
|
onChange={handleChange}
|
|
/>
|
|
{formErrors.date && <small className={styles.errorText}>{formErrors.date}</small>}
|
|
</div>
|
|
|
|
{/* Message Textarea with Placeholder */}
|
|
<div className={styles.inputGroup}>
|
|
<label htmlFor="message" className={styles.label}>Message</label>
|
|
<textarea
|
|
id="message"
|
|
name="message"
|
|
className={styles.textarea}
|
|
placeholder="Any special requests..."
|
|
maxLength={500}
|
|
value={formData.message}
|
|
onChange={handleChange}
|
|
/>
|
|
<span className={styles.charCount}>{charCount}/500 characters</span>
|
|
</div>
|
|
|
|
<div className={styles.recaptchaWrapper}>
|
|
<ReCAPTCHA
|
|
sitekey="6Lckq9MrAAAAABjBD9rQYm19BMGFFWiwb9mPiw2K"
|
|
onChange={handleCaptchaChange}
|
|
/>
|
|
{formErrors.captcha && <small className={styles.errorText}>{formErrors.captcha}</small>}
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
className={styles.submitButton}
|
|
>
|
|
Submit Reservation
|
|
</button>
|
|
|
|
|
|
|
|
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
} |