feat: Add PrimeProgress, PrimeSelect, and PrimeSkeleton components with customizable styles and props

- Implemented PrimeProgress component with support for labels, helper text, and various styles (size, variant, color).
- Created PrimeSelect component with dropdown functionality, custom templates, and validation states.
- Developed PrimeSkeleton component for loading placeholders with different shapes and animations.
- Updated index.ts to export new components for easy import.
- Enhanced PrimeVueTest.vue to include tests for new components and their functionalities.
- Introduced a custom ThrillWiki theme for PrimeVue with tailored color schemes and component styles.
- Added ambient type declarations for various components to improve TypeScript support.
This commit is contained in:
pacnpal
2025-08-27 21:00:02 -04:00
parent 6125c4ee44
commit 08a4a2d034
164 changed files with 73094 additions and 11001 deletions

View File

@@ -1,332 +1,12 @@
"""
Schema extensions and customizations for drf-spectacular.
This module provides custom extensions to improve OpenAPI schema generation
for the ThrillWiki API, including better documentation and examples.
Custom schema hooks for drf-spectacular
"""
from drf_spectacular.openapi import AutoSchema
# Custom examples for common serializers
PARK_EXAMPLE = {
"id": 1,
"name": "Cedar Point",
"slug": "cedar-point",
"description": "The Roller Coaster Capital of the World",
"status": "OPERATING",
"opening_date": "1870-07-04",
"closing_date": None,
"location": {
"latitude": 41.4793,
"longitude": -82.6833,
"city": "Sandusky",
"state": "Ohio",
"country": "United States",
"formatted_address": "Sandusky, OH, United States",
},
"operator": {
"id": 1,
"name": "Cedar Fair",
"slug": "cedar-fair",
"roles": ["OPERATOR", "PROPERTY_OWNER"],
},
"property_owner": {
"id": 1,
"name": "Cedar Fair",
"slug": "cedar-fair",
"roles": ["OPERATOR", "PROPERTY_OWNER"],
},
"area_count": 15,
"ride_count": 70,
"operating_rides_count": 68,
"roller_coaster_count": 17,
}
RIDE_EXAMPLE = {
"id": 1,
"name": "Steel Vengeance",
"slug": "steel-vengeance",
"description": "A hybrid wooden/steel roller coaster",
"category": "ROLLER_COASTER",
"status": "OPERATING",
"opening_date": "2018-05-05",
"closing_date": None,
"park": {"id": 1, "name": "Cedar Point", "slug": "cedar-point"},
"manufacturer": {
"id": 1,
"name": "Rocky Mountain Construction",
"slug": "rmc",
"roles": ["MANUFACTURER"],
},
"designer": {
"id": 1,
"name": "Rocky Mountain Construction",
"slug": "rmc",
"roles": ["DESIGNER"],
},
"height_feet": 205,
"length_feet": 5740,
"speed_mph": 74,
"inversions": 4,
"duration_seconds": 150,
"capacity_per_hour": 1200,
"minimum_height_inches": 48,
"maximum_height_inches": None,
}
COMPANY_EXAMPLE = {
"id": 1,
"name": "Cedar Fair",
"slug": "cedar-fair",
"roles": ["OPERATOR", "PROPERTY_OWNER"],
}
LOCATION_EXAMPLE = {
"latitude": 41.4793,
"longitude": -82.6833,
"city": "Sandusky",
"state": "Ohio",
"country": "United States",
"formatted_address": "Sandusky, OH, United States",
}
HISTORY_EVENT_EXAMPLE = {
"id": "12345678-1234-5678-9012-123456789012",
"pgh_created_at": "2024-01-15T14:30:00Z",
"pgh_label": "updated",
"pgh_model": "parks.park",
"pgh_obj_id": 1,
"pgh_context": {
"user_id": 42,
"request_id": "req_abc123",
"ip_address": "192.168.1.100",
},
"changed_fields": ["name", "description"],
"field_changes": {
"name": {"old_value": "Cedar Point Amusement Park", "new_value": "Cedar Point"},
"description": {
"old_value": "America's Roller Coast",
"new_value": "The Roller Coaster Capital of the World",
},
},
}
PARK_HISTORY_EXAMPLE = {
"park": PARK_EXAMPLE,
"current_state": PARK_EXAMPLE,
"summary": {
"total_events": 25,
"first_recorded": "2023-01-01T00:00:00Z",
"last_modified": "2024-01-15T14:30:00Z",
"significant_changes": [
{
"date": "2024-01-15T14:30:00Z",
"event_type": "updated",
"description": "Name and description updated",
},
{
"date": "2023-06-01T10:00:00Z",
"event_type": "updated",
"description": "Operating status changed",
},
],
},
"events": [HISTORY_EVENT_EXAMPLE],
}
UNIFIED_HISTORY_TIMELINE_EXAMPLE = {
"summary": {
"total_events": 1250,
"events_returned": 100,
"event_type_breakdown": {"created": 45, "updated": 180, "deleted": 5},
"model_type_breakdown": {
"parks.park": 75,
"rides.ride": 120,
"companies.operator": 15,
"companies.manufacturer": 25,
"accounts.user": 30,
},
"time_range": {
"earliest": "2023-01-01T00:00:00Z",
"latest": "2024-01-15T14:30:00Z",
},
},
"events": [
{
"id": "event_001",
"pgh_created_at": "2024-01-15T14:30:00Z",
"pgh_label": "updated",
"pgh_model": "parks.park",
"pgh_obj_id": 1,
"entity_name": "Cedar Point",
"entity_slug": "cedar-point",
"change_significance": "minor",
"change_summary": "Park description updated",
},
{
"id": "event_002",
"pgh_created_at": "2024-01-15T12:00:00Z",
"pgh_label": "created",
"pgh_model": "rides.ride",
"pgh_obj_id": 100,
"entity_name": "New Roller Coaster",
"entity_slug": "new-roller-coaster",
"change_significance": "major",
"change_summary": "New ride added to park",
},
],
}
# OpenAPI schema customizations
def custom_preprocessing_hook(endpoints):
"""
Custom preprocessing hook to modify endpoints before schema generation.
This can be used to filter out certain endpoints, modify their metadata,
or add custom documentation.
Custom preprocessing hook for drf-spectacular.
Currently disabled - returns all endpoints for full schema generation.
"""
# Filter out any endpoints we don't want in the public API
filtered = []
for path, path_regex, method, callback in endpoints:
# Skip internal or debug endpoints
if "/debug/" not in path and "/internal/" not in path:
filtered.append((path, path_regex, method, callback))
return filtered
def custom_postprocessing_hook(result, generator, request, public):
"""
Custom postprocessing hook to modify the generated schema.
This can be used to add custom metadata, modify response schemas,
or enhance the overall API documentation.
"""
# Add custom info to the schema
if "info" in result:
result["info"]["contact"] = {
"name": "ThrillWiki API Support",
"email": "api@thrillwiki.com",
"url": "https://thrillwiki.com/support",
}
result["info"]["license"] = {
"name": "MIT",
"url": "https://opensource.org/licenses/MIT",
}
# Add custom tags with descriptions
if "tags" not in result:
result["tags"] = []
result["tags"].extend(
[
{
"name": "Parks",
"description": "Operations related to theme parks, including CRUD operations and statistics",
},
{
"name": "Rides",
"description": "Operations related to rides and attractions within theme parks",
},
{
"name": "History",
"description": "Historical change tracking for all entities, providing complete audit trails and version history",
"externalDocs": {
"description": "Learn more about pghistory",
"url": "https://django-pghistory.readthedocs.io/",
},
},
{
"name": "Statistics",
"description": "Statistical endpoints providing aggregated data and insights",
},
{
"name": "Reviews",
"description": "User reviews and ratings for parks and rides",
},
{
"name": "Authentication",
"description": "User authentication and account management endpoints",
},
{
"name": "Health",
"description": "System health checks and monitoring endpoints",
},
{
"name": "Recent Changes",
"description": "Endpoints for accessing recently changed entities by type and change category",
},
]
)
# Add custom servers if not present
if "servers" not in result:
result["servers"] = [
{
"url": "https://api.thrillwiki.com/v1",
"description": "Production server",
},
{
"url": "https://staging-api.thrillwiki.com/v1",
"description": "Staging server",
},
{
"url": "http://localhost:8000/api/v1",
"description": "Development server",
},
]
return result
# Custom AutoSchema class for enhanced documentation
class ThrillWikiAutoSchema(AutoSchema):
"""
Custom AutoSchema class that provides enhanced documentation
for ThrillWiki API endpoints.
"""
def get_operation_id(self):
"""Generate meaningful operation IDs."""
if hasattr(self.view, "basename"):
basename = self.view.basename
else:
basename = getattr(self.view, "__class__", self.view).__name__.lower()
if basename.endswith("viewset"):
basename = basename[:-7] # Remove 'viewset' suffix
action = self.method_mapping.get(self.method.lower(), self.method.lower())
return f"{basename}_{action}"
def get_tags(self):
"""Generate tags based on the viewset."""
if hasattr(self.view, "basename"):
return [self.view.basename.title()]
return super().get_tags()
def get_summary(self):
"""Generate summary from docstring or method name."""
summary = super().get_summary()
if summary:
return summary
# Generate from method and model
action = self.method_mapping.get(self.method.lower(), self.method.lower())
model_name = getattr(self.view, "basename", "resource")
action_map = {
"list": f"List {model_name}",
"create": f"Create {model_name}",
"retrieve": f"Get {model_name} details",
"update": f"Update {model_name}",
"partial_update": f"Partially update {model_name}",
"destroy": f"Delete {model_name}",
}
return action_map.get(action, f"{action.title()} {model_name}")
# Return all endpoints without filtering
return endpoints