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

@@ -8,6 +8,7 @@ from django.contrib.sites.shortcuts import get_current_site
from .models import User, PasswordReset
from apps.email_service.services import EmailService
from django.template.loader import render_to_string
from typing import cast
UserModel = get_user_model()
@@ -33,7 +34,7 @@ class UserSerializer(serializers.ModelSerializer):
]
read_only_fields = ["id", "date_joined", "is_active"]
def get_avatar_url(self, obj):
def get_avatar_url(self, obj) -> str | None:
"""Get user avatar URL"""
if hasattr(obj, "profile") and obj.profile.avatar:
return obj.profile.avatar.url
@@ -92,10 +93,11 @@ class SignupSerializer(serializers.ModelSerializer):
}
def validate_email(self, value):
"""Validate email is unique"""
if UserModel.objects.filter(email=value).exists():
"""Validate email is unique (normalize and check case-insensitively)."""
normalized = value.strip().lower() if value is not None else value
if UserModel.objects.filter(email__iexact=normalized).exists():
raise serializers.ValidationError("A user with this email already exists.")
return value
return normalized
def validate_username(self, value):
"""Validate username is unique"""
@@ -137,14 +139,18 @@ class PasswordResetSerializer(serializers.Serializer):
email = serializers.EmailField()
def validate_email(self, value):
"""Validate email exists"""
"""Normalize email and attach the user to the serializer when found (case-insensitive).
Returns the normalized email. Does not reveal whether the email exists.
"""
normalized = value.strip().lower() if value is not None else value
try:
user = UserModel.objects.get(email=value)
user = UserModel.objects.get(email__iexact=normalized)
self.user = user
return value
except UserModel.DoesNotExist:
# Don't reveal if email exists or not for security
return value
# Do not reveal whether the email exists; keep behavior unchanged.
pass
return normalized
def save(self, **kwargs):
"""Send password reset email if user exists"""
@@ -176,8 +182,14 @@ class PasswordResetSerializer(serializers.Serializer):
"accounts/email/password_reset.html", context
)
# Narrow and validate email type for the static checker
email = getattr(self.user, "email", None)
if not email:
# No recipient email; skip sending
return
EmailService.send_email(
to=getattr(self.user, "email", None),
to=cast(str, email),
subject="Reset your password",
text=f"Click the link to reset your password: {reset_url}",
site=site,
@@ -222,11 +234,17 @@ class PasswordChangeSerializer(serializers.Serializer):
def save(self, **kwargs):
"""Change user password"""
user = self.context["request"].user
new_password = (
self.initial_data.get("new_password") if self.initial_data else None
)
if new_password is None:
# Defensively obtain new_password from validated_data if it's a real dict,
# otherwise fall back to initial_data if that's a dict.
new_password = None
validated = getattr(self, "validated_data", None)
if isinstance(validated, dict):
new_password = validated.get("new_password")
elif isinstance(self.initial_data, dict):
new_password = self.initial_data.get("new_password")
if not new_password:
raise serializers.ValidationError("New password is required.")
user.set_password(new_password)
@@ -243,3 +261,5 @@ class SocialProviderSerializer(serializers.Serializer):
id = serializers.CharField()
name = serializers.CharField()
login_url = serializers.URLField()
name = serializers.CharField()
login_url = serializers.URLField()