import { useState, useRef, useEffect } from 'react'; import L from 'leaflet'; import 'leaflet/dist/leaflet.css'; import { isWithinIreland, IRELAND_BOUNDS } from '@/utils/coordinates'; import type { Coordinates } from '@/types'; // Fix for default marker icons in Leaflet with Vite import markerIcon2x from 'leaflet/dist/images/marker-icon-2x.png'; import markerIcon from 'leaflet/dist/images/marker-icon.png'; import markerShadow from 'leaflet/dist/images/marker-shadow.png'; delete (L.Icon.Default.prototype as any)._getIconUrl; L.Icon.Default.mergeOptions({ iconUrl: markerIcon, iconRetinaUrl: markerIcon2x, shadowUrl: markerShadow, }); interface EventFormData { title: string; description: string; latitude: number; longitude: number; locationName: string; scheduledDate: string; scheduledTime: string; duration: number; equipmentProvided: boolean; requiredItems: string[]; } interface EventFormProps { onSubmit: (data: EventFormData) => void; onCancel: () => void; initialData?: Partial; isSubmitting?: boolean; } export function EventForm({ onSubmit, onCancel, initialData, isSubmitting = false }: EventFormProps) { const [formData, setFormData] = useState({ title: initialData?.title || '', description: initialData?.description || '', latitude: initialData?.latitude || 0, longitude: initialData?.longitude || 0, locationName: initialData?.locationName || '', scheduledDate: initialData?.scheduledDate || '', scheduledTime: initialData?.scheduledTime || '', duration: initialData?.duration || 120, equipmentProvided: initialData?.equipmentProvided || false, requiredItems: initialData?.requiredItems || [], }); const [newItem, setNewItem] = useState(''); const [selectedLocation, setSelectedLocation] = useState( initialData?.latitude && initialData?.longitude ? { latitude: initialData.latitude, longitude: initialData.longitude } : null ); const [errors, setErrors] = useState>>({}); const [locationError, setLocationError] = useState(null); const mapRef = useRef(null); const mapInstanceRef = useRef(null); const markerRef = useRef(null); // Initialize map useEffect(() => { if (!mapRef.current || mapInstanceRef.current) return; // Default center: Dublin, Ireland const defaultCenter: [number, number] = [53.3498, -6.2603]; const initialCenter: [number, number] = selectedLocation ? [selectedLocation.latitude, selectedLocation.longitude] : defaultCenter; const map = L.map(mapRef.current).setView(initialCenter, selectedLocation ? 13 : 7); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors', maxZoom: 19, }).addTo(map); // Add Ireland boundary rectangle const irelandBounds = L.latLngBounds( [IRELAND_BOUNDS.minLat, IRELAND_BOUNDS.minLng], [IRELAND_BOUNDS.maxLat, IRELAND_BOUNDS.maxLng] ); L.rectangle(irelandBounds, { color: '#3B82F6', weight: 2, fillOpacity: 0.05, dashArray: '5, 5', }).addTo(map); // Add marker if location is already selected if (selectedLocation) { markerRef.current = L.marker([selectedLocation.latitude, selectedLocation.longitude], { draggable: true, }).addTo(map); markerRef.current.on('dragend', () => { if (markerRef.current) { const position = markerRef.current.getLatLng(); handleMapLocationSelect(position.lat, position.lng); } }); } // Handle map clicks map.on('click', (e: L.LeafletMouseEvent) => { handleMapLocationSelect(e.latlng.lat, e.latlng.lng); }); mapInstanceRef.current = map; return () => { map.remove(); mapInstanceRef.current = null; markerRef.current = null; }; }, []); // Update marker when location changes useEffect(() => { if (!mapInstanceRef.current || !selectedLocation) return; const map = mapInstanceRef.current; if (markerRef.current) { map.removeLayer(markerRef.current); } markerRef.current = L.marker([selectedLocation.latitude, selectedLocation.longitude], { draggable: true, }).addTo(map); markerRef.current.on('dragend', () => { if (markerRef.current) { const position = markerRef.current.getLatLng(); handleMapLocationSelect(position.lat, position.lng); } }); map.setView([selectedLocation.latitude, selectedLocation.longitude], 13); }, [selectedLocation]); const handleMapLocationSelect = (lat: number, lng: number) => { if (!isWithinIreland(lat, lng)) { setLocationError('Selected location is outside Ireland. Please select a location within Ireland.'); return; } const coords = { latitude: lat, longitude: lng }; setSelectedLocation(coords); setLocationError(null); setFormData(prev => ({ ...prev, latitude: lat, longitude: lng, })); }; const handleInputChange = (field: keyof EventFormData, value: string | number) => { setFormData(prev => ({ ...prev, [field]: value })); if (errors[field]) { setErrors(prev => ({ ...prev, [field]: undefined })); } }; const validateForm = (): boolean => { const newErrors: Partial> = {}; if (!formData.title.trim()) { newErrors.title = 'Event title is required'; } else if (formData.title.length < 3) { newErrors.title = 'Title must be at least 3 characters'; } if (!formData.description.trim()) { newErrors.description = 'Event description is required'; } else if (formData.description.length < 10) { newErrors.description = 'Description must be at least 10 characters'; } if (!formData.locationName.trim()) { newErrors.locationName = 'Location name is required'; } if (!selectedLocation) { setLocationError('Please select a location on the map'); } if (!formData.scheduledDate) { newErrors.scheduledDate = 'Event date is required'; } else { const selectedDate = new Date(formData.scheduledDate); const today = new Date(); today.setHours(0, 0, 0, 0); if (selectedDate < today) { newErrors.scheduledDate = 'Event date must be in the future'; } } if (!formData.scheduledTime) { newErrors.scheduledTime = 'Event time is required'; } if (formData.duration < 30) { newErrors.duration = 'Duration must be at least 30 minutes'; } else if (formData.duration > 480) { newErrors.duration = 'Duration cannot exceed 8 hours'; } setErrors(newErrors); return Object.keys(newErrors).length === 0 && selectedLocation !== null; }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!validateForm()) { return; } onSubmit(formData); }; // Get minimum date (today) const getMinDate = () => { const today = new Date(); return today.toISOString().split('T')[0]; }; return (
{/* Title */}
handleInputChange('title', e.target.value)} className={`w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${ errors.title ? 'border-red-500' : 'border-gray-300' }`} placeholder="e.g., Sandymount Beach Cleanup" /> {errors.title &&

{errors.title}

}
{/* Description */}