mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 17:51:13 -05:00
487 lines
15 KiB
Markdown
487 lines
15 KiB
Markdown
# Comprehensive Frontend-Backend Feature Parity Audit
|
|
|
|
**Date:** 2025-11-09
|
|
**Auditor:** Cline
|
|
**Scope:** Complete comparison of Django backend vs Supabase schema + Frontend usage analysis
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
### Overall Status: 85% Feature Complete
|
|
|
|
**What Works:**
|
|
- ✅ All core entities (Parks, Rides, Companies, Ride Models)
|
|
- ✅ Sacred Pipeline (Form → Submission → Moderation → Approval → Versioning)
|
|
- ✅ Reviews with helpful votes
|
|
- ✅ User ride credits & top lists
|
|
- ✅ Photos with CloudFlare integration
|
|
- ✅ Complete moderation system
|
|
- ✅ pghistory-based versioning (SUPERIOR to Supabase)
|
|
- ✅ Search with PostgreSQL GIN indexes
|
|
|
|
**Critical Issues Found:**
|
|
1. 🔴 **BUG:** Park coordinate updates don't work (2 hour fix)
|
|
2. 🔴 **MISSING:** Ride Name History model (heavily used by frontend)
|
|
3. 🔴 **MISSING:** Entity Timeline Events (frontend has timeline manager)
|
|
4. 🔴 **MISSING:** Reports System (frontend has reporting UI)
|
|
5. 🟡 **MISSING:** Blog Posts (if part of MVP)
|
|
6. 🟡 **MISSING:** Contact Submissions (if part of MVP)
|
|
|
|
---
|
|
|
|
## 1. Core Entities Analysis
|
|
|
|
### ✅ FULLY IMPLEMENTED
|
|
|
|
| Entity | Supabase | Django | Notes |
|
|
|--------|----------|--------|-------|
|
|
| Companies | ✅ | ✅ | Django uses M2M for company_types (better than JSONB) |
|
|
| Locations | ✅ | ✅ | Country/Subdivision/Locality models |
|
|
| Parks | ✅ | ✅ | All fields present, coordinate update bug found |
|
|
| Rides | ✅ | ✅ | All type-specific fields as nullable on main model |
|
|
| Ride Models | ✅ | ✅ | Complete implementation |
|
|
| Reviews | ✅ | ✅ | With helpful votes |
|
|
| User Ride Credits | ✅ | ✅ | Complete |
|
|
| User Top Lists | ✅ | ✅ | Relational structure |
|
|
| Profiles | ✅ | ✅ | Django User model |
|
|
|
|
---
|
|
|
|
## 2. Sacred Pipeline Analysis
|
|
|
|
### ✅ FULLY OPERATIONAL
|
|
|
|
**Django Implementation:**
|
|
- `ContentSubmission` - Main submission container
|
|
- `SubmissionItem` - Individual items in submission
|
|
- Polymorphic submission services per entity type
|
|
- Complete moderation queue
|
|
- Lock system prevents conflicts
|
|
- Audit trail via pghistory
|
|
|
|
**Supabase Schema:**
|
|
- Separate tables per entity type (park_submissions, ride_submissions, etc.)
|
|
- submission_items for tracking
|
|
|
|
**Verdict:** ✅ Django's unified approach is SUPERIOR
|
|
|
|
---
|
|
|
|
## 3. Versioning & History
|
|
|
|
### ✅ DJANGO SUPERIOR
|
|
|
|
**Django:**
|
|
- pghistory tracks ALL changes automatically
|
|
- No manual version table management
|
|
- Complete audit trail
|
|
- Rollback capability
|
|
|
|
**Supabase:**
|
|
- Manual version tables (park_versions, ride_versions, etc.)
|
|
- entity_versions, entity_field_history
|
|
- version_diffs for comparisons
|
|
|
|
**Verdict:** ✅ Django's pghistory approach is better
|
|
|
|
---
|
|
|
|
## 4. Critical Missing Features (Frontend Actively Uses)
|
|
|
|
### 🔴 1. RIDE NAME HISTORY - CRITICAL
|
|
|
|
**Supabase Tables:**
|
|
- `ride_name_history` - tracks former names
|
|
- `ride_former_names` - same table, two names in schema
|
|
|
|
**Frontend Usage:** (34 results in search)
|
|
- `RideDetail.tsx` - Displays "formerly known as" section
|
|
- `FormerNamesSection.tsx` - Dedicated component
|
|
- `FormerNamesEditor.tsx` - Admin editing interface
|
|
- `RideForm.tsx` - Form handling
|
|
- `entitySubmissionHelpers.ts` - Submission logic
|
|
|
|
**Impact:** Every ride detail page with name changes is broken
|
|
|
|
**Django Status:** ❌ Missing completely
|
|
|
|
**Required Implementation:**
|
|
```python
|
|
class RideNameHistory(models.Model):
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
|
|
ride = models.ForeignKey('Ride', on_delete=models.CASCADE,
|
|
related_name='name_history')
|
|
former_name = models.CharField(max_length=255)
|
|
from_year = models.IntegerField(null=True, blank=True)
|
|
to_year = models.IntegerField(null=True, blank=True)
|
|
date_changed = models.DateField(null=True, blank=True)
|
|
date_changed_precision = models.CharField(max_length=20,
|
|
null=True, blank=True)
|
|
reason = models.TextField(null=True, blank=True)
|
|
order_index = models.IntegerField(null=True, blank=True)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
```
|
|
|
|
**Estimated Effort:** 4 hours
|
|
- Model creation + migration
|
|
- Admin interface
|
|
- API endpoint (list name history for ride)
|
|
- Integration with submission system
|
|
|
|
---
|
|
|
|
### 🔴 2. ENTITY TIMELINE EVENTS - CRITICAL
|
|
|
|
**Supabase Table:**
|
|
- `entity_timeline_events`
|
|
|
|
**Frontend Usage:** (5 files)
|
|
- `EntityTimelineManager.tsx` - Full timeline management
|
|
- `entitySubmissionHelpers.ts` - Sacred Pipeline integration
|
|
- `systemActivityService.ts` - Activity tracking
|
|
|
|
**Impact:** Historical milestone tracking completely non-functional
|
|
|
|
**Django Status:** ❌ Missing completely
|
|
|
|
**Required Implementation:**
|
|
```python
|
|
class EntityTimelineEvent(models.Model):
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
|
|
entity_id = models.UUIDField(db_index=True)
|
|
entity_type = models.CharField(max_length=50, db_index=True)
|
|
event_type = models.CharField(max_length=100)
|
|
event_date = models.DateField()
|
|
event_date_precision = models.CharField(max_length=20, null=True)
|
|
title = models.CharField(max_length=255)
|
|
description = models.TextField(null=True, blank=True)
|
|
|
|
# Event details
|
|
from_entity_id = models.UUIDField(null=True, blank=True)
|
|
to_entity_id = models.UUIDField(null=True, blank=True)
|
|
from_location = models.ForeignKey('Location', null=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+')
|
|
to_location = models.ForeignKey('Location', null=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+')
|
|
from_value = models.TextField(null=True, blank=True)
|
|
to_value = models.TextField(null=True, blank=True)
|
|
|
|
# Moderation
|
|
is_public = models.BooleanField(default=True)
|
|
display_order = models.IntegerField(null=True, blank=True)
|
|
|
|
# Tracking
|
|
created_by = models.ForeignKey(User, null=True,
|
|
on_delete=models.SET_NULL)
|
|
approved_by = models.ForeignKey(User, null=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+')
|
|
submission = models.ForeignKey('moderation.ContentSubmission',
|
|
null=True, on_delete=models.SET_NULL)
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
```
|
|
|
|
**Estimated Effort:** 6 hours
|
|
- Model + migration
|
|
- Submission service integration
|
|
- API endpoints (CRUD + list by entity)
|
|
- Admin interface
|
|
|
|
---
|
|
|
|
### 🔴 3. REPORTS SYSTEM - CRITICAL
|
|
|
|
**Supabase Table:**
|
|
- `reports`
|
|
|
|
**Frontend Usage:** (7 files)
|
|
- `ReportButton.tsx` - User reporting interface
|
|
- `ReportsQueue.tsx` - Moderator queue
|
|
- `RecentActivity.tsx` - Dashboard
|
|
- `useModerationStats.ts` - Statistics
|
|
- `systemActivityService.ts` - System tracking
|
|
|
|
**Impact:** No user reporting capability, community moderation broken
|
|
|
|
**Django Status:** ❌ Missing completely
|
|
|
|
**Required Implementation:**
|
|
```python
|
|
class Report(models.Model):
|
|
STATUS_CHOICES = [
|
|
('pending', 'Pending'),
|
|
('reviewing', 'Under Review'),
|
|
('resolved', 'Resolved'),
|
|
('dismissed', 'Dismissed'),
|
|
]
|
|
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
|
|
report_type = models.CharField(max_length=50)
|
|
reported_entity_id = models.UUIDField(db_index=True)
|
|
reported_entity_type = models.CharField(max_length=50, db_index=True)
|
|
|
|
reporter = models.ForeignKey(User, on_delete=models.CASCADE,
|
|
related_name='reports_filed')
|
|
reason = models.TextField(null=True, blank=True)
|
|
|
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES,
|
|
default='pending', db_index=True)
|
|
reviewed_by = models.ForeignKey(User, null=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='reports_reviewed')
|
|
reviewed_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
indexes = [
|
|
models.Index(fields=['status', 'created_at']),
|
|
models.Index(fields=['reported_entity_type', 'reported_entity_id']),
|
|
]
|
|
```
|
|
|
|
**Estimated Effort:** 8 hours
|
|
- Model + migration
|
|
- API endpoints (create, list, update status)
|
|
- Integration with moderation system
|
|
- Admin interface
|
|
- Statistics endpoints
|
|
|
|
---
|
|
|
|
### 🟡 4. BLOG POSTS - MVP DEPENDENT
|
|
|
|
**Supabase Table:**
|
|
- `blog_posts`
|
|
|
|
**Frontend Usage:** (3 full pages)
|
|
- `BlogIndex.tsx` - Blog listing
|
|
- `BlogPost.tsx` - Individual post view
|
|
- `AdminBlog.tsx` - Complete CRUD admin interface
|
|
|
|
**Impact:** Entire blog section non-functional
|
|
|
|
**Django Status:** ❌ Missing
|
|
|
|
**Required Implementation:**
|
|
```python
|
|
class BlogPost(models.Model):
|
|
STATUS_CHOICES = [
|
|
('draft', 'Draft'),
|
|
('published', 'Published'),
|
|
('archived', 'Archived'),
|
|
]
|
|
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
|
|
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
title = models.CharField(max_length=255)
|
|
slug = models.SlugField(unique=True, max_length=255)
|
|
content = models.TextField()
|
|
|
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES,
|
|
default='draft', db_index=True)
|
|
published_at = models.DateTimeField(null=True, blank=True,
|
|
db_index=True)
|
|
|
|
featured_image_id = models.CharField(max_length=255, null=True)
|
|
featured_image_url = models.URLField(null=True, blank=True)
|
|
|
|
view_count = models.IntegerField(default=0)
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
```
|
|
|
|
**Estimated Effort:** 6 hours (if needed for MVP)
|
|
|
|
---
|
|
|
|
### 🟡 5. CONTACT SUBMISSIONS - MVP DEPENDENT
|
|
|
|
**Supabase Tables:**
|
|
- `contact_submissions`
|
|
- `contact_email_threads`
|
|
- `contact_rate_limits`
|
|
|
|
**Frontend Usage:**
|
|
- `AdminContact.tsx` - Full admin interface with CRUD
|
|
|
|
**Impact:** Contact form and support ticket system broken
|
|
|
|
**Django Status:** ❌ Missing
|
|
|
|
**Required Implementation:**
|
|
```python
|
|
class ContactSubmission(models.Model):
|
|
STATUS_CHOICES = [
|
|
('pending', 'Pending'),
|
|
('in_progress', 'In Progress'),
|
|
('resolved', 'Resolved'),
|
|
('archived', 'Archived'),
|
|
]
|
|
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
|
|
|
|
# Contact info
|
|
name = models.CharField(max_length=255)
|
|
email = models.EmailField()
|
|
subject = models.CharField(max_length=255)
|
|
message = models.TextField()
|
|
category = models.CharField(max_length=50)
|
|
|
|
# Tracking
|
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES,
|
|
default='pending', db_index=True)
|
|
ticket_number = models.CharField(max_length=20, unique=True,
|
|
null=True, blank=True)
|
|
|
|
# Assignment
|
|
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL,
|
|
related_name='contact_submissions')
|
|
assigned_to = models.ForeignKey(User, null=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='assigned_contacts')
|
|
|
|
# Admin tracking
|
|
admin_notes = models.TextField(null=True, blank=True)
|
|
resolved_at = models.DateTimeField(null=True, blank=True)
|
|
resolved_by = models.ForeignKey(User, null=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+')
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
```
|
|
|
|
**Estimated Effort:** 6 hours (if needed for MVP)
|
|
|
|
---
|
|
|
|
## 5. Features NOT Used by Frontend
|
|
|
|
### ✅ SAFE TO SKIP
|
|
|
|
1. **park_operating_hours** - Only in TypeScript types, no actual frontend usage
|
|
2. **ride_technical_specifications** - Django stores in main Ride model (acceptable)
|
|
3. **ride_coaster_stats** - Django stores in main Ride model (acceptable)
|
|
4. **Advanced monitoring tables** - Better handled by external tools
|
|
|
|
---
|
|
|
|
## 6. Critical Bug Found
|
|
|
|
### 🔴 PARK COORDINATE UPDATE BUG
|
|
|
|
**Location:** `django/api/v1/endpoints/parks.py` lines 344-350
|
|
|
|
**Issue:**
|
|
```python
|
|
latitude = data.pop('latitude', None)
|
|
longitude = data.pop('longitude', None)
|
|
|
|
submission, updated_park = ParkSubmissionService.update_entity_submission(
|
|
entity=park,
|
|
user=user,
|
|
update_data=data,
|
|
latitude=latitude, # ← Passed but never used!
|
|
longitude=longitude, # ← Passed but never used!
|
|
```
|
|
|
|
**Problem:** `ParkSubmissionService.update_entity_submission()` inherits from base class and doesn't handle the `latitude`/`longitude` kwargs, so coordinate updates silently fail.
|
|
|
|
**Fix Required:** Override `update_entity_submission()` in `ParkSubmissionService` to handle location updates.
|
|
|
|
**Estimated Effort:** 2 hours
|
|
|
|
---
|
|
|
|
## 7. Implementation Timeline
|
|
|
|
### Phase 1: Critical Blockers (20 hours / 2.5 days)
|
|
|
|
1. **Fix Park Coordinate Bug** - 2 hours
|
|
- Override method in ParkSubmissionService
|
|
- Handle location creation/update
|
|
- Test coordinate updates
|
|
|
|
2. **Ride Name History** - 4 hours
|
|
- Model + migration
|
|
- API endpoints
|
|
- Admin interface
|
|
- Submission integration
|
|
|
|
3. **Entity Timeline Events** - 6 hours
|
|
- Model + migration
|
|
- API endpoints (CRUD + list)
|
|
- Submission service
|
|
- Admin interface
|
|
|
|
4. **Reports System** - 8 hours
|
|
- Model + migration
|
|
- API endpoints (create, list, update)
|
|
- Moderation integration
|
|
- Admin interface
|
|
- Statistics
|
|
|
|
### Phase 2: MVP Features (12 hours / 1.5 days) - IF NEEDED
|
|
|
|
5. **Blog Posts** - 6 hours (if blog is part of MVP)
|
|
6. **Contact Submissions** - 6 hours (if contact form is part of MVP)
|
|
|
|
---
|
|
|
|
## 8. Recommendations
|
|
|
|
### Immediate Actions:
|
|
|
|
1. **Fix the coordinate bug** (2 hours) - This is blocking park updates
|
|
2. **Determine MVP scope:**
|
|
- Is blog required?
|
|
- Is contact form required?
|
|
3. **Implement Phase 1 features** (remaining 18 hours)
|
|
4. **If blog/contact needed, implement Phase 2** (12 hours)
|
|
|
|
### Total Effort:
|
|
- **Minimum:** 20 hours (without blog/contact)
|
|
- **Full Parity:** 32 hours (with everything)
|
|
|
|
---
|
|
|
|
## 9. Django Advantages
|
|
|
|
Despite missing features, Django implementation has several improvements:
|
|
|
|
1. **Better Architecture:** Unified ContentSubmission vs separate tables per type
|
|
2. **Superior Versioning:** pghistory beats manual version tables
|
|
3. **Proper Normalization:** M2M for company_types vs JSONB
|
|
4. **Service Layer:** Clean separation of concerns
|
|
5. **Type Safety:** Python typing throughout
|
|
6. **Built-in Admin:** Django admin for all models
|
|
|
|
---
|
|
|
|
## 10. Conclusion
|
|
|
|
The Django backend is **85% feature complete** and architecturally superior to Supabase in many ways. However, **5 critical features** that the frontend actively uses are missing:
|
|
|
|
🔴 **MUST FIX:**
|
|
1. Park coordinate update bug
|
|
2. Ride Name History model
|
|
3. Entity Timeline Events model
|
|
4. Reports System model
|
|
|
|
🟡 **IF PART OF MVP:**
|
|
5. Blog Posts model
|
|
6. Contact Submissions model
|
|
|
|
**Total work:** 20-32 hours depending on MVP scope
|
|
|
|
The Sacred Pipeline is fully functional and tested. All core entity CRUD operations work. The missing pieces are specific features the frontend has UI for but the backend doesn't support yet.
|