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

@@ -2,7 +2,9 @@
API viewsets for the ride ranking system.
"""
from django.db.models import Q
from typing import TYPE_CHECKING, Any, Type, cast
from django.db.models import Q, QuerySet
from django.utils import timezone
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter
@@ -11,10 +13,15 @@ from rest_framework import status
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter
from rest_framework.permissions import IsAuthenticatedOrReadOnly, AllowAny
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import BaseSerializer
from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework.views import APIView
if TYPE_CHECKING:
pass
# Import models inside methods to avoid Django initialization issues
from .serializers_rankings import (
RideRankingSerializer,
@@ -101,7 +108,7 @@ class RideRankingViewSet(ReadOnlyModelViewSet):
]
ordering = ["rank"]
def get_queryset(self):
def get_queryset(self) -> QuerySet[Any]: # type: ignore
"""Get rankings with optimized queries."""
from apps.rides.models import RideRanking
@@ -109,13 +116,16 @@ class RideRankingViewSet(ReadOnlyModelViewSet):
"ride", "ride__park", "ride__park__location", "ride__manufacturer"
)
# Cast self.request to DRF Request so type checker recognizes query_params
request = cast(Request, self.request)
# Filter by category
category = self.request.query_params.get("category")
category = request.query_params.get("category")
if category:
queryset = queryset.filter(ride__category=category)
# Filter by minimum mutual riders
min_riders = self.request.query_params.get("min_riders")
min_riders = request.query_params.get("min_riders")
if min_riders:
try:
queryset = queryset.filter(mutual_riders_count__gte=int(min_riders))
@@ -123,21 +133,21 @@ class RideRankingViewSet(ReadOnlyModelViewSet):
pass
# Filter by park
park_slug = self.request.query_params.get("park")
park_slug = request.query_params.get("park")
if park_slug:
queryset = queryset.filter(ride__park__slug=park_slug)
return queryset
def get_serializer_class(self):
def get_serializer_class(self) -> Any: # type: ignore[override]
"""Use different serializers for list vs detail."""
if self.action == "retrieve":
return RideRankingDetailSerializer
return cast(Type[BaseSerializer], RideRankingDetailSerializer)
elif self.action == "history":
return RankingSnapshotSerializer
return cast(Type[BaseSerializer], RankingSnapshotSerializer)
elif self.action == "statistics":
return RankingStatsSerializer
return RideRankingSerializer
return cast(Type[BaseSerializer], RankingStatsSerializer)
return cast(Type[BaseSerializer], RideRankingSerializer)
@action(detail=True, methods=["get"])
def history(self, request, ride_slug=None):
@@ -246,6 +256,12 @@ class RideRankingViewSet(ReadOnlyModelViewSet):
serializer = RankingStatsSerializer(stats)
return Response(serializer.data)
@extend_schema(
summary="Get ride comparisons",
description="Get head-to-head comparisons for a specific ride",
responses={200: OpenApiTypes.OBJECT},
tags=["Rankings"],
)
@action(detail=True, methods=["get"])
def comparisons(self, request, ride_slug=None):
"""Get head-to-head comparisons for a specific ride."""
@@ -331,7 +347,27 @@ class TriggerRankingCalculationView(APIView):
{"error": "Admin access required"}, status=status.HTTP_403_FORBIDDEN
)
from apps.rides.services import RideRankingService
# Replace direct import with a guarded runtime import to avoid static-analysis/initialization errors
try:
from apps.rides.services import RideRankingService # type: ignore
except Exception:
RideRankingService = None # type: ignore
# Attempt a dynamic import as a fallback if the direct import failed
if RideRankingService is None:
try:
import importlib
_services_mod = importlib.import_module("apps.rides.services")
RideRankingService = getattr(_services_mod, "RideRankingService", None)
except Exception:
RideRankingService = None
if not RideRankingService:
return Response(
{"error": "Ranking service unavailable"},
status=status.HTTP_503_SERVICE_UNAVAILABLE,
)
category = request.data.get("category")