-
+
+
-
+
+es:
+ I do not remember my password: No recuerdo mi contraseña
+
diff --git a/src/pages/Login/RecoverPassword.vue b/src/pages/Login/RecoverPassword.vue
new file mode 100644
index 000000000..5e0fd367a
--- /dev/null
+++ b/src/pages/Login/RecoverPassword.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/Login/ResetPassword.vue b/src/pages/Login/ResetPassword.vue
new file mode 100644
index 000000000..eff718e97
--- /dev/null
+++ b/src/pages/Login/ResetPassword.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/Login/TwoFactor.vue b/src/pages/Login/TwoFactor.vue
index 31b4ccc79..dd404094f 100644
--- a/src/pages/Login/TwoFactor.vue
+++ b/src/pages/Login/TwoFactor.vue
@@ -8,6 +8,7 @@ import axios from 'axios';
import { useSession } from 'src/composables/useSession';
import { useLogin } from 'src/composables/useLogin';
import VnInput from 'src/components/common/VnInput.vue';
+import VnOutForm from 'src/components/ui/VnOutForm.vue';
const quasar = useQuasar();
const session = useSession();
@@ -38,24 +39,22 @@ async function onSubmit() {
}
-
-
-
-
{{ t('twoFactor.insert') }}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
-
diff --git a/src/pages/Route/Card/RouteFilter.vue b/src/pages/Route/Card/RouteFilter.vue
index 24b155389..a6cd149f1 100644
--- a/src/pages/Route/Card/RouteFilter.vue
+++ b/src/pages/Route/Card/RouteFilter.vue
@@ -119,6 +119,7 @@ const emit = defineEmits(['search']);
sort-by="numberPlate ASC"
option-value="id"
option-label="numberPlate"
+ option-filter-value="numberPlate"
dense
outlined
rounded
diff --git a/src/pages/Route/Card/RouteForm.vue b/src/pages/Route/Card/RouteForm.vue
index b80b4536d..8c89718fa 100644
--- a/src/pages/Route/Card/RouteForm.vue
+++ b/src/pages/Route/Card/RouteForm.vue
@@ -142,11 +142,6 @@ const onSave = (data, response) => {
@@ -194,7 +189,7 @@ es:
Description: Descripción
Is served: Se ha servido
Created: Creado
- Distance must be lesser than: La distancia debe ser inferior a {maxDistance}
+ The km can not exceed: La distancia debe ser inferior a {maxDistance}
en:
- Distance must be lesser than: Distance must be lesser than {maxDistance}
+ The km can not exceed: Distance must be lesser than {maxDistance}
diff --git a/src/pages/Route/RouteList.vue b/src/pages/Route/RouteList.vue
index a978d8fda..e24ed33ed 100644
--- a/src/pages/Route/RouteList.vue
+++ b/src/pages/Route/RouteList.vue
@@ -99,6 +99,7 @@ const columns = computed(() => [
url: 'vehicles',
fields: ['id', 'numberPlate'],
optionLabel: 'numberPlate',
+ optionFilterValue: 'numberPlate',
find: {
value: 'vehicleFk',
label: 'vehiclePlateNumber',
diff --git a/src/pages/Ticket/Card/TicketBoxing.vue b/src/pages/Ticket/Card/TicketBoxing.vue
index 05bde8977..bff95c0e2 100644
--- a/src/pages/Ticket/Card/TicketBoxing.vue
+++ b/src/pages/Ticket/Card/TicketBoxing.vue
@@ -105,14 +105,14 @@ async function getVideoList(expeditionId, timed) {
label
markers
snap
- color="orange"
+ color="primary"
/>
{
-
+
diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue
index eda2b1b10..cbd102317 100644
--- a/src/pages/Ticket/TicketList.vue
+++ b/src/pages/Ticket/TicketList.vue
@@ -298,6 +298,7 @@ onMounted(() => (stateStore.rightDrawer = true));
-
+
{{ props.title }}
diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue
index e8dc7d878..8fbb23ef8 100644
--- a/src/pages/Worker/Card/WorkerDescriptor.vue
+++ b/src/pages/Worker/Card/WorkerDescriptor.vue
@@ -147,7 +147,7 @@ const refetch = async () => await cardDescriptorRef.value.getData();
diff --git a/src/router/index.js b/src/router/index.js
index faa3ab5d4..686da2dde 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -46,7 +46,7 @@ export { Router };
export default route(function (/* { store, ssrContext } */) {
Router.beforeEach(async (to, from, next) => {
const { isLoggedIn } = session;
- const outLayout = ['Login', 'TwoFactor', 'VerifyEmail'];
+ const outLayout = Router.options.routes[0].children.map((r) => r.name);
if (!isLoggedIn() && !outLayout.includes(to.name)) {
return next({ name: 'Login', query: { redirect: to.fullPath } });
}
diff --git a/src/router/modules/account.js b/src/router/modules/account.js
index 3faa00fbc..cfec2b95d 100644
--- a/src/router/modules/account.js
+++ b/src/router/modules/account.js
@@ -65,13 +65,13 @@ export default {
component: () => import('src/pages/Account/AccountAliasList.vue'),
},
{
- path: 'connections',
- name: 'AccountConnections',
+ path: 'acls',
+ name: 'AccountAcls',
meta: {
- title: 'connections',
+ title: 'acls',
icon: 'check',
},
- component: () => import('src/pages/Account/AccountConnections.vue'),
+ component: () => import('src/pages/Account/AccountAcls.vue'),
},
{
path: 'accounts',
@@ -104,13 +104,13 @@ export default {
component: () => import('src/pages/Account/AccountSamba.vue'),
},
{
- path: 'acls',
- name: 'AccountAcls',
+ path: 'connections',
+ name: 'AccountConnections',
meta: {
- title: 'acls',
- icon: 'check',
+ title: 'connections',
+ icon: 'share',
},
- component: () => import('src/pages/Account/AccountAcls.vue'),
+ component: () => import('src/pages/Account/AccountConnections.vue'),
},
{
path: 'acl-form',
diff --git a/src/router/routes.js b/src/router/routes.js
index 805eefb8c..cced308b5 100644
--- a/src/router/routes.js
+++ b/src/router/routes.js
@@ -46,6 +46,18 @@ const routes = [
meta: { title: 'verifyEmail' },
component: () => import('../pages/Login/VerifyEmail.vue'),
},
+ {
+ path: '/recoverPassword',
+ name: 'RecoverPassword',
+ meta: { title: 'recoverPassword' },
+ component: () => import('../pages/Login/RecoverPassword.vue'),
+ },
+ {
+ path: '/resetPassword',
+ name: 'ResetPassword',
+ meta: { title: 'resetPassword' },
+ component: () => import('../pages/Login/ResetPassword.vue'),
+ },
],
},
{
diff --git a/test/cypress/integration/login.spec.js b/test/cypress/integration/outLogin/login.spec.js
similarity index 100%
rename from test/cypress/integration/login.spec.js
rename to test/cypress/integration/outLogin/login.spec.js
diff --git a/test/cypress/integration/logout.spec.js b/test/cypress/integration/outLogin/logout.spec.js
similarity index 84%
rename from test/cypress/integration/logout.spec.js
rename to test/cypress/integration/outLogin/logout.spec.js
index b35a8415a..423189908 100644
--- a/test/cypress/integration/logout.spec.js
+++ b/test/cypress/integration/outLogin/logout.spec.js
@@ -7,10 +7,8 @@ describe('Logout', () => {
});
describe('by user', () => {
it('should logout', () => {
- cy.get(
- '#user > .q-btn__content > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image'
- ).click();
- cy.get('.block').click();
+ cy.get('#user').click();
+ cy.get('#logout').click();
});
});
describe('not user', () => {
diff --git a/test/cypress/integration/outLogin/recoverPassword.spec.js b/test/cypress/integration/outLogin/recoverPassword.spec.js
new file mode 100755
index 000000000..eec81b661
--- /dev/null
+++ b/test/cypress/integration/outLogin/recoverPassword.spec.js
@@ -0,0 +1,54 @@
+///
+describe('Recover Password', () => {
+ const username = 'trainee';
+ beforeEach(() => {
+ cy.visit('/#/login');
+ cy.get('#switchLanguage').click();
+ cy.get('.q-menu > :nth-child(1) > .q-item').click();
+ });
+
+ it('should go to recover password section and send notification', () => {
+ cy.get('input[aria-label="Username"]').type(username);
+ cy.get(`a[href="#/recoverPassword?user=${username}"]`).click();
+
+ cy.waitForElement('input[aria-label="User or recovery email"]');
+ cy.get('input[aria-label="User or recovery email"]').should(
+ 'have.value',
+ username
+ );
+
+ cy.get('button[type="submit"]').click();
+ cy.get('.q-notification__message').should('have.text', 'Notification sent');
+ });
+
+ it('should change password to user', () => {
+ // Get token from mail
+ cy.request(
+ `http://localhost:3000/api/Mails?filter=%7B%22where%22%3A%20%7B%22receiver%22%3A%20%22${username}%40mydomain.com%22%7D%2C%20%22order%22%3A%20%5B%22id%20DESC%22%5D%7D&access_token=DEFAULT_TOKEN`
+ ).then((response) => {
+ const regex = /access_token=([a-zA-Z0-9]+)/;
+ const [match] = response.body[0].body.match(regex);
+
+ const resetUrl = '/#/resetPassword?' + match;
+ cy.visit(resetUrl);
+ });
+
+ // Change password
+ const newPassword = 'test.1234';
+ cy.waitForElement('input[aria-label="Password"]');
+ cy.waitForElement('input[aria-label="Repeat password"]');
+ cy.get('input[aria-label="Password"]').type(newPassword);
+ cy.get('input[aria-label="Repeat password"]').type(newPassword);
+
+ cy.get('button[type="submit"]').click();
+ cy.get('.q-notification__message').should('have.text', 'Password changed');
+
+ // Try to login successfully
+ cy.get('input[aria-label="Username"]').type(username);
+ cy.get('input[aria-label="Password"]').type(newPassword);
+ cy.get('button[type="submit"]').click();
+ cy.url().should('contain', '/dashboard');
+
+ // ❗The password cannot be returned because "nightmare" does not meet the requirements
+ });
+});
diff --git a/test/cypress/integration/outLogin/twoFactor.spec.js b/test/cypress/integration/outLogin/twoFactor.spec.js
new file mode 100755
index 000000000..4d8561f0f
--- /dev/null
+++ b/test/cypress/integration/outLogin/twoFactor.spec.js
@@ -0,0 +1,56 @@
+///
+describe('Two Factor', () => {
+ const username = 'sysadmin';
+ const userId = 66;
+ beforeEach(() => {
+ cy.visit('/#/login');
+ cy.get('#switchLanguage').click();
+ cy.get('.q-menu > :nth-child(1) > .q-item').click();
+ });
+
+ it('should enable two factor to sysadmin', () => {
+ cy.request(
+ 'PATCH',
+ `http://localhost:3000/api/VnUsers/${userId}/update-user?access_token=DEFAULT_TOKEN`,
+ { twoFactor: 'email' }
+ );
+ });
+
+ it('should fail when login with incorrect two factor', () => {
+ cy.get('input[aria-label="Username"]').type(username);
+ cy.get('input[aria-label="Password"]').type('nightmare');
+ cy.get('button[type="submit"]').click();
+ cy.get('.q-notification__message').should(
+ 'have.text',
+ 'Two-factor verification required'
+ );
+
+ cy.get('input[type="text"]').type('123456');
+ cy.get('button[type="submit"]').click();
+ cy.url().should('contain', '/twoFactor');
+ });
+
+ it('should login with correct two factor', () => {
+ cy.get('input[aria-label="Username"]').type(username);
+ cy.get('input[aria-label="Password"]').type('nightmare');
+ cy.get('button[type="submit"]').click();
+ cy.get('.q-notification__message').should(
+ 'have.text',
+ 'Two-factor verification required'
+ );
+
+ // Get code from mail
+ cy.request(
+ `http://localhost:3000/api/Mails?filter=%7B%22where%22%3A%20%7B%22receiver%22%3A%20%22${username}%40mydomain.com%22%7D%2C%20%22order%22%3A%20%5B%22id%20DESC%22%5D%7D&access_token=DEFAULT_TOKEN`
+ ).then((response) => {
+ const tempDiv = document.createElement('div');
+ tempDiv.innerHTML = response.body[0].body;
+ const codeElement = tempDiv.querySelector('.code');
+ const code = codeElement ? codeElement.textContent.trim() : null;
+
+ cy.get('input[type="text"]').type(code);
+ cy.get('button[type="submit"]').click();
+ cy.url().should('contain', '/dashboard');
+ });
+ });
+});