# Submission Patterns & Guidelines ## Overview This document outlines the patterns and best practices for working with submissions in the moderation queue system. ## Submission Types ### 1. Content Submissions (`content_submissions`) **When to use:** - Creating or updating parks, rides, companies, ride models - Multi-item submissions with dependencies - Submissions requiring moderator review before going live **Data Flow:** ``` User Form → validateEntityData() → createSubmission() → content_submissions table → submission_items table (with dependencies) → Moderation Queue → Approval → process-selective-approval edge function (atomic transaction RPC) → Live entities created (all-or-nothing via PostgreSQL transaction) ``` **Example:** ```typescript // Creating a park with operator dependency const { success } = await createParkSubmission({ name: "Cedar Point", park_type: "theme_park", operator_id: "new_operator_123", // References another item in same submission }); ``` ### 2. Photo Submissions (`photo_submissions`) **When to use:** - User uploading photos to existing entities - Photos require moderation but entity already exists **Data Flow:** ``` UppyPhotoSubmissionUpload → Cloudflare Direct Upload → photo_submissions + photo_submission_items tables → Moderation Queue → Approval → Photos linked to entity ``` **Key Requirements:** - Must be linked to parent `content_submissions` for queue integration - Caption and title sanitized (plain text only, no HTML) - Maximum 10 photos per submission ### 3. Reviews (`reviews`) **When to use:** - User reviewing a park or ride - Rating with optional text content **Data Flow:** ``` ReviewForm → reviews table + content_submissions (NEW) → Moderation Queue → Approval → Review goes live ``` **Sanitization:** - All review content is plain text (HTML stripped) - Maximum 5000 characters - Rating validation (0.5-5.0 scale) ## When to Use Each Table ### Use `content_submissions` when: ✅ Creating new entities (parks, rides, companies) ✅ Updating existing entities ✅ Submissions have multi-item dependencies ✅ Need moderator review before data goes live ### Use Specialized Tables when: ✅ **Photos**: Entity exists, just adding media (`photo_submissions`) ✅ **Reviews**: User feedback on existing entity (`reviews` + `content_submissions`) ✅ **Technical Specs**: Belongs to specific entity (`ride_technical_specifications`) ## Validation Requirements ### All Submissions Must: 1. Pass Zod schema validation (`entityValidationSchemas.ts`) 2. Have proper slug generation (unique, URL-safe) 3. Include source URLs when applicable 4. Pass duplicate detection checks ### Entity-Specific Requirements: **Parks:** - Valid `park_type` enum - Valid location data (country required) - Opening date format validation **Rides:** - Must reference valid `park_id` - Valid `ride_type` enum - Opening date validation **Companies:** - Valid `company_type` enum - Country code validation - Founded year range check ## Dependency Resolution ### Dependency Types: 1. **Same-submission dependencies**: New park references new operator (both in queue) 2. **Existing entity dependencies**: New ride references existing park 3. **Multi-level dependencies**: Ride → Park → Operator → Owner (4 levels) ### Resolution Order: Dependencies are resolved using topological sorting: ``` 1. Load all items in submission 2. Build dependency graph 3. Sort topologically (parents before children) 4. Process in order ``` **Example:** ``` Submission contains: - Item A: Operator (no dependencies) - Item B: Park (depends on A) - Item C: Ride (depends on B) Processing order: A → B → C ``` ## Best Practices ### DO: ✅ Use existing entities when possible (avoid duplicates) ✅ Provide source URLs for verifiability ✅ Write clear submission notes for moderators ✅ Validate data on client-side before submission ✅ Use type guards when working with `SubmissionItemData` ### DON'T: ❌ Store JSON blobs in SQL columns ❌ Skip validation to "speed up" submissions ❌ Create dependencies to non-existent entities ❌ Submit without source verification ❌ Bypass moderation queue (security risk) ## Adding New Submission Types ### Steps: 1. Create type definition in `src/types/moderation.ts` 2. Add type guard to `src/lib/moderation/typeGuards.ts` 3. Create validation schema in `src/lib/entityValidationSchemas.ts` 4. Add submission helper in `src/lib/entitySubmissionHelpers.ts` 5. Update `useModerationQueueManager` query to fetch new type 6. Create renderer component (optional, for complex UI) 7. Add tests for new type ### Example: Adding "Event" Submission Type ```typescript // 1. Type definition (moderation.ts) export interface EventItemData { event_id?: string; name: string; park_id: string; start_date: string; end_date: string; } export type SubmissionItemData = | ParkItemData | RideItemData | EventItemData; // Add here // 2. Type guard (typeGuards.ts) export function isEventItemData(data: SubmissionItemData): data is EventItemData { return 'start_date' in data && 'end_date' in data; } // 3. Validation (entityValidationSchemas.ts) const eventSchema = z.object({ name: z.string().min(1).max(200), park_id: z.string().uuid(), start_date: z.string().datetime(), end_date: z.string().datetime(), }); // 4. Submission helper (entitySubmissionHelpers.ts) export async function createEventSubmission(eventData: EventFormData) { // Validation, submission creation logic } // 5. Update queue query to include events // (already handles all content_submissions) // 6. Optional: Create EventSubmissionDisplay component // 7. Add tests ``` ## Migration Checklist When migrating legacy code to this pattern: - [ ] Remove direct database writes (use submission helpers) - [ ] Add validation schemas - [ ] Update to use `SubmissionItemData` types - [ ] Add type guards where needed - [ ] Test dependency resolution - [ ] Verify sanitization is applied - [ ] Update documentation ## Security Considerations ### Input Validation: - **Server-side validation** is mandatory (Zod schemas) - **Client-side validation** for UX only - **Never trust user input** - always validate and sanitize ### Sanitization: - HTML stripped from user text (use `rehype-sanitize`) - URLs validated and optionally stripped - File uploads validated (type, size, count) - SQL injection prevented (Supabase parameterized queries) ### Access Control: - Only moderators can approve/reject - Users can only submit, not self-approve - RLS policies enforce row-level security - Lock system prevents concurrent modifications ## Troubleshooting ### Common Issues: **"Dependency not found"** → Check if parent entity exists in database or in same submission **"Validation failed"** → Check Zod schema, ensure all required fields present **"Duplicate slug"** → Slug generation collided, system will auto-increment **"Lock expired"** → Moderator must re-claim submission to continue **"Permission denied"** → Check user role (must be moderator/admin) ## References - See `ARCHITECTURE.md` for system design - See `COMPONENTS.md` for UI component usage - See `../IMPLEMENTATION_COMPLETE.md` for recent changes