"Add phone number edit functionality to UserProfile.vue; implement save dialog with validation and backend integration; filter Safety Seal addon in MyBookingsBook.vue."

This commit is contained in:
Jeppe Bundgaard
2025-11-11 13:25:15 +01:00
parent 96a44b47ef
commit a957919986
3 changed files with 102 additions and 14 deletions
+3 -3
View File
@@ -92,7 +92,7 @@ export const getSessionData = async () => {
SessionUser.user.customer_number.value = response.data.data.customer_number;
SessionUser.user.group_id.value = response.data.data.group_id;
SessionUser.user.email.value = response.data.data.email;
SessionUser.user.phone.phone.value = response.data.data.phone.phone;
SessionUser.user.phone.number.value = response.data.data.phone.number;
SessionUser.user.phone.country_code.value = response.data.data.phone.country_code;
SessionUser.user.created_at.value = response.data.data.created_at;
SessionUser.user.updated_at.value = response.data.data.updated_at;
@@ -175,7 +175,7 @@ export const SessionUser = {
group_id: ref(null),
email: ref(null),
phone: {
phone: ref(null),
number: ref(null),
country_code: ref(null),
},
created_at: ref(null),
@@ -344,7 +344,7 @@ export const SessionUser = {
SessionUser.user.display_name.value = null;
SessionUser.user.group_id.value = null;
SessionUser.user.email.value = null;
SessionUser.user.phone.phone.value = null;
SessionUser.user.phone.number.value = null;
SessionUser.user.phone.country_code.value = null;
SessionUser.user.created_at.value = null;
SessionUser.user.updated_at.value = null;
@@ -603,6 +603,13 @@ const getExteriorWash = (product: PosProduct) => {
return { ...product, addons: [], quantity: 1 };
}
// Detects the Safety Seal addon by name (case-insensitive). Prefer ID if available later.
const isSafetySealAddon = (p: any): boolean => {
const name = ((p && p.name) ? String(p.name) : '').toLowerCase();
// Common variants: Danish "plombe"/"sikkerhedsplombe" and English "safety seal"
return name.includes('plombe') || name.includes('safety seal') || name.includes('sikkerhedsplombe');
}
const interiorAddon = computed(() => {
if (!selectedProduct.value) return null;
return getInteriorWashAddon(selectedProduct.value as PosProduct);
@@ -623,9 +630,10 @@ const exteriorPriceLabel = computed(() => {
});
// Filtered list of addons for the UI: hide the interior wash addon (has its own dedicated button)
// and hide the Safety Seal addon (redundant now)
const visibleAddons = computed(() => {
const addons = (selectedProduct.value?.addons || []) as any[];
return addons.filter(a => !a?.product?.name?.includes('Indvendig'));
return addons.filter(a => !a?.product?.name?.includes('Indvendig') && !isSafetySealAddon(a?.product));
});
const completeBasket = computed({
@@ -638,7 +646,7 @@ const completeBasket = computed({
// Selected addons with quantity > 0 (excluding interior wash which is controlled by its own toggle)
const selectedAddons = (product.addons || [])
.map((a: any) => a?.product)
.filter((p: any) => p && (p.quantity || 0) > 0 && !(p?.name || '').includes('Indvendig')) as PosProduct[];
.filter((p: any) => p && (p.quantity || 0) > 0 && !(p?.name || '').includes('Indvendig') && !isSafetySealAddon(p)) as PosProduct[];
// Exterior/base product (default selected)
if (washExterior.value && exteriorWash) {
@@ -290,6 +290,73 @@ const onClickChangePassword = async () => {
});
}
const onClickSavePhoneNumber = async () => {
// Show the phone number and country code input dialog
await Swal.fire({
title: 'Indtast telefonnummer',
html:
'<input id="swal-input1" class="swal2-input" type="number" placeholder="Telefonnummer">' +
'<input id="swal-input2" class="swal2-input" type="number" placeholder="Landekode (f.eks. 45 for Danmark)">',
focusConfirm: false,
showCancelButton: true,
preConfirm: () => {
const phoneNumber = Swal.getPopup().querySelector('#swal-input1').value;
const countryCode = Swal.getPopup().querySelector('#swal-input2').value;
// Simple validation
if (!phoneNumber) {
Swal.showValidationMessage('Indtast venligst et telefonnummer');
return;
}
if (!countryCode) {
Swal.showValidationMessage('Indtast venligst en landekode');
return;
}
isLoading.value = true;
Swal.showLoading();
return { phoneNumber, countryCode };
},
allowOutsideClick: () => !Swal.isLoading(),
showLoaderOnConfirm: true,
}).then((result) => {
if (result.isConfirmed) {
const { phoneNumber, countryCode } = result.value;
// Send the change phone number request
SessionUser.request(
'/account/security/change-phone-number',
'POST',
{
phone_number: parseInt(phoneNumber),
country_code: parseInt(countryCode),
}
).then((response) => {
if (response.status === 200) {
Swal.fire({
title: 'Telefonnummer gemt',
text: 'Dit telefonnummer er blevet gemt.',
icon: 'success',
});
SessionUser.getSessionData();
} else {
// Show validation errors
Swal.fire({
title: 'Fejl',
text: 'Der opstod en fejl under gemme af telefonnummeret.',
icon: 'error',
});
}
}).catch((error) => {
Swal.fire({
title: 'Fejl',
text: SessionUser.functions.parseErrorMessage(error),
icon: 'error',
});
}).finally(() => {
isLoading.value = false;
});
}
})
}
</script>
@@ -331,7 +398,7 @@ const onClickChangePassword = async () => {
<!-- E-mail -->
<ConfigurationInput
:title="SessionUser.objects.global.language.email"
description="Vi bruger denne e-mail til at nulstille din adgangskode"
description="Vi bruger denne e-mail til at nulstille din adgangskode, fremsende vaskecertifikater og andre vigtige oplysninger."
v-bind:value="SessionUser.user.email"
:disabled="true"
/>
@@ -349,20 +416,33 @@ const onClickChangePassword = async () => {
<ConfigurationInput
:title="SessionUser.objects.global.language.phone_number"
description="Telefonnummer"
v-bind:value="SessionUser.user.phone.phone"
v-bind:value="SessionUser.user.phone.number"
:disabled="true"
/>
<!-- Change password -->
<button
class="button is-link is-small mt-2"
@click="onClickChangePassword"
:disabled="isLoading"
>
<div class="buttons">
<!-- Change phone number and country code -->
<button
class="button is-link is-small mt-2"
@click="onClickSavePhoneNumber"
:disabled="isLoading"
>
<span>
<i class="fas fa-edit"></i>
<span class="ml-2">Rediger telefonnummer</span>
</span>
</button>
<!-- Change password -->
<button
class="button is-link is-small mt-2"
@click="onClickChangePassword"
:disabled="isLoading"
>
<span>
<i class="fas fa-key"></i>
<span class="ml-2">Skift adgangskode</span>
</span>
</button>
</button>
</div>
</ConfigurationCategory>
<!-- Microsoft -->
<ConfigurationCategory