diff --git a/docs/logging/SUBMISSION_FLOW_LOGGING.md b/docs/logging/SUBMISSION_FLOW_LOGGING.md index 01a6276f..db5c6f4f 100644 --- a/docs/logging/SUBMISSION_FLOW_LOGGING.md +++ b/docs/logging/SUBMISSION_FLOW_LOGGING.md @@ -8,7 +8,71 @@ The submission flow has structured logging at each critical stage to enable debu ## Logging Stages -### 1. Edit Stage +### 1. Location Selection Stage +**Location**: `src/components/admin/ParkForm.tsx` → `LocationSearch.onLocationSelect()` + +**Log Points**: +- Location selected from search (when user picks from dropdown) +- Location set in form state (confirmation of setValue) + +**Log Format**: +```typescript +console.info('[ParkForm] Location selected:', { + name: string, + city: string | undefined, + state_province: string | undefined, + country: string, + latitude: number, + longitude: number, + display_name: string +}); + +console.info('[ParkForm] Location set in form:', locationObject); +``` + +### 2. Form Submission Stage +**Location**: `src/components/admin/ParkForm.tsx` → `handleFormSubmit()` + +**Log Points**: +- Form data being submitted (what's being passed to submission helper) + +**Log Format**: +```typescript +console.info('[ParkForm] Submitting park data:', { + hasLocation: boolean, + hasLocationId: boolean, + locationData: object | undefined, + parkName: string, + isEditing: boolean +}); +``` + +### 3. Submission Helper Reception Stage +**Location**: `src/lib/entitySubmissionHelpers.ts` → `submitParkCreation()` + +**Log Points**: +- Data received by submission helper (what arrived from form) +- Data being saved to database (temp_location_data structure) + +**Log Format**: +```typescript +console.info('[submitParkCreation] Received data:', { + hasLocation: boolean, + hasLocationId: boolean, + locationData: object | undefined, + parkName: string, + hasComposite: boolean +}); + +console.info('[submitParkCreation] Saving to park_submissions:', { + name: string, + hasLocation: boolean, + hasLocationId: boolean, + temp_location_data: object | null +}); +``` + +### 4. Edit Stage **Location**: `src/lib/submissionItemsService.ts` → `updateSubmissionItem()` **Log Points**: @@ -35,7 +99,7 @@ console.info('[Submission Flow] Saving park data', { }); ``` -### 2. Validation Stage +### 5. Validation Stage **Location**: `src/hooks/moderation/useModerationActions.ts` → `handleApproveSubmission()` **Log Points**: @@ -66,7 +130,7 @@ console.warn('[Submission Flow] Validation found blocking errors', { }); ``` -### 3. Approval Stage +### 6. Approval Stage **Location**: `src/lib/submissionItemsService.ts` → `approveSubmissionItems()` **Log Points**: @@ -99,36 +163,65 @@ console.info('[Submission Flow] Processing item for approval', { ### Park Location Data The most critical transformation logged is the park location data flow: -1. **Database Storage**: `temp_location_data` (JSONB in park_submissions) -2. **Display/Edit**: `location` (transformed for form compatibility) -3. **Validation**: `location` (transformed from temp_location_data) -4. **Save**: `temp_location_data` (transformed back for storage) -5. **Approval**: `location` (transformed from temp_location_data) +1. **User Selection** (LocationSearch): OpenStreetMap result → `location` object +2. **Form State** (ParkForm): `setValue('location', location)` +3. **Form Submission** (ParkForm → submitParkCreation): `data.location` passed in submission +4. **Database Storage** (submitParkCreation): `data.location` → `temp_location_data` (JSONB in park_submissions) +5. **Display/Edit**: `temp_location_data` → `location` (transformed for form compatibility) +6. **Validation**: `temp_location_data` → `location` (transformed for schema validation) +7. **Approval**: `location` used to create actual location record -**Why this matters**: Location validation errors typically indicate a break in this transformation chain. +**Why this matters**: +- If location is NULL in database but user selected one → Check stages 1-4 +- If validation fails with "Location is required" → Check stages 5-6 +- Location validation errors typically indicate a break in this transformation chain. ## Debugging Workflow -### To debug location validation errors: +### To debug "Location is required" validation errors: -1. **Check browser console** for `[Submission Flow]` logs +1. **Check browser console** for `[ParkForm]` and `[Submission Flow]` logs 2. **Verify data at each stage**: ```javascript - // Edit stage - should show temp_location_data being saved + // Stage 1: Location selection + [ParkForm] Location selected: { name: "Farmington, Utah", latitude: 40.98, ... } + [ParkForm] Location set in form: { name: "Farmington, Utah", ... } + + // Stage 2: Form submission + [ParkForm] Submitting park data { hasLocation: true, locationData: {...} } + + // Stage 3: Submission helper receives data + [submitParkCreation] Received data { hasLocation: true, locationData: {...} } + [submitParkCreation] Saving to park_submissions { temp_location_data: {...} } + + // Stage 4: Edit stage (if moderator edits later) [Submission Flow] Saving park data { hasLocation: true, locationData: {...} } - // Validation stage - should show location after transformation + // Stage 5: Validation stage [Submission Flow] Transformed park data { hasLocation: true, transformedHasLocation: true } - // Approval stage - should show location present + // Stage 6: Approval stage [Submission Flow] Processing item { hasLocation: true, locationData: {...} } ``` 3. **Look for missing data**: - - If `hasLocation: false` in "Saving park data" → Edit form didn't capture location + - If `[ParkForm] Location selected` missing → User didn't select location from dropdown + - If `hasLocation: false` in form submission → Location not set in form state (possible React Hook Form issue) + - If `hasLocation: true` in submission but NULL in database → Database write failed (check errors) - If `hasLocation: true` but `transformedHasLocation: false` → Transformation failed - If validation logs missing → Check database query/fetch +### To debug NULL location in new submissions: + +1. **Open browser console** before creating submission +2. **Select location** and verify `[ParkForm] Location selected` appears +3. **Submit form** and verify `[ParkForm] Submitting park data` shows `hasLocation: true` +4. **Check** `[submitParkCreation] Saving to park_submissions` shows `temp_location_data` is not null +5. **If location was selected but is NULL in database**: + - Form state was cleared (page refresh/navigation before submit) + - React Hook Form setValue didn't work (check "Location set in form" log) + - Database write succeeded but data was lost (check for errors) + ## Error Logging Integration Structured errors use the `handleError()` utility from `@/lib/errorHandler`: diff --git a/src/components/admin/ParkForm.tsx b/src/components/admin/ParkForm.tsx index a3b88d9b..728e8d50 100644 --- a/src/components/admin/ParkForm.tsx +++ b/src/components/admin/ParkForm.tsx @@ -271,13 +271,24 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: (tempNewPropertyOwner ? undefined : selectedPropertyOwnerId); } - await onSubmit({ + // Debug: Log what's being submitted + const submissionData = { ...data, operator_id: finalOperatorId, property_owner_id: finalPropertyOwnerId, _compositeSubmission: (tempNewOperator || tempNewPropertyOwner) ? submissionContent : undefined + }; + + console.info('[ParkForm] Submitting park data:', { + hasLocation: !!submissionData.location, + hasLocationId: !!submissionData.location_id, + locationData: submissionData.location, + parkName: submissionData.name, + isEditing }); + await onSubmit(submissionData); + // Parent component handles success feedback } catch (error: unknown) { const errorMessage = getErrorMessage(error); @@ -426,7 +437,9 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: { + console.info('[ParkForm] Location selected:', location); setValue('location', location); + console.info('[ParkForm] Location set in form:', watch('location')); // Manually trigger validation for the location field trigger('location'); }} diff --git a/src/lib/entitySubmissionHelpers.ts b/src/lib/entitySubmissionHelpers.ts index f539d8b7..6793164d 100644 --- a/src/lib/entitySubmissionHelpers.ts +++ b/src/lib/entitySubmissionHelpers.ts @@ -557,6 +557,14 @@ export async function submitParkCreation( data: ParkFormData & { _compositeSubmission?: any }, userId: string ): Promise<{ submitted: boolean; submissionId: string }> { + console.info('[submitParkCreation] Received data:', { + hasLocation: !!data.location, + hasLocationId: !!data.location_id, + locationData: data.location, + parkName: data.name, + hasComposite: !!data._compositeSubmission + }); + // Validate required fields client-side assertValid(validateParkCreateFields(data)); @@ -663,6 +671,25 @@ export async function submitParkCreation( const cardImage = (cardIndex !== null && cardIndex !== undefined) ? uploadedImages[cardIndex] : null; // Insert into relational park_submissions table + const tempLocationData = data.location ? { + name: data.location.name, + city: data.location.city || null, + state_province: data.location.state_province || null, + country: data.location.country, + latitude: data.location.latitude, + longitude: data.location.longitude, + timezone: data.location.timezone || null, + postal_code: data.location.postal_code || null, + display_name: data.location.display_name + } : null; + + console.info('[submitParkCreation] Saving to park_submissions:', { + name: data.name, + hasLocation: !!data.location, + hasLocationId: !!data.location_id, + temp_location_data: tempLocationData + }); + const { data: parkSubmission, error: parkSubmissionError } = await supabase .from('park_submissions' as any) .insert({ @@ -680,17 +707,7 @@ export async function submitParkCreation( operator_id: data.operator_id || null, property_owner_id: data.property_owner_id || null, location_id: data.location_id || null, - temp_location_data: data.location ? { - name: data.location.name, - city: data.location.city || null, - state_province: data.location.state_province || null, - country: data.location.country, - latitude: data.location.latitude, - longitude: data.location.longitude, - timezone: data.location.timezone || null, - postal_code: data.location.postal_code || null, - display_name: data.location.display_name - } : null, + temp_location_data: tempLocationData, banner_image_url: bannerImage?.url || data.banner_image_url || null, banner_image_id: bannerImage?.cloudflare_id || data.banner_image_id || null, card_image_url: cardImage?.url || data.card_image_url || null,