Fix error propagation in forms

This commit is contained in:
gpt-engineer-app[bot]
2025-11-03 16:51:49 +00:00
parent 700c29c910
commit 12de4e2ec1
8 changed files with 71 additions and 16 deletions

View File

@@ -122,6 +122,9 @@ export function EntityForm({ onSubmit, onCancel, initialData }: EntityFormProps)
action: initialData?.id ? 'Update Entity' : 'Create Entity',
metadata: { entityName: data.name }
});
// ⚠️ CRITICAL: Re-throw so parent can handle modal state
throw error;
}
})}>
{/* Form fields */}
@@ -177,33 +180,64 @@ export function EntityListPage() {
## Error Handling
### Form-Level Errors
Forms use `handleError` utility for errors:
### ⚠️ CRITICAL: Error Propagation Pattern
Forms MUST re-throw errors after logging them so parent components can respond appropriately (keep modals open, show additional context, etc.).
**Forms MUST re-throw errors:**
```typescript
try {
await onSubmit(data);
} catch (error: unknown) {
// Log error for debugging and show toast to user
handleError(error, {
action: 'Create Park',
action: 'Submit Park',
userId: user?.id,
metadata: { parkName: data.name }
});
// ⚠️ CRITICAL: Re-throw so parent can handle modal state
throw error;
}
```
### Parent-Level Errors
Parent pages can catch errors for additional handling:
**Why Re-throw?**
- Parent needs to know submission failed
- Modal should stay open so user can retry
- User can fix validation issues and resubmit
- Prevents "success" behavior on failures
- Maintains proper error flow through the app
### Parent-Level Error Handling
```typescript
try {
await submitEntityCreation(data, user.id);
toast({ title: "Success" });
setIsModalOpen(false);
} catch (error) {
// Form already showed error toast
// Parent can log or handle specific cases
if (error.code === 'DUPLICATE_SLUG') {
// Handle specific error
const handleParkSubmit = async (data: FormData) => {
try {
await submitParkCreation(data, user.id);
toast.success('Park submitted for review');
setIsModalOpen(false); // Only close on success
} catch (error) {
// Error already toasted by form via handleError()
// Modal stays open automatically because we don't close it
// User can fix issues and retry
console.error('Submission failed:', error);
}
};
```
**Expected Error Flow:**
1. User submits form → `onSubmit()` called
2. Submission fails → Form catches error
3. Form shows error toast via `handleError()`
4. Form re-throws error to parent
5. Parent's catch block executes
6. Modal stays open (no `setIsModalOpen(false)`)
7. User fixes issue and tries again
**Common Mistake:**
```typescript
// ❌ WRONG - Error not re-thrown, parent never knows
} catch (error: unknown) {
handleError(error, { action: 'Submit' });
// Missing: throw error;
}
```

View File

@@ -94,6 +94,9 @@ export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormPr
action: initialData?.id ? 'Update Designer' : 'Create Designer',
metadata: { companyName: data.name }
});
// Re-throw so parent can handle modal closing
throw error;
}
})} className="space-y-6">
{/* Basic Information */}

View File

@@ -98,6 +98,9 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur
action: initialData?.id ? 'Update Manufacturer' : 'Create Manufacturer',
metadata: { companyName: data.name }
});
// Re-throw so parent can handle modal closing
throw error;
}
})} className="space-y-6">
{/* Basic Information */}

View File

@@ -94,6 +94,9 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr
action: initialData?.id ? 'Update Operator' : 'Create Operator',
metadata: { companyName: data.name }
});
// Re-throw so parent can handle modal closing
throw error;
}
})} className="space-y-6">
{/* Basic Information */}

View File

@@ -274,6 +274,9 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
hasNewOwner: !!tempNewPropertyOwner
}
});
// Re-throw so parent can handle modal closing
throw error;
}
};

View File

@@ -94,6 +94,9 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO
action: initialData?.id ? 'Update Property Owner' : 'Create Property Owner',
metadata: { companyName: data.name }
});
// Re-throw so parent can handle modal closing
throw error;
}
})} className="space-y-6">
{/* Basic Information */}

View File

@@ -352,6 +352,9 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
hasNewModel: !!tempNewRideModel
}
});
// Re-throw so parent can handle modal closing
throw error;
}
};

View File

@@ -113,6 +113,9 @@ export function RideModelForm({
handleError(error, {
action: initialData?.id ? 'Update Ride Model' : 'Create Ride Model'
});
// Re-throw so parent can handle modal closing
throw error;
}
};