'use client';
import { useState, useEffect, forwardRef, useImperativeHandle, Ref } from 'react';
import { Event } from '@/types/types';
import ReservationDetails from './ReservationDetails';
import ReservationForm from './ReservationForm';
import { createClient } from '@/utils/supabase/client';
import { motion } from 'framer-motion';
interface ReserveFormProps {
eventType: string;
event?: Event;
onPersonsChange?: (persons: string) => void;
}
const sanitizePhoneNumber = (phone: string, countryCode: string) => {
let sanitized = phone.replace(/\D/g, ''); // Remove non-numeric characters
if (sanitized.startsWith('0')) {
sanitized = sanitized.slice(1); // Remove leading zero
}
return `${countryCode}${sanitized}`;
};
const convertLocalToUTC = (dateStr: string, timeStr: string, addHours: number = 0) => {
// Create a date object in local timezone
const localDate = new Date(`${dateStr}T${timeStr}`);
// Add hours if specified
if (addHours) {
localDate.setHours(localDate.getHours() + addHours);
}
// Convert to UTC string and extract date and time
const utcStr = localDate.toISOString();
return utcStr.slice(0, 19); // Get YYYY-MM-DDTHH:mm:ss format
};
// localStorage keys
const STORAGE_KEY_NAME = 'reservation_name';
const STORAGE_KEY_PHONE = 'reservation_phone';
const ReserveForm = forwardRef<unknown, ReserveFormProps>(({ eventType, event, onPersonsChange }, ref) => {
const supabase = createClient();
const [alert, setAlert] = useState<{
type: 'success' | 'error';
message: string;
} | null>(null);
const [reservationSuccess, setReservationSuccess] = useState(false);
const [person_name, setPersonName] = useState(() => {
if (typeof window !== 'undefined') {
return localStorage.getItem(STORAGE_KEY_NAME) || '';
}
return '';
});
const [phone, setPhone] = useState(() => {
if (typeof window !== 'undefined') {
return localStorage.getItem(STORAGE_KEY_PHONE) || '';
}
return '';
});
const [countryCode, setCountryCode] = useState('+359');
const [persons, setPersons] = useState('');
const [from_date, setFromDate] = useState(
event?.from_date
? new Date(event.from_date).toISOString().split('T')[0]
: new Date().toISOString().split('T')[0]
);
const [from_time, setFromTime] = useState(
event?.from_date
? new Date(event.from_date).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
: '19:00'
);
const [description, setDescription] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
// Expose methods to parent component via ref
useImperativeHandle(ref, () => ({
updateDescription: (newDescription: string) => {
setDescription(newDescription);
}
}));
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsSubmitting(true);
const sanitizedPhone = sanitizePhoneNumber(phone, countryCode);
const fromDateTime = convertLocalToUTC(from_date, from_time);
const toDateTime = convertLocalToUTC(from_date, from_time, 2);
try {
const { error } = await supabase
// @ts-ignore
.from('reservations')
.insert([{
person_name,
phone: sanitizedPhone,
persons: parseInt(persons, 10),
from_date: fromDateTime,
to_date: toDateTime,
description,
approved: false,
event_id: event?.id ? Number(event.id) : null
}]);
if (!error) {
// Save successful reservation data to localStorage
if (typeof window !== 'undefined') {
localStorage.setItem(STORAGE_KEY_NAME, person_name);
localStorage.setItem(STORAGE_KEY_PHONE, phone);
}
setAlert({
type: 'success',
message: 'Благодарим ви! Ще получите СМС за потвърждение.'
});
setReservationSuccess(true);
} else {
setAlert({ type: 'error', message: `Грешка: ${error.message}` });
}
} catch (error) {
setAlert({
type: 'error',
message: 'Възникна грешка. Моля опитайте отново.'
});
} finally {
setTimeout(() => setIsSubmitting(false), 3000);
}
};
const handleChange = (
e: React.ChangeEvent<
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
>
) => {
const { name, value } = e.target;
switch (name) {
case 'person_name':
setPersonName(value);
break;
case 'phone':
// Allow only numeric characters and remove spaces
const sanitizedValue = value.replace(/\D/g, '');
setPhone(sanitizedValue);
break;
case 'persons':
setPersons(value);
// Notify parent component if callback provided
if (onPersonsChange) {
onPersonsChange(value);
}
break;
case 'from_date':
setFromDate(value);
break;
case 'from_time':
setFromTime(value);
break;
case 'description':
setDescription(value);
break;
case 'countryCode':
setCountryCode(value);
break;
default:
break;
}
};
const handleNewReservation = () => {
setReservationSuccess(false);
setPersons('');
setDescription('');
setAlert(null);
// Keep the same date/time if an event was provided
if (!event) {
setFromDate(new Date().toISOString().split('T')[0]);
setFromTime('19:00');
}
// Reset persons count in parent component
if (onPersonsChange) {
onPersonsChange('');
}
// Note: we don't clear the name and phone now since we want to remember them
};
useEffect(() => {
if (event) {
setFromDate(new Date(event.from_date).toISOString().split('T')[0]);
setFromTime(new Date(event.from_date).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }));
}
}, [event]);
useEffect(() => {
if (alert && alert.type === 'error') {
const timer = setTimeout(() => setAlert(null), 5000);
return () => clearTimeout(timer);
}
}, [alert]);
return (
<div className="max-w-lg mx-auto">
{reservationSuccess ? (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
<ReservationDetails
person_name={person_name}
phone={phone}
persons={persons}
from_date={from_date}
from_time={from_time}
description={description}
isSuccess={true}
alert={alert?.message || ''}
onNewReservation={handleNewReservation}
/>
</motion.div>
) : (
<ReservationForm
handleSubmit={handleSubmit}
handleChange={handleChange}
person_name={person_name}
phone={phone}
countryCode={countryCode}
persons={persons}
from_date={from_date}
from_time={from_time}
description={description}
isSubmitting={isSubmitting}
/>
)}
</div>
);
});
ReserveForm.displayName = 'ReserveForm';
export default ReserveForm;