633 lines
25 KiB
TypeScript
633 lines
25 KiB
TypeScript
/*
|
|
* DESIGN: "Clinical Warmth"
|
|
* Sezione prenotazione: form to mail con conferma non vincolante.
|
|
*/
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { motion, useInView } from "framer-motion";
|
|
import {
|
|
Calendar,
|
|
CheckCircle2,
|
|
Clock,
|
|
Mail,
|
|
PawPrint,
|
|
Phone,
|
|
ShieldCheck,
|
|
User,
|
|
} from "lucide-react";
|
|
import { toast } from "sonner";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
const bookingFormPaused = false;
|
|
|
|
const services = [
|
|
"Visita clinica generale",
|
|
"Ecografia",
|
|
"Radiologia",
|
|
"Laboratorio",
|
|
"Vaccinazione",
|
|
"Oncologia",
|
|
"Dermatologia",
|
|
"Oculistica",
|
|
"Nutrizione",
|
|
"Ortopedia",
|
|
"Endoscopia",
|
|
"Laparoscopia",
|
|
];
|
|
|
|
const doctors = [
|
|
"Dott. Paolo Parmeggiani",
|
|
"Dott.ssa Irene Paganelli",
|
|
"Dott. Simone Tinti",
|
|
"Dott.ssa Michela Sghedoni",
|
|
"Dott.ssa Sara Casali",
|
|
"Dott. Riccardo Suffritti",
|
|
];
|
|
|
|
const openingHours = [
|
|
{ days: "Visite: Lunedì - Venerdì", hours: "09:00 - 19:30" },
|
|
{ days: "Visite: Sabato", hours: "09:00 - 17:00" },
|
|
{ days: "Urgenze: Lunedì - Venerdì", hours: "08:00 - 22:30" },
|
|
{ days: "Urgenze: Sabato e Festivi", hours: "09:00 - 20:00" },
|
|
];
|
|
|
|
const preferredPeriods = [
|
|
"Questa settimana",
|
|
"Prossima settimana",
|
|
"Tra 2 settimane",
|
|
"Il mese prossimo",
|
|
];
|
|
|
|
const preferredTimeWindows = [
|
|
"Mattino",
|
|
"Pausa pranzo",
|
|
"Pomeriggio",
|
|
];
|
|
|
|
type BookingFormState = {
|
|
name: string;
|
|
email: string;
|
|
phone: string;
|
|
petName: string;
|
|
petType: string;
|
|
doctor: string;
|
|
service: string;
|
|
date: string;
|
|
time: string;
|
|
notes: string;
|
|
website: string;
|
|
captchaAnswer: string;
|
|
};
|
|
|
|
const initialForm: BookingFormState = {
|
|
name: "",
|
|
email: "",
|
|
phone: "",
|
|
petName: "",
|
|
petType: "cane",
|
|
doctor: "",
|
|
service: "",
|
|
date: "",
|
|
time: "",
|
|
notes: "",
|
|
website: "",
|
|
captchaAnswer: "",
|
|
};
|
|
|
|
type CaptchaChallenge = {
|
|
captcha_id: string;
|
|
question: string;
|
|
};
|
|
|
|
export default function BookingSection() {
|
|
const ref = useRef(null);
|
|
const isInView = useInView(ref, { once: true, margin: "-80px" });
|
|
const [submitted, setSubmitted] = useState(false);
|
|
const [submitting, setSubmitting] = useState(false);
|
|
const [submittedName, setSubmittedName] = useState("");
|
|
const [form, setForm] = useState<BookingFormState>(initialForm);
|
|
const [captcha, setCaptcha] = useState<CaptchaChallenge | null>(null);
|
|
const [captchaLoading, setCaptchaLoading] = useState(false);
|
|
|
|
const loadCaptcha = async () => {
|
|
setCaptchaLoading(true);
|
|
|
|
try {
|
|
const response = await fetch("/api/booking-captcha");
|
|
|
|
if (!response.ok) {
|
|
throw new Error("Verifica anti-spam non disponibile");
|
|
}
|
|
|
|
const payload = await response.json();
|
|
setCaptcha(payload);
|
|
setForm((currentForm) => ({ ...currentForm, captchaAnswer: "" }));
|
|
} catch {
|
|
setCaptcha(null);
|
|
toast.error("Verifica anti-spam non disponibile", {
|
|
description: "Riprova tra qualche istante prima di inviare il modulo.",
|
|
});
|
|
} finally {
|
|
setCaptchaLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (!bookingFormPaused) {
|
|
loadCaptcha();
|
|
}
|
|
}, []);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (!form.name || !form.email || !form.phone || !form.doctor || !form.service || !form.date) {
|
|
toast.error("Compila tutti i campi obbligatori");
|
|
return;
|
|
}
|
|
|
|
if (!captcha || !form.captchaAnswer) {
|
|
toast.error("Completa la verifica anti-spam");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setSubmitting(true);
|
|
|
|
const response = await fetch("/api/booking-request", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
name: form.name,
|
|
email: form.email,
|
|
phone: form.phone,
|
|
pet_name: form.petName,
|
|
pet_type: form.petType,
|
|
doctor: form.doctor,
|
|
service: form.service,
|
|
date: form.date,
|
|
time: form.time,
|
|
notes: form.notes,
|
|
website: form.website,
|
|
captcha_id: captcha.captcha_id,
|
|
captcha_answer: form.captchaAnswer,
|
|
}),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorPayload = await response.json().catch(() => null);
|
|
throw new Error(errorPayload?.detail || "Invio non riuscito");
|
|
}
|
|
|
|
const payload = await response.json();
|
|
|
|
setSubmittedName(form.name);
|
|
setSubmitted(true);
|
|
setForm(initialForm);
|
|
loadCaptcha();
|
|
toast.success("Richiesta inviata", {
|
|
description: payload.message,
|
|
});
|
|
} catch (error) {
|
|
loadCaptcha();
|
|
|
|
const message =
|
|
error instanceof Error
|
|
? error.message
|
|
: "Si e verificato un problema durante l'invio.";
|
|
|
|
toast.error("Invio non riuscito", {
|
|
description: message,
|
|
});
|
|
} finally {
|
|
setSubmitting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<section id="prenotazione" className="relative overflow-hidden bg-[#1B4F72] py-20 md:py-28">
|
|
<div className="absolute right-0 top-0 h-96 w-96 translate-x-1/2 -translate-y-1/2 rounded-full bg-white/5" />
|
|
<div className="absolute bottom-0 left-0 h-64 w-64 -translate-x-1/2 translate-y-1/2 rounded-full bg-[#4ECDC4]/10" />
|
|
|
|
<div className="container relative z-10">
|
|
<div className="grid grid-cols-1 items-start gap-16 lg:grid-cols-2">
|
|
<motion.div
|
|
ref={ref}
|
|
initial={{ opacity: 0, x: -30 }}
|
|
animate={isInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ duration: 0.8 }}
|
|
className="text-white"
|
|
>
|
|
<div className="mb-4 flex items-center gap-3">
|
|
<div className="h-0.5 w-12 bg-[#4ECDC4]" />
|
|
<span className="text-sm font-semibold uppercase tracking-widest text-[#4ECDC4]">
|
|
Prenotazioni
|
|
</span>
|
|
</div>
|
|
|
|
<h2
|
|
className="mb-6 text-white"
|
|
style={{
|
|
fontFamily: "'Cormorant Garamond', serif",
|
|
fontSize: "clamp(2rem, 4vw, 3rem)",
|
|
fontWeight: 600,
|
|
lineHeight: 1.2,
|
|
}}
|
|
>
|
|
Richiedi una visita <span className="italic text-[#4ECDC4]">online</span>
|
|
</h2>
|
|
|
|
<p className="mb-8 text-base leading-relaxed text-white/80">
|
|
Compila il modulo per inviare una richiesta di appuntamento. La richiesta
|
|
non è vincolante e dovrà essere confermata dallo staff della clinica, che ti
|
|
ricontatterà il prima possibile.
|
|
</p>
|
|
|
|
<div className="mb-8 rounded-xl border border-white/20 bg-white/10 p-5 backdrop-blur-sm">
|
|
<p className="mb-2 text-sm font-semibold uppercase tracking-wide text-[#4ECDC4]">
|
|
Urgenze
|
|
</p>
|
|
<a
|
|
href="tel:3205322439"
|
|
className="text-2xl font-bold text-white transition-colors hover:text-[#4ECDC4]"
|
|
style={{ fontFamily: "'Cormorant Garamond', serif" }}
|
|
>
|
|
320 532.24.39
|
|
</a>
|
|
</div>
|
|
|
|
<div className="mb-8 rounded-2xl border border-[#4ECDC4]/30 bg-[#14384F] p-5">
|
|
<div className="flex items-start gap-3">
|
|
<div className="mt-0.5 flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-[#4ECDC4]/15">
|
|
<ShieldCheck size={18} className="text-[#4ECDC4]" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-semibold text-white">Richiesta non vincolante</p>
|
|
<p className="mt-1 text-sm leading-relaxed text-white/75">
|
|
L'invio del modulo non costituisce conferma automatica dell'appuntamento.
|
|
Il team verificherà disponibilità, tipologia di visita e urgenza del caso prima
|
|
di confermare periodo e fascia oraria.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="max-w-[39rem]">
|
|
<div className="rounded-[26px] border border-white/20 bg-white/10 px-6 py-6 shadow-[0_18px_45px_rgba(0,0,0,0.18)] backdrop-blur-md sm:px-7 sm:py-7">
|
|
<h4
|
|
className="mb-5 text-white/95 uppercase tracking-[0.28em]"
|
|
style={{
|
|
fontFamily: "'Nunito Sans', sans-serif",
|
|
fontSize: "0.8rem",
|
|
fontWeight: 700,
|
|
}}
|
|
>
|
|
Orari di apertura
|
|
</h4>
|
|
|
|
<div className="space-y-4">
|
|
{openingHours.map((slot) => (
|
|
<div key={slot.days} className="flex items-start gap-3.5">
|
|
<div className="mt-0.5 rounded-full bg-[#4ECDC4]/20 p-2">
|
|
<Clock size={14} className="text-[#7ce3dc]" />
|
|
</div>
|
|
|
|
<div>
|
|
<p className="text-sm font-semibold text-white/92 sm:text-[0.98rem]">
|
|
{slot.days}
|
|
</p>
|
|
<p className="mt-0.5 text-sm text-white/72 sm:text-[0.96rem]">
|
|
{slot.hours}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, x: 30 }}
|
|
animate={isInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ duration: 0.8, delay: 0.2 }}
|
|
>
|
|
{bookingFormPaused ? (
|
|
<div className="rounded-2xl bg-white p-8 text-center shadow-2xl">
|
|
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-[#4ECDC4]/15">
|
|
<Clock size={32} className="text-[#4ECDC4]" />
|
|
</div>
|
|
<h3
|
|
className="mb-3 text-[#1B4F72]"
|
|
style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: "1.9rem" }}
|
|
>
|
|
Form in aggiornamento
|
|
</h3>
|
|
<p className="mb-6 text-sm leading-relaxed text-gray-600">
|
|
Il modulo di richiesta appuntamento è temporaneamente sospeso per manutenzione.
|
|
Per prenotazioni o informazioni contatta direttamente la clinica.
|
|
</p>
|
|
<a
|
|
href="tel:3205322439"
|
|
className="inline-flex items-center justify-center rounded-lg bg-[#1B4F72] px-5 py-3 text-sm font-bold text-white transition-colors hover:bg-[#163d5a]"
|
|
>
|
|
Chiama 320 532.24.39
|
|
</a>
|
|
</div>
|
|
) : submitted ? (
|
|
<div className="rounded-2xl bg-white p-8 text-center shadow-2xl">
|
|
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-[#4ECDC4]/15">
|
|
<CheckCircle2 size={32} className="text-[#4ECDC4]" />
|
|
</div>
|
|
<h3
|
|
className="mb-2 text-[#1B4F72]"
|
|
style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: "1.9rem" }}
|
|
>
|
|
Grazie {submittedName || "per la tua richiesta"}
|
|
</h3>
|
|
<p className="mb-3 text-sm leading-relaxed text-gray-600">
|
|
La tua richiesta di prenotazione è stata inviata correttamente.
|
|
</p>
|
|
<p className="mb-6 text-sm leading-relaxed text-gray-600">
|
|
Il team della Clinica Veterinaria Formiginese la prenderà in carico il prima
|
|
possibile e ti ricontatterà per confermare disponibilità, data e orario.
|
|
</p>
|
|
<div className="mb-6 rounded-xl border border-[#E4D7C6] bg-[#FFF9F1] px-4 py-3 text-left text-sm text-gray-600">
|
|
<strong className="text-[#1B4F72]">Nota importante:</strong> la richiesta inviata
|
|
non equivale a una prenotazione gia confermata.
|
|
</div>
|
|
<Button
|
|
className="bg-[#1B4F72] text-white hover:bg-[#163d5a]"
|
|
onClick={() => setSubmitted(false)}
|
|
>
|
|
Invia una nuova richiesta
|
|
</Button>
|
|
</div>
|
|
) : (
|
|
<form
|
|
onSubmit={handleSubmit}
|
|
className="space-y-4 rounded-2xl bg-white p-6 shadow-2xl md:p-8"
|
|
>
|
|
<div>
|
|
<h3
|
|
className="mb-2 text-[#1B4F72]"
|
|
style={{
|
|
fontFamily: "'Cormorant Garamond', serif",
|
|
fontSize: "1.5rem",
|
|
fontWeight: 600,
|
|
}}
|
|
>
|
|
Richiedi un appuntamento
|
|
</h3>
|
|
<p className="text-sm leading-relaxed text-gray-500">
|
|
Compila i campi richiesti e inviaci una preferenza di periodo: sarà lo staff a
|
|
ricontattarti per confermare la visita.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
<div>
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Nome e Cognome *
|
|
</label>
|
|
<div className="relative">
|
|
<User size={15} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
|
<input
|
|
type="text"
|
|
required
|
|
placeholder="Mario Rossi"
|
|
value={form.name}
|
|
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
|
className="w-full rounded-lg border border-gray-200 py-2.5 pl-9 pr-3 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4]"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Email *
|
|
</label>
|
|
<div className="relative">
|
|
<Mail size={15} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
|
<input
|
|
type="email"
|
|
required
|
|
placeholder="nome@email.it"
|
|
value={form.email}
|
|
onChange={(e) => setForm({ ...form, email: e.target.value })}
|
|
className="w-full rounded-lg border border-gray-200 py-2.5 pl-9 pr-3 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4]"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Telefono *
|
|
</label>
|
|
<div className="relative">
|
|
<Phone size={15} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
|
<input
|
|
type="tel"
|
|
required
|
|
placeholder="333 123 4567"
|
|
value={form.phone}
|
|
onChange={(e) => setForm({ ...form, phone: e.target.value })}
|
|
className="w-full rounded-lg border border-gray-200 py-2.5 pl-9 pr-3 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4]"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
<div>
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Nome dell'animale
|
|
</label>
|
|
<div className="relative">
|
|
<PawPrint size={15} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
|
<input
|
|
type="text"
|
|
placeholder="Fido"
|
|
value={form.petName}
|
|
onChange={(e) => setForm({ ...form, petName: e.target.value })}
|
|
className="w-full rounded-lg border border-gray-200 py-2.5 pl-9 pr-3 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4]"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Tipo di animale
|
|
</label>
|
|
<select
|
|
value={form.petType}
|
|
onChange={(e) => setForm({ ...form, petType: e.target.value })}
|
|
className="w-full rounded-lg border border-gray-200 bg-white px-3 py-2.5 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4]"
|
|
>
|
|
<option value="cane">Cane</option>
|
|
<option value="gatto">Gatto</option>
|
|
<option value="altro">Altro</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Medico richiesto *
|
|
</label>
|
|
<select
|
|
required
|
|
value={form.doctor}
|
|
onChange={(e) => setForm({ ...form, doctor: e.target.value })}
|
|
className="w-full rounded-lg border border-gray-200 bg-white px-3 py-2.5 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4]"
|
|
>
|
|
<option value="">Seleziona un medico</option>
|
|
{doctors.map((doctor) => (
|
|
<option key={doctor} value={doctor}>
|
|
{doctor}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Tipo di visita *
|
|
</label>
|
|
<select
|
|
required
|
|
value={form.service}
|
|
onChange={(e) => setForm({ ...form, service: e.target.value })}
|
|
className="w-full rounded-lg border border-gray-200 bg-white px-3 py-2.5 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4]"
|
|
>
|
|
<option value="">Seleziona un servizio</option>
|
|
{services.map((service) => (
|
|
<option key={service} value={service}>
|
|
{service}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
<div>
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Periodo preferito *
|
|
</label>
|
|
<div className="relative">
|
|
<Calendar size={15} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
|
<select
|
|
required
|
|
value={form.date}
|
|
onChange={(e) => setForm({ ...form, date: e.target.value })}
|
|
className="w-full rounded-lg border border-gray-200 bg-white py-2.5 pl-9 pr-3 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4]"
|
|
>
|
|
<option value="">Seleziona un periodo</option>
|
|
{preferredPeriods.map((period) => (
|
|
<option key={period} value={period}>
|
|
{period}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Fascia oraria preferita
|
|
</label>
|
|
<div className="relative">
|
|
<Clock size={15} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
|
<select
|
|
value={form.time}
|
|
onChange={(e) => setForm({ ...form, time: e.target.value })}
|
|
className="w-full rounded-lg border border-gray-200 bg-white py-2.5 pl-9 pr-3 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4]"
|
|
>
|
|
<option value="">Seleziona una fascia oraria</option>
|
|
{preferredTimeWindows.map((time) => (
|
|
<option key={time} value={time}>
|
|
{time}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Note aggiuntive
|
|
</label>
|
|
<textarea
|
|
rows={3}
|
|
placeholder="Descrivi brevemente il motivo della visita..."
|
|
value={form.notes}
|
|
onChange={(e) => setForm({ ...form, notes: e.target.value })}
|
|
className="w-full resize-none rounded-lg border border-gray-200 px-3 py-2.5 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4]"
|
|
/>
|
|
</div>
|
|
|
|
<div className="hidden" aria-hidden="true">
|
|
<label>
|
|
Sito web
|
|
<input
|
|
type="text"
|
|
tabIndex={-1}
|
|
autoComplete="off"
|
|
value={form.website}
|
|
onChange={(e) => setForm({ ...form, website: e.target.value })}
|
|
/>
|
|
</label>
|
|
</div>
|
|
|
|
<div className="rounded-xl border border-gray-200 bg-gray-50 px-4 py-3">
|
|
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-600">
|
|
Verifica anti-spam *
|
|
</label>
|
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-center">
|
|
<span className="text-sm font-semibold text-[#1B4F72]">
|
|
{captchaLoading ? "Caricamento..." : captcha?.question || "Verifica non disponibile"}
|
|
</span>
|
|
<input
|
|
type="text"
|
|
inputMode="numeric"
|
|
required
|
|
placeholder="Risposta"
|
|
value={form.captchaAnswer}
|
|
onChange={(e) => setForm({ ...form, captchaAnswer: e.target.value })}
|
|
disabled={!captcha || captchaLoading}
|
|
className="w-full rounded-lg border border-gray-200 px-3 py-2.5 text-sm transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-[#4ECDC4] disabled:cursor-not-allowed disabled:bg-gray-100 sm:max-w-32"
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={loadCaptcha}
|
|
disabled={captchaLoading || submitting}
|
|
className="text-left text-xs font-semibold uppercase tracking-wide text-[#1B4F72] transition-colors hover:text-[#4ECDC4] disabled:cursor-wait disabled:opacity-60 sm:text-center"
|
|
>
|
|
Ricarica
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<Button
|
|
type="submit"
|
|
disabled={submitting || captchaLoading || !captcha}
|
|
className="w-full bg-[#4ECDC4] py-3 text-base font-bold text-white shadow-lg shadow-[#4ECDC4]/30 transition-all duration-300 hover:bg-[#3ab5ad] hover:shadow-xl disabled:cursor-wait disabled:opacity-80"
|
|
>
|
|
{submitting ? "Invio in corso..." : "Invia richiesta di prenotazione"}
|
|
</Button>
|
|
|
|
<p className="text-center text-xs text-gray-500">
|
|
* Campi obbligatori. La richiesta sarà valutata e confermata dallo staff.
|
|
</p>
|
|
</form>
|
|
)}
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|