From 6a9df807fac37c018b64a900f0251d7406772477 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 2 Nov 2025 20:21:10 +0000 Subject: [PATCH] Fix submission flow issues --- src/components/admin/ParkForm.tsx | 21 ++++- src/lib/submissionItemsService.ts | 90 ++++++++++++------- .../process-selective-approval/index.ts | 9 ++ 3 files changed, 83 insertions(+), 37 deletions(-) diff --git a/src/components/admin/ParkForm.tsx b/src/components/admin/ParkForm.tsx index 5d545fb8..7f98c1a4 100644 --- a/src/components/admin/ParkForm.tsx +++ b/src/components/admin/ParkForm.tsx @@ -223,10 +223,23 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: submissionContent.park.property_owner_id = null; } - const finalOperatorId = tempNewOperator ? undefined : (selectedOperatorId || undefined); - const finalPropertyOwnerId = operatorIsOwner - ? finalOperatorId - : (tempNewPropertyOwner ? undefined : (selectedPropertyOwnerId || undefined)); + // Determine final IDs to pass + // When creating new entities via composite submission, IDs should be undefined + // When using existing entities, pass their IDs directly + let finalOperatorId: string | undefined; + let finalPropertyOwnerId: string | undefined; + + if (tempNewOperator) { + // New operator being created via composite submission + finalOperatorId = undefined; + finalPropertyOwnerId = operatorIsOwner ? undefined : + (tempNewPropertyOwner ? undefined : selectedPropertyOwnerId); + } else { + // Using existing operator + finalOperatorId = selectedOperatorId || undefined; + finalPropertyOwnerId = operatorIsOwner ? finalOperatorId : + (tempNewPropertyOwner ? undefined : selectedPropertyOwnerId); + } await onSubmit({ ...data, diff --git a/src/lib/submissionItemsService.ts b/src/lib/submissionItemsService.ts index 99f196b8..4e5dae59 100644 --- a/src/lib/submissionItemsService.ts +++ b/src/lib/submissionItemsService.ts @@ -185,24 +185,25 @@ export async function approveSubmissionItems( ); // Create the entity based on type with dependency resolution + // PASS sortedItems to enable correct index-based resolution switch (item.item_type) { case 'park': - entityId = await createPark(item.item_data, dependencyMap); + entityId = await createPark(item.item_data, dependencyMap, sortedItems); break; case 'ride': - entityId = await createRide(item.item_data, dependencyMap); + entityId = await createRide(item.item_data, dependencyMap, sortedItems); break; case 'manufacturer': case 'operator': case 'property_owner': case 'designer': - entityId = await createCompany(item.item_data, item.item_type, dependencyMap); + entityId = await createCompany(item.item_data, item.item_type, dependencyMap, sortedItems); break; case 'ride_model': - entityId = await createRideModel(item.item_data, dependencyMap); + entityId = await createRideModel(item.item_data, dependencyMap, sortedItems); break; case 'photo': - entityId = await approvePhotos(item.item_data, dependencyMap, userId, item.submission_id); + entityId = await approvePhotos(item.item_data, dependencyMap, userId, item.submission_id, sortedItems); break; } @@ -343,7 +344,7 @@ function extractImageAssignments(images: any) { /** * Helper functions to create entities with dependency resolution */ -async function createPark(data: any, dependencyMap: Map): Promise { +async function createPark(data: any, dependencyMap: Map, sortedItems: SubmissionItemWithDeps[]): Promise { const { transformParkData, validateSubmissionData } = await import('./entityTransformers'); const { ensureUniqueSlug } = await import('./slugUtils'); @@ -352,7 +353,7 @@ async function createPark(data: any, dependencyMap: Map): Promis if (isEdit) { // Handle park edit - const resolvedData = resolveDependencies(data, dependencyMap); + const resolvedData = resolveDependencies(data, dependencyMap, sortedItems); // Resolve location_id if location data is provided let locationId = resolvedData.location_id; @@ -401,7 +402,7 @@ async function createPark(data: any, dependencyMap: Map): Promis // Handle park creation validateSubmissionData(data, 'Park'); - const resolvedData = resolveDependencies(data, dependencyMap); + const resolvedData = resolveDependencies(data, dependencyMap, sortedItems); // Resolve location_id if location data is provided let locationId = resolvedData.location_id; @@ -498,7 +499,7 @@ async function resolveLocationId(locationData: any): Promise { return newLocation.id; } -async function createRide(data: any, dependencyMap: Map): Promise { +async function createRide(data: any, dependencyMap: Map, sortedItems: SubmissionItemWithDeps[]): Promise { const { transformRideData, validateSubmissionData } = await import('./entityTransformers'); const { ensureUniqueSlug } = await import('./slugUtils'); @@ -507,7 +508,7 @@ async function createRide(data: any, dependencyMap: Map): Promis if (isEdit) { // Handle ride edit - const resolvedData = resolveDependencies(data, dependencyMap); + const resolvedData = resolveDependencies(data, dependencyMap, sortedItems); // Extract image assignments from ImageAssignments structure const imageData = extractImageAssignments(resolvedData.images); @@ -556,7 +557,7 @@ async function createRide(data: any, dependencyMap: Map): Promis // Handle ride creation validateSubmissionData(data, 'Ride'); - const resolvedData = resolveDependencies(data, dependencyMap); + const resolvedData = resolveDependencies(data, dependencyMap, sortedItems); if (!resolvedData.park_id) { throw new Error('Ride must be associated with a park'); @@ -588,7 +589,8 @@ async function createRide(data: any, dependencyMap: Map): Promis async function createCompany( data: any, companyType: string, - dependencyMap: Map + dependencyMap: Map, + sortedItems: SubmissionItemWithDeps[] ): Promise { const { transformCompanyData, validateSubmissionData } = await import('./entityTransformers'); const { ensureUniqueSlug } = await import('./slugUtils'); @@ -598,7 +600,7 @@ async function createCompany( if (isEdit) { // Handle company edit - const resolvedData = resolveDependencies(data, dependencyMap); + const resolvedData = resolveDependencies(data, dependencyMap, sortedItems); // Extract image assignments from ImageAssignments structure const imageData = extractImageAssignments(resolvedData.images); @@ -630,7 +632,7 @@ async function createCompany( // Handle company creation validateSubmissionData(data, 'Company'); - const resolvedData = resolveDependencies(data, dependencyMap); + const resolvedData = resolveDependencies(data, dependencyMap, sortedItems); const uniqueSlug = await ensureUniqueSlug(resolvedData.slug, 'companies'); resolvedData.slug = uniqueSlug; @@ -663,7 +665,7 @@ async function createCompany( return company.id; } -async function createRideModel(data: any, dependencyMap: Map): Promise { +async function createRideModel(data: any, dependencyMap: Map, sortedItems: SubmissionItemWithDeps[]): Promise { const { transformRideModelData, validateSubmissionData } = await import('./entityTransformers'); const { ensureUniqueSlug } = await import('./slugUtils'); @@ -672,7 +674,7 @@ async function createRideModel(data: any, dependencyMap: Map): P if (isEdit) { // Handle ride model edit - const resolvedData = resolveDependencies(data, dependencyMap); + const resolvedData = resolveDependencies(data, dependencyMap, sortedItems); // Extract image assignments from ImageAssignments structure const imageData = extractImageAssignments(resolvedData.images); @@ -704,7 +706,7 @@ async function createRideModel(data: any, dependencyMap: Map): P // Handle ride model creation validateSubmissionData(data, 'Ride Model'); - const resolvedData = resolveDependencies(data, dependencyMap); + const resolvedData = resolveDependencies(data, dependencyMap, sortedItems); // Validate manufacturer_id is present (required for ride models) if (!resolvedData.manufacturer_id) { @@ -739,10 +741,10 @@ async function createRideModel(data: any, dependencyMap: Map): P return model.id; } -async function approvePhotos(data: any, dependencyMap: Map, userId: string, submissionId: string): Promise { +async function approvePhotos(data: any, dependencyMap: Map, userId: string, submissionId: string, sortedItems: SubmissionItemWithDeps[]): Promise { // Photos are already uploaded to Cloudflare // Resolve dependencies for entity associations - const resolvedData = resolveDependencies(data, dependencyMap); + const resolvedData = resolveDependencies(data, dependencyMap, sortedItems); if (!resolvedData.photos || !Array.isArray(resolvedData.photos) || resolvedData.photos.length === 0) { throw new Error('No photos found in submission'); @@ -900,8 +902,11 @@ async function updateEntityFeaturedImage( /** * Resolve dependency references in item_data by looking up approved entity IDs * Replaces temporary references (_temp_*_ref) with actual database entity IDs + * + * FIXED: Now uses sortedItems array for stable index-based resolution + * instead of unreliable Array.from(dependencyMap.keys())[refIndex] */ -function resolveDependencies(data: any, dependencyMap: Map): any { +function resolveDependencies(data: any, dependencyMap: Map, sortedItems: SubmissionItemWithDeps[]): any { const resolved = { ...data }; // List of foreign key fields that may need resolution @@ -915,43 +920,62 @@ function resolveDependencies(data: any, dependencyMap: Map): any 'location_id', ]; - // Resolve temporary references first + // Resolve temporary references using sortedItems array (FIXED) if (resolved._temp_manufacturer_ref !== undefined) { const refIndex = resolved._temp_manufacturer_ref; - const refItemId = Array.from(dependencyMap.keys())[refIndex]; - if (refItemId && dependencyMap.has(refItemId)) { - resolved.manufacturer_id = dependencyMap.get(refItemId); + if (refIndex >= 0 && refIndex < sortedItems.length) { + const refItemId = sortedItems[refIndex].id; + if (dependencyMap.has(refItemId)) { + resolved.manufacturer_id = dependencyMap.get(refItemId); + } } delete resolved._temp_manufacturer_ref; } if (resolved._temp_operator_ref !== undefined) { const refIndex = resolved._temp_operator_ref; - const refItemId = Array.from(dependencyMap.keys())[refIndex]; - if (refItemId && dependencyMap.has(refItemId)) { - resolved.operator_id = dependencyMap.get(refItemId); + if (refIndex >= 0 && refIndex < sortedItems.length) { + const refItemId = sortedItems[refIndex].id; + if (dependencyMap.has(refItemId)) { + resolved.operator_id = dependencyMap.get(refItemId); + } } delete resolved._temp_operator_ref; } if (resolved._temp_property_owner_ref !== undefined) { const refIndex = resolved._temp_property_owner_ref; - const refItemId = Array.from(dependencyMap.keys())[refIndex]; - if (refItemId && dependencyMap.has(refItemId)) { - resolved.property_owner_id = dependencyMap.get(refItemId); + if (refIndex >= 0 && refIndex < sortedItems.length) { + const refItemId = sortedItems[refIndex].id; + if (dependencyMap.has(refItemId)) { + resolved.property_owner_id = dependencyMap.get(refItemId); + } } delete resolved._temp_property_owner_ref; } if (resolved._temp_ride_model_ref !== undefined) { const refIndex = resolved._temp_ride_model_ref; - const refItemId = Array.from(dependencyMap.keys())[refIndex]; - if (refItemId && dependencyMap.has(refItemId)) { - resolved.ride_model_id = dependencyMap.get(refItemId); + if (refIndex >= 0 && refIndex < sortedItems.length) { + const refItemId = sortedItems[refIndex].id; + if (dependencyMap.has(refItemId)) { + resolved.ride_model_id = dependencyMap.get(refItemId); + } } delete resolved._temp_ride_model_ref; } + if (resolved._temp_designer_ref !== undefined) { + const refIndex = resolved._temp_designer_ref; + if (refIndex >= 0 && refIndex < sortedItems.length) { + const refItemId = sortedItems[refIndex].id; + if (dependencyMap.has(refItemId)) { + resolved.designer_id = dependencyMap.get(refItemId); + } + } + delete resolved._temp_designer_ref; + } + // Resolve each foreign key if it's a submission item ID for (const key of foreignKeys) { if (resolved[key] && dependencyMap.has(resolved[key])) { diff --git a/supabase/functions/process-selective-approval/index.ts b/supabase/functions/process-selective-approval/index.ts index 616a3ed8..9d57afbc 100644 --- a/supabase/functions/process-selective-approval/index.ts +++ b/supabase/functions/process-selective-approval/index.ts @@ -1301,6 +1301,15 @@ async function createRideModel(supabase: any, data: any): Promise { if (error) throw new Error(`Failed to update ride model: ${error.message}`); } else { console.log('Creating new ride model'); + + // Validate required fields + if (!data.manufacturer_id) { + throw new Error('Ride model must be associated with a manufacturer'); + } + if (!data.name || !data.slug) { + throw new Error('Ride model must have a name and slug'); + } + const sanitizedData = sanitizeDateFields(data); const filteredData = filterDatabaseFields(sanitizedData, RIDE_MODEL_FIELDS); const { data: model, error } = await supabase