progress on conversion to tailwind
This commit is contained in:
parent
6a872124c6
commit
6e2843c60e
110 changed files with 4997 additions and 1691 deletions
|
|
@ -1,15 +1,63 @@
|
|||
from django import forms
|
||||
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
|
||||
from .models import CustomUser
|
||||
from .models import CustomUser, FriendCode
|
||||
from allauth.account.forms import SignupForm
|
||||
|
||||
class CustomUserCreationForm(UserCreationForm):
|
||||
|
||||
class Meta(UserCreationForm.Meta):
|
||||
model = CustomUser
|
||||
fields = ('email', 'username',)
|
||||
fields = ('email',)
|
||||
|
||||
class CustomUserChangeForm(UserChangeForm):
|
||||
|
||||
class Meta:
|
||||
model = CustomUser
|
||||
fields = ('email', 'username',)
|
||||
fields = ('email',)
|
||||
|
||||
class FriendCodeForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = FriendCode
|
||||
fields = ["friend_code"]
|
||||
|
||||
def clean_friend_code(self):
|
||||
friend_code = self.cleaned_data.get("friend_code", "").strip()
|
||||
# Remove any dashes from the input for validation.
|
||||
friend_code_clean = friend_code.replace("-", "")
|
||||
if len(friend_code_clean) != 16 or not friend_code_clean.isdigit():
|
||||
raise forms.ValidationError("Friend code must be exactly 16 digits long.")
|
||||
# Format the friend code as: XXXX-XXXX-XXXX-XXXX.
|
||||
friend_code_formatted = f"{friend_code_clean[:4]}-{friend_code_clean[4:8]}-{friend_code_clean[8:12]}-{friend_code_clean[12:16]}"
|
||||
return friend_code_formatted
|
||||
|
||||
class CustomSignupForm(SignupForm):
|
||||
friend_code = forms.CharField(
|
||||
max_length=19,
|
||||
required=True,
|
||||
label="Friend Code",
|
||||
help_text="Enter your friend code in the format XXXX-XXXX-XXXX-XXXX.",
|
||||
widget=forms.TextInput(attrs={'placeholder': 'XXXX-XXXX-XXXX-XXXX'})
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# Remove the username field completely.
|
||||
if "username" in self.fields:
|
||||
del self.fields["username"]
|
||||
|
||||
def clean_friend_code(self):
|
||||
friend_code = self.cleaned_data.get("friend_code", "").strip().replace("-", "")
|
||||
if len(friend_code) != 16 or not friend_code.isdigit():
|
||||
raise forms.ValidationError("Friend code must be exactly 16 digits long.")
|
||||
formatted = f"{friend_code[:4]}-{friend_code[4:8]}-{friend_code[8:12]}-{friend_code[12:16]}"
|
||||
return formatted
|
||||
|
||||
def save(self, request):
|
||||
# First, complete the normal signup process.
|
||||
user = super().save(request)
|
||||
# Create the associated FriendCode record.
|
||||
FriendCode.objects.create(
|
||||
friend_code=self.cleaned_data["friend_code"],
|
||||
user=user
|
||||
)
|
||||
return user
|
||||
0
accounts/management/__init__.py
Normal file
0
accounts/management/__init__.py
Normal file
0
accounts/management/commands/__init__.py
Normal file
0
accounts/management/commands/__init__.py
Normal file
19
accounts/management/commands/seed_default_friend_codes.py
Normal file
19
accounts/management/commands/seed_default_friend_codes.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
from accounts.models import CustomUser, FriendCode
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Seed default friend codes for TestUsers after friend codes have been loaded."
|
||||
|
||||
def handle(self, *args, **options):
|
||||
users_updated = 0
|
||||
for user in CustomUser.objects.all():
|
||||
# Automatically select the earliest friend code added for the user:
|
||||
default_code = FriendCode.objects.filter(user=user).order_by('created_at').first()
|
||||
if default_code:
|
||||
user.default_friend_code = default_code
|
||||
user.save(update_fields=["default_friend_code"])
|
||||
self.stdout.write(f"Set default friend code for user {user.username} to {default_code.friend_code}.")
|
||||
users_updated += 1
|
||||
else:
|
||||
self.stdout.write(f"No friend code found for user {user.username}.")
|
||||
self.stdout.write(self.style.SUCCESS(f"Seeded default friend codes for {users_updated} user(s)."))
|
||||
61
accounts/migrations/0001_initial.py
Normal file
61
accounts/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Generated by Django 5.1.2 on 2025-03-07 01:04
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CustomUser',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'user',
|
||||
'verbose_name_plural': 'users',
|
||||
'abstract': False,
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FriendCode',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('friend_code', models.CharField(max_length=19)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='friend_codes', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customuser',
|
||||
name='default_friend_code',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.friendcode'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,8 +1,47 @@
|
|||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
class CustomUser(AbstractUser):
|
||||
pass
|
||||
default_friend_code = models.ForeignKey("FriendCode", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.email
|
||||
return self.email
|
||||
|
||||
def set_default_friend_code(self, friend_code):
|
||||
"""Set a friend code as default if it belongs to the user."""
|
||||
if friend_code.user != self:
|
||||
raise ValidationError("Friend code does not belong to this user.")
|
||||
self.default_friend_code = friend_code
|
||||
self.save(update_fields=["default_friend_code"])
|
||||
|
||||
def remove_default_friend_code(self, friend_code):
|
||||
"""
|
||||
If the given friend code is the current default,
|
||||
assign another of the user's friend codes (if any) as default.
|
||||
"""
|
||||
if self.default_friend_code == friend_code:
|
||||
other_codes = self.friend_codes.exclude(pk=friend_code.pk)
|
||||
self.default_friend_code = other_codes.first() if other_codes.exists() else None
|
||||
self.save(update_fields=["default_friend_code"])
|
||||
|
||||
class FriendCode(models.Model):
|
||||
friend_code = models.CharField(max_length=19)
|
||||
user = models.ForeignKey(CustomUser, on_delete=models.PROTECT, related_name='friend_codes')
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""
|
||||
When a new friend code is saved,
|
||||
if the user has no default friend code yet,
|
||||
automatically set this as the default.
|
||||
"""
|
||||
is_new = self.pk is None
|
||||
super().save(*args, **kwargs)
|
||||
if is_new and not self.user.default_friend_code:
|
||||
self.user.default_friend_code = self
|
||||
self.user.save(update_fields=["default_friend_code"])
|
||||
|
||||
def __str__(self):
|
||||
return self.friend_code
|
||||
|
|
@ -26,6 +26,7 @@ def gravatar_url(email, size=20):
|
|||
params = urlencode({'d': default, 's': str(size)})
|
||||
return f"https://www.gravatar.com/avatar/{email_hash}?{params}"
|
||||
|
||||
@register.filter
|
||||
def gravatar_profile_url(email=None):
|
||||
"""
|
||||
Returns the Gravatar Profile URL for a given email.
|
||||
|
|
|
|||
10
accounts/urls.py
Normal file
10
accounts/urls.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from django.urls import path
|
||||
from .views import ListFriendCodesView, AddFriendCodeView, DeleteFriendCodeView, ChangeDefaultFriendCodeView
|
||||
|
||||
urlpatterns = [
|
||||
# ... other account URLs ...
|
||||
path("friend-codes/", ListFriendCodesView.as_view(), name="list_friend_codes"),
|
||||
path("friend-codes/add/", AddFriendCodeView.as_view(), name="add_friend_code"),
|
||||
path("friend-codes/delete/<int:pk>/", DeleteFriendCodeView.as_view(), name="delete_friend_code"),
|
||||
path("friend-codes/default/<int:pk>/", ChangeDefaultFriendCodeView.as_view(), name="change_default_friend_code"),
|
||||
]
|
||||
|
|
@ -1,3 +1,113 @@
|
|||
from django.shortcuts import render
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.shortcuts import redirect, get_object_or_404
|
||||
from django.views.generic import ListView, CreateView, DeleteView, View
|
||||
from accounts.models import FriendCode
|
||||
from accounts.forms import FriendCodeForm
|
||||
|
||||
# Create your views here.
|
||||
class ListFriendCodesView(LoginRequiredMixin, ListView):
|
||||
"""
|
||||
Display the current user's friend codes.
|
||||
"""
|
||||
model = FriendCode
|
||||
template_name = "friend_codes/list_friend_codes.html"
|
||||
context_object_name = "friend_codes"
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.user.friend_codes.all()
|
||||
|
||||
class AddFriendCodeView(LoginRequiredMixin, CreateView):
|
||||
"""
|
||||
Add a new friend code for the current user. If the user does not yet have a default,
|
||||
the newly added code will automatically become the default.
|
||||
"""
|
||||
model = FriendCode
|
||||
form_class = FriendCodeForm
|
||||
template_name = "friend_codes/add_friend_code.html"
|
||||
success_url = reverse_lazy("list_friend_codes")
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.user = self.request.user
|
||||
messages.success(self.request, "Friend code added successfully.")
|
||||
return super().form_valid(form)
|
||||
|
||||
class DeleteFriendCodeView(LoginRequiredMixin, DeleteView):
|
||||
"""
|
||||
Remove an existing friend code.
|
||||
Prevent deletion if the friend code is bound to any trade offers.
|
||||
Also, prevent deletion if the friend code is either the only one or
|
||||
is set as the default friend code.
|
||||
"""
|
||||
model = FriendCode
|
||||
template_name = "friend_codes/confirm_delete_friend_code.html"
|
||||
context_object_name = "friend_code"
|
||||
success_url = reverse_lazy("list_friend_codes")
|
||||
|
||||
def get_queryset(self):
|
||||
# Only allow deletion of friend codes owned by the current user.
|
||||
return FriendCode.objects.filter(user=self.request.user)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
friend_code = self.get_object()
|
||||
user = self.request.user
|
||||
|
||||
# Determine if the deletion should be disabled.
|
||||
disable_delete = False
|
||||
error_message = None
|
||||
|
||||
if user.friend_codes.count() == 1:
|
||||
disable_delete = True
|
||||
error_message = "Cannot delete your only friend code."
|
||||
elif user.default_friend_code == friend_code:
|
||||
disable_delete = True
|
||||
error_message = (
|
||||
"Cannot delete your default friend code. "
|
||||
"Please set a different default first."
|
||||
)
|
||||
|
||||
context["disable_delete"] = disable_delete
|
||||
context["error_message"] = error_message
|
||||
return context
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
user = self.object.user
|
||||
|
||||
# Check if the friend code is the only one; prevent deletion.
|
||||
if user.friend_codes.count() == 1:
|
||||
messages.error(request, "Cannot remove your only friend code.")
|
||||
return redirect(self.success_url)
|
||||
|
||||
# Check if the friend code is set as default; prevent deletion.
|
||||
if user.default_friend_code == self.object:
|
||||
messages.error(
|
||||
request,
|
||||
"Cannot delete your default friend code. Please set a different default first."
|
||||
)
|
||||
return redirect(self.success_url)
|
||||
|
||||
# Also check if this friend code is referenced by any trade offer.
|
||||
if self.object.initiated_by.exists() or self.object.accepted_by.exists():
|
||||
messages.error(
|
||||
request,
|
||||
"Cannot remove this friend code because there are existing trade offers associated with it."
|
||||
)
|
||||
return redirect(self.success_url)
|
||||
|
||||
# Proceed to safe deletion.
|
||||
self.object.delete()
|
||||
messages.success(request, "Friend code removed successfully.")
|
||||
return redirect(self.success_url + "?deleted=true")
|
||||
|
||||
class ChangeDefaultFriendCodeView(LoginRequiredMixin, View):
|
||||
"""
|
||||
Change the default friend code for the current user.
|
||||
"""
|
||||
def post(self, request, *args, **kwargs):
|
||||
friend_code_id = kwargs.get("pk")
|
||||
friend_code = get_object_or_404(FriendCode, pk=friend_code_id, user=request.user)
|
||||
request.user.set_default_friend_code(friend_code)
|
||||
messages.success(request, "Default friend code updated successfully.")
|
||||
return redirect("list_friend_codes")
|
||||
Loading…
Add table
Add a link
Reference in a new issue