mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 13:11:08 -05:00
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:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user