Compare commits

...

4 Commits

Author SHA1 Message Date
gpt-engineer-app[bot]
bf860de3c4 Refactor: Restrict coaster materials to steel and wood 2025-10-30 13:15:20 +00:00
gpt-engineer-app[bot]
dd420a1684 Fix: Add missing ride fields to database 2025-10-30 13:10:43 +00:00
gpt-engineer-app[bot]
8540d227b2 feat: Implement bot detection plan 2025-10-30 13:05:32 +00:00
gpt-engineer-app[bot]
8fd8d2e843 Refactor: Implement comprehensive ride field integration 2025-10-30 12:59:23 +00:00
9 changed files with 682 additions and 18 deletions

View File

@@ -99,21 +99,13 @@ const intensityLevels = [
];
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' },
{ value: 'wood', label: 'Wood' },
];
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 = [

View File

@@ -27,6 +27,8 @@ export interface RideFilterState {
seatingTypes: string[];
intensityLevels: string[];
trackMaterials: string[];
supportMaterials: string[];
propulsionMethods: string[];
minSpeed: number;
maxSpeed: number;
minHeight: number;
@@ -54,6 +56,8 @@ export const defaultRideFilters: RideFilterState = {
seatingTypes: [],
intensityLevels: [],
trackMaterials: [],
supportMaterials: [],
propulsionMethods: [],
minSpeed: 0,
maxSpeed: 200,
minHeight: 0,
@@ -199,7 +203,22 @@ export function RideFilters({ filters, onFiltersChange, rides }: RideFiltersProp
const trackMaterialOptions: MultiSelectOption[] = [
{ label: 'Steel', value: 'steel' },
{ label: 'Wood', value: 'wood' },
{ label: 'Hybrid', value: 'hybrid' },
];
const supportMaterialOptions: MultiSelectOption[] = [
{ label: 'Steel', value: 'steel' },
{ label: 'Wood', value: 'wood' },
];
const propulsionMethodOptions: MultiSelectOption[] = [
{ label: 'Chain Lift', value: 'chain_lift' },
{ label: 'Cable Lift', value: 'cable_lift' },
{ label: 'Launched (LIM/LSM)', value: 'launched_lim_lsm' },
{ label: 'Hydraulic Launch', value: 'hydraulic_launch' },
{ label: 'Compressed Air', value: 'compressed_air' },
{ label: 'Flywheel', value: 'flywheel' },
{ label: 'Gravity', value: 'gravity' },
{ label: 'Other', value: 'other' },
];
const resetFilters = () => {
@@ -339,6 +358,20 @@ export function RideFilters({ filters, onFiltersChange, rides }: RideFiltersProp
onChange={(value) => onFiltersChange({ ...filters, trackMaterials: value })}
placeholder="Select material"
/>
<FilterMultiSelectCombobox
label="Support Material"
options={supportMaterialOptions}
value={filters.supportMaterials}
onChange={(value) => onFiltersChange({ ...filters, supportMaterials: value })}
placeholder="Select support material"
/>
<FilterMultiSelectCombobox
label="Propulsion Method"
options={propulsionMethodOptions}
value={filters.propulsionMethods}
onChange={(value) => onFiltersChange({ ...filters, propulsionMethods: value })}
placeholder="Select propulsion"
/>
</div>
</FilterSection>

View File

@@ -184,12 +184,15 @@ export function useRideCreditFilters(credits: UserRideCredit[]) {
);
}
// Track material filter
// Track material filter (handles array)
if (filters.trackMaterial && filters.trackMaterial.length > 0) {
result = result.filter(credit =>
credit.rides?.track_material &&
filters.trackMaterial!.includes(credit.rides.track_material)
);
result = result.filter(credit => {
if (!credit.rides?.track_material) return false;
const rideMaterials = Array.isArray(credit.rides.track_material)
? credit.rides.track_material
: [credit.rides.track_material];
return rideMaterials.some(material => filters.trackMaterial!.includes(material));
});
}
// User rating

View File

@@ -3510,13 +3510,17 @@ export type Database = {
rides: {
Row: {
age_requirement: number | null
animatronics_count: number | null
arm_length_meters: number | null
average_rating: number | null
banner_image_id: string | null
banner_image_url: string | null
boat_capacity: number | null
capacity_per_hour: number | null
card_image_id: string | null
card_image_url: string | null
category: string
character_theme: string | null
closing_date: string | null
closing_date_precision: string | null
coaster_type: string | null
@@ -3525,6 +3529,8 @@ export type Database = {
designer_id: string | null
drop_height_meters: number | null
duration_seconds: number | null
educational_theme: string | null
flume_type: string | null
height_requirement: number | null
id: string
image_url: string | null
@@ -3532,36 +3538,63 @@ export type Database = {
inversions: number | null
length_meters: number | null
manufacturer_id: string | null
max_age: number | null
max_g_force: number | null
max_height_meters: number | null
max_height_reached_meters: number | null
max_speed_kmh: number | null
min_age: number | null
motion_pattern: string | null
name: string
opening_date: string | null
opening_date_precision: string | null
park_id: string
platform_count: number | null
projection_type: string | null
propulsion_method: string[] | null
review_count: number | null
ride_model_id: string | null
ride_sub_type: string | null
ride_system: string | null
rotation_speed_rpm: number | null
rotation_type: string | null
round_trip_duration_seconds: number | null
route_length_meters: number | null
scenes_count: number | null
seating_type: string | null
show_duration_seconds: number | null
slug: string
splash_height_meters: number | null
stations_count: number | null
status: string
story_description: string | null
support_material: string[] | null
swing_angle_degrees: number | null
theme_name: string | null
track_material: string[] | null
transport_type: string | null
updated_at: string
vehicle_capacity: number | null
vehicles_count: number | null
view_count_30d: number | null
view_count_7d: number | null
view_count_all: number | null
water_depth_cm: number | null
wetness_level: string | null
}
Insert: {
age_requirement?: number | null
animatronics_count?: number | null
arm_length_meters?: number | null
average_rating?: number | null
banner_image_id?: string | null
banner_image_url?: string | null
boat_capacity?: number | null
capacity_per_hour?: number | null
card_image_id?: string | null
card_image_url?: string | null
category: string
character_theme?: string | null
closing_date?: string | null
closing_date_precision?: string | null
coaster_type?: string | null
@@ -3570,6 +3603,8 @@ export type Database = {
designer_id?: string | null
drop_height_meters?: number | null
duration_seconds?: number | null
educational_theme?: string | null
flume_type?: string | null
height_requirement?: number | null
id?: string
image_url?: string | null
@@ -3577,36 +3612,63 @@ export type Database = {
inversions?: number | null
length_meters?: number | null
manufacturer_id?: string | null
max_age?: number | null
max_g_force?: number | null
max_height_meters?: number | null
max_height_reached_meters?: number | null
max_speed_kmh?: number | null
min_age?: number | null
motion_pattern?: string | null
name: string
opening_date?: string | null
opening_date_precision?: string | null
park_id: string
platform_count?: number | null
projection_type?: string | null
propulsion_method?: string[] | null
review_count?: number | null
ride_model_id?: string | null
ride_sub_type?: string | null
ride_system?: string | null
rotation_speed_rpm?: number | null
rotation_type?: string | null
round_trip_duration_seconds?: number | null
route_length_meters?: number | null
scenes_count?: number | null
seating_type?: string | null
show_duration_seconds?: number | null
slug: string
splash_height_meters?: number | null
stations_count?: number | null
status?: string
story_description?: string | null
support_material?: string[] | null
swing_angle_degrees?: number | null
theme_name?: string | null
track_material?: string[] | null
transport_type?: string | null
updated_at?: string
vehicle_capacity?: number | null
vehicles_count?: number | null
view_count_30d?: number | null
view_count_7d?: number | null
view_count_all?: number | null
water_depth_cm?: number | null
wetness_level?: string | null
}
Update: {
age_requirement?: number | null
animatronics_count?: number | null
arm_length_meters?: number | null
average_rating?: number | null
banner_image_id?: string | null
banner_image_url?: string | null
boat_capacity?: number | null
capacity_per_hour?: number | null
card_image_id?: string | null
card_image_url?: string | null
category?: string
character_theme?: string | null
closing_date?: string | null
closing_date_precision?: string | null
coaster_type?: string | null
@@ -3615,6 +3677,8 @@ export type Database = {
designer_id?: string | null
drop_height_meters?: number | null
duration_seconds?: number | null
educational_theme?: string | null
flume_type?: string | null
height_requirement?: number | null
id?: string
image_url?: string | null
@@ -3622,26 +3686,49 @@ export type Database = {
inversions?: number | null
length_meters?: number | null
manufacturer_id?: string | null
max_age?: number | null
max_g_force?: number | null
max_height_meters?: number | null
max_height_reached_meters?: number | null
max_speed_kmh?: number | null
min_age?: number | null
motion_pattern?: string | null
name?: string
opening_date?: string | null
opening_date_precision?: string | null
park_id?: string
platform_count?: number | null
projection_type?: string | null
propulsion_method?: string[] | null
review_count?: number | null
ride_model_id?: string | null
ride_sub_type?: string | null
ride_system?: string | null
rotation_speed_rpm?: number | null
rotation_type?: string | null
round_trip_duration_seconds?: number | null
route_length_meters?: number | null
scenes_count?: number | null
seating_type?: string | null
show_duration_seconds?: number | null
slug?: string
splash_height_meters?: number | null
stations_count?: number | null
status?: string
story_description?: string | null
support_material?: string[] | null
swing_angle_degrees?: number | null
theme_name?: string | null
track_material?: string[] | null
transport_type?: string | null
updated_at?: string
vehicle_capacity?: number | null
vehicles_count?: number | null
view_count_30d?: number | null
view_count_7d?: number | null
view_count_all?: number | null
water_depth_cm?: number | null
wetness_level?: string | null
}
Relationships: [
{

View File

@@ -79,6 +79,40 @@ export function transformRideData(submissionData: RideSubmissionData): RideInser
track_material: submissionData.track_material || null,
support_material: submissionData.support_material || null,
propulsion_method: submissionData.propulsion_method || null,
// Water ride specific fields
water_depth_cm: submissionData.water_depth_cm || null,
splash_height_meters: submissionData.splash_height_meters || null,
wetness_level: submissionData.wetness_level || null,
flume_type: submissionData.flume_type || null,
boat_capacity: submissionData.boat_capacity || null,
// Dark ride specific fields
theme_name: submissionData.theme_name || null,
story_description: submissionData.story_description || null,
show_duration_seconds: submissionData.show_duration_seconds || null,
animatronics_count: submissionData.animatronics_count || null,
projection_type: submissionData.projection_type || null,
ride_system: submissionData.ride_system || null,
scenes_count: submissionData.scenes_count || null,
// Flat ride specific fields
rotation_type: submissionData.rotation_type || null,
motion_pattern: submissionData.motion_pattern || null,
platform_count: submissionData.platform_count || null,
swing_angle_degrees: submissionData.swing_angle_degrees || null,
rotation_speed_rpm: submissionData.rotation_speed_rpm || null,
arm_length_meters: submissionData.arm_length_meters || null,
max_height_reached_meters: submissionData.max_height_reached_meters || null,
// Kiddie ride specific fields
min_age: submissionData.min_age || null,
max_age: submissionData.max_age || null,
educational_theme: submissionData.educational_theme || null,
character_theme: submissionData.character_theme || null,
// Transportation ride specific fields
transport_type: submissionData.transport_type || null,
route_length_meters: submissionData.route_length_meters || null,
stations_count: submissionData.stations_count || null,
vehicle_capacity: submissionData.vehicle_capacity || null,
vehicles_count: submissionData.vehicles_count || null,
round_trip_duration_seconds: submissionData.round_trip_duration_seconds || 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,

View File

@@ -29,7 +29,11 @@ import {
Waves,
Theater,
Train,
Edit
Edit,
Wrench,
Sparkles,
Repeat,
Baby
} from 'lucide-react';
import { ReviewsSection } from '@/components/reviews/ReviewsSection';
import { MeasurementDisplay } from '@/components/ui/measurement-display';
@@ -703,6 +707,345 @@ export default function RideDetail() {
</div>
</CardContent>
</Card>
{/* Coaster Materials & Propulsion */}
{ride.category === 'roller_coaster' && (
ride.track_material?.length > 0 ||
ride.support_material?.length > 0 ||
ride.propulsion_method?.length > 0
) && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Wrench className="w-5 h-5" />
Coaster Construction
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{ride.track_material && ride.track_material.length > 0 && (
<div>
<div className="text-sm font-medium mb-2">Track Material</div>
<div className="flex flex-wrap gap-2">
{ride.track_material.map(material => (
<Badge key={material} variant="outline">
{material.replace('_', ' ').toUpperCase()}
</Badge>
))}
</div>
</div>
)}
{ride.support_material && ride.support_material.length > 0 && (
<div>
<div className="text-sm font-medium mb-2">Support Material</div>
<div className="flex flex-wrap gap-2">
{ride.support_material.map(material => (
<Badge key={material} variant="outline">
{material.replace('_', ' ').toUpperCase()}
</Badge>
))}
</div>
</div>
)}
{ride.propulsion_method && ride.propulsion_method.length > 0 && (
<div>
<div className="text-sm font-medium mb-2">Propulsion Method</div>
<div className="flex flex-wrap gap-2">
{ride.propulsion_method.map(method => (
<Badge key={method} variant="outline">
{method.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
</Badge>
))}
</div>
</div>
)}
</CardContent>
</Card>
)}
{/* Water Ride Details */}
{ride.category === 'water_ride' && (
ride.water_depth_cm || ride.splash_height_meters || ride.wetness_level ||
ride.flume_type || ride.boat_capacity
) && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Waves className="w-5 h-5" />
Water Ride Details
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{ride.water_depth_cm && (
<div className="flex justify-between">
<span>Water Depth</span>
<span className="font-medium">
<MeasurementDisplay value={ride.water_depth_cm} type="height" />
</span>
</div>
)}
{ride.splash_height_meters && (
<div className="flex justify-between">
<span>Splash Height</span>
<span className="font-medium">
<MeasurementDisplay value={ride.splash_height_meters} type="distance" />
</span>
</div>
)}
{ride.wetness_level && (
<div className="flex justify-between">
<span>Wetness Level</span>
<Badge variant="outline">
{ride.wetness_level.charAt(0).toUpperCase() + ride.wetness_level.slice(1)}
</Badge>
</div>
)}
{ride.flume_type && (
<div className="flex justify-between">
<span>Flume Type</span>
<span className="font-medium">
{ride.flume_type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
</span>
</div>
)}
{ride.boat_capacity && (
<div className="flex justify-between">
<span>Boat Capacity</span>
<span className="font-medium">{ride.boat_capacity} riders</span>
</div>
)}
</CardContent>
</Card>
)}
{/* Dark Ride Details */}
{ride.category === 'dark_ride' && (
ride.theme_name || ride.story_description || ride.show_duration_seconds ||
ride.animatronics_count || ride.projection_type || ride.ride_system || ride.scenes_count
) && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Sparkles className="w-5 h-5" />
Dark Ride Details
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{ride.theme_name && (
<div className="flex justify-between">
<span>Theme</span>
<span className="font-medium">{ride.theme_name}</span>
</div>
)}
{ride.story_description && (
<div>
<div className="text-sm font-medium mb-1">Story</div>
<p className="text-sm text-muted-foreground">{ride.story_description}</p>
</div>
)}
{ride.show_duration_seconds && (
<div className="flex justify-between">
<span>Show Duration</span>
<span className="font-medium">
{Math.floor(ride.show_duration_seconds / 60)}:{(ride.show_duration_seconds % 60).toString().padStart(2, '0')}
</span>
</div>
)}
{ride.animatronics_count && (
<div className="flex justify-between">
<span>Animatronics</span>
<span className="font-medium">{ride.animatronics_count}</span>
</div>
)}
{ride.scenes_count && (
<div className="flex justify-between">
<span>Scenes</span>
<span className="font-medium">{ride.scenes_count}</span>
</div>
)}
{ride.projection_type && (
<div className="flex justify-between">
<span>Projection Type</span>
<span className="font-medium">
{ride.projection_type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
</span>
</div>
)}
{ride.ride_system && (
<div className="flex justify-between">
<span>Ride System</span>
<span className="font-medium">
{ride.ride_system.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
</span>
</div>
)}
</CardContent>
</Card>
)}
{/* Flat Ride Details */}
{ride.category === 'flat_ride' && (
ride.rotation_type || ride.motion_pattern || ride.platform_count ||
ride.swing_angle_degrees || ride.rotation_speed_rpm || ride.arm_length_meters ||
ride.max_height_reached_meters
) && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Repeat className="w-5 h-5" />
Flat Ride Details
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{ride.rotation_type && (
<div className="flex justify-between">
<span>Rotation Type</span>
<span className="font-medium">
{ride.rotation_type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
</span>
</div>
)}
{ride.motion_pattern && (
<div className="flex justify-between">
<span>Motion Pattern</span>
<span className="font-medium">
{ride.motion_pattern.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
</span>
</div>
)}
{ride.platform_count && (
<div className="flex justify-between">
<span>Platforms</span>
<span className="font-medium">{ride.platform_count}</span>
</div>
)}
{ride.swing_angle_degrees && (
<div className="flex justify-between">
<span>Swing Angle</span>
<span className="font-medium">{ride.swing_angle_degrees}°</span>
</div>
)}
{ride.rotation_speed_rpm && (
<div className="flex justify-between">
<span>Rotation Speed</span>
<span className="font-medium">{ride.rotation_speed_rpm} RPM</span>
</div>
)}
{ride.arm_length_meters && (
<div className="flex justify-between">
<span>Arm Length</span>
<span className="font-medium">
<MeasurementDisplay value={ride.arm_length_meters} type="distance" />
</span>
</div>
)}
{ride.max_height_reached_meters && (
<div className="flex justify-between">
<span>Max Height Reached</span>
<span className="font-medium">
<MeasurementDisplay value={ride.max_height_reached_meters} type="distance" />
</span>
</div>
)}
</CardContent>
</Card>
)}
{/* Kiddie Ride Details */}
{ride.category === 'kiddie_ride' && (
ride.min_age || ride.max_age || ride.educational_theme || ride.character_theme
) && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Baby className="w-5 h-5" />
Kiddie Ride Details
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{(ride.min_age || ride.max_age) && (
<div className="flex justify-between">
<span>Age Range</span>
<span className="font-medium">
{ride.min_age && ride.max_age ? `${ride.min_age}-${ride.max_age} years` :
ride.min_age ? `${ride.min_age}+ years` :
ride.max_age ? `Up to ${ride.max_age} years` : 'N/A'}
</span>
</div>
)}
{ride.educational_theme && (
<div className="flex justify-between">
<span>Educational Theme</span>
<span className="font-medium">{ride.educational_theme}</span>
</div>
)}
{ride.character_theme && (
<div className="flex justify-between">
<span>Character Theme</span>
<span className="font-medium">{ride.character_theme}</span>
</div>
)}
</CardContent>
</Card>
)}
{/* Transportation Details */}
{ride.category === 'transportation' && (
ride.transport_type || ride.route_length_meters || ride.stations_count ||
ride.vehicle_capacity || ride.vehicles_count || ride.round_trip_duration_seconds
) && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Train className="w-5 h-5" />
Transportation Details
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{ride.transport_type && (
<div className="flex justify-between">
<span>Transport Type</span>
<span className="font-medium">
{ride.transport_type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
</span>
</div>
)}
{ride.route_length_meters && (
<div className="flex justify-between">
<span>Route Length</span>
<span className="font-medium">
<MeasurementDisplay value={ride.route_length_meters} type="distance" />
</span>
</div>
)}
{ride.stations_count && (
<div className="flex justify-between">
<span>Stations</span>
<span className="font-medium">{ride.stations_count}</span>
</div>
)}
{ride.vehicle_capacity && (
<div className="flex justify-between">
<span>Vehicle Capacity</span>
<span className="font-medium">{ride.vehicle_capacity} passengers</span>
</div>
)}
{ride.vehicles_count && (
<div className="flex justify-between">
<span>Number of Vehicles</span>
<span className="font-medium">{ride.vehicles_count}</span>
</div>
)}
{ride.round_trip_duration_seconds && (
<div className="flex justify-between">
<span>Round Trip Duration</span>
<span className="font-medium">
{Math.floor(ride.round_trip_duration_seconds / 60)}:{(ride.round_trip_duration_seconds % 60).toString().padStart(2, '0')}
</span>
</div>
)}
</CardContent>
</Card>
)}
</div>
</TabsContent>
@@ -768,11 +1111,50 @@ export default function RideDetail() {
max_height_meters: ride.max_height_meters,
length_meters: ride.length_meters,
inversions: ride.inversions,
// Coaster fields
coaster_type: ride.coaster_type,
seating_type: ride.seating_type,
intensity_level: ride.intensity_level,
track_material: ride.track_material,
support_material: ride.support_material,
propulsion_method: ride.propulsion_method,
drop_height_meters: ride.drop_height_meters,
max_g_force: ride.max_g_force,
// Water ride fields
water_depth_cm: ride.water_depth_cm,
splash_height_meters: ride.splash_height_meters,
wetness_level: ride.wetness_level as any,
flume_type: ride.flume_type,
boat_capacity: ride.boat_capacity,
// Dark ride fields
theme_name: ride.theme_name,
story_description: ride.story_description,
show_duration_seconds: ride.show_duration_seconds,
animatronics_count: ride.animatronics_count,
projection_type: ride.projection_type,
ride_system: ride.ride_system,
scenes_count: ride.scenes_count,
// Flat ride fields
rotation_type: ride.rotation_type as any,
motion_pattern: ride.motion_pattern,
platform_count: ride.platform_count,
swing_angle_degrees: ride.swing_angle_degrees,
rotation_speed_rpm: ride.rotation_speed_rpm,
arm_length_meters: ride.arm_length_meters,
max_height_reached_meters: ride.max_height_reached_meters,
// Kiddie ride fields
min_age: ride.min_age,
max_age: ride.max_age,
educational_theme: ride.educational_theme,
character_theme: ride.character_theme,
// Transportation fields
transport_type: ride.transport_type as any,
route_length_meters: ride.route_length_meters,
stations_count: ride.stations_count,
vehicle_capacity: ride.vehicle_capacity,
vehicles_count: ride.vehicles_count,
round_trip_duration_seconds: ride.round_trip_duration_seconds,
// Common fields
manufacturer_id: ride.manufacturer?.id,
ride_model_id: ride.ride_model?.id,
banner_image_url: ride.banner_image_url,

View File

@@ -180,7 +180,7 @@ export default function Rides() {
}
}
// Track material filter
// Track material filter (array field)
if (filters.trackMaterials.length > 0) {
if (!ride.track_material || ride.track_material.length === 0 ||
!ride.track_material.some(material => filters.trackMaterials.includes(material))) {
@@ -188,6 +188,22 @@ export default function Rides() {
}
}
// Support material filter (array field)
if (filters.supportMaterials.length > 0) {
if (!ride.support_material || ride.support_material.length === 0 ||
!ride.support_material.some(material => filters.supportMaterials.includes(material))) {
return false;
}
}
// Propulsion method filter (array field)
if (filters.propulsionMethods.length > 0) {
if (!ride.propulsion_method || ride.propulsion_method.length === 0 ||
!ride.propulsion_method.some(method => filters.propulsionMethods.includes(method))) {
return false;
}
}
// Speed filter
if (filters.minSpeed > 0 || filters.maxSpeed < 200) {
const speed = ride.max_speed_kmh || 0;

View File

@@ -162,7 +162,7 @@ export interface Ride {
banner_image_id?: string;
card_image_url?: string;
card_image_id?: string;
// New roller coaster specific fields
// Roller coaster specific fields
coaster_type?: string;
seating_type?: string;
intensity_level?: string;
@@ -171,6 +171,40 @@ export interface Ride {
propulsion_method?: string[];
drop_height_meters?: number;
max_g_force?: number;
// Water ride specific fields
water_depth_cm?: number;
splash_height_meters?: number;
wetness_level?: string;
flume_type?: string;
boat_capacity?: number;
// Dark ride specific fields
theme_name?: string;
story_description?: string;
show_duration_seconds?: number;
animatronics_count?: number;
projection_type?: string;
ride_system?: string;
scenes_count?: number;
// Flat ride specific fields
rotation_type?: string;
motion_pattern?: string;
platform_count?: number;
swing_angle_degrees?: number;
rotation_speed_rpm?: number;
arm_length_meters?: number;
max_height_reached_meters?: number;
// Kiddie ride specific fields
min_age?: number;
max_age?: number;
educational_theme?: string;
character_theme?: string;
// Transportation specific fields
transport_type?: string;
route_length_meters?: number;
stations_count?: number;
vehicle_capacity?: number;
vehicles_count?: number;
round_trip_duration_seconds?: number;
}
export interface Profile {

View File

@@ -0,0 +1,83 @@
-- Add category-specific fields to rides table
-- Water ride specific columns
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS water_depth_cm INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS splash_height_meters DECIMAL(10,2);
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS wetness_level TEXT;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS flume_type TEXT;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS boat_capacity INTEGER;
-- Dark ride specific columns
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS theme_name TEXT;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS story_description TEXT;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS show_duration_seconds INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS animatronics_count INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS projection_type TEXT;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS ride_system TEXT;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS scenes_count INTEGER;
-- Flat ride specific columns
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS rotation_type TEXT;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS motion_pattern TEXT;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS platform_count INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS swing_angle_degrees INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS rotation_speed_rpm INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS arm_length_meters DECIMAL(10,2);
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS max_height_reached_meters DECIMAL(10,2);
-- Kiddie ride specific columns
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS min_age INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS max_age INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS educational_theme TEXT;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS character_theme TEXT;
-- Transportation ride specific columns
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS transport_type TEXT;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS route_length_meters DECIMAL(10,2);
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS stations_count INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS vehicle_capacity INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS vehicles_count INTEGER;
ALTER TABLE public.rides ADD COLUMN IF NOT EXISTS round_trip_duration_seconds INTEGER;
-- Add check constraints for enum-like fields
ALTER TABLE public.rides DROP CONSTRAINT IF EXISTS check_wetness_level;
ALTER TABLE public.rides ADD CONSTRAINT check_wetness_level
CHECK (wetness_level IS NULL OR wetness_level IN ('none', 'light', 'moderate', 'heavy', 'soaked'));
ALTER TABLE public.rides DROP CONSTRAINT IF EXISTS check_flume_type;
ALTER TABLE public.rides ADD CONSTRAINT check_flume_type
CHECK (flume_type IS NULL OR flume_type IN ('log_flume', 'shoot_the_chute', 'river_rapids', 'water_coaster', 'flume_ride'));
ALTER TABLE public.rides DROP CONSTRAINT IF EXISTS check_projection_type;
ALTER TABLE public.rides ADD CONSTRAINT check_projection_type
CHECK (projection_type IS NULL OR projection_type IN ('2d', '3d', '4d', 'holographic', 'projection_mapping', 'none'));
ALTER TABLE public.rides DROP CONSTRAINT IF EXISTS check_ride_system;
ALTER TABLE public.rides ADD CONSTRAINT check_ride_system
CHECK (ride_system IS NULL OR ride_system IN ('omnimover', 'trackless', 'motion_simulator', 'boat', 'suspended', 'walk_through'));
ALTER TABLE public.rides DROP CONSTRAINT IF EXISTS check_rotation_type;
ALTER TABLE public.rides ADD CONSTRAINT check_rotation_type
CHECK (rotation_type IS NULL OR rotation_type IN ('horizontal', 'vertical', 'diagonal', 'multi_axis', 'none'));
ALTER TABLE public.rides DROP CONSTRAINT IF EXISTS check_motion_pattern;
ALTER TABLE public.rides ADD CONSTRAINT check_motion_pattern
CHECK (motion_pattern IS NULL OR motion_pattern IN ('circular', 'pendulum', 'spinning', 'tilting', 'rocking', 'wave', 'bouncing'));
ALTER TABLE public.rides DROP CONSTRAINT IF EXISTS check_transport_type;
ALTER TABLE public.rides ADD CONSTRAINT check_transport_type
CHECK (transport_type IS NULL OR transport_type IN ('monorail', 'train', 'chairlift', 'gondola', 'tram', 'peoplemover', 'cable_car'));
-- Add indexes for commonly queried fields
CREATE INDEX IF NOT EXISTS idx_rides_wetness_level ON public.rides(wetness_level) WHERE wetness_level IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_rides_transport_type ON public.rides(transport_type) WHERE transport_type IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_rides_rotation_type ON public.rides(rotation_type) WHERE rotation_type IS NOT NULL;
-- Add comments for documentation
COMMENT ON COLUMN public.rides.water_depth_cm IS 'Water depth in centimeters for water rides';
COMMENT ON COLUMN public.rides.splash_height_meters IS 'Maximum splash height in meters';
COMMENT ON COLUMN public.rides.wetness_level IS 'How wet riders get: none, light, moderate, heavy, soaked';
COMMENT ON COLUMN public.rides.theme_name IS 'Theme name for dark rides';
COMMENT ON COLUMN public.rides.story_description IS 'Story/narrative description for dark rides';
COMMENT ON COLUMN public.rides.rotation_type IS 'Type of rotation for flat rides';
COMMENT ON COLUMN public.rides.transport_type IS 'Type of transportation system';