7.1 KiB
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
→ Live entities created
Example:
// 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_submissionsfor 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:
- Pass Zod schema validation (
entityValidationSchemas.ts) - Have proper slug generation (unique, URL-safe)
- Include source URLs when applicable
- Pass duplicate detection checks
Entity-Specific Requirements:
Parks:
- Valid
park_typeenum - Valid location data (country required)
- Opening date format validation
Rides:
- Must reference valid
park_id - Valid
ride_typeenum - Opening date validation
Companies:
- Valid
company_typeenum - Country code validation
- Founded year range check
Dependency Resolution
Dependency Types:
- Same-submission dependencies: New park references new operator (both in queue)
- Existing entity dependencies: New ride references existing park
- 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:
- Create type definition in
src/types/moderation.ts - Add type guard to
src/lib/moderation/typeGuards.ts - Create validation schema in
src/lib/entityValidationSchemas.ts - Add submission helper in
src/lib/entitySubmissionHelpers.ts - Update
useModerationQueueManagerquery to fetch new type - Create renderer component (optional, for complex UI)
- Add tests for new type
Example: Adding "Event" Submission Type
// 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
SubmissionItemDatatypes - 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.mdfor system design - See
COMPONENTS.mdfor UI component usage - See
../IMPLEMENTATION_COMPLETE.mdfor recent changes