'use client'; import React from 'react'; import LabeledInput from '@/components/custom-ui/labeled-input'; import { Button } from '@/components/ui/button'; import Logo from '@/components/misc/logo'; import TimePicker from '@/components/time-picker'; import { Label } from '@/components/ui/label'; import { usePostApiEvent, useGetApiEventEventID, usePatchApiEventEventID, } from '@/generated/api/event/event'; import { useRouter } from 'next/navigation'; import { toast } from 'sonner'; import { ToastInner } from '@/components/misc/toast-inner'; import { UserSearchInput } from '@/components/misc/user-search'; import ParticipantListEntry from '../custom-ui/participant-list-entry'; import { useSearchParams } from 'next/navigation'; import zod from 'zod/v4'; import { PublicUserSchema } from '@/app/api/user/validation'; import Calendar from '@/components/calendar'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '../ui/dialog'; import { useGetApiUserMe } from '@/generated/api/user/user'; type User = zod.output; interface EventFormProps { type: 'create' | 'edit'; eventId?: string; } const EventForm: React.FC = (props) => { // Runtime validation if (props.type === 'edit' && !props.eventId) { throw new Error( 'Error [event-form]: eventId must be provided when type is "edit".', ); } const searchParams = useSearchParams(); const startFromUrl = searchParams.get('start'); const endFromUrl = searchParams.get('end'); const { mutateAsync: createEvent, status, isSuccess, error, } = usePostApiEvent(); const { data: eventData } = useGetApiEventEventID(props.eventId!, { query: { enabled: props.type === 'edit' }, }); const { data, isLoading, isError } = useGetApiUserMe(); const patchEvent = usePatchApiEventEventID(); const router = useRouter(); // State for date and time fields const [startDate, setStartDate] = React.useState(undefined); const [startTime, setStartTime] = React.useState(''); const [endDate, setEndDate] = React.useState(undefined); const [endTime, setEndTime] = React.useState(''); // State for participants const [selectedParticipants, setSelectedParticipants] = React.useState< User[] >([]); // State for form fields const [title, setTitle] = React.useState(''); const [location, setLocation] = React.useState(''); const [description, setDescription] = React.useState(''); const [calendarOpen, setCalendarOpen] = React.useState(false); // Update state when event data loads React.useEffect(() => { if (props.type === 'edit' && eventData?.data?.event) { setTitle(eventData?.data?.event.title || ''); // Parse start_time and end_time if (eventData?.data?.event.start_time) { const start = new Date(eventData?.data?.event.start_time); setStartDate(start); setStartTime(start.toTimeString().slice(0, 5)); // "HH:mm" } if (eventData?.data?.event.end_time) { const end = new Date(eventData?.data?.event.end_time); setEndDate(end); setEndTime(end.toTimeString().slice(0, 5)); // "HH:mm" } setLocation(eventData?.data?.event.location || ''); setDescription(eventData?.data?.event.description || ''); setSelectedParticipants( eventData?.data?.event.participants?.map((u) => u.user) || [], ); } else if (props.type === 'create' && startFromUrl && endFromUrl) { // If creating a new event with URL params, set title and dates setTitle(''); const start = new Date(startFromUrl); setStartDate(start); setStartTime(start.toTimeString().slice(0, 5)); // "HH:mm" const end = new Date(endFromUrl); setEndDate(end); setEndTime(end.toTimeString().slice(0, 5)); // "HH:mm" } }, [eventData?.data?.event, props.type, startFromUrl, endFromUrl]); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); const formData = new FormData(e.currentTarget); function combine(date?: Date, time?: string) { if (!date || !time) return undefined; const [hours, minutes] = time.split(':'); const d = new Date(date); d.setHours(Number(hours), Number(minutes), 0, 0); return d; } const start = combine(startDate, startTime); const end = combine(endDate, endTime); //validate form data if (!formData.get('eventName')) { alert('Event name is required.'); return; } if (!start || !end) { alert('Please provide both start and end date/time.'); return; } else if (start >= end) { alert('End time must be after start time.'); return; } const data = { title: formData.get('eventName') as string, description: formData.get('eventDescription') as string, start_time: start.toISOString(), end_time: end.toISOString(), location: formData.get('eventLocation') as string, created_at: formData.get('createdAt') as string, updated_at: formData.get('updatedAt') as string, organiser: formData.get('organiser') as string, participants: selectedParticipants.map((u) => u.id), }; let eventID: string | undefined; if (props.type === 'edit' && props.eventId) { const mutationResult = await patchEvent.mutateAsync({ eventID: props.eventId, data: { title: data.title, description: data.description, start_time: data.start_time, end_time: data.end_time, location: data.location, participants: data.participants, }, }); eventID = mutationResult.data.event.id; console.log('Updating event'); } else { console.log('Creating event'); const mutationResult = await createEvent({ data }); eventID = mutationResult.data.event.id; } toast.custom((t) => ( router.push(`/events/${eventID}`)} variant='success' buttonText='show' /> )); router.back(); } // Use DB values for created_at/updated_at in edit mode const createdAtValue = props.type === 'edit' && eventData?.data?.event?.created_at ? eventData.data.event.created_at : new Date().toISOString(); const updatedAtValue = props.type === 'edit' && eventData?.data?.event?.updated_at ? eventData.data.event.updated_at : new Date().toISOString(); // Format date for display const createdAtDisplay = new Date(createdAtValue).toLocaleDateString(); const updatedAtDisplay = new Date(updatedAtValue).toLocaleDateString(); const [isClient, setIsClient] = React.useState(false); React.useEffect(() => { setIsClient(true); }, []); if (props.type === 'edit' && isLoading) return
Loading...
; if (props.type === 'edit' && isError) return
Error loading event.
; return (
setTitle(e.target.value)} data-cy='event-name-input' />
setLocation(e.target.value)} data-cy='event-location-input' />

{updatedAtDisplay}

{!isClient || isLoading ? 'Loading...' : data?.data.user.name || 'Unknown User'}

setDescription(e.target.value)} data-cy='event-description-input' >
{ setSelectedParticipants((current) => current.find((u) => u.id === user.id) ? current : [...current, user], ); }} removeUserAction={(user) => { setSelectedParticipants((current) => current.filter((u) => u.id !== user.id), ); }} />
{selectedParticipants.map((user) => ( ))}
{isSuccess &&

Event created!

} {error &&

Error: {error.message}

}
Calendar Calendar for selected participants u.id)} additionalEvents={[ { id: 'temp-event', title: title || 'New Event', start: startDate ? new Date(startDate) : new Date(), end: endDate ? new Date(endDate) : new Date(), type: 'event', userId: 'create-event', colorOverride: '#ff9800', }, ]} height='600px' />
); }; export default EventForm;