0
0
Fork 0

Merge branch 'dev' into 4988-fixE2e

This commit is contained in:
Pablo Natek 2024-10-04 11:32:40 +00:00
commit efe5208d30
15 changed files with 309 additions and 346 deletions

View File

@ -0,0 +1,136 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnRow from '../ui/VnRow.vue';
import VnInput from './VnInput.vue';
import FetchData from '../FetchData.vue';
import useNotify from 'src/composables/useNotify';
const props = defineProps({
submitFn: { type: Function, default: () => {} },
askOldPass: { type: Boolean, default: false },
});
const emit = defineEmits(['onSubmit']);
const { t } = useI18n();
const { notify } = useNotify();
const form = ref();
const changePassDialog = ref();
const passwords = ref({ newPassword: null, repeatPassword: null });
const requirements = ref([]);
const isLoading = ref(false);
const validate = async () => {
const { newPassword, repeatPassword, oldPassword } = passwords.value;
if (!newPassword) {
notify(t('You must enter a new password'), 'negative');
return;
}
if (newPassword !== repeatPassword) {
notify(t("Passwords don't match"), 'negative');
return;
}
try {
isLoading.value = true;
await props.submitFn(newPassword, oldPassword);
emit('onSubmit');
} catch (e) {
notify('errors.writeRequest', 'negative');
} finally {
changePassDialog.value.hide();
isLoading.value = false;
}
};
defineExpose({ show: () => changePassDialog.value.show() });
</script>
<template>
<FetchData
url="UserPasswords/findOne"
auto-load
@on-fetch="(data) => (requirements = data)"
/>
<QDialog ref="changePassDialog">
<QCard style="width: 350px">
<QCardSection>
<slot name="header">
<VnRow class="items-center" style="flex-direction: row">
<span class="text-h6" v-text="t('globals.changePass')" />
<QIcon
class="cursor-pointer"
name="close"
size="xs"
style="flex: 0"
v-close-popup
/>
</VnRow>
</slot>
</QCardSection>
<QForm ref="form">
<QCardSection>
<VnInput
v-if="props.askOldPass"
:label="t('Old password')"
v-model="passwords.oldPassword"
type="password"
:required="true"
autofocus
/>
<VnInput
:label="t('New password')"
v-model="passwords.newPassword"
type="password"
:required="true"
:info="
t('passwordRequirements', {
length: requirements.length,
nAlpha: requirements.nAlpha,
nUpper: requirements.nUpper,
nDigits: requirements.nDigits,
nPunct: requirements.nPunct,
})
"
autofocus
/>
<VnInput
:label="t('Repeat password')"
v-model="passwords.repeatPassword"
type="password"
/>
</QCardSection>
</QForm>
<QCardActions>
<slot name="actions">
<QBtn
:disabled="isLoading"
:loading="isLoading"
:label="t('globals.cancel')"
class="q-ml-sm"
color="primary"
flat
type="reset"
v-close-popup
/>
<QBtn
:disabled="isLoading"
:loading="isLoading"
:label="t('globals.confirm')"
color="primary"
@click="validate"
/>
</slot>
</QCardActions>
</QCard>
</QDialog>
</template>
<i18n>
es:
New password: Nueva contraseña
Repeat password: Repetir contraseña
You must enter a new password: Debes introducir la nueva contraseña
Passwords don't match: Las contraseñas no coinciden
</i18n>

View File

