From 31630de4fd9b82cb82bf0b849089887cead475ad Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Tue, 5 Aug 2025 11:53:02 -0400 Subject: [PATCH] feat: add measurement system field to CustomUser model and update related serializers, migrations, and UI components --- backend/server/adventures/admin.py | 4 +- .../0005_customuser_measurement_system.py | 18 +++++++ backend/server/users/models.py | 1 + backend/server/users/serializers.py | 6 ++- frontend/src/app.d.ts | 1 + frontend/src/lib/types.ts | 1 + frontend/src/locales/en.json | 4 +- frontend/src/routes/settings/+page.server.ts | 9 ++++ frontend/src/routes/settings/+page.svelte | 53 +++++++++++++------ 9 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 backend/server/users/migrations/0005_customuser_measurement_system.py diff --git a/backend/server/adventures/admin.py b/backend/server/adventures/admin.py index c9db330..68a2443 100644 --- a/backend/server/adventures/admin.py +++ b/backend/server/adventures/admin.py @@ -82,11 +82,11 @@ from users.models import CustomUser class CustomUserAdmin(UserAdmin): 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',) search_fields = ('username',) 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): if obj.profile_pic: diff --git a/backend/server/users/migrations/0005_customuser_measurement_system.py b/backend/server/users/migrations/0005_customuser_measurement_system.py new file mode 100644 index 0000000..1a45c6a --- /dev/null +++ b/backend/server/users/migrations/0005_customuser_measurement_system.py @@ -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), + ), + ] diff --git a/backend/server/users/models.py b/backend/server/users/models.py index bec028f..dbc540d 100644 --- a/backend/server/users/models.py +++ b/backend/server/users/models.py @@ -9,6 +9,7 @@ class CustomUser(AbstractUser): uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) public_profile = 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): return self.username \ No newline at end of file diff --git a/backend/server/users/serializers.py b/backend/server/users/serializers.py index 9d9bd33..758d3d1 100644 --- a/backend/server/users/serializers.py +++ b/backend/server/users/serializers.py @@ -50,7 +50,7 @@ class UserDetailsSerializer(serializers.ModelSerializer): class Meta: model = CustomUser - extra_fields = ['profile_pic', 'uuid', 'public_profile'] + extra_fields = ['profile_pic', 'uuid', 'public_profile', 'measurement_system'] if hasattr(UserModel, 'USERNAME_FIELD'): extra_fields.append(UserModel.USERNAME_FIELD) @@ -66,6 +66,8 @@ class UserDetailsSerializer(serializers.ModelSerializer): extra_fields.append('is_staff') if hasattr(UserModel, 'disable_password'): extra_fields.append('disable_password') + if hasattr(UserModel, 'measurement_system'): + extra_fields.append('measurement_system') fields = ['pk', *extra_fields] 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): 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') @staticmethod diff --git a/frontend/src/app.d.ts b/frontend/src/app.d.ts index cfe51ed..d0e3b4e 100644 --- a/frontend/src/app.d.ts +++ b/frontend/src/app.d.ts @@ -17,6 +17,7 @@ declare global { public_profile: boolean; has_password: boolean; disable_password: boolean; + measurement_system: 'metric' | 'imperial'; } | null; locale: string; } diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index b975a27..712d48b 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -12,6 +12,7 @@ export type User = { public_profile: boolean; has_password: boolean; disable_password: boolean; + measurement_system: 'metric' | 'imperial'; }; export type ContentImage = { diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 185bf26..429483d 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -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.", "select_backup_file": "Select backup file", "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_created": "Collection created successfully!", diff --git a/frontend/src/routes/settings/+page.server.ts b/frontend/src/routes/settings/+page.server.ts index da17a87..7e6c911 100644 --- a/frontend/src/routes/settings/+page.server.ts +++ b/frontend/src/routes/settings/+page.server.ts @@ -129,6 +129,7 @@ export const actions: Actions = { let last_name = formData.get('last_name') as string | 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 measurement_system = formData.get('measurement_system') as string | null | undefined; const resCurrent = await fetch(`${endpoint}/auth/user-metadata/`, { headers: { @@ -148,6 +149,13 @@ export const actions: Actions = { 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; if (username === currentUser.username || !username) { @@ -178,6 +186,7 @@ export const actions: Actions = { formDataToSend.append('profile_pic', profile_pic); } formDataToSend.append('public_profile', public_profile.toString()); + formDataToSend.append('measurement_system', measurement_system.toString()); let csrfToken = await fetchCSRFToken(); diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index dcef869..dedcbfb 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -515,23 +515,44 @@ accept="image/*" /> - -
- +
+ +
+ + +
+ +