1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-08-10 07:35:17 +02:00

feat: add measurement system field to CustomUser model and update related serializers, migrations, and UI components

This commit is contained in:
Sean Morley 2025-08-05 11:53:02 -04:00
parent 2af78e0a53
commit 31630de4fd
9 changed files with 76 additions and 21 deletions

View file

@ -82,11 +82,11 @@ from users.models import CustomUser
class CustomUserAdmin(UserAdmin): class CustomUserAdmin(UserAdmin):
model = CustomUser model = CustomUser
list_display = ['username', 'is_staff', 'is_active', 'image_display'] list_display = ['username', 'is_staff', 'is_active', 'image_display', 'measurement_system']
readonly_fields = ('uuid',) readonly_fields = ('uuid',)
search_fields = ('username',) search_fields = ('username',)
fieldsets = UserAdmin.fieldsets + ( fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('profile_pic', 'uuid', 'public_profile', 'disable_password')}), (None, {'fields': ('profile_pic', 'uuid', 'public_profile', 'disable_password', 'measurement_system')}),
) )
def image_display(self, obj): def image_display(self, obj):
if obj.profile_pic: if obj.profile_pic:

View file

@ -0,0 +1,18 @@
# Generated by Django 5.2.2 on 2025-08-05 15:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0004_customuser_disable_password'),
]
operations = [
migrations.AddField(
model_name='customuser',
name='measurement_system',
field=models.CharField(choices=[('metric', 'Metric'), ('imperial', 'Imperial')], default='metric', max_length=10),
),
]

View file

@ -9,6 +9,7 @@ class CustomUser(AbstractUser):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
public_profile = models.BooleanField(default=False) public_profile = models.BooleanField(default=False)
disable_password = models.BooleanField(default=False) disable_password = models.BooleanField(default=False)
measurement_system = models.CharField(max_length=10, choices=[('metric', 'Metric'), ('imperial', 'Imperial')], default='metric')
def __str__(self): def __str__(self):
return self.username return self.username

View file

@ -50,7 +50,7 @@ class UserDetailsSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = CustomUser model = CustomUser
extra_fields = ['profile_pic', 'uuid', 'public_profile'] extra_fields = ['profile_pic', 'uuid', 'public_profile', 'measurement_system']
if hasattr(UserModel, 'USERNAME_FIELD'): if hasattr(UserModel, 'USERNAME_FIELD'):
extra_fields.append(UserModel.USERNAME_FIELD) extra_fields.append(UserModel.USERNAME_FIELD)
@ -66,6 +66,8 @@ class UserDetailsSerializer(serializers.ModelSerializer):
extra_fields.append('is_staff') extra_fields.append('is_staff')
if hasattr(UserModel, 'disable_password'): if hasattr(UserModel, 'disable_password'):
extra_fields.append('disable_password') extra_fields.append('disable_password')
if hasattr(UserModel, 'measurement_system'):
extra_fields.append('measurement_system')
fields = ['pk', *extra_fields] fields = ['pk', *extra_fields]
read_only_fields = ('email', 'date_joined', 'is_staff', 'is_superuser', 'is_active', 'pk', 'disable_password') read_only_fields = ('email', 'date_joined', 'is_staff', 'is_superuser', 'is_active', 'pk', 'disable_password')
@ -96,7 +98,7 @@ class CustomUserDetailsSerializer(UserDetailsSerializer):
class Meta(UserDetailsSerializer.Meta): class Meta(UserDetailsSerializer.Meta):
model = CustomUser model = CustomUser
fields = UserDetailsSerializer.Meta.fields + ['profile_pic', 'uuid', 'public_profile', 'has_password', 'disable_password'] fields = UserDetailsSerializer.Meta.fields + ['profile_pic', 'uuid', 'public_profile', 'has_password', 'disable_password', 'measurement_system']
read_only_fields = UserDetailsSerializer.Meta.read_only_fields + ('uuid', 'has_password', 'disable_password') read_only_fields = UserDetailsSerializer.Meta.read_only_fields + ('uuid', 'has_password', 'disable_password')
@staticmethod @staticmethod

View file

@ -17,6 +17,7 @@ declare global {
public_profile: boolean; public_profile: boolean;
has_password: boolean; has_password: boolean;
disable_password: boolean; disable_password: boolean;
measurement_system: 'metric' | 'imperial';
} | null; } | null;
locale: string; locale: string;
} }

View file