@ -300,6 +300,7 @@ globals:
from: From from: From
To: To To: To
stateFk: State stateFk: State
changePass: Change password
errors: errors:
statusUnauthorized: Access denied statusUnauthorized: Access denied
statusInternalServerError: An internal server error has ocurred statusInternalServerError: An internal server error has ocurred
@ -870,35 +871,7 @@ wagon:
minHeightBetweenTrays: 'The minimum height between trays is ' minHeightBetweenTrays: 'The minimum height between trays is '
maxWagonHeight: 'The maximum height of the wagon is ' maxWagonHeight: 'The maximum height of the wagon is '
uncompleteTrays: There are incomplete trays uncompleteTrays: There are incomplete trays
route:
pageTitles:
agency: Agency List
routes: Routes
cmrsList: CMRs list
RouteList: List
routeCreate: New route
basicData: Basic Data
summary: Summary
RouteRoadmap: Roadmaps
RouteRoadmapCreate: Create roadmap
tickets: Tickets
log: Log
autonomous: Autonomous
RouteExtendedList: Router
cmr:
list:
results: results
cmrFk: CMR id
hasCmrDms: Attached in gestdoc
'true': 'Yes'
'false': 'No'
ticketFk: Ticketd id
routeFk: Route id
country: Country
clientFk: Client id
shipped: Preparation date
viewCmr: View CMR
downloadCmrs: Download CMRs
supplier: supplier:
list: list:
payMethod: Pay method payMethod: Pay method

View File

@ -304,6 +304,7 @@ globals:
from: Desde from: Desde
To: Hasta To: Hasta
stateFk: Estado stateFk: Estado
changePass: Cambiar contraseña
errors: errors:
statusUnauthorized: Acceso denegado statusUnauthorized: Acceso denegado
statusInternalServerError: Ha ocurrido un error interno del servidor statusInternalServerError: Ha ocurrido un error interno del servidor
@ -868,21 +869,6 @@ wagon:
minHeightBetweenTrays: 'La distancia mínima entre bandejas es ' minHeightBetweenTrays: 'La distancia mínima entre bandejas es '
maxWagonHeight: 'La altura máxima del vagón es ' maxWagonHeight: 'La altura máxima del vagón es '
uncompleteTrays: Hay bandejas sin completar uncompleteTrays: Hay bandejas sin completar
route:
cmr:
list:
results: resultados
cmrFk: Id CMR
hasCmrDms: Gestdoc
'true':
'false': 'No'
ticketFk: Id ticket
routeFk: Id ruta
country: País
clientFk: Id cliente
shipped: Fecha preparación
viewCmr: Ver CMR
downloadCmrs: Descargar CMRs
supplier: supplier:
list: list:
payMethod: Método de pago payMethod: Método de pago

View File

