# Form Submission Patterns ## Overview This document defines the standard patterns for handling form submissions, toast notifications, and modal behavior across ThrillWiki. ## Core Principles ### Separation of Concerns - **Forms** handle UI, validation, and data collection - **Parent Pages** handle submission logic and user feedback - **Submission Helpers** handle database operations ### Single Source of Truth - Only parent pages show success toasts - Forms should not assume submission outcomes - Modal closing is controlled by parent after successful submission ## Toast Notification Rules ### ✅ DO **Parent Pages Show Toasts** ```typescript const handleParkSubmit = async (data: FormData) => { try { await submitParkCreation(data, user.id); toast({ title: "Park Submitted", description: "Your submission has been sent for review." }); setIsModalOpen(false); // Close modal after success } catch (error) { // Error already handled by form via handleError utility } }; ``` **Use Correct Terminology** - ✅ "Submitted for review" (for new entities) - ✅ "Edit submitted" (for updates) - ❌ "Created" or "Updated" (implies immediate approval) **Conditional Toast in Forms (Only for standalone usage)** ```typescript // Only show toast if NOT being called from a parent handler if (!initialData?.id) { toast.success('Designer submitted for review'); onCancel(); } ``` ### ❌ DON'T **Forms Should NOT Show Success Toasts for Main Submissions** ```typescript // ❌ WRONG - Form doesn't know if submission succeeded const handleFormSubmit = async (data: FormData) => { await onSubmit(data); toast({ title: "Park Created", // ❌ Misleading terminology description: "The new park has been created successfully." }); }; ``` **Duplicate Toasts** ```typescript // ❌ WRONG - Both form and parent showing toasts // Form: toast({ title: "Park Created" }); // Parent: toast({ title: "Park Submitted" }); ``` ## Modal Behavior ### Expected Flow 1. User fills form and clicks submit 2. Form validates and calls `onSubmit` prop 3. Parent page handles submission 4. Parent shows appropriate toast 5. Parent closes modal via `setIsModalOpen(false)` ### Common Issues **Issue**: Modal doesn't close after submission **Cause**: Form is showing a toast that interferes with normal flow **Solution**: Remove form-level success toasts **Issue**: User sees "Created" but item isn't visible **Cause**: Using wrong terminology - submissions go to moderation **Solution**: Use "Submitted for review" instead of "Created" ## Form Component Template ```typescript export function EntityForm({ onSubmit, onCancel, initialData }: EntityFormProps) { const { user } = useAuth(); const { register, handleSubmit, /* ... */ } = useForm({ // ... form config }); return (
); } ``` ## Parent Page Template ```typescript export function EntityListPage() { const [isModalOpen, setIsModalOpen] = useState(false); const handleEntitySubmit = async (data: FormData) => { try { const result = await submitEntityCreation(data, user.id); // ✅ Parent shows success feedback toast({ title: "Entity Submitted", description: "Your submission has been sent for review." }); // ✅ Parent closes modal setIsModalOpen(false); // ✅ Parent refreshes data queryClient.invalidateQueries(['entities']); } catch (error) { // Form already showed error via handleError // Parent can optionally add additional handling console.error('Submission failed:', error); } }; return ( <> > ); } ``` ## Error Handling ### Form-Level Errors Forms use `handleError` utility for errors: ```typescript try { await onSubmit(data); } catch (error: unknown) { handleError(error, { action: 'Create Park', userId: user?.id, metadata: { parkName: data.name } }); } ``` ### Parent-Level Errors Parent pages can catch errors for additional 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 } } ``` ## Current Implementation Status ### ✅ Correct Implementation - `DesignerForm.tsx` - Shows "Designer submitted for review" only when `!initialData?.id` - `OperatorForm.tsx` - Shows "Operator submitted for review" only when `!initialData?.id` - `PropertyOwnerForm.tsx` - Shows "Property owner submitted for review" only when `!initialData?.id` - `ManufacturerForm.tsx` - Shows "Manufacturer submitted for review" only when `!initialData?.id` - `RideModelForm.tsx` - No toasts, parent handles everything - `RideForm.tsx` - Shows "Submission Sent" with conditional description - `ParkForm.tsx` - Fixed to remove premature success toast ### Parent Pages - `Parks.tsx` - Shows "Park Submitted" ✅ - `Operators.tsx` - Shows "Operator Submitted" ✅ - `Designers.tsx` - Shows "Designer Submitted" ✅ - `Manufacturers.tsx` - Shows "Manufacturer Submitted" ✅ - `ParkDetail.tsx` - Shows "Submission Sent" ✅ ## Testing Checklist When implementing or updating a form: - [ ] Form validates input correctly - [ ] Form calls `onSubmit` prop with clean data - [ ] Form only shows error toasts, not success toasts (unless standalone) - [ ] Parent page shows appropriate success toast - [ ] Success toast uses correct terminology ("submitted" not "created") - [ ] Modal closes after successful submission - [ ] User sees single toast, not duplicates - [ ] Error handling provides actionable feedback - [ ] Form can be used both in modals and standalone ## Related Files - `src/lib/errorHandler.ts` - Error handling utilities - `src/lib/entitySubmissionHelpers.ts` - Submission logic - `src/hooks/use-toast.ts` - Toast notification hook - `tests/e2e/submission/park-creation.spec.ts` - E2E tests for submission flow