import { Database } from '@/integrations/supabase/types'; import type { ParkSubmissionData, RideSubmissionData, CompanySubmissionData, RideModelSubmissionData, } from '@/types/submission-data'; type ParkInsert = Database['public']['Tables']['parks']['Insert']; type RideInsert = Database['public']['Tables']['rides']['Insert']; type CompanyInsert = Database['public']['Tables']['companies']['Insert']; type RideModelInsert = Database['public']['Tables']['ride_models']['Insert']; /** * Transform park submission data to database insert format * @param submissionData - Validated park submission data * @returns Database insert object for parks table */ export function transformParkData(submissionData: ParkSubmissionData): ParkInsert { return { name: submissionData.name, slug: submissionData.slug, description: submissionData.description || null, park_type: submissionData.park_type, status: normalizeStatus(submissionData.status), opening_date: submissionData.opening_date || null, closing_date: submissionData.closing_date || null, website_url: submissionData.website_url || null, phone: submissionData.phone || null, email: submissionData.email || null, operator_id: submissionData.operator_id || null, property_owner_id: submissionData.property_owner_id || null, location_id: submissionData.location_id || 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, card_image_id: submissionData.card_image_id || null, average_rating: 0, review_count: 0, ride_count: 0, coaster_count: 0, }; } /** * Transform ride submission data to database insert format * Note: Relational data (technical_specs, coaster_stats, former_names) are now * stored in separate tables and should not be included in the main ride insert. * @param submissionData - Validated ride submission data * @returns Database insert object for rides table */ export function transformRideData(submissionData: RideSubmissionData): RideInsert { return { name: submissionData.name, slug: submissionData.slug, description: submissionData.description || null, category: submissionData.category, ride_sub_type: submissionData.ride_sub_type || null, status: normalizeStatus(submissionData.status), park_id: submissionData.park_id, ride_model_id: submissionData.ride_model_id || null, manufacturer_id: submissionData.manufacturer_id || null, designer_id: submissionData.designer_id || null, opening_date: submissionData.opening_date || null, closing_date: submissionData.closing_date || null, height_requirement: submissionData.height_requirement || null, age_requirement: submissionData.age_requirement || null, capacity_per_hour: submissionData.capacity_per_hour || null, duration_seconds: submissionData.duration_seconds || null, max_speed_kmh: submissionData.max_speed_kmh || null, max_height_meters: submissionData.max_height_meters || null, length_meters: submissionData.length_meters || null, drop_height_meters: submissionData.drop_height_meters || null, inversions: submissionData.inversions || null, max_g_force: submissionData.max_g_force || null, 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, // 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, card_image_id: submissionData.card_image_id || null, image_url: submissionData.image_url || null, average_rating: 0, review_count: 0, }; } /** * Transform company submission data to database insert format * @param submissionData - Validated company submission data * @param companyType - Type of company (manufacturer, operator, property_owner, designer) * @returns Database insert object for companies table */ export function transformCompanyData( submissionData: CompanySubmissionData, companyType: 'manufacturer' | 'operator' | 'property_owner' | 'designer' ): CompanyInsert { return { name: submissionData.name, slug: submissionData.slug, description: submissionData.description || null, company_type: companyType, person_type: submissionData.person_type || 'company', founded_year: submissionData.founded_year || null, headquarters_location: submissionData.headquarters_location || null, website_url: submissionData.website_url || null, logo_url: submissionData.logo_url || null, average_rating: 0, review_count: 0, }; } /** * Transform ride model submission data to database insert format * Note: Technical specifications are now stored in the ride_model_technical_specifications * table and should not be included in the main ride model insert. * @param submissionData - Validated ride model submission data * @returns Database insert object for ride_models table */ export function transformRideModelData(submissionData: RideModelSubmissionData): RideModelInsert { return { name: submissionData.name, slug: submissionData.slug, manufacturer_id: submissionData.manufacturer_id, category: submissionData.category, ride_type: submissionData.ride_type || null, description: submissionData.description || 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, card_image_id: submissionData.card_image_id || null, }; } /** * Normalize status values to match database enums */ function normalizeStatus(status: string): string { if (!status) return 'operating'; const statusMap: Record = { 'Operating': 'operating', 'operating': 'operating', 'Seasonal': 'seasonal', 'seasonal': 'seasonal', 'Closed Temporarily': 'closed_temporarily', 'closed_temporarily': 'closed_temporarily', 'Closed Permanently': 'closed_permanently', 'closed_permanently': 'closed_permanently', 'Under Construction': 'under_construction', 'under_construction': 'under_construction', 'Planned': 'planned', 'planned': 'planned', 'SBNO': 'sbno', 'sbno': 'sbno', }; return statusMap[status] || 'operating'; } /** * Extract Cloudflare image ID from URL */ export function extractImageId(url: string): string { const match = url.match(/\/([a-f0-9-]{36})\//); return match ? match[1] : ''; } /** * Validate and sanitize submission data before transformation * @param data - Submission data to validate * @param itemType - Type of entity being validated (for error messages) * @throws Error if validation fails */ export function validateSubmissionData( data: ParkSubmissionData | RideSubmissionData | CompanySubmissionData | RideModelSubmissionData, itemType: string ): void { if (!data.name || typeof data.name !== 'string' || data.name.trim() === '') { throw new Error(`${itemType} name is required`); } if (!data.slug || typeof data.slug !== 'string' || data.slug.trim() === '') { throw new Error(`${itemType} slug is required`); } // Validate slug format if (!/^[a-z0-9-]+$/.test(data.slug)) { throw new Error(`${itemType} slug must contain only lowercase letters, numbers, and hyphens`); } }