From 5b870c2c2c24e1517dce63388492b3f904d5421d Mon Sep 17 00:00:00 2001 From: jorgep Date: Thu, 17 Apr 2025 11:39:48 +0200 Subject: [PATCH 1/7] fix: refs #7939 show userErr & useNotify composable --- src/pages/Login/LoginMain.vue | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/pages/Login/LoginMain.vue b/src/pages/Login/LoginMain.vue index a4c3566a9..ce945e0ad 100644 --- a/src/pages/Login/LoginMain.vue +++ b/src/pages/Login/LoginMain.vue @@ -6,7 +6,7 @@ import { useRouter } from 'vue-router'; import VnInputPassword from 'src/components/common/VnInputPassword.vue'; import { useSession } from 'src/composables/useSession'; import { useLogin } from 'src/composables/useLogin'; - +import useNotify from 'src/composables/useNotify'; import VnLogo from 'components/ui/VnLogo.vue'; import VnInput from 'src/components/common/VnInput.vue'; import axios from 'axios'; @@ -15,6 +15,7 @@ const session = useSession(); const loginCache = useLogin(); const router = useRouter(); const { t } = useI18n(); +const { notify } = useNotify(); const username = ref(''); const password = ref(''); @@ -32,23 +33,20 @@ async function onSubmit() { data.keepLogin = keepLogin.value; await session.setLogin(data); } catch (res) { - if (res.response?.data?.error?.code === 'REQUIRES_2FA') { - Notify.create({ - message: t('login.twoFactorRequired'), - icon: 'phoneLink_lock', - type: 'warning', - }); + const err = res.response?.data?.error; + if (err?.code === 'REQUIRES_2FA') { + notify(t('login.twoFactorRequired'), 'warning', 'phoneLink_lock'); params.keepLogin = keepLogin.value; loginCache.setUser(params); return router.push({ name: 'TwoFactor', query: router.currentRoute.value?.query, }); + } else if (err?.name == 'UserError') { + notify(err.message, 'negative'); + } else { + notify(t('login.loginError'), 'negative'); } - Notify.create({ - message: t('login.loginError'), - type: 'negative', - }); } } From a8d39a9b9663a4510c0be8ad180060588da0c30c Mon Sep 17 00:00:00 2001 From: jorgep Date: Tue, 22 Apr 2025 16:48:13 +0200 Subject: [PATCH 2/7] feat(login): refs #7939 add session expiration handling and improve error notifications --- src/boot/quasar.js | 10 +++++++++- src/i18n/locale/en.yml | 2 ++ src/i18n/locale/es.yml | 2 ++ src/pages/Login/LoginMain.vue | 14 +++----------- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/boot/quasar.js b/src/boot/quasar.js index a8c397b83..8ef748c01 100644 --- a/src/boot/quasar.js +++ b/src/boot/quasar.js @@ -7,7 +7,7 @@ import { QLayout } from 'quasar'; import mainShortcutMixin from './mainShortcutMixin'; import { useCau } from 'src/composables/useCau'; -export default boot(({ app }) => { +export default boot(({ app, router }) => { QForm.mixins = [qFormMixin]; QLayout.mixins = [mainShortcutMixin]; @@ -22,6 +22,14 @@ export default boot(({ app }) => { } switch (response?.status) { + case 401: + if (!router.currentRoute.value.name.includes('login')) { + message = 'errors.sessionExpired'; + } else message = 'login.loginError'; + break; + case 403: + if (!message || message.toLowerCase() === 'access denied') + message = 'errors.accessDenied'; case 422: if (error.name == 'ValidationError') message += diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 3c1c80954..efcad468e 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -395,6 +395,8 @@ errors: updateUserConfig: Error updating user config tokenConfig: Error fetching token config writeRequest: The requested operation could not be completed + sessionExpired: Your session has expired. Please log in again + accessDenied: Access denied login: title: Login username: Username diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 518985831..71105db3e 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -391,6 +391,8 @@ errors: updateUserConfig: Error al actualizar la configuración de usuario tokenConfig: Error al obtener configuración de token writeRequest: No se pudo completar la operación solicitada + sessionExpired: Tu sesión ha expirado, por favor vuelve a iniciar sesión + accessDenied: Acceso denegado login: title: Inicio de sesión username: Nombre de usuario diff --git a/src/pages/Login/LoginMain.vue b/src/pages/Login/LoginMain.vue index ce945e0ad..0a84f8d02 100644 --- a/src/pages/Login/LoginMain.vue +++ b/src/pages/Login/LoginMain.vue @@ -1,6 +1,5 @@ From 8dd67efeb99bd7e5bf4942282c6e783065205a43 Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 25 Apr 2025 13:16:50 +0200 Subject: [PATCH 3/7] fix: refs #7939 normalize login route check to be case insensitive --- src/boot/quasar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/quasar.js b/src/boot/quasar.js index 8ef748c01..a5d915305 100644 --- a/src/boot/quasar.js +++ b/src/boot/quasar.js @@ -23,7 +23,7 @@ export default boot(({ app, router }) => { switch (response?.status) { case 401: - if (!router.currentRoute.value.name.includes('login')) { + if (!router.currentRoute.value.name.toLowerCase().includes('login')) { message = 'errors.sessionExpired'; } else message = 'login.loginError'; break; From 3ceabd5831ebbb22341281ec6f05c9705993984f Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 25 Apr 2025 13:28:32 +0200 Subject: [PATCH 4/7] refactor: refs #7939 streamline logout tests and improve session expiration error handling --- test/cypress/integration/login/logout.spec.js | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/test/cypress/integration/login/logout.spec.js b/test/cypress/integration/login/logout.spec.js index b17e42794..174ca6066 100644 --- a/test/cypress/integration/login/logout.spec.js +++ b/test/cypress/integration/login/logout.spec.js @@ -5,33 +5,28 @@ describe('Logout', () => { cy.visit(`/#/dashboard`); cy.waitForElement('.q-page', 6000); }); - describe('by user', () => { - it('should logout', () => { - cy.get('#user').click(); - cy.get('#logout').click(); - }); + + it('should logout', () => { + cy.get('#user').click(); + cy.get('#logout').click(); }); - describe('not user', () => { - beforeEach(() => { - cy.intercept('GET', '**StarredModules**', { - statusCode: 401, - body: { - error: { - statusCode: 401, - name: 'Error', - message: 'Authorization Required', - code: 'AUTHORIZATION_REQUIRED', - }, + + it('should throw session expired error if token has expired or is not valid during navigation', () => { + cy.intercept('GET', '**StarredModules**', { + statusCode: 401, + body: { + error: { + statusCode: 401, + name: 'Error', + message: 'Authorization Required', + code: 'AUTHORIZATION_REQUIRED', }, - statusMessage: 'AUTHORIZATION_REQUIRED', - }).as('badRequest'); - }); + }, + statusMessage: 'AUTHORIZATION_REQUIRED', + }).as('badRequest'); + cy.get('.q-list').should('be.visible').first().should('be.visible').click(); + cy.wait('@badRequest'); - it('when token not exists', () => { - cy.get('.q-list').should('be.visible').first().should('be.visible').click(); - cy.wait('@badRequest'); - - cy.checkNotification('Authorization Required'); - }); + cy.checkNotification('Your session has expired. Please log in again'); }); }); From dd218361f0dcb20045f89b35b25ad3b9de1a7ce5 Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 25 Apr 2025 14:17:46 +0200 Subject: [PATCH 5/7] fix: refs #7939 add orderBy property to columns configuration in EntryPreAccount.vue --- src/pages/Entry/EntryPreAccount.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Entry/EntryPreAccount.vue b/src/pages/Entry/EntryPreAccount.vue index 26683b6f4..feb26604c 100644 --- a/src/pages/Entry/EntryPreAccount.vue +++ b/src/pages/Entry/EntryPreAccount.vue @@ -73,6 +73,7 @@ const columns = computed(() => [ optionLabel: 'code', options: companies.value, }, + orderBy: false, }, { name: 'warehouse', From c432709a04bccead6769a4f72e36a3d2304669cf Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 25 Apr 2025 17:20:45 +0200 Subject: [PATCH 6/7] feat: refs #7939 add VnSelectExpense --- src/components/VnTable/VnColumn.vue | 6 +- src/components/common/VnSelectExpense.vue | 49 ++ src/pages/InvoiceIn/Card/InvoiceInVat.vue | 531 ++++++---------------- 3 files changed, 181 insertions(+), 405 deletions(-) create mode 100644 src/components/common/VnSelectExpense.vue diff --git a/src/components/VnTable/VnColumn.vue b/src/components/VnTable/VnColumn.vue index 3ce62c5de..b6cc5665f 100644 --- a/src/components/VnTable/VnColumn.vue +++ b/src/components/VnTable/VnColumn.vue @@ -180,7 +180,11 @@ const col = computed(() => { ) newColumn.component = 'checkbox'; if ($props.default && !newColumn.component) newColumn.component = $props.default; - + + if (typeof newColumn.component !== 'string') { + newColumn.attrs = { ...newColumn.component.attrs, autofocus: $props.autofocus }; + newColumn.event = { ...newColumn.component.event, ...$props?.eventHandlers }; + } return newColumn; }); diff --git a/src/components/common/VnSelectExpense.vue b/src/components/common/VnSelectExpense.vue new file mode 100644 index 000000000..406715b84 --- /dev/null +++ b/src/components/common/VnSelectExpense.vue @@ -0,0 +1,49 @@ + + + +es: + Create a new expense: Crear nuevo gasto + diff --git a/src/pages/InvoiceIn/Card/InvoiceInVat.vue b/src/pages/InvoiceIn/Card/InvoiceInVat.vue index 61c3040ae..aa9c671d9 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInVat.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInVat.vue @@ -1,21 +1,16 @@