mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 12:31:12 -05:00
feat: Implement phases 5, 6, and 7
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
|
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
@@ -14,37 +15,7 @@ import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData';
|
|||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
||||||
|
|
||||||
const designerSchema = z.object({
|
type DesignerFormData = z.infer<typeof entitySchemas.designer>;
|
||||||
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<typeof designerSchema>;
|
|
||||||
|
|
||||||
// Input type for the form (before transformation)
|
// Input type for the form (before transformation)
|
||||||
type DesignerFormInput = {
|
type DesignerFormInput = {
|
||||||
@@ -89,7 +60,7 @@ export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormPr
|
|||||||
watch,
|
watch,
|
||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useForm<DesignerFormInput>({
|
} = useForm<DesignerFormInput>({
|
||||||
resolver: zodResolver(designerSchema),
|
resolver: zodResolver(entitySchemas.designer),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: initialData?.name || '',
|
name: initialData?.name || '',
|
||||||
slug: initialData?.slug || '',
|
slug: initialData?.slug || '',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
|
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
@@ -14,37 +15,7 @@ import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData';
|
|||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
||||||
|
|
||||||
const manufacturerSchema = z.object({
|
type ManufacturerFormData = z.infer<typeof entitySchemas.manufacturer>;
|
||||||
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<typeof manufacturerSchema>;
|
|
||||||
|
|
||||||
// Input type for the form (before transformation)
|
// Input type for the form (before transformation)
|
||||||
type ManufacturerFormInput = {
|
type ManufacturerFormInput = {
|
||||||
@@ -89,7 +60,7 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur
|
|||||||
watch,
|
watch,
|
||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useForm<ManufacturerFormInput>({
|
} = useForm<ManufacturerFormInput>({
|
||||||
resolver: zodResolver(manufacturerSchema),
|
resolver: zodResolver(entitySchemas.manufacturer),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: initialData?.name || '',
|
name: initialData?.name || '',
|
||||||
slug: initialData?.slug || '',
|
slug: initialData?.slug || '',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
|
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
@@ -14,37 +15,7 @@ import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData';
|
|||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
||||||
|
|
||||||
const operatorSchema = z.object({
|
type OperatorFormData = z.infer<typeof entitySchemas.operator>;
|
||||||
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<typeof operatorSchema>;
|
|
||||||
|
|
||||||
// Input type for the form (before transformation)
|
// Input type for the form (before transformation)
|
||||||
type OperatorFormInput = {
|
type OperatorFormInput = {
|
||||||
@@ -89,7 +60,7 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr
|
|||||||
watch,
|
watch,
|
||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useForm<OperatorFormInput>({
|
} = useForm<OperatorFormInput>({
|
||||||
resolver: zodResolver(operatorSchema),
|
resolver: zodResolver(entitySchemas.operator),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: initialData?.name || '',
|
name: initialData?.name || '',
|
||||||
slug: initialData?.slug || '',
|
slug: initialData?.slug || '',
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
watch,
|
watch,
|
||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useForm<ParkFormData>({
|
} = useForm<ParkFormData>({
|
||||||
resolver: zodResolver(parkSchema),
|
resolver: zodResolver(entitySchemas.park),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: initialData?.name || '',
|
name: initialData?.name || '',
|
||||||
slug: initialData?.slug || '',
|
slug: initialData?.slug || '',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useForm } from 'react-hook-form';
|
|||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
import { validateSubmissionHandler } from '@/lib/entityFormValidation';
|
import { validateSubmissionHandler } from '@/lib/entityFormValidation';
|
||||||
|
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
@@ -34,47 +35,7 @@ import {
|
|||||||
getHeightUnit
|
getHeightUnit
|
||||||
} from '@/lib/units';
|
} from '@/lib/units';
|
||||||
|
|
||||||
const rideSchema = z.object({
|
type RideFormData = z.infer<typeof entitySchemas.ride>;
|
||||||
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<typeof rideSchema>;
|
|
||||||
|
|
||||||
interface RideFormProps {
|
interface RideFormProps {
|
||||||
onSubmit: (data: RideFormData) => Promise<void>;
|
onSubmit: (data: RideFormData) => Promise<void>;
|
||||||
@@ -186,7 +147,7 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
|||||||
watch,
|
watch,
|
||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useForm<RideFormData>({
|
} = useForm<RideFormData>({
|
||||||
resolver: zodResolver(rideSchema),
|
resolver: zodResolver(entitySchemas.ride),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: initialData?.name || '',
|
name: initialData?.name || '',
|
||||||
slug: initialData?.slug || '',
|
slug: initialData?.slug || '',
|
||||||
|
|||||||
Reference in New Issue
Block a user