mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-27 09:06:59 -05:00
feat: Implement all 7 phases
This commit is contained in:
261
docs/moderation/SUBMISSION_PATTERNS.md
Normal file
261
docs/moderation/SUBMISSION_PATTERNS.md
Normal file
@@ -0,0 +1,261 @@
|
||||
# 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:**
|
||||
```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
|
||||
Reference in New Issue
Block a user