mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 06:11:11 -05:00
Add coaster-specific fields
This commit is contained in:
@@ -20,6 +20,7 @@ import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Combobox } from '@/components/ui/combobox';
|
||||
import { SlugField } from '@/components/ui/slug-field';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { toast } from '@/hooks/use-toast';
|
||||
import { handleError } from '@/lib/errorHandler';
|
||||
import { Plus, Zap, Save, X } from 'lucide-react';
|
||||
@@ -97,6 +98,39 @@ const intensityLevels = [
|
||||
'extreme'
|
||||
];
|
||||
|
||||
const TRACK_MATERIALS = [
|
||||
{ value: 'wood', label: 'Wood' },
|
||||
{ value: 'steel', label: 'Steel' },
|
||||
{ value: 'hybrid', label: 'Hybrid' },
|
||||
{ value: 'aluminum', label: 'Aluminum' },
|
||||
{ value: 'composite', label: 'Composite' },
|
||||
{ value: 'other', label: 'Other' },
|
||||
];
|
||||
|
||||
const SUPPORT_MATERIALS = [
|
||||
{ value: 'steel', label: 'Steel' },
|
||||
{ value: 'wood', label: 'Wood' },
|
||||
{ value: 'concrete', label: 'Concrete' },
|
||||
{ value: 'aluminum', label: 'Aluminum' },
|
||||
{ value: 'composite', label: 'Composite' },
|
||||
{ value: 'other', label: 'Other' },
|
||||
];
|
||||
|
||||
const PROPULSION_METHODS = [
|
||||
{ value: 'chain_lift', label: 'Chain Lift' },
|
||||
{ value: 'cable_lift', label: 'Cable Lift' },
|
||||
{ value: 'friction_wheel_lift', label: 'Friction Wheel Lift' },
|
||||
{ value: 'lsm_launch', label: 'LSM Launch' },
|
||||
{ value: 'lim_launch', label: 'LIM Launch' },
|
||||
{ value: 'hydraulic_launch', label: 'Hydraulic Launch' },
|
||||
{ value: 'compressed_air_launch', label: 'Compressed Air Launch' },
|
||||
{ value: 'flywheel_launch', label: 'Flywheel Launch' },
|
||||
{ value: 'gravity', label: 'Gravity' },
|
||||
{ value: 'tire_drive', label: 'Tire Drive' },
|
||||
{ value: 'water_propulsion', label: 'Water Propulsion' },
|
||||
{ value: 'other', label: 'Other' },
|
||||
];
|
||||
|
||||
// Status value mappings between display (form) and database values
|
||||
const STATUS_DISPLAY_TO_DB: Record<string, string> = {
|
||||
'Operating': 'operating',
|
||||
@@ -645,24 +679,82 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Track Material</Label>
|
||||
<Select
|
||||
onValueChange={(value) => setValue('track_material', value === '' ? undefined : value as 'wood' | 'steel' | 'hybrid' | 'aluminum' | 'other')}
|
||||
defaultValue={initialData?.track_material || ''}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select track material" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">None</SelectItem>
|
||||
<SelectItem value="wood">Wood</SelectItem>
|
||||
<SelectItem value="steel">Steel</SelectItem>
|
||||
<SelectItem value="hybrid">Hybrid (Wood/Steel)</SelectItem>
|
||||
<SelectItem value="aluminum">Aluminum</SelectItem>
|
||||
<SelectItem value="other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="space-y-3">
|
||||
<Label>Track Material(s)</Label>
|
||||
<p className="text-sm text-muted-foreground">Select all materials used in the track</p>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{TRACK_MATERIALS.map((material) => (
|
||||
<div key={material.value} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={`track_${material.value}`}
|
||||
checked={watch('track_material')?.includes(material.value) || false}
|
||||
onCheckedChange={(checked) => {
|
||||
const current = watch('track_material') || [];
|
||||
if (checked) {
|
||||
setValue('track_material', [...current, material.value]);
|
||||
} else {
|
||||
setValue('track_material', current.filter((v) => v !== material.value));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor={`track_${material.value}`} className="font-normal cursor-pointer">
|
||||
{material.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label>Support Material(s)</Label>
|
||||
<p className="text-sm text-muted-foreground">Select all materials used in the supports</p>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{SUPPORT_MATERIALS.map((material) => (
|
||||
<div key={material.value} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={`support_${material.value}`}
|
||||
checked={watch('support_material')?.includes(material.value) || false}
|
||||
onCheckedChange={(checked) => {
|
||||
const current = watch('support_material') || [];
|
||||
if (checked) {
|
||||
setValue('support_material', [...current, material.value]);
|
||||
} else {
|
||||
setValue('support_material', current.filter((v) => v !== material.value));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor={`support_${material.value}`} className="font-normal cursor-pointer">
|
||||
{material.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label>Propulsion Method(s)</Label>
|
||||
<p className="text-sm text-muted-foreground">Select all propulsion methods used</p>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{PROPULSION_METHODS.map((method) => (
|
||||
<div key={method.value} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={`propulsion_${method.value}`}
|
||||
checked={watch('propulsion_method')?.includes(method.value) || false}
|
||||
onCheckedChange={(checked) => {
|
||||
const current = watch('propulsion_method') || [];
|
||||
if (checked) {
|
||||
setValue('propulsion_method', [...current, method.value]);
|
||||
} else {
|
||||
setValue('propulsion_method', current.filter((v) => v !== method.value));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor={`propulsion_${method.value}`} className="font-normal cursor-pointer">
|
||||
{method.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3081,13 +3081,15 @@ export type Database = {
|
||||
opening_date: string | null
|
||||
opening_date_precision: string | null
|
||||
park_id: string | null
|
||||
propulsion_method: string[] | null
|
||||
ride_model_id: string | null
|
||||
ride_sub_type: string | null
|
||||
seating_type: string | null
|
||||
slug: string
|
||||
status: string
|
||||
submission_id: string
|
||||
track_material: string | null
|
||||
support_material: string[] | null
|
||||
track_material: string[] | null
|
||||
updated_at: string
|
||||
}
|
||||
Insert: {
|
||||
@@ -3120,13 +3122,15 @@ export type Database = {
|
||||
opening_date?: string | null
|
||||
opening_date_precision?: string | null
|
||||
park_id?: string | null
|
||||
propulsion_method?: string[] | null
|
||||
ride_model_id?: string | null
|
||||
ride_sub_type?: string | null
|
||||
seating_type?: string | null
|
||||
slug: string
|
||||
status?: string
|
||||
submission_id: string
|
||||
track_material?: string | null
|
||||
support_material?: string[] | null
|
||||
track_material?: string[] | null
|
||||
updated_at?: string
|
||||
}
|
||||
Update: {
|
||||
@@ -3159,13 +3163,15 @@ export type Database = {
|
||||
opening_date?: string | null
|
||||
opening_date_precision?: string | null
|
||||
park_id?: string | null
|
||||
propulsion_method?: string[] | null
|
||||
ride_model_id?: string | null
|
||||
ride_sub_type?: string | null
|
||||
seating_type?: string | null
|
||||
slug?: string
|
||||
status?: string
|
||||
submission_id?: string
|
||||
track_material?: string | null
|
||||
support_material?: string[] | null
|
||||
track_material?: string[] | null
|
||||
updated_at?: string
|
||||
}
|
||||
Relationships: [
|
||||
@@ -3304,12 +3310,14 @@ export type Database = {
|
||||
opening_date: string | null
|
||||
opening_date_precision: string | null
|
||||
park_id: string | null
|
||||
propulsion_method: string[] | null
|
||||
ride_id: string
|
||||
ride_model_id: string | null
|
||||
slug: string
|
||||
status: string
|
||||
submission_id: string | null
|
||||
track_material: string | null
|
||||
support_material: string[] | null
|
||||
track_material: string[] | null
|
||||
version_id: string
|
||||
version_number: number
|
||||
}
|
||||
@@ -3344,12 +3352,14 @@ export type Database = {
|
||||
opening_date?: string | null
|
||||
opening_date_precision?: string | null
|
||||
park_id?: string | null
|
||||
propulsion_method?: string[] | null
|
||||
ride_id: string
|
||||
ride_model_id?: string | null
|
||||
slug: string
|
||||
status: string
|
||||
submission_id?: string | null
|
||||
track_material?: string | null
|
||||
support_material?: string[] | null
|
||||
track_material?: string[] | null
|
||||
version_id?: string
|
||||
version_number: number
|
||||
}
|
||||
@@ -3384,12 +3394,14 @@ export type Database = {
|
||||
opening_date?: string | null
|
||||
opening_date_precision?: string | null
|
||||
park_id?: string | null
|
||||
propulsion_method?: string[] | null
|
||||
ride_id?: string
|
||||
ride_model_id?: string | null
|
||||
slug?: string
|
||||
status?: string
|
||||
submission_id?: string | null
|
||||
track_material?: string | null
|
||||
support_material?: string[] | null
|
||||
track_material?: string[] | null
|
||||
version_id?: string
|
||||
version_number?: number
|
||||
}
|
||||
@@ -3527,13 +3539,15 @@ export type Database = {
|
||||
opening_date: string | null
|
||||
opening_date_precision: string | null
|
||||
park_id: string
|
||||
propulsion_method: string[] | null
|
||||
review_count: number | null
|
||||
ride_model_id: string | null
|
||||
ride_sub_type: string | null
|
||||
seating_type: string | null
|
||||
slug: string
|
||||
status: string
|
||||
track_material: string | null
|
||||
support_material: string[] | null
|
||||
track_material: string[] | null
|
||||
updated_at: string
|
||||
view_count_30d: number | null
|
||||
view_count_7d: number | null
|
||||
@@ -3570,13 +3584,15 @@ export type Database = {
|
||||
opening_date?: string | null
|
||||
opening_date_precision?: string | null
|
||||
park_id: string
|
||||
propulsion_method?: string[] | null
|
||||
review_count?: number | null
|
||||
ride_model_id?: string | null
|
||||
ride_sub_type?: string | null
|
||||
seating_type?: string | null
|
||||
slug: string
|
||||
status?: string
|
||||
track_material?: string | null
|
||||
support_material?: string[] | null
|
||||
track_material?: string[] | null
|
||||
updated_at?: string
|
||||
view_count_30d?: number | null
|
||||
view_count_7d?: number | null
|
||||
@@ -3613,13 +3629,15 @@ export type Database = {
|
||||
opening_date?: string | null
|
||||
opening_date_precision?: string | null
|
||||
park_id?: string
|
||||
propulsion_method?: string[] | null
|
||||
review_count?: number | null
|
||||
ride_model_id?: string | null
|
||||
ride_sub_type?: string | null
|
||||
seating_type?: string | null
|
||||
slug?: string
|
||||
status?: string
|
||||
track_material?: string | null
|
||||
support_material?: string[] | null
|
||||
track_material?: string[] | null
|
||||
updated_at?: string
|
||||
view_count_30d?: number | null
|
||||
view_count_7d?: number | null
|
||||
|
||||
@@ -76,6 +76,9 @@ export function transformRideData(submissionData: RideSubmissionData): RideInser
|
||||
coaster_type: submissionData.coaster_type || null,
|
||||
seating_type: submissionData.seating_type || null,
|
||||
intensity_level: submissionData.intensity_level || null,
|
||||
track_material: submissionData.track_material || null,
|
||||
support_material: submissionData.support_material || null,
|
||||
propulsion_method: submissionData.propulsion_method || null,
|
||||
banner_image_url: submissionData.banner_image_url || null,
|
||||
banner_image_id: submissionData.banner_image_id || null,
|
||||
card_image_url: submissionData.card_image_url || null,
|
||||
|
||||
@@ -133,7 +133,9 @@ export const rideValidationSchema = z.object({
|
||||
coaster_type: z.string().optional(),
|
||||
seating_type: z.string().optional(),
|
||||
intensity_level: z.string().optional(),
|
||||
track_material: z.enum(['wood', 'steel', 'hybrid', 'aluminum', 'other']).optional(),
|
||||
track_material: z.array(z.string()).optional().nullable(),
|
||||
support_material: z.array(z.string()).optional().nullable(),
|
||||
propulsion_method: z.array(z.string()).optional().nullable(),
|
||||
// Water ride specific fields
|
||||
water_depth_cm: z.preprocess(
|
||||
(val) => val === '' || val === null || val === undefined ? undefined : Number(val),
|
||||
|
||||
@@ -182,7 +182,8 @@ export default function Rides() {
|
||||
|
||||
// Track material filter
|
||||
if (filters.trackMaterials.length > 0) {
|
||||
if (!ride.track_material || !filters.trackMaterials.includes(ride.track_material)) {
|
||||
if (!ride.track_material || ride.track_material.length === 0 ||
|
||||
!ride.track_material.some(material => filters.trackMaterials.includes(material))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,9 @@ export interface Ride {
|
||||
coaster_type?: string;
|
||||
seating_type?: string;
|
||||
intensity_level?: string;
|
||||
track_material?: string;
|
||||
track_material?: string[];
|
||||
support_material?: string[];
|
||||
propulsion_method?: string[];
|
||||
drop_height_meters?: number;
|
||||
max_g_force?: number;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,9 @@ export interface RideSubmissionData {
|
||||
coaster_type?: string | null;
|
||||
seating_type?: string | null;
|
||||
intensity_level?: string | null;
|
||||
track_material?: string | null;
|
||||
track_material?: string[] | null;
|
||||
support_material?: string[] | null;
|
||||
propulsion_method?: string[] | null;
|
||||
// Water ride specific
|
||||
water_depth_cm?: number | null;
|
||||
splash_height_meters?: number | null;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
-- Add coaster-specific multi-select fields for track material, support material, and propulsion method
|
||||
|
||||
-- Update rides table
|
||||
ALTER TABLE rides
|
||||
ALTER COLUMN track_material TYPE TEXT[] USING
|
||||
CASE
|
||||
WHEN track_material IS NULL THEN NULL
|
||||
ELSE ARRAY[track_material]
|
||||
END,
|
||||
ADD COLUMN IF NOT EXISTS support_material TEXT[],
|
||||
ADD COLUMN IF NOT EXISTS propulsion_method TEXT[];
|
||||
|
||||
-- Update ride_versions table
|
||||
ALTER TABLE ride_versions
|
||||
ALTER COLUMN track_material TYPE TEXT[] USING
|
||||
CASE
|
||||
WHEN track_material IS NULL THEN NULL
|
||||
ELSE ARRAY[track_material]
|
||||
END,
|
||||
ADD COLUMN IF NOT EXISTS support_material TEXT[],
|
||||
ADD COLUMN IF NOT EXISTS propulsion_method TEXT[];
|
||||
|
||||
-- Update ride_submissions table
|
||||
ALTER TABLE ride_submissions
|
||||
ALTER COLUMN track_material TYPE TEXT[] USING
|
||||
CASE
|
||||
WHEN track_material IS NULL THEN NULL
|
||||
ELSE ARRAY[track_material]
|
||||
END,
|
||||
ADD COLUMN IF NOT EXISTS support_material TEXT[],
|
||||
ADD COLUMN IF NOT EXISTS propulsion_method TEXT[];
|
||||
|
||||
-- Create indexes for efficient filtering
|
||||
CREATE INDEX IF NOT EXISTS idx_rides_track_material ON rides USING GIN(track_material);
|
||||
CREATE INDEX IF NOT EXISTS idx_rides_support_material ON rides USING GIN(support_material);
|
||||
CREATE INDEX IF NOT EXISTS idx_rides_propulsion_method ON rides USING GIN(propulsion_method);
|
||||
|
||||
-- Add comments for documentation
|
||||
COMMENT ON COLUMN rides.track_material IS 'Array of track materials used in the ride (e.g., wood, steel, hybrid)';
|
||||
COMMENT ON COLUMN rides.support_material IS 'Array of support materials used in the ride (e.g., steel, wood, concrete)';
|
||||
COMMENT ON COLUMN rides.propulsion_method IS 'Array of propulsion methods used in the ride (e.g., chain_lift, lsm_launch, hydraulic_launch)';
|
||||
Reference in New Issue
Block a user