From a95791998623cec3f43a1f478bd17e98c0ebf04b Mon Sep 17 00:00:00 2001 From: Jeppe Bundgaard Date: Tue, 11 Nov 2025 13:25:15 +0100 Subject: [PATCH] "Add phone number edit functionality to UserProfile.vue; implement save dialog with validation and backend integration; filter Safety Seal addon in MyBookingsBook.vue." --- src/components/session/token/SessionUser.vue | 6 +- .../userDashboard/bookings/MyBookingsBook.vue | 12 ++- .../userDashboard/profile/UserProfile.vue | 98 +++++++++++++++++-- 3 files changed, 102 insertions(+), 14 deletions(-) diff --git a/src/components/session/token/SessionUser.vue b/src/components/session/token/SessionUser.vue index c05e52fc..e81fc5dc 100644 --- a/src/components/session/token/SessionUser.vue +++ b/src/components/session/token/SessionUser.vue @@ -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; diff --git a/src/views/dashboards/userDashboard/bookings/MyBookingsBook.vue b/src/views/dashboards/userDashboard/bookings/MyBookingsBook.vue index 2c4b82a4..95e10c8d 100644 --- a/src/views/dashboards/userDashboard/bookings/MyBookingsBook.vue +++ b/src/views/dashboards/userDashboard/bookings/MyBookingsBook.vue @@ -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) { diff --git a/src/views/dashboards/userDashboard/profile/UserProfile.vue b/src/views/dashboards/userDashboard/profile/UserProfile.vue index ad89e090..76fec0c9 100644 --- a/src/views/dashboards/userDashboard/profile/UserProfile.vue +++ b/src/views/dashboards/userDashboard/profile/UserProfile.vue @@ -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: + '' + + '', + 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; + }); + } + }) +} + @@ -331,7 +398,7 @@ const onClickChangePassword = async () => { @@ -349,20 +416,33 @@ const onClickChangePassword = async () => { - - + + + +