mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 22:31:13 -05:00
feat: Implement bot detection plan
This commit is contained in:
@@ -29,7 +29,11 @@ import {
|
|||||||
Waves,
|
Waves,
|
||||||
Theater,
|
Theater,
|
||||||
Train,
|
Train,
|
||||||
Edit
|
Edit,
|
||||||
|
Wrench,
|
||||||
|
Sparkles,
|
||||||
|
Repeat,
|
||||||
|
Baby
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { ReviewsSection } from '@/components/reviews/ReviewsSection';
|
import { ReviewsSection } from '@/components/reviews/ReviewsSection';
|
||||||
import { MeasurementDisplay } from '@/components/ui/measurement-display';
|
import { MeasurementDisplay } from '@/components/ui/measurement-display';
|
||||||
@@ -703,6 +707,345 @@ export default function RideDetail() {
|
|||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</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>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
@@ -768,11 +1111,50 @@ export default function RideDetail() {
|
|||||||
max_height_meters: ride.max_height_meters,
|
max_height_meters: ride.max_height_meters,
|
||||||
length_meters: ride.length_meters,
|
length_meters: ride.length_meters,
|
||||||
inversions: ride.inversions,
|
inversions: ride.inversions,
|
||||||
|
// Coaster fields
|
||||||
coaster_type: ride.coaster_type,
|
coaster_type: ride.coaster_type,
|
||||||
seating_type: ride.seating_type,
|
seating_type: ride.seating_type,
|
||||||
intensity_level: ride.intensity_level,
|
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,
|
drop_height_meters: ride.drop_height_meters,
|
||||||
max_g_force: ride.max_g_force,
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
manufacturer_id: ride.manufacturer?.id,
|
||||||
ride_model_id: ride.ride_model?.id,
|
ride_model_id: ride.ride_model?.id,
|
||||||
banner_image_url: ride.banner_image_url,
|
banner_image_url: ride.banner_image_url,
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ export interface Ride {
|
|||||||
banner_image_id?: string;
|
banner_image_id?: string;
|
||||||
card_image_url?: string;
|
card_image_url?: string;
|
||||||
card_image_id?: string;
|
card_image_id?: string;
|
||||||
// New roller coaster specific fields
|
// Roller coaster specific fields
|
||||||
coaster_type?: string;
|
coaster_type?: string;
|
||||||
seating_type?: string;
|
seating_type?: string;
|
||||||
intensity_level?: string;
|
intensity_level?: string;
|
||||||
@@ -171,6 +171,40 @@ export interface Ride {
|
|||||||
propulsion_method?: string[];
|
propulsion_method?: string[];
|
||||||
drop_height_meters?: number;
|
drop_height_meters?: number;
|
||||||
max_g_force?: number;
|
max_g_force?: number;
|
||||||
|
// Water ride specific fields
|
||||||
|
water_depth_cm?: number;
|
||||||
|
splash_height_meters?: number;
|
||||||
|
wetness_level?: 'dry' | 'light' | 'moderate' | 'soaked';
|
||||||
|
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?: 'horizontal' | 'vertical' | 'multi_axis' | 'pendulum' | 'none';
|
||||||
|
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?: 'monorail' | 'train' | 'skylift' | 'ferry' | 'cable_car' | 'peoplemover';
|
||||||
|
route_length_meters?: number;
|
||||||
|
stations_count?: number;
|
||||||
|
vehicle_capacity?: number;
|
||||||
|
vehicles_count?: number;
|
||||||
|
round_trip_duration_seconds?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Profile {
|
export interface Profile {
|
||||||
|
|||||||
Reference in New Issue
Block a user