mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 05:31:10 -05:00
Add models, enums, and services for user roles, theme preferences, slug history, and ID generation
This commit is contained in:
82
memory-bank/models/CompanyModel.md
Normal file
82
memory-bank/models/CompanyModel.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Operator Model Conversion
|
||||
|
||||
## Original Django Model Structure
|
||||
|
||||
### Company Model (Now Operator)
|
||||
```python
|
||||
class Company(TrackedModel):
|
||||
name = models.CharField(max_length=255)
|
||||
slug = models.SlugField(max_length=255, unique=True)
|
||||
website = models.URLField(blank=True)
|
||||
headquarters = models.CharField(max_length=255, blank=True)
|
||||
description = models.TextField(blank=True)
|
||||
total_parks = models.IntegerField(default=0)
|
||||
total_rides = models.IntegerField(default=0)
|
||||
```
|
||||
|
||||
### Manufacturer Model
|
||||
```python
|
||||
class Manufacturer(TrackedModel):
|
||||
name = models.CharField(max_length=255)
|
||||
slug = models.SlugField(max_length=255, unique=True)
|
||||
website = models.URLField(blank=True)
|
||||
headquarters = models.CharField(max_length=255, blank=True)
|
||||
description = models.TextField(blank=True)
|
||||
total_rides = models.IntegerField(default=0)
|
||||
total_roller_coasters = models.IntegerField(default=0)
|
||||
```
|
||||
|
||||
## Laravel Implementation Plan
|
||||
|
||||
### Database Migrations
|
||||
|
||||
1. Create operators table:
|
||||
```php
|
||||
Schema::create('operators', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->string('website')->nullable();
|
||||
$table->string('headquarters')->nullable();
|
||||
$table->text('description')->nullable();
|
||||
$table->integer('total_parks')->default(0);
|
||||
$table->integer('total_rides')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
```
|
||||
|
||||
2. Create manufacturers table:
|
||||
```php
|
||||
Schema::create('manufacturers', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->string('website')->nullable();
|
||||
$table->string('headquarters')->nullable();
|
||||
$table->text('description')->nullable();
|
||||
$table->integer('total_rides')->default(0);
|
||||
$table->integer('total_roller_coasters')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
```
|
||||
|
||||
### Models
|
||||
|
||||
1. Operator Model:
|
||||
- Implement Sluggable trait
|
||||
- Add relationships (parks)
|
||||
- Add statistics updating methods
|
||||
- Add slug history functionality
|
||||
|
||||
2. Manufacturer Model:
|
||||
- Implement Sluggable trait
|
||||
- Add relationships (rides)
|
||||
- Add statistics updating methods
|
||||
- Add slug history functionality
|
||||
|
||||
### Next Steps
|
||||
1. [ ] Create operators table migration
|
||||
2. [ ] Create manufacturers table migration
|
||||
3. [ ] Create Operator model
|
||||
4. [ ] Create Manufacturer model
|
||||
5. [ ] Implement statistics update methods
|
||||
104
memory-bank/models/LocationModel.md
Normal file
104
memory-bank/models/LocationModel.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Location Model
|
||||
|
||||
## Overview
|
||||
The Location model provides polymorphic location management for parks, areas, and other entities in ThrillWiki. It handles geocoding, coordinate management, and location-based search capabilities.
|
||||
|
||||
## Structure
|
||||
|
||||
### Database Table
|
||||
- Table Name: `locations`
|
||||
- Primary Key: `id` (bigint)
|
||||
- Polymorphic Fields: `locatable_type`, `locatable_id`
|
||||
- Timestamps: `created_at`, `updated_at`
|
||||
|
||||
### Fields
|
||||
- **Address Components**
|
||||
- `address` (string, nullable) - Street address
|
||||
- `city` (string) - City name
|
||||
- `state` (string, nullable) - State/province
|
||||
- `country` (string) - Country name
|
||||
- `postal_code` (string, nullable) - Postal/ZIP code
|
||||
|
||||
- **Coordinates**
|
||||
- `latitude` (decimal, 10,8) - Latitude coordinate
|
||||
- `longitude` (decimal, 11,8) - Longitude coordinate
|
||||
- `elevation` (decimal, 8,2, nullable) - Elevation in meters
|
||||
|
||||
- **Additional Details**
|
||||
- `timezone` (string, nullable) - Location timezone
|
||||
- `metadata` (json, nullable) - Additional location data
|
||||
- `is_approximate` (boolean) - Indicates if location is approximate
|
||||
- `source` (string, nullable) - Data source identifier
|
||||
|
||||
- **Geocoding**
|
||||
- `geocoding_data` (json, nullable) - Cached geocoding response
|
||||
- `geocoded_at` (timestamp, nullable) - Last geocoding timestamp
|
||||
|
||||
### Indexes
|
||||
- Coordinates: `(latitude, longitude)`
|
||||
- Location: `(country, state, city)`
|
||||
- Postal: `postal_code`
|
||||
|
||||
## Relationships
|
||||
|
||||
### Polymorphic
|
||||
- `locatable()` - Polymorphic relationship to parent model (Park, Area, etc.)
|
||||
|
||||
## Accessors & Mutators
|
||||
- `coordinates` - Returns [lat, lng] array
|
||||
- `formatted_address` - Returns formatted address string
|
||||
- `map_url` - Returns Google Maps URL
|
||||
|
||||
## Methods
|
||||
|
||||
### Location Management
|
||||
- `updateCoordinates(float $lat, float $lng)` - Update coordinates
|
||||
- `setAddress(array $components)` - Set address components
|
||||
- `geocode()` - Trigger geocoding refresh
|
||||
- `reverseGeocode()` - Get address from coordinates
|
||||
|
||||
### Queries
|
||||
- `scopeNearby($query, $lat, $lng, $radius)` - Find nearby locations
|
||||
- `scopeInBounds($query, $ne, $sw)` - Find locations in bounds
|
||||
- `scopeInCountry($query, $country)` - Filter by country
|
||||
- `scopeInState($query, $state)` - Filter by state
|
||||
- `scopeInCity($query, $city)` - Filter by city
|
||||
|
||||
### Calculations
|
||||
- `distanceTo($lat, $lng)` - Calculate distance to point
|
||||
- `bearingTo($lat, $lng)` - Calculate bearing to point
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```php
|
||||
// Create location for park
|
||||
$park->location()->create([
|
||||
'address' => '123 Main St',
|
||||
'city' => 'Orlando',
|
||||
'state' => 'FL',
|
||||
'country' => 'USA',
|
||||
'latitude' => 28.538336,
|
||||
'longitude' => -81.379234
|
||||
]);
|
||||
|
||||
// Find parks within 50km
|
||||
$nearbyParks = Park::whereHas('location', function ($query) {
|
||||
$query->nearby(28.538336, -81.379234, 50);
|
||||
})->get();
|
||||
```
|
||||
|
||||
## Integration Points
|
||||
|
||||
### Services
|
||||
- GeocodeService - Address/coordinate lookup
|
||||
- LocationSearchService - Advanced location search
|
||||
|
||||
### Components
|
||||
- LocationSelector - Map-based location picker
|
||||
- LocationDisplay - Location visualization
|
||||
|
||||
## Notes
|
||||
- Coordinates use high precision for accuracy
|
||||
- Geocoding results are cached to reduce API calls
|
||||
- Polymorphic design allows reuse across models
|
||||
- Search methods use spatial indexes for performance
|
||||
164
memory-bank/models/ParkModel.md
Normal file
164
memory-bank/models/ParkModel.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Park Model Conversion
|
||||
|
||||
## Original Django Model Structure
|
||||
|
||||
### Park Model
|
||||
```python
|
||||
class Park(TrackedModel):
|
||||
# Status choices
|
||||
STATUS_CHOICES = [
|
||||
("OPERATING", "Operating"),
|
||||
("CLOSED_TEMP", "Temporarily Closed"),
|
||||
("CLOSED_PERM", "Permanently Closed"),
|
||||
("UNDER_CONSTRUCTION", "Under Construction"),
|
||||
("DEMOLISHED", "Demolished"),
|
||||
("RELOCATED", "Relocated"),
|
||||
]
|
||||
|
||||
# Basic info
|
||||
name = models.CharField(max_length=255)
|
||||
slug = models.SlugField(max_length=255, unique=True)
|
||||
description = models.TextField(blank=True)
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="OPERATING")
|
||||
|
||||
# Location fields (GenericRelation)
|
||||
location = GenericRelation(Location)
|
||||
|
||||
# Details
|
||||
opening_date = models.DateField(null=True, blank=True)
|
||||
closing_date = models.DateField(null=True, blank=True)
|
||||
operating_season = models.CharField(max_length=255, blank=True)
|
||||
size_acres = models.DecimalField(max_digits=10, decimal_places=2, null=True)
|
||||
website = models.URLField(blank=True)
|
||||
|
||||
# Statistics
|
||||
average_rating = models.DecimalField(max_digits=3, decimal_places=2, null=True)
|
||||
ride_count = models.IntegerField(null=True)
|
||||
coaster_count = models.IntegerField(null=True)
|
||||
|
||||
# Relationships
|
||||
operator = models.ForeignKey(Operator, SET_NULL, null=True, related_name="parks")
|
||||
photos = GenericRelation(Photo)
|
||||
```
|
||||
|
||||
### ParkArea Model
|
||||
```python
|
||||
class ParkArea(TrackedModel):
|
||||
park = models.ForeignKey(Park, CASCADE, related_name="areas")
|
||||
name = models.CharField(max_length=255)
|
||||
slug = models.SlugField(max_length=255)
|
||||
description = models.TextField(blank=True)
|
||||
opening_date = models.DateField(null=True, blank=True)
|
||||
closing_date = models.DateField(null=True, blank=True)
|
||||
```
|
||||
|
||||
## Laravel Implementation Plan
|
||||
|
||||
### Enums
|
||||
1. Create ParkStatus enum with status options and color methods:
|
||||
```php
|
||||
enum ParkStatus: string {
|
||||
case OPERATING = 'OPERATING';
|
||||
case CLOSED_TEMP = 'CLOSED_TEMP';
|
||||
case CLOSED_PERM = 'CLOSED_PERM';
|
||||
case UNDER_CONSTRUCTION = 'UNDER_CONSTRUCTION';
|
||||
case DEMOLISHED = 'DEMOLISHED';
|
||||
case RELOCATED = 'RELOCATED';
|
||||
}
|
||||
```
|
||||
|
||||
### Database Migrations
|
||||
|
||||
1. Create parks table:
|
||||
```php
|
||||
Schema::create('parks', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->text('description')->nullable();
|
||||
$table->string('status', 20);
|
||||
|
||||
// Details
|
||||
$table->date('opening_date')->nullable();
|
||||
$table->date('closing_date')->nullable();
|
||||
$table->string('operating_season')->nullable();
|
||||
$table->decimal('size_acres', 10, 2)->nullable();
|
||||
$table->string('website')->nullable();
|
||||
|
||||
// Statistics
|
||||
$table->decimal('average_rating', 3, 2)->nullable();
|
||||
$table->integer('ride_count')->nullable();
|
||||
$table->integer('coaster_count')->nullable();
|
||||
|
||||
// Foreign keys
|
||||
$table->foreignId('operator_id')->nullable()->constrained('operators')->nullOnDelete();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
```
|
||||
|
||||
2. Create park_areas table:
|
||||
```php
|
||||
Schema::create('park_areas', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('park_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('name');
|
||||
$table->string('slug');
|
||||
$table->text('description')->nullable();
|
||||
$table->date('opening_date')->nullable();
|
||||
$table->date('closing_date')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['park_id', 'slug']);
|
||||
});
|
||||
```
|
||||
|
||||
### Models
|
||||
|
||||
1. Park Model:
|
||||
- Implement Sluggable trait
|
||||
- Add status color methods
|
||||
- Set up relationships (operator, areas, photos, location)
|
||||
- Add history tracking
|
||||
- Implement slug history functionality
|
||||
|
||||
2. ParkArea Model:
|
||||
- Implement Sluggable trait
|
||||
- Set up relationship with Park
|
||||
- Add history tracking
|
||||
- Implement slug history functionality
|
||||
|
||||
### Livewire Components
|
||||
|
||||
1. ParkListComponent:
|
||||
- Display parks with status badges
|
||||
- Filter by status
|
||||
- Sort functionality
|
||||
- Search by name
|
||||
|
||||
2. ParkFormComponent:
|
||||
- Create/edit park details
|
||||
- Location selection
|
||||
- Operator selection
|
||||
- Status management
|
||||
|
||||
3. ParkAreaComponent:
|
||||
- Manage park areas
|
||||
- Add/edit/delete areas
|
||||
- Sort/reorder areas
|
||||
|
||||
### Features to Implement
|
||||
1. Slug history tracking
|
||||
2. Location management
|
||||
3. Photo management
|
||||
4. Statistics calculation
|
||||
5. Area management
|
||||
6. Park status badges with colors
|
||||
|
||||
### Next Steps
|
||||
1. [ ] Create ParkStatus enum
|
||||
2. [ ] Create parks table migration
|
||||
3. [ ] Create park_areas table migration
|
||||
4. [ ] Create Park model
|
||||
5. [ ] Create ParkArea model
|
||||
6. [ ] Implement Livewire components
|
||||
122
memory-bank/models/UserModel.md
Normal file
122
memory-bank/models/UserModel.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# User Model Conversion
|
||||
|
||||
## Original Django Model Structure
|
||||
|
||||
### User Model (extends AbstractUser)
|
||||
```python
|
||||
class User(AbstractUser):
|
||||
# Custom fields
|
||||
user_id = models.CharField(max_length=10, unique=True, editable=False)
|
||||
role = models.CharField(max_length=10, choices=['USER', 'MODERATOR', 'ADMIN', 'SUPERUSER'])
|
||||
is_banned = models.BooleanField(default=False)
|
||||
ban_reason = models.TextField(blank=True)
|
||||
ban_date = models.DateTimeField(null=True, blank=True)
|
||||
pending_email = models.EmailField(blank=True, null=True)
|
||||
theme_preference = models.CharField(max_length=5, choices=['light', 'dark'])
|
||||
```
|
||||
|
||||
### UserProfile Model
|
||||
```python
|
||||
class UserProfile:
|
||||
profile_id = models.CharField(max_length=10, unique=True, editable=False)
|
||||
user = models.OneToOneField(User, related_name='profile')
|
||||
display_name = models.CharField(max_length=50, unique=True)
|
||||
avatar = models.ImageField(upload_to='avatars/')
|
||||
pronouns = models.CharField(max_length=50, blank=True)
|
||||
bio = models.TextField(max_length=500, blank=True)
|
||||
|
||||
# Social media
|
||||
twitter = models.URLField(blank=True)
|
||||
instagram = models.URLField(blank=True)
|
||||
youtube = models.URLField(blank=True)
|
||||
discord = models.CharField(max_length=100, blank=True)
|
||||
|
||||
# Stats
|
||||
coaster_credits = models.IntegerField(default=0)
|
||||
dark_ride_credits = models.IntegerField(default=0)
|
||||
flat_ride_credits = models.IntegerField(default=0)
|
||||
water_ride_credits = models.IntegerField(default=0)
|
||||
```
|
||||
|
||||
## Laravel Implementation Plan
|
||||
|
||||
### Database Migrations
|
||||
|
||||
1. Extend users table (`database/migrations/[timestamp]_add_user_fields.php`):
|
||||
```php
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('user_id', 10)->unique();
|
||||
$table->enum('role', ['USER', 'MODERATOR', 'ADMIN', 'SUPERUSER'])->default('USER');
|
||||
$table->boolean('is_banned')->default(false);
|
||||
$table->text('ban_reason')->nullable();
|
||||
$table->timestamp('ban_date')->nullable();
|
||||
$table->string('pending_email')->nullable();
|
||||
$table->enum('theme_preference', ['light', 'dark'])->default('light');
|
||||
});
|
||||
```
|
||||
|
||||
2. Create profiles table (`database/migrations/[timestamp]_create_profiles_table.php`):
|
||||
```php
|
||||
Schema::create('profiles', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('profile_id', 10)->unique();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('display_name', 50)->unique();
|
||||
$table->string('avatar')->nullable();
|
||||
$table->string('pronouns', 50)->nullable();
|
||||
$table->text('bio')->nullable();
|
||||
|
||||
// Social media
|
||||
$table->string('twitter')->nullable();
|
||||
$table->string('instagram')->nullable();
|
||||
$table->string('youtube')->nullable();
|
||||
$table->string('discord', 100)->nullable();
|
||||
|
||||
// Stats
|
||||
$table->integer('coaster_credits')->default(0);
|
||||
$table->integer('dark_ride_credits')->default(0);
|
||||
$table->integer('flat_ride_credits')->default(0);
|
||||
$table->integer('water_ride_credits')->default(0);
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
```
|
||||
|
||||
### Model Implementation
|
||||
|
||||
1. User Model (`app/Models/User.php`):
|
||||
- Extend Laravel's base User model
|
||||
- Add custom attributes
|
||||
- Add relationship to Profile
|
||||
- Add role management methods
|
||||
- Add ban management methods
|
||||
|
||||
2. Profile Model (`app/Models/Profile.php`):
|
||||
- Create new model
|
||||
- Add relationship to User
|
||||
- Add avatar handling methods
|
||||
- Add credit management methods
|
||||
|
||||
### Livewire Components
|
||||
1. ProfileComponent - Handle profile management
|
||||
2. AvatarUploadComponent - Handle avatar uploads
|
||||
3. UserSettingsComponent - Handle user settings/preferences
|
||||
4. UserBanComponent - For moderator use to handle bans
|
||||
|
||||
### Services
|
||||
1. UserService - Business logic for user management
|
||||
2. ProfileService - Business logic for profile management
|
||||
3. AvatarService - Handle avatar generation and storage
|
||||
|
||||
### Next Steps
|
||||
1. [ ] Create user fields migration
|
||||
2. [ ] Create profiles table migration
|
||||
3. [ ] Enhance User model with new fields and methods
|
||||
4. [ ] Create Profile model
|
||||
5. [ ] Implement initial Livewire components for profile management
|
||||
|
||||
### Notes
|
||||
- Will use Laravel's built-in authentication (already scaffolded)
|
||||
- Email verification will be handled by Laravel's built-in features
|
||||
- Password reset functionality will use Laravel's default implementation
|
||||
- Will implement custom avatar generation similar to Django version
|
||||
Reference in New Issue
Block a user