diff --git a/src/components/admin/DesignerForm.tsx b/src/components/admin/DesignerForm.tsx index 9b6d5a14..5f55a4e5 100644 --- a/src/components/admin/DesignerForm.tsx +++ b/src/components/admin/DesignerForm.tsx @@ -1,6 +1,7 @@ import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; +import { entitySchemas } from '@/lib/entityValidationSchemas'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; @@ -14,37 +15,7 @@ import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; -const designerSchema = z.object({ - name: z.string().min(1, 'Name is required'), - slug: z.string().min(1, 'Slug is required'), - description: z.string().optional(), - person_type: z.enum(['company', 'individual', 'firm', 'organization']), - website_url: z.string().url().optional().or(z.literal('')), - founded_year: z.string() - .optional() - .transform(val => { - if (!val || val.trim() === '') return undefined; - const num = Number(val); - return isNaN(num) ? undefined : num; - }) - .refine(val => val === undefined || (typeof val === 'number' && val >= 1800 && val <= new Date().getFullYear()), { - message: "Founded year must be between 1800 and current year" - }), - headquarters_location: z.string().optional(), - images: z.object({ - uploaded: z.array(z.object({ - url: z.string(), - cloudflare_id: z.string().optional(), - file: z.any().optional(), - isLocal: z.boolean().optional(), - caption: z.string().optional() - })), - banner_assignment: z.number().nullable().optional(), - card_assignment: z.number().nullable().optional() - }).optional() -}); - -type DesignerFormData = z.infer; +type DesignerFormData = z.infer; // Input type for the form (before transformation) type DesignerFormInput = { @@ -89,7 +60,7 @@ export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormPr watch, formState: { errors } } = useForm({ - resolver: zodResolver(designerSchema), + resolver: zodResolver(entitySchemas.designer), defaultValues: { name: initialData?.name || '', slug: initialData?.slug || '', diff --git a/src/components/admin/ManufacturerForm.tsx b/src/components/admin/ManufacturerForm.tsx index 151b2626..09134c2e 100644 --- a/src/components/admin/ManufacturerForm.tsx +++ b/src/components/admin/ManufacturerForm.tsx @@ -1,6 +1,7 @@ import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; +import { entitySchemas } from '@/lib/entityValidationSchemas'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; @@ -14,37 +15,7 @@ import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; -const manufacturerSchema = z.object({ - name: z.string().min(1, 'Name is required'), - slug: z.string().min(1, 'Slug is required'), - description: z.string().optional(), - person_type: z.enum(['company', 'individual', 'firm', 'organization']), - website_url: z.string().url().optional().or(z.literal('')), - founded_year: z.string() - .optional() - .transform(val => { - if (!val || val.trim() === '') return undefined; - const num = Number(val); - return isNaN(num) ? undefined : num; - }) - .refine(val => val === undefined || (typeof val === 'number' && val >= 1800 && val <= new Date().getFullYear()), { - message: "Founded year must be between 1800 and current year" - }), - headquarters_location: z.string().optional(), - images: z.object({ - uploaded: z.array(z.object({ - url: z.string(), - cloudflare_id: z.string().optional(), - file: z.any().optional(), - isLocal: z.boolean().optional(), - caption: z.string().optional() - })), - banner_assignment: z.number().nullable().optional(), - card_assignment: z.number().nullable().optional() - }).optional() -}); - -type ManufacturerFormData = z.infer; +type ManufacturerFormData = z.infer; // Input type for the form (before transformation) type ManufacturerFormInput = { @@ -89,7 +60,7 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur watch, formState: { errors } } = useForm({ - resolver: zodResolver(manufacturerSchema), + resolver: zodResolver(entitySchemas.manufacturer), defaultValues: { name: initialData?.name || '', slug: initialData?.slug || '', diff --git a/src/components/admin/OperatorForm.tsx b/src/components/admin/OperatorForm.tsx index 73481f5f..fa2589e8 100644 --- a/src/components/admin/OperatorForm.tsx +++ b/src/components/admin/OperatorForm.tsx @@ -1,6 +1,7 @@ import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; +import { entitySchemas } from '@/lib/entityValidationSchemas'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; @@ -14,37 +15,7 @@ import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; -const operatorSchema = z.object({ - name: z.string().min(1, 'Name is required'), - slug: z.string().min(1, 'Slug is required'), - description: z.string().optional(), - person_type: z.enum(['company', 'individual', 'firm', 'organization']), - website_url: z.string().url().optional().or(z.literal('')), - founded_year: z.string() - .optional() - .transform(val => { - if (!val || val.trim() === '') return undefined; - const num = Number(val); - return isNaN(num) ? undefined : num; - }) - .refine(val => val === undefined || (typeof val === 'number' && val >= 1800 && val <= new Date().getFullYear()), { - message: "Founded year must be between 1800 and current year" - }), - headquarters_location: z.string().optional(), - images: z.object({ - uploaded: z.array(z.object({ - url: z.string(), - cloudflare_id: z.string().optional(), - file: z.any().optional(), - isLocal: z.boolean().optional(), - caption: z.string().optional() - })), - banner_assignment: z.number().nullable().optional(), - card_assignment: z.number().nullable().optional() - }).optional() -}); - -type OperatorFormData = z.infer; +type OperatorFormData = z.infer; // Input type for the form (before transformation) type OperatorFormInput = { @@ -89,7 +60,7 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr watch, formState: { errors } } = useForm({ - resolver: zodResolver(operatorSchema), + resolver: zodResolver(entitySchemas.operator), defaultValues: { name: initialData?.name || '', slug: initialData?.slug || '', diff --git a/src/components/admin/ParkForm.tsx b/src/components/admin/ParkForm.tsx index 6a03f84e..5e032a27 100644 --- a/src/components/admin/ParkForm.tsx +++ b/src/components/admin/ParkForm.tsx @@ -128,7 +128,7 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: watch, formState: { errors } } = useForm({ - resolver: zodResolver(parkSchema), + resolver: zodResolver(entitySchemas.park), defaultValues: { name: initialData?.name || '', slug: initialData?.slug || '', diff --git a/src/components/admin/RideForm.tsx b/src/components/admin/RideForm.tsx index 5b8fc437..75971990 100644 --- a/src/components/admin/RideForm.tsx +++ b/src/components/admin/RideForm.tsx @@ -3,6 +3,7 @@ import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; import { validateSubmissionHandler } from '@/lib/entityFormValidation'; +import { entitySchemas } from '@/lib/entityValidationSchemas'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -34,47 +35,7 @@ import { getHeightUnit } from '@/lib/units'; -const rideSchema = z.object({ - name: z.string().min(1, 'Ride name is required'), - slug: z.string().min(1, 'Slug is required'), - description: z.string().optional(), - category: z.string().min(1, 'Category is required'), - ride_sub_type: z.string().optional(), - status: z.string().min(1, 'Status is required'), - opening_date: z.string().optional(), - closing_date: z.string().optional(), - height_requirement: z.number().optional(), - age_requirement: z.number().optional(), - capacity_per_hour: z.number().optional(), - duration_seconds: z.number().optional(), - max_speed_kmh: z.number().optional(), - max_height_meters: z.number().optional(), - length_meters: z.number().optional(), - inversions: z.number().optional(), - // New roller coaster specific fields - coaster_type: z.string().optional(), - seating_type: z.string().optional(), - intensity_level: z.string().optional(), - drop_height_meters: z.number().optional(), - max_g_force: z.number().optional(), - // Manufacturer and model - manufacturer_id: z.string().uuid().optional(), - ride_model_id: z.string().uuid().optional(), - // Images - images: z.object({ - uploaded: z.array(z.object({ - url: z.string(), - cloudflare_id: z.string().optional(), - file: z.any().optional(), - isLocal: z.boolean().optional(), - caption: z.string().optional(), - })), - banner_assignment: z.number().nullable().optional(), - card_assignment: z.number().nullable().optional(), - }).optional() -}); - -type RideFormData = z.infer; +type RideFormData = z.infer; interface RideFormProps { onSubmit: (data: RideFormData) => Promise; @@ -186,7 +147,7 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: watch, formState: { errors } } = useForm({ - resolver: zodResolver(rideSchema), + resolver: zodResolver(entitySchemas.ride), defaultValues: { name: initialData?.name || '', slug: initialData?.slug || '',