vkashti / components / ui / Reserve / index.tsx
index.tsx
Raw
'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;