@ -12,6 +12,7 @@ export type User = {
public_profile: boolean; public_profile: boolean;
has_password: boolean; has_password: boolean;
disable_password: boolean; disable_password: boolean;
measurement_system: 'metric' | 'imperial';
}; };
export type ContentImage = { export type ContentImage = {

View file

@ -542,7 +542,9 @@
"data_override_warning_desc": "Restoring data will completely replace all existing data (that is included \t\t\t\t\t\t\t\t\t\t\t\tin the backup) in your account. This action cannot be undone.", "data_override_warning_desc": "Restoring data will completely replace all existing data (that is included \t\t\t\t\t\t\t\t\t\t\t\tin the backup) in your account. This action cannot be undone.",
"select_backup_file": "Select backup file", "select_backup_file": "Select backup file",
"data_override_acknowledge": "I acknowledge that this will override all my existing data", "data_override_acknowledge": "I acknowledge that this will override all my existing data",
"data_override_acknowledge_desc": "This action is irreversible and will replace all locations, collections, \t\t\t\t\t\t\t\t\t\t\t\t\t\tand visits in your account." "data_override_acknowledge_desc": "This action is irreversible and will replace all locations, collections, \t\t\t\t\t\t\t\t\t\t\t\t\t\tand visits in your account.",
"use_imperial": "Use Imperial Units",
"use_imperial_desc": "Use imperial units (feet, inches, pounds) instead of metric units"
}, },
"collection": { "collection": {
"collection_created": "Collection created successfully!", "collection_created": "Collection created successfully!",

View file

@ -129,6 +129,7 @@ export const actions: Actions = {
let last_name = formData.get('last_name') as string | null | undefined; let last_name = formData.get('last_name') as string | null | undefined;
let profile_pic = formData.get('profile_pic') as File | null | undefined; let profile_pic = formData.get('profile_pic') as File | null | undefined;
let public_profile = formData.get('public_profile') as string | null | undefined | boolean; let public_profile = formData.get('public_profile') as string | null | undefined | boolean;
let measurement_system = formData.get('measurement_system') as string | null | undefined;
const resCurrent = await fetch(`${endpoint}/auth/user-metadata/`, { const resCurrent = await fetch(`${endpoint}/auth/user-metadata/`, {
headers: { headers: {
@ -148,6 +149,13 @@ export const actions: Actions = {
public_profile = false; public_profile = false;
} }
// Gets the boolean value of the measurement_system input checked means imperial
if (measurement_system === 'on') {
measurement_system = 'imperial';
} else {
measurement_system = 'metric';
}
let currentUser = (await resCurrent.json()) as User; let currentUser = (await resCurrent.json()) as User;
if (username === currentUser.username || !username) { if (username === currentUser.username || !username) {
@ -178,6 +186,7 @@ export const actions: Actions = {
formDataToSend.append('profile_pic', profile_pic); formDataToSend.append('profile_pic', profile_pic);
} }
formDataToSend.append('public_profile', public_profile.toString()); formDataToSend.append('public_profile', public_profile.toString());
formDataToSend.append('measurement_system', measurement_system.toString());
let csrfToken = await fetchCSRFToken(); let csrfToken = await fetchCSRFToken();

View file

@ -515,23 +515,44 @@
accept="image/*" accept="image/*"
/> />
</div> </div>
</div>
<div class="form-control"> <div class="form-control">
<label class="label cursor-pointer justify-start gap-4"> <label class="label cursor-pointer justify-start gap-4">
<input <input
type="checkbox" type="checkbox"
bind:checked={user.public_profile} bind:checked={user.public_profile}
name="public_profile" name="public_profile"
class="toggle toggle-primary" class="toggle toggle-primary"
/> />
<div> <div>
<span class="label-text font-medium">{$t('auth.public_profile')}</span> <span class="label-text font-medium">{$t('auth.public_profile')}</span>
<p class="text-sm text-base-content/60"> <p class="text-sm text-base-content/60">
{$t('settings.public_profile_desc')} {$t('settings.public_profile_desc')}
</p> </p>
</div> </div>
</label> </label>
</div>
<!-- metric or imperal toggle -->
<div class="form-control">
<label class="label cursor-pointer justify-start gap-4">
<input
type="checkbox"
checked={user.measurement_system === 'imperial'}
name="measurement_system"
class="toggle toggle-primary"
on:change={() =>
(user.measurement_system =
user.measurement_system === 'metric' ? 'imperial' : 'metric')}
/>
<div>
<span class="label-text font-medium">{$t('settings.use_imperial')}</span>
<p class="text-sm text-base-content/60">
{$t('settings.use_imperial_desc')}
</p>
</div>
</label>
</div>
</div> </div>
<button class="btn btn-primary btn-wide"> <button class="btn btn-primary btn-wide">