@ -4,9 +4,12 @@ import { computed, ref, toRefs } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useVnConfirm } from 'composables/useVnConfirm'; import { useVnConfirm } from 'composables/useVnConfirm';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useAcl } from 'src/composables/useAcl';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
import VnChangePassword from 'src/components/common/VnChangePassword.vue';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
const $props = defineProps({ const $props = defineProps({
hasAccount: { hasAccount: {
type: Boolean, type: Boolean,
@ -62,6 +65,19 @@ async function sync() {
} }
</script> </script>
<template> <template>
<VnChangePassword
ref="changePassRef"
:ask-old-pass="true"
:submit-fn="
async (newPassword, oldPassword) => {
await axios.patch(`Accounts/change-password`, {
userId: entityId,
newPassword,
oldPassword,
});
}
"
/>
<VnConfirm <VnConfirm
v-model="showSyncDialog" v-model="showSyncDialog"
:message="t('account.card.actions.sync.message')" :message="t('account.card.actions.sync.message')"
@ -92,6 +108,17 @@ async function sync() {
/> />
</template> </template>
</VnConfirm> </VnConfirm>
<QItem
v-if="
entityId == account.id &&
useAcl().hasAny([{ model: 'Account', props: '*', accessType: 'WRITE' }])
"
v-ripple
clickable
@click="$refs.changePassRef.show()"
>
<QItemSection>{{ t('globals.changePass') }}</QItemSection>
</QItem>
<QItem <QItem
v-if="account.hasAccount" v-if="account.hasAccount"
v-ripple v-ripple
@ -138,6 +165,5 @@ async function sync() {
<QItem v-ripple clickable @click="showSyncDialog = true"> <QItem v-ripple clickable @click="showSyncDialog = true">
<QItemSection>{{ t('account.card.actions.sync.name') }}</QItemSection> <QItemSection>{{ t('account.card.actions.sync.name') }}</QItemSection>
</QItem> </QItem>
<QSeparator /> <QSeparator />
</template> </template>

View File

@ -92,7 +92,7 @@ const onSubmit = async () => {
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');
unpaidClient.value = true; unpaidClient.value = true;
} catch (error) { } catch (error) {
notify('errors.create', 'negative'); notify('errors.writeRequest', 'negative');
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }

View File

@ -3,14 +3,11 @@ import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import axios from 'axios'; import axios from 'axios';
import { useQuasar } from 'quasar';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import CustomerChangePassword from 'src/pages/Customer/components/CustomerChangePassword.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnChangePassword from 'src/components/common/VnChangePassword.vue';
const { t } = useI18n(); const { t } = useI18n();
const quasar = useQuasar();
const route = useRoute(); const route = useRoute();
const canChangePassword = ref(0); const canChangePassword = ref(0);
@ -21,21 +18,11 @@ const filter = computed(() => {
}; };
}); });
const showChangePasswordDialog = () => {
quasar.dialog({
component: CustomerChangePassword,
componentProps: {
id: route.params.id,
},
});
};
async function hasCustomerRole() { async function hasCustomerRole() {
const { data } = await axios(`Clients/${route.params.id}/hasCustomerRole`); const { data } = await axios(`Clients/${route.params.id}/hasCustomerRole`);
canChangePassword.value = data; canChangePassword.value = data;
} }
</script> </script>
<template> <template>
<FormModel <FormModel
url="VnUsers/preview" url="VnUsers/preview"
@ -69,21 +56,29 @@ async function hasCustomerRole() {
</template> </template>
<template #moreActions> <template #moreActions>
<QBtn <QBtn
:label="t('Change password')" :label="t('globals.changePass')"
color="primary" color="primary"
icon="edit" icon="edit"
:disable="!canChangePassword" :disable="!canChangePassword"
@click="showChangePasswordDialog()" @click="$refs.changePassRef.show"
/> />
</template> </template>
</FormModel> </FormModel>
<VnChangePassword
ref="changePassRef"
:submit-fn="
async (newPass) => {
await axios.patch(`Clients/${$route.params.id}/setPassword`, {
newPassword: newPass,
});
}
"
/>
</template> </template>
<i18n> <i18n>
es: es:
Enable web access: Habilitar acceso web Enable web access: Habilitar acceso web
User: Usuario User: Usuario
Recovery email: Correo de recuperacion Recovery email: Correo de recuperacion
This email is used for user to regain access their account: Este correo electrónico se usa para que el usuario recupere el acceso a su cuenta This email is used for user to regain access their account: Este correo electrónico se usa para que el usuario recupere el acceso a su cuenta
Change password: Cambiar contraseña
</i18n> </i18n>

View File

@ -1,138 +0,0 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { useDialogPluginComponent } from 'quasar';
import useNotify from 'src/composables/useNotify';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import FetchData from 'src/components/FetchData.vue';
const { dialogRef } = useDialogPluginComponent();
const { notify } = useNotify();
const { t } = useI18n();
const $props = defineProps({
id: {
type: String,
required: true,
},
promise: {
type: Function,
required: true,
},
});
const userPasswords = ref({});
const closeButton = ref(null);
const isLoading = ref(false);
const newPassword = ref('');
const requestPassword = ref('');
const onSubmit = async () => {
isLoading.value = true;
if (newPassword.value !== requestPassword.value) {
notify(t("Passwords don't match"), 'negative');
isLoading.value = false;
return;
}
const payload = {
newPassword: newPassword.value,
};
try {
await axios.patch(`Clients/${$props.id}/setPassword`, payload);
} catch (error) {
notify('errors.create', 'negative');
} finally {
isLoading.value = false;
if (closeButton.value) closeButton.value.click();
}
};
</script>
<template>
<QDialog ref="dialogRef">
<FetchData
@on-fetch="(data) => (userPasswords = data[0])"
auto-load
url="UserPasswords"
/>
<QCard class="q-pa-lg">
<QCardSection>
<QForm @submit.prevent="onSubmit">
<span
ref="closeButton"
class="row justify-end close-icon"
v-close-popup
>
<QIcon name="close" size="sm" />
</span>
<VnRow class="row q-gutter-md q-mb-md" style="flex-direction: column">
<div class="col">
<VnInput
:label="t('New password')"
clearable
v-model="newPassword"
type="password"
>
<template #append>
<QIcon name="info" class="cursor-info">
<QTooltip>
{{
t('customer.card.passwordRequirements', {
...userPasswords,
})
}}
</QTooltip>
</QIcon>
</template>
</VnInput>
</div>
<div class="col">
<VnInput
:label="t('Request password')"
clearable
v-model="requestPassword"
type="password"
/>
</div>
</VnRow>
<div class="q-mt-lg row justify-end">
<QBtn
:disabled="isLoading"
:label="t('globals.cancel')"
:loading="isLoading"
class="q-ml-sm"
color="primary"
flat
type="reset"
v-close-popup
/>
<QBtn
:disabled="isLoading"
:label="t('Change password')"
:loading="isLoading"
color="primary"
type="submit"
/>
</div>
</QForm>
</QCardSection>
</QCard>
</QDialog>
</template>
<i18n>
es:
New password: Nueva contraseña
Request password: Repetir contraseña
Change password: Cambiar contraseña
Passwords don't match: Las contraseñas no coinciden
</i18n>

View File

@ -138,7 +138,7 @@ const onSubmit = async () => {
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');
onDataSaved(data); onDataSaved(data);
} catch (error) { } catch (error) {
notify('errors.create', 'negative'); notify('errors.writeRequest', 'negative');
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }

View File

@ -45,6 +45,7 @@ const columns = [
optionValue: 'id', optionValue: 'id',
useLike: false, useLike: false,
}, },
columnFilter: false,
}, },
{ {
align: 'center', align: 'center',
@ -157,7 +158,7 @@ function round(value) {
@on-fetch=" @on-fetch="
(data) => { (data) => {
travel = data.find( travel = data.find(
(data) => data.warehouseIn.code.toLowerCase() === 'vnh' (data) => data.warehouseIn?.code.toLowerCase() === 'vnh'
); );
} }
" "
@ -165,7 +166,7 @@ function round(value) {
<VnRow class="travel"> <VnRow class="travel">
<div v-if="travel"> <div v-if="travel">
<span style="color: var(--vn-label-color)"> <span style="color: var(--vn-label-color)">
{{ t('Booked trucks') }}: {{ t('Purchase Spaces') }}:
</span> </span>
<span> <span>
{{ travel?.m3 }} {{ travel?.m3 }}
@ -236,6 +237,7 @@ function round(value) {
:footer="true" :footer="true"
table-height="80vh" table-height="80vh"
auto-load auto-load
:column-search="false"
> >
<template #column-workerFk="{ row }"> <template #column-workerFk="{ row }">
<span class="link" @click.stop> <span class="link" @click.stop>
@ -288,7 +290,7 @@ function round(value) {
es: es:
Edit travel: Editar envío Edit travel: Editar envío
Travel: Envíos Travel: Envíos
Booked trucks: Camiones reservados Purchase Spaces: Espacios de compra
Buyer: Comprador Buyer: Comprador
Reserve: Reservado Reserve: Reservado
Bought: Comprado Bought: Comprado

View File

@ -23,3 +23,17 @@ route:
Summary: Summary Summary: Summary
Route is closed: Route is closed Route is closed: Route is closed
Route is not served: Route is not served Route is not served: Route is not served
cmr:
list:
results: results
cmrFk: CMR id
hasCmrDms: Attached in gestdoc
'true': 'Yes'
'false': 'No'
ticketFk: Ticketd id
routeFk: Route id
country: Country
clientFk: Client id
shipped: Preparation date
viewCmr: View CMR
downloadCmrs: Download CMRs

View File

@ -23,3 +23,17 @@ route:
Summary: Resumen Summary: Resumen
Route is closed: La ruta está cerrada Route is closed: La ruta está cerrada
Route is not served: La ruta no está servida Route is not served: La ruta no está servida
cmr:
list:
results: resultados
cmrFk: Id CMR
hasCmrDms: Gestdoc
'true':
'false': 'No'
ticketFk: Id ticket
routeFk: Id ruta
country: País
clientFk: Id cliente
shipped: Fecha preparación
viewCmr: Ver CMR
downloadCmrs: Descargar CMRs

View File

@ -30,18 +30,9 @@ const router = useRouter();
const state = useState(); const state = useState();
const { notify } = useNotify(); const { notify } = useNotify();
const thermographFilter = {
fields: ['id', 'thermographFk'],
where: {
or: [{ travelFk: null }, { travelFk: route.params.id }],
},
order: 'thermographFk ASC',
};
const fetchTravelThermographsRef = ref(null); const fetchTravelThermographsRef = ref(null);
const allowedContentTypes = ref(''); const allowedContentTypes = ref('');
const user = state.getUser(); const user = state.getUser();
const thermographsOptions = ref([]);
const dmsTypesOptions = ref([]); const dmsTypesOptions = ref([]);
const companiesOptions = ref([]); const companiesOptions = ref([]);
const warehousesOptions = ref([]); const warehousesOptions = ref([]);
@ -172,13 +163,6 @@ const onThermographCreated = async (data) => {
auto-load auto-load
url="Temperatures" url="Temperatures"
/> />
<FetchData
ref="fetchTravelThermographsRef"
url="TravelThermographs"
@on-fetch="(data) => (thermographsOptions = data)"
:filter="thermographFilter"
auto-load
/>
<QPage class="column items-center full-width"> <QPage class="column items-center full-width">
<QForm <QForm
model="travel" model="travel"
@ -214,7 +198,12 @@ const onThermographCreated = async (data) => {
<VnSelectDialog <VnSelectDialog
:label="t('travel.thermographs.thermograph')" :label="t('travel.thermographs.thermograph')"
v-model="thermographForm.travelThermographFk" v-model="thermographForm.travelThermographFk"
:options="thermographsOptions" url="TravelThermographs"
:fields="['id', 'thermographFk']"
:where="{
or: [{ travelFk: null }, { travelFk: $route.params.id }],
}"
sort-by="thermographFk ASC"
option-label="thermographFk" option-label="thermographFk"
:disable="viewAction === 'edit'" :disable="viewAction === 'edit'"
:tooltip="t('New thermograph')" :tooltip="t('New thermograph')"

View File

@ -1,99 +0,0 @@
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import VnRow from 'components/ui/VnRow.vue';
import FormPopup from 'src/components/FormPopup.vue';
import VnInput from 'src/components/common/VnInput.vue';
import axios from 'axios';
import useNotify from 'src/composables/useNotify.js';
const $props = defineProps({
id: {
type: Object,
default: () => {},
},
});
const emit = defineEmits(['onSubmit']);
const { t } = useI18n();
const { notify } = useNotify();
const formData = reactive({
newPassword: null,
repeatPassword: null,
});
const passRequirements = ref([]);
const setPassword = async () => {
try {
if (!formData.newPassword) {
notify(t('You must enter a new password'), 'negative');
return;
}
if (formData.newPassword != formData.repeatPassword) {
notify(t(`Passwords don't match`), 'negative');
return;
}
await axios.patch(`Workers/${$props.id}/setPassword`, {
newPass: formData.newPassword,
});
notify(t('Password changed!'), 'positive');
emit('onSubmit');
} catch (err) {
console.error('Error setting password', err);
}
};
const getPassRequirements = async () => {
const { data } = await axios.get('UserPasswords/findOne');
passRequirements.value = data;
};
onMounted(async () => await getPassRequirements());
</script>
<template>
<FormPopup :title="t('Reset password')" @on-submit="setPassword()">
<template #form-inputs>
<VnRow>
<VnInput
:label="t('New password')"
v-model="formData.newPassword"
type="password"
:required="true"
:info="
t('passwordRequirements', {
length: passRequirements.length,
nAlpha: passRequirements.nAlpha,
nUpper: passRequirements.nUpper,
nDigits: passRequirements.nDigits,
nPunct: passRequirements.nPunct,
})
"
/>
</VnRow>
<VnRow>
<VnInput
:label="t('Repeat password')"
v-model="formData.repeatPassword"
type="password"
/>
</VnRow>
</template>
</FormPopup>
</template>
<i18n>
es:
Reset password: Restablecer contraseña
New password: Nueva contraseña
Repeat password: Repetir contraseña
You must enter a new password: Debes introducir la nueva contraseña
Passwords don't match: Las contraseñas no coinciden
</i18n>

View File

@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n';
import CardDescriptor from 'src/components/ui/CardDescriptor.vue'; import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
import WorkerChangePasswordForm from 'src/pages/Worker/Card/WorkerChangePasswordForm.vue'; import VnChangePassword from 'src/components/common/VnChangePassword.vue';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
import axios from 'axios'; import axios from 'axios';
import VnImg from 'src/components/ui/VnImg.vue'; import VnImg from 'src/components/ui/VnImg.vue';
@ -24,18 +24,13 @@ const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
const state = useState(); const state = useState();
const user = state.getUser(); const user = state.getUser();
const changePasswordFormDialog = ref(null);
const cardDescriptorRef = ref(null);
const showEditPhotoForm = ref(false); const showEditPhotoForm = ref(false);
const toggleEditPictureForm = () => { const toggleEditPictureForm = () => {
showEditPhotoForm.value = !showEditPhotoForm.value; showEditPhotoForm.value = !showEditPhotoForm.value;
}; };
const entityId = computed(() => { const entityId = computed(() => {
return $props.id || route.params.id; return $props.id || route.params.id;
}); });
const worker = ref();
const workerExcluded = ref(false); const workerExcluded = ref(false);
const getIsExcluded = async () => { const getIsExcluded = async () => {
@ -61,10 +56,10 @@ const handleExcluded = async () => {
workerExcluded.value = !workerExcluded.value; workerExcluded.value = !workerExcluded.value;
}; };
const handlePhotoUpdated = (evt = false) => { const handlePhotoUpdated = (evt = false) => {
image.value.reload(evt); image.value.reload(evt);
}; };
const refetch = async () => await cardDescriptorRef.value.getData();
</script> </script>
<template> <template>
<CardDescriptor <CardDescriptor
@ -74,15 +69,10 @@ const refetch = async () => await cardDescriptorRef.value.getData();
url="Workers/descriptor" url="Workers/descriptor"
:filter="{ where: { id: entityId } }" :filter="{ where: { id: entityId } }"
title="user.nickname" title="user.nickname"
@on-fetch=" @on-fetch="getIsExcluded"
(data) => {
worker = data;
getIsExcluded();
}
"
> >
<template #menu="{}"> <template #menu="{ entity }">
<QItem v-ripple clickable @click="handleExcluded()"> <QItem v-ripple clickable @click="handleExcluded">
<QItemSection> <QItemSection>
{{ {{
workerExcluded workerExcluded
@ -92,16 +82,13 @@ const refetch = async () => await cardDescriptorRef.value.getData();
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem <QItem
v-if="!worker.user.emailVerified && user.id != worker.id" v-if="!entity.user.emailVerified && user.id != entity.id"
v-ripple v-ripple
clickable clickable
@click="$refs.changePasswordFormDialog.show()" @click="$refs.changePassRef.show"
> >
<QItemSection> <QItemSection>
{{ t('Change password') }} {{ t('globals.changePass') }}
<QDialog ref="changePasswordFormDialog">
<WorkerChangePasswordForm @on-submit="refetch()" :id="entityId" />
</QDialog>
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
@ -167,10 +154,10 @@ const refetch = async () => await cardDescriptorRef.value.getData();
<VnLinkPhone :phone-number="entity.phone" /> <VnLinkPhone :phone-number="entity.phone" />
</template> </template>
</VnLv> </VnLv>
<VnLv :value="worker?.sip?.extension"> <VnLv :value="entity?.sip?.extension">
<template #label> <template #label>
{{ t('worker.summary.sipExtension') }} {{ t('worker.summary.sipExtension') }}
<VnLinkPhone :phone-number="worker?.sip?.extension" /> <VnLinkPhone :phone-number="entity?.sip?.extension" />
</template> </template>
</VnLv> </VnLv>
</template> </template>
@ -201,6 +188,15 @@ const refetch = async () => await cardDescriptorRef.value.getData();
</QCardActions> </QCardActions>
</template> </template>
</CardDescriptor> </CardDescriptor>
<VnChangePassword
ref="changePassRef"
:submit-fn="
async (newPass) => {
await axios.patch(`Workers/${$route.params.id}/setPassword`, { newPass });
}
"
@on-submit="$refs.cardDescriptorRef?.getData"
/>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -213,5 +209,4 @@ const refetch = async () => await cardDescriptorRef.value.getData();
es: es:
Click to allow the user to be disabled: Marcar para deshabilitar Click to allow the user to be disabled: Marcar para deshabilitar
Click to exclude the user from getting disabled: Marcar para no deshabilitar Click to exclude the user from getting disabled: Marcar para no deshabilitar
Change password: Cambiar contraseña
</i18n> </i18n>

View File

@ -0,0 +1,70 @@
import { createWrapper, axios } from 'app/test/vitest/helper';
import VnChangePassword from 'src/components/common/VnChangePassword.vue';
import { vi, beforeEach, afterEach, beforeAll, describe, expect, it } from 'vitest';
import { Notify } from 'quasar';
describe('VnSmsDialog', () => {
let vm;
beforeAll(() => {
vi.spyOn(axios, 'get').mockResolvedValue({
data: [],
});
vm = createWrapper(VnChangePassword, {
propsData: {
submitFn: vi.fn(),
},
}).vm;
});
beforeEach(() => {
Notify.create = vi.fn();
});
afterEach(() => {
vi.clearAllMocks();
});
it('should notify when new password is empty', async () => {
vm.passwords.newPassword = '';
vm.passwords.repeatPassword = 'password';
await vm.validate();
expect(Notify.create).toHaveBeenCalledWith(
expect.objectContaining({
message: 'You must enter a new password',
type: 'negative',
})
);
});
it("should notify when passwords don't match", async () => {
vm.passwords.newPassword = 'password1';
vm.passwords.repeatPassword = 'password2';
await vm.validate();
expect(Notify.create).toHaveBeenCalledWith(
expect.objectContaining({
message: `Passwords don't match`,
type: 'negative',
})
);
});
describe('if passwords match', () => {
it('should call submitFn and emit password', async () => {
vm.passwords.newPassword = 'password';
vm.passwords.repeatPassword = 'password';
await vm.validate();
expect(vm.props.submitFn).toHaveBeenCalledWith('password', undefined);
});
it('should call submitFn and emit password and old password', async () => {
vm.passwords.newPassword = 'password';
vm.passwords.repeatPassword = 'password';
vm.passwords.oldPassword = 'oldPassword';
await vm.validate();
expect(vm.props.submitFn).toHaveBeenCalledWith('password', 'oldPassword');
});
});
});