Fix ride submission data loss

Implement the plan to fix critical data loss in ride submissions. This includes:
- Storing ride technical specifications, coaster statistics, and name history in submission tables.
- Adding missing category-specific fields to the `ride_submissions` table via a new migration.
- Updating submission helpers and the edge function to include these new fields.
- Fixing the park location Zod schema to include `street_address`.
This commit is contained in:
gpt-engineer-app[bot]
2025-11-06 14:51:36 +00:00
parent 9f5240ae95
commit de9a48951f
5 changed files with 262 additions and 2 deletions

View File

@@ -3867,12 +3867,16 @@ export type Database = {
ride_submissions: {
Row: {
age_requirement: number | null
animatronics_count: number | null
arm_length_meters: 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
@@ -3881,6 +3885,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
@@ -3888,32 +3894,59 @@ 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 | null
platform_count: number | null
projection_type: string | null
propulsion_method: string[] | 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
submission_id: string
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
water_depth_cm: number | null
wetness_level: string | null
}
Insert: {
age_requirement?: number | null
animatronics_count?: number | null
arm_length_meters?: 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
@@ -3922,6 +3955,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
@@ -3929,32 +3964,59 @@ 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 | null
platform_count?: number | null
projection_type?: string | null
propulsion_method?: string[] | 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
submission_id: string
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
water_depth_cm?: number | null
wetness_level?: string | null
}
Update: {
age_requirement?: number | null
animatronics_count?: number | null
arm_length_meters?: 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
@@ -3963,6 +4025,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
@@ -3970,23 +4034,46 @@ 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 | null
platform_count?: number | null
projection_type?: string | null
propulsion_method?: string[] | 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
submission_id?: string
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
water_depth_cm?: number | null
wetness_level?: string | null
}
Relationships: [
{

View File

@@ -1177,13 +1177,109 @@ export async function submitRideCreation(
banner_image_id: bannerImage?.cloudflare_id || data.banner_image_id || null,
card_image_url: cardImage?.url || data.card_image_url || null,
card_image_id: cardImage?.cloudflare_id || data.card_image_id || null,
image_url: null
image_url: null,
// Category-specific fields
track_material: (data as any).track_material || null,
support_material: (data as any).support_material || null,
propulsion_method: (data as any).propulsion_method || null,
water_depth_cm: (data as any).water_depth_cm || null,
splash_height_meters: (data as any).splash_height_meters || null,
wetness_level: (data as any).wetness_level || null,
flume_type: (data as any).flume_type || null,
boat_capacity: (data as any).boat_capacity || null,
theme_name: (data as any).theme_name || null,
story_description: (data as any).story_description || null,
show_duration_seconds: (data as any).show_duration_seconds || null,
animatronics_count: (data as any).animatronics_count || null,
projection_type: (data as any).projection_type || null,
ride_system: (data as any).ride_system || null,
scenes_count: (data as any).scenes_count || null,
rotation_type: (data as any).rotation_type || null,
motion_pattern: (data as any).motion_pattern || null,
platform_count: (data as any).platform_count || null,
swing_angle_degrees: (data as any).swing_angle_degrees || null,
rotation_speed_rpm: (data as any).rotation_speed_rpm || null,
arm_length_meters: (data as any).arm_length_meters || null,
max_height_reached_meters: (data as any).max_height_reached_meters || null,
min_age: (data as any).min_age || null,
max_age: (data as any).max_age || null,
educational_theme: (data as any).educational_theme || null,
character_theme: (data as any).character_theme || null,
transport_type: (data as any).transport_type || null,
route_length_meters: (data as any).route_length_meters || null,
stations_count: (data as any).stations_count || null,
vehicle_capacity: (data as any).vehicle_capacity || null,
vehicles_count: (data as any).vehicles_count || null,
round_trip_duration_seconds: (data as any).round_trip_duration_seconds || null
} as any)
.select('id')
.single();
if (rideSubmissionError) throw rideSubmissionError;
// Insert technical specifications if present
if ((data as any)._technical_specifications?.length > 0) {
const { error: techSpecError } = await supabase
.from('ride_technical_specs' as any)
.insert(
(data as any)._technical_specifications.map((spec: any) => ({
ride_submission_id: (rideSubmission as any).id,
spec_name: spec.spec_name,
spec_value: spec.spec_value,
spec_type: spec.spec_type,
category: spec.category || null,
unit: spec.unit || null
}))
);
if (techSpecError) {
logger.error('Failed to insert technical specs', { error: techSpecError });
throw techSpecError;
}
}
// Insert coaster statistics if present
if ((data as any)._coaster_statistics?.length > 0) {
const { error: statsError } = await supabase
.from('ride_coaster_stats' as any)
.insert(
(data as any)._coaster_statistics.map((stat: any) => ({
ride_submission_id: (rideSubmission as any).id,
stat_name: stat.stat_name,
stat_value: stat.stat_value,
unit: stat.unit || null,
category: stat.category || null
}))
);
if (statsError) {
logger.error('Failed to insert coaster stats', { error: statsError });
throw statsError;
}
}
// Insert name history if present
if ((data as any)._name_history?.length > 0) {
const { error: historyError } = await supabase
.from('ride_name_history_submissions' as any)
.insert(
(data as any)._name_history.map((name: any) => ({
ride_submission_id: (rideSubmission as any).id,
former_name: name.former_name,
date_changed: name.date_changed ? new Date(name.date_changed).toISOString().split('T')[0] : null,
reason: name.reason || null,
from_year: name.from_year || null,
to_year: name.to_year || null,
order_index: name.order_index || 0
}))
);
if (historyError) {
logger.error('Failed to insert name history', { error: historyError });
throw historyError;
}
}
// Create submission_items record linking to ride_submissions
const { error: itemError } = await supabase
.from('submission_items')

View File

@@ -41,6 +41,7 @@ export const parkValidationSchema = z.object({
location_id: z.string().uuid().optional().nullable(),
location: z.object({
name: z.string(),
street_address: z.string().optional().nullable(),
city: z.string().optional().nullable(),
state_province: z.string().optional().nullable(),
country: z.string(),

View File

@@ -864,7 +864,40 @@ serve(withRateLimit(async (req) => {
case 'ride':
itemData = {
...(item as any).ride_submission,
...(tempRefsByItemId.get(item.id) || {})
...(tempRefsByItemId.get(item.id) || {}),
// Ensure all category-specific fields are included
track_material: (item as any).ride_submission?.track_material,
support_material: (item as any).ride_submission?.support_material,
propulsion_method: (item as any).ride_submission?.propulsion_method,
water_depth_cm: (item as any).ride_submission?.water_depth_cm,
splash_height_meters: (item as any).ride_submission?.splash_height_meters,
wetness_level: (item as any).ride_submission?.wetness_level,
flume_type: (item as any).ride_submission?.flume_type,
boat_capacity: (item as any).ride_submission?.boat_capacity,
theme_name: (item as any).ride_submission?.theme_name,
story_description: (item as any).ride_submission?.story_description,
show_duration_seconds: (item as any).ride_submission?.show_duration_seconds,
animatronics_count: (item as any).ride_submission?.animatronics_count,
projection_type: (item as any).ride_submission?.projection_type,
ride_system: (item as any).ride_submission?.ride_system,
scenes_count: (item as any).ride_submission?.scenes_count,
rotation_type: (item as any).ride_submission?.rotation_type,
motion_pattern: (item as any).ride_submission?.motion_pattern,
platform_count: (item as any).ride_submission?.platform_count,
swing_angle_degrees: (item as any).ride_submission?.swing_angle_degrees,
rotation_speed_rpm: (item as any).ride_submission?.rotation_speed_rpm,
arm_length_meters: (item as any).ride_submission?.arm_length_meters,
max_height_reached_meters: (item as any).ride_submission?.max_height_reached_meters,
min_age: (item as any).ride_submission?.min_age,
max_age: (item as any).ride_submission?.max_age,
educational_theme: (item as any).ride_submission?.educational_theme,
character_theme: (item as any).ride_submission?.character_theme,
transport_type: (item as any).ride_submission?.transport_type,
route_length_meters: (item as any).ride_submission?.route_length_meters,
stations_count: (item as any).ride_submission?.stations_count,
vehicle_capacity: (item as any).ride_submission?.vehicle_capacity,
vehicles_count: (item as any).ride_submission?.vehicles_count,
round_trip_duration_seconds: (item as any).ride_submission?.round_trip_duration_seconds
};
break;
case 'manufacturer':

View File

@@ -0,0 +1,43 @@
-- Add missing category-specific fields to ride_submissions table
-- This ensures all ride category data can flow through the submission pipeline
ALTER TABLE ride_submissions
ADD COLUMN IF NOT EXISTS track_material TEXT[],
ADD COLUMN IF NOT EXISTS support_material TEXT[],
ADD COLUMN IF NOT EXISTS propulsion_method TEXT[],
-- Water ride fields
ADD COLUMN IF NOT EXISTS water_depth_cm INTEGER,
ADD COLUMN IF NOT EXISTS splash_height_meters NUMERIC,
ADD COLUMN IF NOT EXISTS wetness_level TEXT,
ADD COLUMN IF NOT EXISTS flume_type TEXT,
ADD COLUMN IF NOT EXISTS boat_capacity INTEGER,
-- Dark ride fields
ADD COLUMN IF NOT EXISTS theme_name TEXT,
ADD COLUMN IF NOT EXISTS story_description TEXT,
ADD COLUMN IF NOT EXISTS show_duration_seconds INTEGER,
ADD COLUMN IF NOT EXISTS animatronics_count INTEGER,
ADD COLUMN IF NOT EXISTS projection_type TEXT,
ADD COLUMN IF NOT EXISTS ride_system TEXT,
ADD COLUMN IF NOT EXISTS scenes_count INTEGER,
-- Flat ride fields
ADD COLUMN IF NOT EXISTS rotation_type TEXT,
ADD COLUMN IF NOT EXISTS motion_pattern TEXT,
ADD COLUMN IF NOT EXISTS platform_count INTEGER,
ADD COLUMN IF NOT EXISTS swing_angle_degrees NUMERIC,
ADD COLUMN IF NOT EXISTS rotation_speed_rpm NUMERIC,
ADD COLUMN IF NOT EXISTS arm_length_meters NUMERIC,
ADD COLUMN IF NOT EXISTS max_height_reached_meters NUMERIC,
-- Kiddie ride fields
ADD COLUMN IF NOT EXISTS min_age INTEGER,
ADD COLUMN IF NOT EXISTS max_age INTEGER,
ADD COLUMN IF NOT EXISTS educational_theme TEXT,
ADD COLUMN IF NOT EXISTS character_theme TEXT,
-- Transportation ride fields
ADD COLUMN IF NOT EXISTS transport_type TEXT,
ADD COLUMN IF NOT EXISTS route_length_meters NUMERIC,
ADD COLUMN IF NOT EXISTS stations_count INTEGER,
ADD COLUMN IF NOT EXISTS vehicle_capacity INTEGER,
ADD COLUMN IF NOT EXISTS vehicles_count INTEGER,
ADD COLUMN IF NOT EXISTS round_trip_duration_seconds INTEGER;
COMMENT ON TABLE ride_submissions IS 'Submission data for rides - includes all category-specific fields to prevent data loss during moderation';