Clarify customer login and invoice emails (#250)
Separate Pleno login email from e-conomic invoice email, make the login-email update flow authoritative and cache-safe, clear stale economic profile state, and add focused frontend coverage.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue';
|
import { getCurrentInstance, ref, watch } from 'vue';
|
||||||
import {parseError, getError} from "@/components/request/HandleGlobalError.vue";
|
import {parseError, getError} from "@/components/request/HandleGlobalError.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -24,6 +24,7 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const isEditting = ref(false);
|
const isEditting = ref(false);
|
||||||
|
const inputId = `configuration-input-${getCurrentInstance()?.uid}`;
|
||||||
|
|
||||||
const v_model = ref(props.value);
|
const v_model = ref(props.value);
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ const parseErrorOnSave = (error) => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">{{ title }}</label>
|
<label class="label" :for="inputId">{{ title }}</label>
|
||||||
<!-- If the value is not set, show a warning message -->
|
<!-- If the value is not set, show a warning message -->
|
||||||
<div v-if="!props.value && showEmptyWarning && !isEditting"
|
<div v-if="!props.value && showEmptyWarning && !isEditting"
|
||||||
class="message is-warning">
|
class="message is-warning">
|
||||||
@@ -58,6 +59,7 @@ const parseErrorOnSave = (error) => {
|
|||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<input
|
<input
|
||||||
|
:id="inputId"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
type="text"
|
||||||
v-model="v_model"
|
v-model="v_model"
|
||||||
|
|||||||
@@ -254,6 +254,20 @@ export const refreshReleaseRuntime = async ({ throwOnError = false } = {}) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resetEconomicData = () => {
|
||||||
|
SessionUser.economicData.customerNumber.value = null;
|
||||||
|
SessionUser.economicData.name.value = null;
|
||||||
|
SessionUser.economicData.address.value = null;
|
||||||
|
SessionUser.economicData.zip.value = null;
|
||||||
|
SessionUser.economicData.city.value = null;
|
||||||
|
SessionUser.economicData.mobilePhone.value = null;
|
||||||
|
SessionUser.economicData.email.value = null;
|
||||||
|
SessionUser.economicData.cvr.value = null;
|
||||||
|
SessionUser.economicData.currency.value = null;
|
||||||
|
SessionUser.economicData.country.value = null;
|
||||||
|
SessionUser.economicData.cached_at.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
const resetSessionState = () => {
|
const resetSessionState = () => {
|
||||||
SessionUser.user.id.value = null;
|
SessionUser.user.id.value = null;
|
||||||
SessionUser.user.customer_number.value = null;
|
SessionUser.user.customer_number.value = null;
|
||||||
@@ -302,17 +316,7 @@ const resetSessionState = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionUser.economicData.customerNumber.value = null;
|
resetEconomicData();
|
||||||
SessionUser.economicData.name.value = null;
|
|
||||||
SessionUser.economicData.address.value = null;
|
|
||||||
SessionUser.economicData.zip.value = null;
|
|
||||||
SessionUser.economicData.city.value = null;
|
|
||||||
SessionUser.economicData.mobilePhone.value = null;
|
|
||||||
SessionUser.economicData.email.value = null;
|
|
||||||
SessionUser.economicData.cvr.value = null;
|
|
||||||
SessionUser.economicData.currency.value = null;
|
|
||||||
SessionUser.economicData.country.value = null;
|
|
||||||
SessionUser.economicData.cached_at.value = null;
|
|
||||||
|
|
||||||
SessionUser.runtimeConfig.economic.transactionDraftCustomerNumber.value = null;
|
SessionUser.runtimeConfig.economic.transactionDraftCustomerNumber.value = null;
|
||||||
SessionUser.runtimeConfig.economic.defaultDistributionDepartmentId.value = null;
|
SessionUser.runtimeConfig.economic.defaultDistributionDepartmentId.value = null;
|
||||||
@@ -698,6 +702,8 @@ export const getSessionData = async () => {
|
|||||||
SessionUser.economicData.currency.value = session.economic_customer.currency;
|
SessionUser.economicData.currency.value = session.economic_customer.currency;
|
||||||
SessionUser.economicData.country.value = session.economic_customer.country;
|
SessionUser.economicData.country.value = session.economic_customer.country;
|
||||||
SessionUser.economicData.cached_at.value = new Date();
|
SessionUser.economicData.cached_at.value = new Date();
|
||||||
|
} else {
|
||||||
|
resetEconomicData();
|
||||||
}
|
}
|
||||||
SessionUser.initiated.value = true;
|
SessionUser.initiated.value = true;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6908,6 +6908,9 @@
|
|||||||
"account_name_desc": "@:{'words.generated.kundekonto'} @:{'words.generated.navn'}",
|
"account_name_desc": "@:{'words.generated.kundekonto'} @:{'words.generated.navn'}",
|
||||||
"account_number_desc": "@:{'words.generated.kundekonto'} @:{'words.generated.nummer'}",
|
"account_number_desc": "@:{'words.generated.kundekonto'} @:{'words.generated.nummer'}",
|
||||||
"description": "@.capitalize:{'words.generated.dine'} @:{'words.generated.fakturaoplysninger'}",
|
"description": "@.capitalize:{'words.generated.dine'} @:{'words.generated.fakturaoplysninger'}",
|
||||||
|
"invoice_email_desc": "Administreres i e-conomic og ændres ikke, når du ændrer din login-e-mail.",
|
||||||
|
"invoice_email_label": "Faktura-e-mail (e-conomic)",
|
||||||
|
"invoice_email_missing": "Ikke angivet i e-conomic",
|
||||||
"title": "@.capitalize:{'words.generated.fakturaoplysninger'}"
|
"title": "@.capitalize:{'words.generated.fakturaoplysninger'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -6935,7 +6938,9 @@
|
|||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"description": "@.capitalize:{'words.generated.dine'} sikkerhedsindstillinger",
|
"description": "@.capitalize:{'words.generated.dine'} sikkerhedsindstillinger",
|
||||||
"email_desc": "@.capitalize:{'words.generated.vi'} @:{'words.generated.bruger'} @:{'words.generated.denne'} @:{'words.generated.e'}-@:{'words.generated.mail'} @:{'words.generated.til'} @:{'words.generated.at'} nulstille @:{'words.generated.din'} @:{'words.generated.adgangskode'}, fremsende @:{'words.generated.vaskecertifikater'} @:{'words.generated.og'} @:{'words.generated.andre'} vigtige @:{'words.generated.oplysninger'}."
|
"email_desc": "@.capitalize:{'words.generated.vi'} @:{'words.generated.bruger'} @:{'words.generated.denne'} @:{'words.generated.e'}-@:{'words.generated.mail'} @:{'words.generated.til'} @:{'words.generated.at'} nulstille @:{'words.generated.din'} @:{'words.generated.adgangskode'}, fremsende @:{'words.generated.vaskecertifikater'} @:{'words.generated.og'} @:{'words.generated.andre'} vigtige @:{'words.generated.oplysninger'}.",
|
||||||
|
"login_email_desc": "Bruges til at logge ind og nulstille din adgangskode. Dette ændrer ikke faktura-e-mailen i e-conomic.",
|
||||||
|
"login_email_label": "Login-e-mail"
|
||||||
},
|
},
|
||||||
"title": "@.capitalize:{'words.generated.brugerprofil'}"
|
"title": "@.capitalize:{'words.generated.brugerprofil'}"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7018,6 +7018,9 @@
|
|||||||
"account_name_desc": "Kundenkontoname",
|
"account_name_desc": "Kundenkontoname",
|
||||||
"account_number_desc": "Kundenkontonummer",
|
"account_number_desc": "Kundenkontonummer",
|
||||||
"description": "@.capitalize:{'words.generated.ihre'} @:{'words.generated.rechnungsinformationen'}",
|
"description": "@.capitalize:{'words.generated.ihre'} @:{'words.generated.rechnungsinformationen'}",
|
||||||
|
"invoice_email_desc": "Wird in e-conomic verwaltet und nicht geändert, wenn Sie Ihre Anmelde-E-Mail ändern.",
|
||||||
|
"invoice_email_label": "Rechnungs-E-Mail (e-conomic)",
|
||||||
|
"invoice_email_missing": "Nicht in e-conomic hinterlegt",
|
||||||
"title": "@:{'words.generated.rechnungsinformationen'}"
|
"title": "@:{'words.generated.rechnungsinformationen'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -7045,7 +7048,9 @@
|
|||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"description": "@.capitalize:{'words.generated.ihre'} Sicherheitseinstellungen",
|
"description": "@.capitalize:{'words.generated.ihre'} Sicherheitseinstellungen",
|
||||||
"email_desc": "@.capitalize:{'words.generated.wir'} @:{'words.generated.verwenden'} @:{'words.generated.diese'} @.upper:{'words.generated.e'}-@:{'words.generated.mail'}, @:{'words.generated.um'} @:{'words.generated.ihr'} @:{'words.generated.passwort'} @:{'words.generated.zur'}?ckzusetzen, @:{'words.generated.waschzertifikate'} @:{'words.generated.und'} @:{'words.generated.weitere'} wichtige @:{'words.generated.informationen'} @:{'words.generated.zu'} @:{'words.generated.senden'}."
|
"email_desc": "@.capitalize:{'words.generated.wir'} @:{'words.generated.verwenden'} @:{'words.generated.diese'} @.upper:{'words.generated.e'}-@:{'words.generated.mail'}, @:{'words.generated.um'} @:{'words.generated.ihr'} @:{'words.generated.passwort'} @:{'words.generated.zur'}?ckzusetzen, @:{'words.generated.waschzertifikate'} @:{'words.generated.und'} @:{'words.generated.weitere'} wichtige @:{'words.generated.informationen'} @:{'words.generated.zu'} @:{'words.generated.senden'}.",
|
||||||
|
"login_email_desc": "Wird zur Anmeldung und zum Zurücksetzen Ihres Passworts verwendet. Die Rechnungs-E-Mail in e-conomic wird dadurch nicht geändert.",
|
||||||
|
"login_email_label": "Anmelde-E-Mail"
|
||||||
},
|
},
|
||||||
"title": "Benutzerprofil"
|
"title": "Benutzerprofil"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6739,6 +6739,9 @@
|
|||||||
"account_name_desc": "@.capitalize:{'words.generated.customer'} @:{'words.generated.account'} @:{'words.generated.name'}",
|
"account_name_desc": "@.capitalize:{'words.generated.customer'} @:{'words.generated.account'} @:{'words.generated.name'}",
|
||||||
"account_number_desc": "@.capitalize:{'words.generated.customer'} @:{'words.generated.account'} @:{'words.generated.number'}",
|
"account_number_desc": "@.capitalize:{'words.generated.customer'} @:{'words.generated.account'} @:{'words.generated.number'}",
|
||||||
"description": "@.capitalize:{'words.generated.your'} @:{'words.generated.invoice'} @:{'words.generated.information'}",
|
"description": "@.capitalize:{'words.generated.your'} @:{'words.generated.invoice'} @:{'words.generated.information'}",
|
||||||
|
"invoice_email_desc": "Managed in e-conomic and not changed when you change your login email.",
|
||||||
|
"invoice_email_label": "Invoice email (e-conomic)",
|
||||||
|
"invoice_email_missing": "Not set in e-conomic",
|
||||||
"title": "@.capitalize:{'words.generated.invoice'} @.capitalize:{'words.generated.information'}"
|
"title": "@.capitalize:{'words.generated.invoice'} @.capitalize:{'words.generated.information'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -6766,7 +6769,9 @@
|
|||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"description": "@.capitalize:{'words.generated.your'} @:{'words.generated.security'} @:{'words.generated.settings'}",
|
"description": "@.capitalize:{'words.generated.your'} @:{'words.generated.security'} @:{'words.generated.settings'}",
|
||||||
"email_desc": "@.capitalize:{'words.generated.we'} @:{'words.generated.use'} @:{'words.generated.this'} @:{'words.generated.email'} @:{'words.generated.to'} @:{'words.generated.reset'} @:{'words.generated.your'} @:{'words.generated.password'}, @:{'words.generated.send'} @:{'words.generated.wash'} @:{'words.generated.certificates'} @:{'words.generated.and'} @:{'words.generated.other'} important @:{'words.generated.information'}."
|
"email_desc": "@.capitalize:{'words.generated.we'} @:{'words.generated.use'} @:{'words.generated.this'} @:{'words.generated.email'} @:{'words.generated.to'} @:{'words.generated.reset'} @:{'words.generated.your'} @:{'words.generated.password'}, @:{'words.generated.send'} @:{'words.generated.wash'} @:{'words.generated.certificates'} @:{'words.generated.and'} @:{'words.generated.other'} important @:{'words.generated.information'}.",
|
||||||
|
"login_email_desc": "Used to sign in and reset your password. This does not change the invoice email in e-conomic.",
|
||||||
|
"login_email_label": "Login email"
|
||||||
},
|
},
|
||||||
"title": "@.capitalize:{'words.generated.user'} @.capitalize:{'words.generated.profile'}"
|
"title": "@.capitalize:{'words.generated.user'} @.capitalize:{'words.generated.profile'}"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7119,6 +7119,9 @@
|
|||||||
"account_name_desc": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.account_name_desc'}",
|
"account_name_desc": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.account_name_desc'}",
|
||||||
"account_number_desc": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.account_number_desc'}",
|
"account_number_desc": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.account_number_desc'}",
|
||||||
"description": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.description'}",
|
"description": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.description'}",
|
||||||
|
"invoice_email_desc": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.invoice_email_desc'}",
|
||||||
|
"invoice_email_label": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.invoice_email_label'}",
|
||||||
|
"invoice_email_missing": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.invoice_email_missing'}",
|
||||||
"title": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.title'}"
|
"title": "@:{'templates.generated.compat.user_dashboard.profile.invoicing.title'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -7157,6 +7160,8 @@
|
|||||||
"edit_email": "@:{'templates.generated.compat.global.edit'}",
|
"edit_email": "@:{'templates.generated.compat.global.edit'}",
|
||||||
"edit_phone": "@:{'templates.generated.compat.global.edit'}",
|
"edit_phone": "@:{'templates.generated.compat.global.edit'}",
|
||||||
"email_desc": "@:{'templates.generated.compat.user_dashboard.profile.security.email_desc'}",
|
"email_desc": "@:{'templates.generated.compat.user_dashboard.profile.security.email_desc'}",
|
||||||
|
"login_email_desc": "@:{'templates.generated.compat.user_dashboard.profile.security.login_email_desc'}",
|
||||||
|
"login_email_label": "@:{'templates.generated.compat.user_dashboard.profile.security.login_email_label'}",
|
||||||
"logout": "@:common.logout",
|
"logout": "@:common.logout",
|
||||||
"password_label": "@:global.password",
|
"password_label": "@:global.password",
|
||||||
"title": "@:settings.security"
|
"title": "@:settings.security"
|
||||||
|
|||||||
@@ -7021,6 +7021,9 @@
|
|||||||
"account_name_desc": "Kundekontonavn",
|
"account_name_desc": "Kundekontonavn",
|
||||||
"account_number_desc": "Kundekontonummer",
|
"account_number_desc": "Kundekontonummer",
|
||||||
"description": "@.capitalize:{'words.generated.din'} @:{'words.generated.fakturainformasjon'}",
|
"description": "@.capitalize:{'words.generated.din'} @:{'words.generated.fakturainformasjon'}",
|
||||||
|
"invoice_email_desc": "Administreres i e-conomic og endres ikke når du endrer innloggings-e-posten din.",
|
||||||
|
"invoice_email_label": "Faktura-e-post (e-conomic)",
|
||||||
|
"invoice_email_missing": "Ikke angitt i e-conomic",
|
||||||
"title": "@.capitalize:{'words.generated.fakturainformasjon'}"
|
"title": "@.capitalize:{'words.generated.fakturainformasjon'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -7048,7 +7051,9 @@
|
|||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"description": "Sikkerhetsinnstillingene @:{'words.generated.dine'}",
|
"description": "Sikkerhetsinnstillingene @:{'words.generated.dine'}",
|
||||||
"email_desc": "@.capitalize:{'words.generated.vi'} @:{'words.generated.bruker'} @:{'words.generated.denne'} @:{'words.generated.e'}-@:{'words.generated.posten'} @:{'words.generated.til'} @:{'words.generated.a'} tilbakestille @:{'words.generated.passordet'} @:{'words.generated.ditt'}, @:{'words.generated.sende'} @:{'words.generated.vaskesertifikater'} @:{'words.generated.og'} @:{'words.generated.annen'} viktig @:{'words.generated.informasjon'}."
|
"email_desc": "@.capitalize:{'words.generated.vi'} @:{'words.generated.bruker'} @:{'words.generated.denne'} @:{'words.generated.e'}-@:{'words.generated.posten'} @:{'words.generated.til'} @:{'words.generated.a'} tilbakestille @:{'words.generated.passordet'} @:{'words.generated.ditt'}, @:{'words.generated.sende'} @:{'words.generated.vaskesertifikater'} @:{'words.generated.og'} @:{'words.generated.annen'} viktig @:{'words.generated.informasjon'}.",
|
||||||
|
"login_email_desc": "Brukes til å logge inn og tilbakestille passordet ditt. Dette endrer ikke faktura-e-posten i e-conomic.",
|
||||||
|
"login_email_label": "Innloggings-e-post"
|
||||||
},
|
},
|
||||||
"title": "Brukerprofil"
|
"title": "Brukerprofil"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7071,6 +7071,9 @@
|
|||||||
"account_name_desc": "@:{'words.generated.kundens'} kontonamn",
|
"account_name_desc": "@:{'words.generated.kundens'} kontonamn",
|
||||||
"account_number_desc": "@:{'words.generated.kundens'} kontonummer",
|
"account_number_desc": "@:{'words.generated.kundens'} kontonummer",
|
||||||
"description": "@.capitalize:{'words.generated.din'} @:{'words.generated.fakturainformation'}",
|
"description": "@.capitalize:{'words.generated.din'} @:{'words.generated.fakturainformation'}",
|
||||||
|
"invoice_email_desc": "Hanteras i e-conomic och ändras inte när du ändrar din e-postadress för inloggning.",
|
||||||
|
"invoice_email_label": "E-post för fakturor (e-conomic)",
|
||||||
|
"invoice_email_missing": "Inte angiven i e-conomic",
|
||||||
"title": "@.capitalize:{'words.generated.fakturainformation'}"
|
"title": "@.capitalize:{'words.generated.fakturainformation'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -7098,7 +7101,9 @@
|
|||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"description": "@.capitalize:{'words.generated.dina'} säkerhetsinställningar",
|
"description": "@.capitalize:{'words.generated.dina'} säkerhetsinställningar",
|
||||||
"email_desc": "@.capitalize:{'words.generated.vi'} @:{'words.generated.anvander'} @:{'words.generated.denna'} @:{'words.generated.e'}-@:{'words.generated.post'} @:{'words.generated.for'} @:{'words.generated.att'} @:{'words.generated.aterstalla'} @:{'words.generated.ditt'} @:{'words.generated.losenord'}, @:{'words.generated.skicka'} @:{'words.generated.tvattcertifikat'} @:{'words.generated.och'} @:{'words.generated.annan'} viktig @:{'words.generated.information'}."
|
"email_desc": "@.capitalize:{'words.generated.vi'} @:{'words.generated.anvander'} @:{'words.generated.denna'} @:{'words.generated.e'}-@:{'words.generated.post'} @:{'words.generated.for'} @:{'words.generated.att'} @:{'words.generated.aterstalla'} @:{'words.generated.ditt'} @:{'words.generated.losenord'}, @:{'words.generated.skicka'} @:{'words.generated.tvattcertifikat'} @:{'words.generated.och'} @:{'words.generated.annan'} viktig @:{'words.generated.information'}.",
|
||||||
|
"login_email_desc": "Används för att logga in och återställa ditt lösenord. Detta ändrar inte e-postadressen för fakturor i e-conomic.",
|
||||||
|
"login_email_label": "E-post för inloggning"
|
||||||
},
|
},
|
||||||
"title": "@.capitalize:{'words.generated.user'} @.capitalize:{'words.generated.profile'}"
|
"title": "@.capitalize:{'words.generated.user'} @.capitalize:{'words.generated.profile'}"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -147,6 +147,9 @@
|
|||||||
"account_name_desc": "@:{'terms.glossary.kundekonto'} @:{'terms.glossary.navn'}",
|
"account_name_desc": "@:{'terms.glossary.kundekonto'} @:{'terms.glossary.navn'}",
|
||||||
"account_number_desc": "@:{'terms.glossary.kundekonto'} @:{'terms.glossary.nummer'}",
|
"account_number_desc": "@:{'terms.glossary.kundekonto'} @:{'terms.glossary.nummer'}",
|
||||||
"description": "@.capitalize:{'terms.glossary.dine'} @:{'terms.glossary.fakturaoplysninger'}",
|
"description": "@.capitalize:{'terms.glossary.dine'} @:{'terms.glossary.fakturaoplysninger'}",
|
||||||
|
"invoice_email_desc": "Administreres i e-conomic og ændres ikke, når du ændrer din login-e-mail.",
|
||||||
|
"invoice_email_label": "Faktura-e-mail (e-conomic)",
|
||||||
|
"invoice_email_missing": "Ikke angivet i e-conomic",
|
||||||
"title": "@.capitalize:{'terms.glossary.fakturaoplysninger'}"
|
"title": "@.capitalize:{'terms.glossary.fakturaoplysninger'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -174,7 +177,9 @@
|
|||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"description": "@.capitalize:{'terms.glossary.dine'} sikkerhedsindstillinger",
|
"description": "@.capitalize:{'terms.glossary.dine'} sikkerhedsindstillinger",
|
||||||
"email_desc": "@.capitalize:{'terms.glossary.vi'} @:{'terms.glossary.bruger'} @:{'terms.glossary.denne'} @:{'terms.glossary.e'}-@:{'terms.glossary.mail'} @:{'terms.glossary.til'} @:{'terms.glossary.at'} nulstille @:{'terms.glossary.din'} @:{'terms.glossary.adgangskode'}, fremsende @:{'terms.glossary.vaskecertifikater'} @:{'terms.glossary.og'} @:{'terms.glossary.andre'} vigtige @:{'terms.glossary.oplysninger'}."
|
"email_desc": "@.capitalize:{'terms.glossary.vi'} @:{'terms.glossary.bruger'} @:{'terms.glossary.denne'} @:{'terms.glossary.e'}-@:{'terms.glossary.mail'} @:{'terms.glossary.til'} @:{'terms.glossary.at'} nulstille @:{'terms.glossary.din'} @:{'terms.glossary.adgangskode'}, fremsende @:{'terms.glossary.vaskecertifikater'} @:{'terms.glossary.og'} @:{'terms.glossary.andre'} vigtige @:{'terms.glossary.oplysninger'}.",
|
||||||
|
"login_email_desc": "Bruges til at logge ind og nulstille din adgangskode. Dette ændrer ikke faktura-e-mailen i e-conomic.",
|
||||||
|
"login_email_label": "Login-e-mail"
|
||||||
},
|
},
|
||||||
"title": "@.capitalize:{'terms.glossary.brugerprofil'}"
|
"title": "@.capitalize:{'terms.glossary.brugerprofil'}"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -140,6 +140,9 @@
|
|||||||
"account_name_desc": "Kundenkontoname",
|
"account_name_desc": "Kundenkontoname",
|
||||||
"account_number_desc": "Kundenkontonummer",
|
"account_number_desc": "Kundenkontonummer",
|
||||||
"description": "@.capitalize:{'terms.glossary.ihre'} @:{'terms.glossary.rechnungsinformationen'}",
|
"description": "@.capitalize:{'terms.glossary.ihre'} @:{'terms.glossary.rechnungsinformationen'}",
|
||||||
|
"invoice_email_desc": "Wird in e-conomic verwaltet und nicht geändert, wenn Sie Ihre Anmelde-E-Mail ändern.",
|
||||||
|
"invoice_email_label": "Rechnungs-E-Mail (e-conomic)",
|
||||||
|
"invoice_email_missing": "Nicht in e-conomic hinterlegt",
|
||||||
"title": "@:{'terms.glossary.rechnungsinformationen'}"
|
"title": "@:{'terms.glossary.rechnungsinformationen'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -167,7 +170,9 @@
|
|||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"description": "@.capitalize:{'terms.glossary.ihre'} Sicherheitseinstellungen",
|
"description": "@.capitalize:{'terms.glossary.ihre'} Sicherheitseinstellungen",
|
||||||
"email_desc": "@.capitalize:{'terms.glossary.wir'} @:{'terms.glossary.verwenden'} @:{'terms.glossary.diese'} @.upper:{'terms.glossary.e'}-@:{'terms.glossary.mail'}, @:{'terms.glossary.um'} @:{'terms.glossary.ihr'} @:{'terms.glossary.passwort'} @:{'terms.glossary.zur'}?ckzusetzen, @:{'terms.glossary.waschzertifikate'} @:{'terms.glossary.und'} @:{'terms.glossary.weitere'} wichtige @:{'terms.glossary.informationen'} @:{'terms.glossary.zu'} @:{'terms.glossary.senden'}."
|
"email_desc": "@.capitalize:{'terms.glossary.wir'} @:{'terms.glossary.verwenden'} @:{'terms.glossary.diese'} @.upper:{'terms.glossary.e'}-@:{'terms.glossary.mail'}, @:{'terms.glossary.um'} @:{'terms.glossary.ihr'} @:{'terms.glossary.passwort'} @:{'terms.glossary.zur'}?ckzusetzen, @:{'terms.glossary.waschzertifikate'} @:{'terms.glossary.und'} @:{'terms.glossary.weitere'} wichtige @:{'terms.glossary.informationen'} @:{'terms.glossary.zu'} @:{'terms.glossary.senden'}.",
|
||||||
|
"login_email_desc": "Wird zur Anmeldung und zum Zurücksetzen Ihres Passworts verwendet. Die Rechnungs-E-Mail in e-conomic wird dadurch nicht geändert.",
|
||||||
|
"login_email_label": "Anmelde-E-Mail"
|
||||||
},
|
},
|
||||||
"title": "Benutzerprofil"
|
"title": "Benutzerprofil"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -147,6 +147,9 @@
|
|||||||
"account_name_desc": "@.capitalize:{'terms.glossary.customer'} @:{'terms.glossary.account'} @:{'terms.glossary.name'}",
|
"account_name_desc": "@.capitalize:{'terms.glossary.customer'} @:{'terms.glossary.account'} @:{'terms.glossary.name'}",
|
||||||
"account_number_desc": "@.capitalize:{'terms.glossary.customer'} @:{'terms.glossary.account'} @:{'terms.glossary.number'}",
|
"account_number_desc": "@.capitalize:{'terms.glossary.customer'} @:{'terms.glossary.account'} @:{'terms.glossary.number'}",
|
||||||
"description": "@.capitalize:{'terms.glossary.your'} @:{'terms.glossary.invoice'} @:{'terms.glossary.information'}",
|
"description": "@.capitalize:{'terms.glossary.your'} @:{'terms.glossary.invoice'} @:{'terms.glossary.information'}",
|
||||||
|
"invoice_email_desc": "Managed in e-conomic and not changed when you change your login email.",
|
||||||
|
"invoice_email_label": "Invoice email (e-conomic)",
|
||||||
|
"invoice_email_missing": "Not set in e-conomic",
|
||||||
"title": "@.capitalize:{'terms.glossary.invoice'} @.capitalize:{'terms.glossary.information'}"
|
"title": "@.capitalize:{'terms.glossary.invoice'} @.capitalize:{'terms.glossary.information'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -174,7 +177,9 @@
|
|||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"description": "@.capitalize:{'terms.glossary.your'} @:{'terms.glossary.security'} @:{'terms.glossary.settings'}",
|
"description": "@.capitalize:{'terms.glossary.your'} @:{'terms.glossary.security'} @:{'terms.glossary.settings'}",
|
||||||
"email_desc": "@.capitalize:{'terms.glossary.we'} @:{'terms.glossary.use'} @:{'terms.glossary.this'} @:{'terms.glossary.email'} @:{'terms.glossary.to'} @:{'terms.glossary.reset'} @:{'terms.glossary.your'} @:{'terms.glossary.password'}, @:{'terms.glossary.send'} @:{'terms.glossary.wash'} @:{'terms.glossary.certificates'} @:{'terms.glossary.and'} @:{'terms.glossary.other'} important @:{'terms.glossary.information'}."
|
"email_desc": "@.capitalize:{'terms.glossary.we'} @:{'terms.glossary.use'} @:{'terms.glossary.this'} @:{'terms.glossary.email'} @:{'terms.glossary.to'} @:{'terms.glossary.reset'} @:{'terms.glossary.your'} @:{'terms.glossary.password'}, @:{'terms.glossary.send'} @:{'terms.glossary.wash'} @:{'terms.glossary.certificates'} @:{'terms.glossary.and'} @:{'terms.glossary.other'} important @:{'terms.glossary.information'}.",
|
||||||
|
"login_email_desc": "Used to sign in and reset your password. This does not change the invoice email in e-conomic.",
|
||||||
|
"login_email_label": "Login email"
|
||||||
},
|
},
|
||||||
"title": "@.capitalize:{'terms.glossary.user'} @.capitalize:{'terms.glossary.profile'}"
|
"title": "@.capitalize:{'terms.glossary.user'} @.capitalize:{'terms.glossary.profile'}"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -184,6 +184,9 @@
|
|||||||
"account_name_desc": "@:{'phrases.compat.user_dashboard.profile.invoicing.account_name_desc'}",
|
"account_name_desc": "@:{'phrases.compat.user_dashboard.profile.invoicing.account_name_desc'}",
|
||||||
"account_number_desc": "@:{'phrases.compat.user_dashboard.profile.invoicing.account_number_desc'}",
|
"account_number_desc": "@:{'phrases.compat.user_dashboard.profile.invoicing.account_number_desc'}",
|
||||||
"description": "@:{'phrases.compat.user_dashboard.profile.invoicing.description'}",
|
"description": "@:{'phrases.compat.user_dashboard.profile.invoicing.description'}",
|
||||||
|
"invoice_email_desc": "@:{'phrases.compat.user_dashboard.profile.invoicing.invoice_email_desc'}",
|
||||||
|
"invoice_email_label": "@:{'phrases.compat.user_dashboard.profile.invoicing.invoice_email_label'}",
|
||||||
|
"invoice_email_missing": "@:{'phrases.compat.user_dashboard.profile.invoicing.invoice_email_missing'}",
|
||||||
"title": "@:{'phrases.compat.user_dashboard.profile.invoicing.title'}"
|
"title": "@:{'phrases.compat.user_dashboard.profile.invoicing.title'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -222,6 +225,8 @@
|
|||||||
"edit_email": "@:{'phrases.compat.global.edit'}",
|
"edit_email": "@:{'phrases.compat.global.edit'}",
|
||||||
"edit_phone": "@:{'phrases.compat.global.edit'}",
|
"edit_phone": "@:{'phrases.compat.global.edit'}",
|
||||||
"email_desc": "@:{'phrases.compat.user_dashboard.profile.security.email_desc'}",
|
"email_desc": "@:{'phrases.compat.user_dashboard.profile.security.email_desc'}",
|
||||||
|
"login_email_desc": "@:{'phrases.compat.user_dashboard.profile.security.login_email_desc'}",
|
||||||
|
"login_email_label": "@:{'phrases.compat.user_dashboard.profile.security.login_email_label'}",
|
||||||
"logout": "@:common.logout",
|
"logout": "@:common.logout",
|
||||||
"password_label": "@:global.password",
|
"password_label": "@:global.password",
|
||||||
"title": "@:settings.security"
|
"title": "@:settings.security"
|
||||||
|
|||||||
@@ -140,6 +140,9 @@
|
|||||||
"account_name_desc": "Kundekontonavn",
|
"account_name_desc": "Kundekontonavn",
|
||||||
"account_number_desc": "Kundekontonummer",
|
"account_number_desc": "Kundekontonummer",
|
||||||
"description": "@.capitalize:{'terms.glossary.din'} @:{'terms.glossary.fakturainformasjon'}",
|
"description": "@.capitalize:{'terms.glossary.din'} @:{'terms.glossary.fakturainformasjon'}",
|
||||||
|
"invoice_email_desc": "Administreres i e-conomic og endres ikke når du endrer innloggings-e-posten din.",
|
||||||
|
"invoice_email_label": "Faktura-e-post (e-conomic)",
|
||||||
|
"invoice_email_missing": "Ikke angitt i e-conomic",
|
||||||
"title": "@.capitalize:{'terms.glossary.fakturainformasjon'}"
|
"title": "@.capitalize:{'terms.glossary.fakturainformasjon'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -167,7 +170,9 @@
|
|||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"description": "Sikkerhetsinnstillingene @:{'terms.glossary.dine'}",
|
"description": "Sikkerhetsinnstillingene @:{'terms.glossary.dine'}",
|
||||||
"email_desc": "@.capitalize:{'terms.glossary.vi'} @:{'terms.glossary.bruker'} @:{'terms.glossary.denne'} @:{'terms.glossary.e'}-@:{'terms.glossary.posten'} @:{'terms.glossary.til'} @:{'terms.glossary.a'} tilbakestille @:{'terms.glossary.passordet'} @:{'terms.glossary.ditt'}, @:{'terms.glossary.sende'} @:{'terms.glossary.vaskesertifikater'} @:{'terms.glossary.og'} @:{'terms.glossary.annen'} viktig @:{'terms.glossary.informasjon'}."
|
"email_desc": "@.capitalize:{'terms.glossary.vi'} @:{'terms.glossary.bruker'} @:{'terms.glossary.denne'} @:{'terms.glossary.e'}-@:{'terms.glossary.posten'} @:{'terms.glossary.til'} @:{'terms.glossary.a'} tilbakestille @:{'terms.glossary.passordet'} @:{'terms.glossary.ditt'}, @:{'terms.glossary.sende'} @:{'terms.glossary.vaskesertifikater'} @:{'terms.glossary.og'} @:{'terms.glossary.annen'} viktig @:{'terms.glossary.informasjon'}.",
|
||||||
|
"login_email_desc": "Brukes til å logge inn og tilbakestille passordet ditt. Dette endrer ikke faktura-e-posten i e-conomic.",
|
||||||
|
"login_email_label": "Innloggings-e-post"
|
||||||
},
|
},
|
||||||
"title": "Brukerprofil"
|
"title": "Brukerprofil"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -140,6 +140,9 @@
|
|||||||
"account_name_desc": "@:{'terms.glossary.kundens'} kontonamn",
|
"account_name_desc": "@:{'terms.glossary.kundens'} kontonamn",
|
||||||
"account_number_desc": "@:{'terms.glossary.kundens'} kontonummer",
|
"account_number_desc": "@:{'terms.glossary.kundens'} kontonummer",
|
||||||
"description": "@.capitalize:{'terms.glossary.din'} @:{'terms.glossary.fakturainformation'}",
|
"description": "@.capitalize:{'terms.glossary.din'} @:{'terms.glossary.fakturainformation'}",
|
||||||
|
"invoice_email_desc": "Hanteras i e-conomic och ändras inte när du ändrar din e-postadress för inloggning.",
|
||||||
|
"invoice_email_label": "E-post för fakturor (e-conomic)",
|
||||||
|
"invoice_email_missing": "Inte angiven i e-conomic",
|
||||||
"title": "@.capitalize:{'terms.glossary.fakturainformation'}"
|
"title": "@.capitalize:{'terms.glossary.fakturainformation'}"
|
||||||
},
|
},
|
||||||
"microsoft_auth": {
|
"microsoft_auth": {
|
||||||
@@ -167,7 +170,9 @@
|
|||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"description": "@.capitalize:{'terms.glossary.dina'} säkerhetsinställningar",
|
"description": "@.capitalize:{'terms.glossary.dina'} säkerhetsinställningar",
|
||||||
"email_desc": "@.capitalize:{'terms.glossary.vi'} @:{'terms.glossary.anvander'} @:{'terms.glossary.denna'} @:{'terms.glossary.e'}-@:{'terms.glossary.post'} @:{'terms.glossary.for'} @:{'terms.glossary.att'} @:{'terms.glossary.aterstalla'} @:{'terms.glossary.ditt'} @:{'terms.glossary.losenord'}, @:{'terms.glossary.skicka'} @:{'terms.glossary.tvattcertifikat'} @:{'terms.glossary.och'} @:{'terms.glossary.annan'} viktig @:{'terms.glossary.information'}."
|
"email_desc": "@.capitalize:{'terms.glossary.vi'} @:{'terms.glossary.anvander'} @:{'terms.glossary.denna'} @:{'terms.glossary.e'}-@:{'terms.glossary.post'} @:{'terms.glossary.for'} @:{'terms.glossary.att'} @:{'terms.glossary.aterstalla'} @:{'terms.glossary.ditt'} @:{'terms.glossary.losenord'}, @:{'terms.glossary.skicka'} @:{'terms.glossary.tvattcertifikat'} @:{'terms.glossary.och'} @:{'terms.glossary.annan'} viktig @:{'terms.glossary.information'}.",
|
||||||
|
"login_email_desc": "Används för att logga in och återställa ditt lösenord. Detta ändrar inte e-postadressen för fakturor i e-conomic.",
|
||||||
|
"login_email_label": "E-post för inloggning"
|
||||||
},
|
},
|
||||||
"title": "@.capitalize:{'terms.glossary.user'} @.capitalize:{'terms.glossary.profile'}"
|
"title": "@.capitalize:{'terms.glossary.user'} @.capitalize:{'terms.glossary.profile'}"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { isAccountDeletionEligible } from "@/services/accountDeletion.js";
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
const isSavingEmail = ref(false);
|
||||||
const canRequestAccountDeletion = computed(
|
const canRequestAccountDeletion = computed(
|
||||||
() =>
|
() =>
|
||||||
!SessionUser.hasSuperUserToken() &&
|
!SessionUser.hasSuperUserToken() &&
|
||||||
@@ -197,115 +198,98 @@ const onClickVerifySubuserContact = async (channel) => {
|
|||||||
};
|
};
|
||||||
/** Email */
|
/** Email */
|
||||||
const onClickSaveEmail = async () => {
|
const onClickSaveEmail = async () => {
|
||||||
// Get the password from Swal
|
const passwordResult = await Swal.fire({
|
||||||
await Swal.fire({
|
|
||||||
title: t("profile.email.enter_password"),
|
title: t("profile.email.enter_password"),
|
||||||
input: "password",
|
input: "password",
|
||||||
inputPlaceholder: t("auth.password"),
|
inputPlaceholder: t("auth.password"),
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
preConfirm: () => {
|
preConfirm: async (password) => {
|
||||||
const value = Swal.getInput().value;
|
if (!password) {
|
||||||
if (!value) {
|
|
||||||
Swal.showValidationMessage(t("profile.email.password_prompt"));
|
Swal.showValidationMessage(t("profile.email.password_prompt"));
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await SessionUser.request("/account/security/validate-password", "POST", {
|
||||||
|
password,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
Swal.showValidationMessage(t("profile.error.validation_error"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return password;
|
||||||
|
} catch (error) {
|
||||||
|
Swal.showValidationMessage(SessionUser.functions.parseErrorMessage(error));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
Swal.showLoading();
|
|
||||||
return value;
|
|
||||||
},
|
},
|
||||||
inputAttributes: {
|
inputAttributes: {
|
||||||
"aria-label": t("auth.password"),
|
"aria-label": t("auth.password"),
|
||||||
},
|
},
|
||||||
inputValidator: async (value) => {
|
|
||||||
if (!value) {
|
|
||||||
return t("profile.email.password_prompt");
|
|
||||||
}
|
|
||||||
// Validate the password
|
|
||||||
await SessionUser.request("/account/security/validate-password", "POST", {
|
|
||||||
password: value,
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (result.status === 200) {
|
|
||||||
// Show the email input dialog
|
|
||||||
onSaveEmail(value);
|
|
||||||
} else {
|
|
||||||
Swal.fire({
|
|
||||||
title: t("common.error"),
|
|
||||||
text: t("profile.error.validation_error"),
|
|
||||||
icon: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
Swal.fire({
|
|
||||||
title: t("common.error"),
|
|
||||||
text: SessionUser.functions.parseErrorMessage(error),
|
|
||||||
icon: "error",
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
isLoading.value = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
allowOutsideClick: () => !Swal.isLoading(),
|
allowOutsideClick: () => !Swal.isLoading(),
|
||||||
showLoaderOnConfirm: true,
|
showLoaderOnConfirm: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (passwordResult.isConfirmed) {
|
||||||
|
await onSaveEmail(passwordResult.value);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const onSaveEmail = async (password) => {
|
const onSaveEmail = async (password) => {
|
||||||
// Show the confirmation dialog
|
const emailResult = await Swal.fire({
|
||||||
await Swal.fire({
|
|
||||||
title: t("profile.email.new_email_prompt"),
|
title: t("profile.email.new_email_prompt"),
|
||||||
text: t("profile.email.new_email_text"),
|
text: t("profile.email.new_email_text"),
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
input: "text",
|
input: "text",
|
||||||
inputLabel: t("profile.email.label"),
|
inputLabel: t("profile.email.label"),
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
preConfirm: () => {
|
preConfirm: async (value) => {
|
||||||
const value = Swal.getInput().value;
|
const email = String(value || "").trim();
|
||||||
if (!value) {
|
if (!email) {
|
||||||
Swal.showValidationMessage(t("profile.email.enter_email"));
|
Swal.showValidationMessage(t("profile.email.enter_email"));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// Validate the email address
|
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
if (!emailRegex.test(value)) {
|
if (!emailRegex.test(email)) {
|
||||||
Swal.showValidationMessage(t("profile.email.invalid_email"));
|
Swal.showValidationMessage(t("profile.email.invalid_email"));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
isLoading.value = true;
|
|
||||||
Swal.showLoading();
|
isSavingEmail.value = true;
|
||||||
SessionUser.request("/account/security/change-email", "POST", {
|
try {
|
||||||
email: value,
|
const response = await SessionUser.request("/account/security/change-email", "POST", {
|
||||||
password: password,
|
email,
|
||||||
})
|
password,
|
||||||
.then((result) => {
|
|
||||||
if (result.status === 200) {
|
|
||||||
Swal.fire({
|
|
||||||
title: t("profile.email.saved"),
|
|
||||||
text: t("profile.email.saved_text"),
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
SessionUser.getSessionData();
|
|
||||||
} else {
|
|
||||||
Swal.fire({
|
|
||||||
title: t("common.error"),
|
|
||||||
text: t("profile.error.save_email_error"),
|
|
||||||
icon: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
Swal.fire({
|
|
||||||
title: t("common.error"),
|
|
||||||
text: SessionUser.functions.parseErrorMessage(error),
|
|
||||||
icon: "error",
|
|
||||||
});
|
|
||||||
// Reset the password
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
isLoading.value = false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
Swal.showValidationMessage(t("profile.error.save_email_error"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedEmail = response?.data?.data?.email || response?.data?.email || email;
|
||||||
|
SessionUser.user.email.value = savedEmail;
|
||||||
|
return savedEmail;
|
||||||
|
} catch (error) {
|
||||||
|
Swal.showValidationMessage(SessionUser.functions.parseErrorMessage(error));
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
isSavingEmail.value = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
allowOutsideClick: () => !Swal.isLoading(),
|
||||||
|
showLoaderOnConfirm: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (emailResult.isConfirmed) {
|
||||||
|
await Swal.fire({
|
||||||
|
title: t("profile.email.saved"),
|
||||||
|
text: t("profile.email.saved_text"),
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
/** Default department */
|
/** Default department */
|
||||||
const defaultDepartment = ref(null);
|
const defaultDepartment = ref(null);
|
||||||
@@ -732,6 +716,7 @@ const onClickSaveWashCertificateEmail = async () => {
|
|||||||
:title="$t('user_dashboard.profile.invoicing.title')"
|
:title="$t('user_dashboard.profile.invoicing.title')"
|
||||||
:description="$t('user_dashboard.profile.invoicing.description')"
|
:description="$t('user_dashboard.profile.invoicing.description')"
|
||||||
icon="fas fa-user"
|
icon="fas fa-user"
|
||||||
|
data-testid="user-profile-invoicing-card"
|
||||||
>
|
>
|
||||||
<!-- Konto navn -->
|
<!-- Konto navn -->
|
||||||
<ConfigurationInput
|
<ConfigurationInput
|
||||||
@@ -748,6 +733,15 @@ const onClickSaveWashCertificateEmail = async () => {
|
|||||||
:value="SessionUser.user.customer_number"
|
:value="SessionUser.user.customer_number"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
/>
|
/>
|
||||||
|
<ConfigurationInput
|
||||||
|
class="mt-4"
|
||||||
|
:title="$t('user_dashboard.profile.invoicing.invoice_email_label')"
|
||||||
|
:description="$t('user_dashboard.profile.invoicing.invoice_email_desc')"
|
||||||
|
:value="SessionUser.economicData.email.value || $t('user_dashboard.profile.invoicing.invoice_email_missing')"
|
||||||
|
:disabled="true"
|
||||||
|
:show-empty-warning="false"
|
||||||
|
data-testid="user-profile-invoice-email"
|
||||||
|
/>
|
||||||
</ConfigurationCategory>
|
</ConfigurationCategory>
|
||||||
|
|
||||||
<!-- Security -->
|
<!-- Security -->
|
||||||
@@ -763,16 +757,18 @@ const onClickSaveWashCertificateEmail = async () => {
|
|||||||
<div class="columns is-desktop is-align-items-flex-end mb-2">
|
<div class="columns is-desktop is-align-items-flex-end mb-2">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<ConfigurationInput
|
<ConfigurationInput
|
||||||
:title="SessionUser.objects.global.language.email"
|
:title="$t('user_dashboard.profile.security.login_email_label')"
|
||||||
|
:description="$t('user_dashboard.profile.security.login_email_desc')"
|
||||||
:value="SessionUser.user.email"
|
:value="SessionUser.user.email"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
|
data-testid="user-profile-login-email"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<button
|
<button
|
||||||
class="button is-link is-fullwidth-mobile mb-1 is-fullwidth"
|
class="button is-link is-fullwidth-mobile mb-1 is-fullwidth"
|
||||||
@click="onClickSaveEmail"
|
@click="onClickSaveEmail"
|
||||||
:disabled="isLoading"
|
:disabled="isLoading || isSavingEmail"
|
||||||
data-testid="user-profile-security-edit-email"
|
data-testid="user-profile-security-edit-email"
|
||||||
>
|
>
|
||||||
<span class="icon is-small"><i class="fas fa-edit"></i></span>
|
<span class="icon is-small"><i class="fas fa-edit"></i></span>
|
||||||
|
|||||||
@@ -1,6 +1,92 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test, type Page } from "@playwright/test";
|
||||||
import { loginAsUser } from "./fixtures";
|
import { loginAsUser } from "./fixtures";
|
||||||
|
|
||||||
|
type ProfileEmailSession = {
|
||||||
|
loginEmail: string;
|
||||||
|
invoiceEmail?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildProfileSession = ({ loginEmail, invoiceEmail }: ProfileEmailSession) => ({
|
||||||
|
id: 1,
|
||||||
|
customer_number: 12345679,
|
||||||
|
group_id: 1,
|
||||||
|
email: loginEmail,
|
||||||
|
phone: {
|
||||||
|
number: "12345678",
|
||||||
|
country_code: 45,
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
wash_certificate_email: null,
|
||||||
|
email_notifications_enabled: true,
|
||||||
|
sms_notifications_enabled: false,
|
||||||
|
superuser_new_customer_email_notifications_enabled: false,
|
||||||
|
},
|
||||||
|
created_at: "2026-01-01T00:00:00.000Z",
|
||||||
|
updated_at: "2026-01-01T00:00:00.000Z",
|
||||||
|
display_name: "E2E User",
|
||||||
|
permissions: ["user"],
|
||||||
|
economic_customer: invoiceEmail
|
||||||
|
? {
|
||||||
|
customerNumber: 12345679,
|
||||||
|
name: "E2E User",
|
||||||
|
address: "Test Street 1",
|
||||||
|
zip: "1000",
|
||||||
|
city: "Copenhagen",
|
||||||
|
mobilePhone: "12345678",
|
||||||
|
email: invoiceEmail,
|
||||||
|
corporateIdentificationNumber: "12345678",
|
||||||
|
currency: "DKK",
|
||||||
|
country: "DK",
|
||||||
|
}
|
||||||
|
: [],
|
||||||
|
runtime_config: {
|
||||||
|
economic: {
|
||||||
|
transaction_draft_customer_number: null,
|
||||||
|
default_distribution_department_id: null,
|
||||||
|
},
|
||||||
|
release: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function openUserProfileWithEmails(page: Page, session: ProfileEmailSession) {
|
||||||
|
await loginAsUser(page);
|
||||||
|
|
||||||
|
let sessionRequestCount = 0;
|
||||||
|
await page.route("**/auth/session", async (route) => {
|
||||||
|
sessionRequestCount += 1;
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({ data: buildProfileSession(session) }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.goto("/user/profile");
|
||||||
|
const invoicingCard = page.getByTestId("user-profile-invoicing-card");
|
||||||
|
const securityCard = page.getByTestId("user-profile-security-card");
|
||||||
|
await expect(invoicingCard).toBeVisible();
|
||||||
|
await expect(securityCard).toBeVisible();
|
||||||
|
await invoicingCard.locator(".card-header").click();
|
||||||
|
await securityCard.locator(".card-header").click();
|
||||||
|
await expect(page.getByTestId("user-profile-login-email")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("user-profile-invoice-email")).toBeVisible();
|
||||||
|
|
||||||
|
return {
|
||||||
|
getSessionRequestCount: () => sessionRequestCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailValue = (page: Page, testId: string) => page.getByTestId(testId).locator("input");
|
||||||
|
|
||||||
|
async function completePasswordStep(page: Page, password = "valid-password") {
|
||||||
|
await page.getByTestId("user-profile-security-edit-email").click();
|
||||||
|
const popup = page.locator(".swal2-popup");
|
||||||
|
await expect(popup).toBeVisible();
|
||||||
|
await popup.locator(".swal2-input").fill(password);
|
||||||
|
await popup.locator(".swal2-confirm").click();
|
||||||
|
await expect(popup.locator(".swal2-input")).toHaveAttribute("type", "text");
|
||||||
|
}
|
||||||
|
|
||||||
async function openUserSecurity(page) {
|
async function openUserSecurity(page) {
|
||||||
await loginAsUser(page);
|
await loginAsUser(page);
|
||||||
await page.goto("/user/profile");
|
await page.goto("/user/profile");
|
||||||
@@ -44,6 +130,153 @@ test("[PROFILE][User][Security] renders the current security surface", async ({
|
|||||||
await expect(page.getByTestId("subuser-profile-security-logout")).toHaveCount(0);
|
await expect(page.getByTestId("subuser-profile-security-logout")).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("[PROFILE][User][Security] shows exact login and invoice emails under distinct labels", async ({ page }) => {
|
||||||
|
await openUserProfileWithEmails(page, {
|
||||||
|
loginEmail: "k.sand@example.test",
|
||||||
|
invoiceEmail: "billing+wash@example.test",
|
||||||
|
});
|
||||||
|
|
||||||
|
const loginEmail = page.getByTestId("user-profile-login-email");
|
||||||
|
const invoiceEmail = page.getByTestId("user-profile-invoice-email");
|
||||||
|
const loginLabel = (await loginEmail.locator("label").innerText()).trim();
|
||||||
|
const invoiceLabel = (await invoiceEmail.locator("label").innerText()).trim();
|
||||||
|
expect(loginLabel).not.toBe("");
|
||||||
|
expect(invoiceLabel).not.toBe("");
|
||||||
|
expect(loginLabel).not.toBe(invoiceLabel);
|
||||||
|
await expect(page.getByRole("textbox", { name: loginLabel, exact: true })).toHaveValue("k.sand@example.test");
|
||||||
|
await expect(page.getByRole("textbox", { name: invoiceLabel, exact: true })).toHaveValue("billing+wash@example.test");
|
||||||
|
await expect(emailValue(page, "user-profile-login-email")).toHaveValue("k.sand@example.test");
|
||||||
|
await expect(emailValue(page, "user-profile-invoice-email")).toHaveValue("billing+wash@example.test");
|
||||||
|
|
||||||
|
for (const field of [loginEmail, invoiceEmail]) {
|
||||||
|
const bounds = await field.boundingBox();
|
||||||
|
const viewport = page.viewportSize();
|
||||||
|
expect(bounds).not.toBeNull();
|
||||||
|
expect(viewport).not.toBeNull();
|
||||||
|
expect(bounds!.x).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(bounds!.x + bounds!.width).toBeLessThanOrEqual(viewport!.width + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("[PROFILE][User][Security] keeps equal login and invoice emails as separate fields", async ({ page }) => {
|
||||||
|
await openUserProfileWithEmails(page, {
|
||||||
|
loginEmail: "same.address@example.test",
|
||||||
|
invoiceEmail: "same.address@example.test",
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(emailValue(page, "user-profile-login-email")).toHaveValue("same.address@example.test");
|
||||||
|
await expect(emailValue(page, "user-profile-invoice-email")).toHaveValue("same.address@example.test");
|
||||||
|
await expect(page.getByTestId("user-profile-login-email").locator("label")).not.toHaveText("");
|
||||||
|
await expect(page.getByTestId("user-profile-invoice-email").locator("label")).not.toHaveText("");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("[PROFILE][User][Security] shows a neutral state when the invoice email is missing", async ({ page }) => {
|
||||||
|
await openUserProfileWithEmails(page, {
|
||||||
|
loginEmail: "k.sand@example.test",
|
||||||
|
invoiceEmail: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const invoiceEmail = page.getByTestId("user-profile-invoice-email");
|
||||||
|
await expect(emailValue(page, "user-profile-invoice-email")).toHaveValue(/e-conomic/i);
|
||||||
|
await expect(invoiceEmail.locator(".message.is-warning")).toHaveCount(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("[PROFILE][User][Security] applies the confirmed login email without changing invoice email or refetching session", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const profile = await openUserProfileWithEmails(page, {
|
||||||
|
loginEmail: "old.login@example.test",
|
||||||
|
invoiceEmail: "invoice@example.test",
|
||||||
|
});
|
||||||
|
const sessionRequestsBeforeSave = profile.getSessionRequestCount();
|
||||||
|
let changeEmailPayload: Record<string, unknown> | null = null;
|
||||||
|
|
||||||
|
await page.route("**/account/security/validate-password", async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({ data: { valid: true } }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await page.route("**/account/security/change-email", async (route) => {
|
||||||
|
changeEmailPayload = route.request().postDataJSON();
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({ data: { message: "Email changed", email: "k.sand@example.test" } }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await completePasswordStep(page);
|
||||||
|
const popup = page.locator(".swal2-popup");
|
||||||
|
await popup.locator(".swal2-input").fill("K.Sand@example.test");
|
||||||
|
await popup.locator(".swal2-confirm").click();
|
||||||
|
|
||||||
|
await expect(emailValue(page, "user-profile-login-email")).toHaveValue("k.sand@example.test");
|
||||||
|
await expect(emailValue(page, "user-profile-invoice-email")).toHaveValue("invoice@example.test");
|
||||||
|
expect(changeEmailPayload).toEqual({ email: "K.Sand@example.test", password: "valid-password" });
|
||||||
|
expect(profile.getSessionRequestCount()).toBe(sessionRequestsBeforeSave);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("[PROFILE][User][Security] falls back to the submitted email for an older success response", async ({ page }) => {
|
||||||
|
await openUserProfileWithEmails(page, {
|
||||||
|
loginEmail: "old.login@example.test",
|
||||||
|
invoiceEmail: "invoice@example.test",
|
||||||
|
});
|
||||||
|
await page.route("**/account/security/validate-password", async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({ data: { valid: true } }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await page.route("**/account/security/change-email", async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({ data: { message: "Email changed" } }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await completePasswordStep(page);
|
||||||
|
const popup = page.locator(".swal2-popup");
|
||||||
|
await popup.locator(".swal2-input").fill("fallback.address@example.test");
|
||||||
|
await popup.locator(".swal2-confirm").click();
|
||||||
|
|
||||||
|
await expect(emailValue(page, "user-profile-login-email")).toHaveValue("fallback.address@example.test");
|
||||||
|
await expect(emailValue(page, "user-profile-invoice-email")).toHaveValue("invoice@example.test");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("[PROFILE][User][Security] leaves both emails unchanged when saving fails", async ({ page }) => {
|
||||||
|
await openUserProfileWithEmails(page, {
|
||||||
|
loginEmail: "old.login@example.test",
|
||||||
|
invoiceEmail: "invoice@example.test",
|
||||||
|
});
|
||||||
|
await page.route("**/account/security/validate-password", async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({ data: { valid: true } }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await page.route("**/account/security/change-email", async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 422,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({ message: "Email is already in use" }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await completePasswordStep(page);
|
||||||
|
const popup = page.locator(".swal2-popup");
|
||||||
|
await popup.locator(".swal2-input").fill("duplicate@example.test");
|
||||||
|
await popup.locator(".swal2-confirm").click();
|
||||||
|
|
||||||
|
await expect(popup.locator(".swal2-validation-message")).toBeVisible();
|
||||||
|
await expect(emailValue(page, "user-profile-login-email")).toHaveValue("old.login@example.test");
|
||||||
|
await expect(emailValue(page, "user-profile-invoice-email")).toHaveValue("invoice@example.test");
|
||||||
|
});
|
||||||
|
|
||||||
test("[PROFILE][User][Security] opens the logout confirmation dialog", async ({ page }) => {
|
test("[PROFILE][User][Security] opens the logout confirmation dialog", async ({ page }) => {
|
||||||
await openUserSecurity(page);
|
await openUserSecurity(page);
|
||||||
|
|
||||||
|
|||||||
@@ -183,6 +183,51 @@ describe("session bootstrap failure handling", () => {
|
|||||||
expect(mocks.loginRedirect).toHaveBeenCalledTimes(1);
|
expect(mocks.loginRedirect).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("clears stale economic data when a refreshed session has no economic customer", async () => {
|
||||||
|
seedStoredSession("customer-session-token");
|
||||||
|
SessionUser.economicData.customerNumber.value = 12345679;
|
||||||
|
SessionUser.economicData.name.value = "Stale Customer";
|
||||||
|
SessionUser.economicData.address.value = "Old Street 1";
|
||||||
|
SessionUser.economicData.zip.value = "1000";
|
||||||
|
SessionUser.economicData.city.value = "Copenhagen";
|
||||||
|
SessionUser.economicData.mobilePhone.value = "12345678";
|
||||||
|
SessionUser.economicData.email.value = "stale.invoice@example.test";
|
||||||
|
SessionUser.economicData.cvr.value = "12345678";
|
||||||
|
SessionUser.economicData.currency.value = "DKK";
|
||||||
|
SessionUser.economicData.country.value = "DK";
|
||||||
|
SessionUser.economicData.cached_at.value = new Date();
|
||||||
|
mocks.authenticatedRequest.mockResolvedValueOnce({
|
||||||
|
data: {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
customer_number: 12345679,
|
||||||
|
group_id: 1,
|
||||||
|
email: "login@example.test",
|
||||||
|
phone: { number: "12345678", country_code: 45 },
|
||||||
|
notifications: {},
|
||||||
|
display_name: "Customer",
|
||||||
|
permissions: ["user"],
|
||||||
|
economic_customer: null,
|
||||||
|
runtime_config: { economic: {}, release: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await getSessionData();
|
||||||
|
|
||||||
|
expect(SessionUser.economicData.customerNumber.value).toBeNull();
|
||||||
|
expect(SessionUser.economicData.name.value).toBeNull();
|
||||||
|
expect(SessionUser.economicData.address.value).toBeNull();
|
||||||
|
expect(SessionUser.economicData.zip.value).toBeNull();
|
||||||
|
expect(SessionUser.economicData.city.value).toBeNull();
|
||||||
|
expect(SessionUser.economicData.mobilePhone.value).toBeNull();
|
||||||
|
expect(SessionUser.economicData.email.value).toBeNull();
|
||||||
|
expect(SessionUser.economicData.cvr.value).toBeNull();
|
||||||
|
expect(SessionUser.economicData.currency.value).toBeNull();
|
||||||
|
expect(SessionUser.economicData.country.value).toBeNull();
|
||||||
|
expect(SessionUser.economicData.cached_at.value).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("clears an invalid stored session after ping confirms the API is reachable", async () => {
|
it("clears an invalid stored session after ping confirms the API is reachable", async () => {
|
||||||
seedStoredSession("invalid-token");
|
seedStoredSession("invalid-token");
|
||||||
mocks.authenticatedRequest.mockRejectedValueOnce(invalidSessionError());
|
mocks.authenticatedRequest.mockRejectedValueOnce(invalidSessionError());
|
||||||
|
|||||||
Reference in New Issue
Block a user