diff --git a/src/components/admin/ParkForm.tsx b/src/components/admin/ParkForm.tsx new file mode 100644 index 00000000..9bf62eb3 --- /dev/null +++ b/src/components/admin/ParkForm.tsx @@ -0,0 +1,338 @@ +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import * as z from 'zod'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Textarea } from '@/components/ui/textarea'; +import { Label } from '@/components/ui/label'; +import { PhotoUpload } from '@/components/upload/PhotoUpload'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { toast } from '@/hooks/use-toast'; +import { MapPin, Save, X } from 'lucide-react'; + +const parkSchema = z.object({ + name: z.string().min(1, 'Park name is required'), + slug: z.string().min(1, 'Slug is required'), + description: z.string().optional(), + park_type: z.string().min(1, 'Park type is required'), + status: z.string().min(1, 'Status is required'), + opening_date: z.string().optional(), + closing_date: z.string().optional(), + website_url: z.string().url().optional().or(z.literal('')), + phone: z.string().optional(), + email: z.string().email().optional().or(z.literal('')) +}); + +type ParkFormData = z.infer; + +interface ParkFormProps { + onSubmit: (data: ParkFormData & { banner_image_url?: string; card_image_url?: string }) => Promise; + onCancel?: () => void; + initialData?: Partial; + isEditing?: boolean; +} + +const parkTypes = [ + 'Theme Park', + 'Amusement Park', + 'Water Park', + 'Family Entertainment Center', + 'Adventure Park', + 'Safari Park', + 'Carnival', + 'Fair' +]; + +const statusOptions = [ + 'Operating', + 'Seasonal', + 'Closed Temporarily', + 'Closed Permanently', + 'Under Construction', + 'Planned' +]; + +export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: ParkFormProps) { + const [submitting, setSubmitting] = useState(false); + const [bannerImage, setBannerImage] = useState(initialData?.banner_image_url || ''); + const [cardImage, setCardImage] = useState(initialData?.card_image_url || ''); + + const { + register, + handleSubmit, + setValue, + watch, + formState: { errors } + } = useForm({ + resolver: zodResolver(parkSchema), + defaultValues: { + name: initialData?.name || '', + slug: initialData?.slug || '', + description: initialData?.description || '', + park_type: initialData?.park_type || '', + status: initialData?.status || 'Operating', + opening_date: initialData?.opening_date || '', + closing_date: initialData?.closing_date || '', + website_url: initialData?.website_url || '', + phone: initialData?.phone || '', + email: initialData?.email || '' + } + }); + + const generateSlug = (name: string) => { + return name + .toLowerCase() + .replace(/[^a-z0-9\s-]/g, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .trim(); + }; + + const handleNameChange = (e: React.ChangeEvent) => { + const name = e.target.value; + const slug = generateSlug(name); + setValue('slug', slug); + }; + + const handleFormSubmit = async (data: ParkFormData) => { + setSubmitting(true); + try { + await onSubmit({ + ...data, + banner_image_url: bannerImage || undefined, + card_image_url: cardImage || undefined + }); + + toast({ + title: isEditing ? "Park Updated" : "Park Created", + description: isEditing + ? "The park information has been updated successfully." + : "The new park has been created successfully." + }); + } catch (error: any) { + toast({ + title: "Error", + description: error.message || "Failed to save park information.", + variant: "destructive" + }); + } finally { + setSubmitting(false); + } + }; + + return ( + + + + + {isEditing ? 'Edit Park' : 'Create New Park'} + + + +
+ {/* Basic Information */} +
+
+ + { + register('name').onChange(e); + handleNameChange(e); + }} + placeholder="Enter park name" + /> + {errors.name && ( +

{errors.name.message}

+ )} +
+ +
+ + + {errors.slug && ( +

{errors.slug.message}

+ )} +
+
+ + {/* Description */} +
+ +