From fb26219354a5767fa11141c8fa5bda5fc42dded8 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Fri, 19 Apr 2024 12:09:04 +0200 Subject: [PATCH 01/26] refs #7190 feat: useTokenConfig --- src/composables/useState.js | 12 ++++++++++++ src/composables/useTokenConfig.js | 26 ++++++++++++++++++++++++++ src/i18n/locale/en.yml | 1 + src/i18n/locale/es.yml | 1 + 4 files changed, 40 insertions(+) create mode 100644 src/composables/useTokenConfig.js diff --git a/src/composables/useState.js b/src/composables/useState.js index e0b742a739..e671d41bde 100644 --- a/src/composables/useState.js +++ b/src/composables/useState.js @@ -13,6 +13,7 @@ const user = ref({ }); const roles = ref([]); +const tokenConfig = ref({}); const drawer = ref(true); const headerMounted = ref(false); @@ -52,6 +53,15 @@ export function useState() { function setRoles(data) { roles.value = data; } + function getTokenConfig() { + return computed(() => { + return tokenConfig.value; + }); + } + + function setTokenConfig(data) { + tokenConfig.value = data; + } function set(name, data) { state.value[name] = ref(data); @@ -70,6 +80,8 @@ export function useState() { setUser, getRoles, setRoles, + getTokenConfig, + setTokenConfig, set, get, unset, diff --git a/src/composables/useTokenConfig.js b/src/composables/useTokenConfig.js new file mode 100644 index 0000000000..3119a6aa69 --- /dev/null +++ b/src/composables/useTokenConfig.js @@ -0,0 +1,26 @@ +import axios from 'axios'; +import { useState } from './useState'; +import useNotify from './useNotify'; + +export function useTokenConfig() { + const state = useState(); + const { notify } = useNotify(); + + async function fetch() { + try { + const tokenConfig = await axios.get('AccessTokenConfigs/findOne', { + filter: { fields: ['renewInterval', 'renewPeriod'] }, + }); + if (!tokenConfig) return; + state.setTokenConfig(tokenConfig); + return tokenConfig; + } catch (error) { + notify('errors.tokenConfig', 'negative'); + console.error('Error fetching token config:', error); + } + } + + return { + fetch, + }; +} diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index db34b393c7..bcdc6dca17 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -96,6 +96,7 @@ errors: statusBadGateway: It seems that the server has fall down statusGatewayTimeout: Could not contact the server userConfig: Error fetching user config + tokenConfig: Error fetching token config writeRequest: The requested operation could not be completed login: title: Login diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index cb5876dd32..73eb70e60a 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -96,6 +96,7 @@ errors: statusBadGateway: Parece ser que el servidor ha caído statusGatewayTimeout: No se ha podido contactar con el servidor userConfig: Error al obtener configuración de usuario + tokenConfig: Error al obtener configuración de token writeRequest: No se pudo completar la operación solicitada login: title: Inicio de sesión From aecea5f273110c90d820c728d402b9b911f71836 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Fri, 19 Apr 2024 12:26:43 +0200 Subject: [PATCH 02/26] refs #7190 feat: renewToken --- src/composables/useSession.js | 48 ++++++++++++++++++++++++++++++- src/composables/useTokenConfig.js | 3 ++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/composables/useSession.js b/src/composables/useSession.js index 8583e10d4b..b4c5e4abbe 100644 --- a/src/composables/useSession.js +++ b/src/composables/useSession.js @@ -3,11 +3,13 @@ import { useRole } from './useRole'; import { useUserConfig } from './useUserConfig'; import axios from 'axios'; import useNotify from './useNotify'; +import { useTokenConfig } from './useTokenConfig'; const TOKEN_MULTIMEDIA = 'tokenMultimedia'; const TOKEN = 'token'; export function useSession() { const { notify } = useNotify(); + let isCheckingToken = false; function getToken() { const localToken = localStorage.getItem(TOKEN); @@ -23,9 +25,12 @@ export function useSession() { } function setToken(data) { - const storage = data.keepLogin ? localStorage : sessionStorage; + let keepLogin = data.keepLogin ?? sessionStorage.getItem('keepLogin'); + if (!keepLogin) keepLogin = false; + const storage = keepLogin ? localStorage : sessionStorage; storage.setItem(TOKEN, data.token); storage.setItem(TOKEN_MULTIMEDIA, data.tokenMultimedia); + sessionStorage.setItem('keepLogin', keepLogin); } async function destroyToken(url, storage, key) { if (storage.getItem(key)) { @@ -59,6 +64,8 @@ export function useSession() { lang: '', darkMode: null, }); + + stopRenewer(); } async function login(token, tokenMultimedia, keepLogin) { @@ -66,6 +73,9 @@ export function useSession() { await useRole().fetch(); await useUserConfig().fetch(); + await useTokenConfig().fetch(); + + await checkValidity(); } function isLoggedIn() { @@ -75,6 +85,42 @@ export function useSession() { return !!(localToken || sessionToken); } + function stopRenewer() { + clearInterval(this.intervalId); + } + + async function renewToken() { + const _token = getToken(); + const token = await axios.post('VnUsers/renewToken', { + headers: { Authorization: _token }, + }); + const _tokenMultimedia = getTokenMultimedia(); + const tokenMultimedia = await axios.post('VnUsers/renewToken', { + headers: { Authorization: _tokenMultimedia }, + }); + setToken({ token, tokenMultimedia }); + } + + async function checkValidity() { + const { getTokenConfig } = useState(); + + const tokenConfig = getTokenConfig(); + const created = +localStorage.getItem('created'); + const ttl = localStorage.getItem('ttl'); + + if (isCheckingToken || !created) return; + isCheckingToken = true; + + const renewPeriodInSeconds = Math.min(ttl, tokenConfig.value.renewPeriod) * 1000; + const maxDate = created + renewPeriodInSeconds; + const now = new Date(); + + if (now.getTime() <= maxDate) return (isCheckingToken = false); + + await renewToken(); + isCheckingToken = false; + } + return { getToken, getTokenMultimedia, diff --git a/src/composables/useTokenConfig.js b/src/composables/useTokenConfig.js index 3119a6aa69..afe7a52721 100644 --- a/src/composables/useTokenConfig.js +++ b/src/composables/useTokenConfig.js @@ -8,6 +8,9 @@ export function useTokenConfig() { async function fetch() { try { + let tokenConfigState = state.getTokenConfig(); + if (tokenConfigState) return tokenConfigState; + const tokenConfig = await axios.get('AccessTokenConfigs/findOne', { filter: { fields: ['renewInterval', 'renewPeriod'] }, }); From 2b24e18f755c135ae7f94e284fcf58d0cdba1b98 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 22 Apr 2024 07:37:35 +0200 Subject: [PATCH 03/26] FEAT: SETiNTERVAL WITH RENEWpERIOD --- src/composables/useSession.js | 68 +++++++++++++++++++++++-------- src/composables/useTokenConfig.js | 12 +++--- src/pages/Login/LoginMain.vue | 8 +++- src/router/index.js | 2 + 4 files changed, 64 insertions(+), 26 deletions(-) diff --git a/src/composables/useSession.js b/src/composables/useSession.js index b4c5e4abbe..f308bf3e2b 100644 --- a/src/composables/useSession.js +++ b/src/composables/useSession.js @@ -10,6 +10,7 @@ const TOKEN = 'token'; export function useSession() { const { notify } = useNotify(); let isCheckingToken = false; + let intervalId = null; function getToken() { const localToken = localStorage.getItem(TOKEN); @@ -24,14 +25,25 @@ export function useSession() { return localTokenMultimedia || sessionTokenMultimedia || ''; } - function setToken(data) { - let keepLogin = data.keepLogin ?? sessionStorage.getItem('keepLogin'); - if (!keepLogin) keepLogin = false; + function setSession(data) { + let keepLogin = data.keepLogin; const storage = keepLogin ? localStorage : sessionStorage; storage.setItem(TOKEN, data.token); storage.setItem(TOKEN_MULTIMEDIA, data.tokenMultimedia); + storage.setItem('created', data.created); + storage.setItem('ttl', data.ttl); sessionStorage.setItem('keepLogin', keepLogin); } + + function keepLogin() { + return sessionStorage.getItem('keepLogin'); + } + + function setToken({ token, tokenMultimedia }) { + const storage = keepLogin() ? localStorage : sessionStorage; + storage.setItem(TOKEN, token); + storage.setItem(TOKEN_MULTIMEDIA, tokenMultimedia); + } async function destroyToken(url, storage, key) { if (storage.getItem(key)) { try { @@ -50,11 +62,15 @@ export function useSession() { tokenMultimedia: 'Accounts/logout', token: 'VnUsers/logout', }; + const storage = keepLogin() ? localStorage : sessionStorage; + for (const [key, url] of Object.entries(tokens)) { - await destroyToken(url, localStorage, key); - await destroyToken(url, sessionStorage, key); + await destroyToken(url, storage, key); } + localStorage.clear(); + sessionStorage.clear(); + const { setUser } = useState(); setUser({ @@ -68,25 +84,33 @@ export function useSession() { stopRenewer(); } - async function login(token, tokenMultimedia, keepLogin) { - setToken({ token, tokenMultimedia, keepLogin }); + async function login(data) { + setSession(data); await useRole().fetch(); await useUserConfig().fetch(); await useTokenConfig().fetch(); - - await checkValidity(); + startInterval(); } function isLoggedIn() { const localToken = localStorage.getItem(TOKEN); const sessionToken = sessionStorage.getItem(TOKEN); - + startInterval(); return !!(localToken || sessionToken); } + function startInterval() { + stopRenewer(); + const renewPeriod = +sessionStorage.getItem('renewPeriod'); + if (!renewPeriod) return; + intervalId = setInterval(() => checkValidity(), renewPeriod * 1000); + // JUST FOR TEST + // checkValidity(); + } + function stopRenewer() { - clearInterval(this.intervalId); + clearInterval(intervalId); } async function renewToken() { @@ -98,24 +122,32 @@ export function useSession() { const tokenMultimedia = await axios.post('VnUsers/renewToken', { headers: { Authorization: _tokenMultimedia }, }); - setToken({ token, tokenMultimedia }); + setToken({ token: token.data.id, tokenMultimedia: tokenMultimedia.data.id }); } async function checkValidity() { const { getTokenConfig } = useState(); - const tokenConfig = getTokenConfig(); - const created = +localStorage.getItem('created'); - const ttl = localStorage.getItem('ttl'); + const tokenConfig = getTokenConfig() ?? sessionStorage.getItem('tokenConfig'); + const storage = keepLogin() ? localStorage : sessionStorage; + + const created = +storage.getItem('created'); + const ttl = +storage.getItem('ttl'); if (isCheckingToken || !created) return; isCheckingToken = true; const renewPeriodInSeconds = Math.min(ttl, tokenConfig.value.renewPeriod) * 1000; - const maxDate = created + renewPeriodInSeconds; - const now = new Date(); - if (now.getTime() <= maxDate) return (isCheckingToken = false); + const maxDate = created + renewPeriodInSeconds; + const now = new Date().getTime(); + + //COMMENT THIS IF JUST FOR TEST + if (isNaN(renewPeriodInSeconds) || now <= maxDate) { + console.error('Token not renewed'); + return (isCheckingToken = false); + } + console.error('Token renewed'); await renewToken(); isCheckingToken = false; diff --git a/src/composables/useTokenConfig.js b/src/composables/useTokenConfig.js index afe7a52721..6001221997 100644 --- a/src/composables/useTokenConfig.js +++ b/src/composables/useTokenConfig.js @@ -8,15 +8,13 @@ export function useTokenConfig() { async function fetch() { try { - let tokenConfigState = state.getTokenConfig(); - if (tokenConfigState) return tokenConfigState; - - const tokenConfig = await axios.get('AccessTokenConfigs/findOne', { + const { data } = await axios.get('AccessTokenConfigs/findOne', { filter: { fields: ['renewInterval', 'renewPeriod'] }, }); - if (!tokenConfig) return; - state.setTokenConfig(tokenConfig); - return tokenConfig; + if (!data) return; + state.setTokenConfig(data); + sessionStorage.setItem('renewPeriod', data.renewPeriod); + return data; } catch (error) { notify('errors.tokenConfig', 'negative'); console.error('Error fetching token config:', error); diff --git a/src/pages/Login/LoginMain.vue b/src/pages/Login/LoginMain.vue index fcde51edff..5a3490f50d 100644 --- a/src/pages/Login/LoginMain.vue +++ b/src/pages/Login/LoginMain.vue @@ -38,7 +38,13 @@ async function onSubmit() { if (!multimediaToken) return; - await session.login(data.token, multimediaToken.id, keepLogin.value); + const login = { + ...data, + created: Date.now(), + tokenMultimedia: multimediaToken.id, + keepLogin: keepLogin.value, + }; + await session.login(login); quasar.notify({ message: t('login.loginSuccess'), diff --git a/src/router/index.js b/src/router/index.js index 3e442f0e62..7a0aedcaec 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -12,6 +12,7 @@ import { useSession } from 'src/composables/useSession'; import { useRole } from 'src/composables/useRole'; import { useUserConfig } from 'src/composables/useUserConfig'; import { toLowerCamel } from 'src/filters'; +import { useTokenConfig } from 'src/composables/useTokenConfig'; const state = useState(); const session = useSession(); @@ -55,6 +56,7 @@ export default route(function (/* { store, ssrContext } */) { if (stateRoles.length === 0) { await useRole().fetch(); await useUserConfig().fetch(); + await useTokenConfig().fetch(); } const matches = to.matched; const hasRequiredRoles = matches.every((route) => { From 78561c5f42e1660333c28a85618fbc199f2390b8 Mon Sep 17 00:00:00 2001 From: wbuezas Date: Mon, 22 Apr 2024 08:34:38 -0300 Subject: [PATCH 04/26] Item types --- src/i18n/locale/en.yml | 19 ++++ src/i18n/locale/es.yml | 24 +++++ src/pages/Item/ItemTypeCreate.vue | 91 ++++++++++++++++ src/pages/Item/ItemTypeList.vue | 86 +++++++++++++++ src/pages/ItemType/Card/ItemTypeBasicData.vue | 79 ++++++++++++++ src/pages/ItemType/Card/ItemTypeCard.vue | 26 +++++ .../ItemType/Card/ItemTypeDescriptor.vue | 82 ++++++++++++++ src/pages/ItemType/Card/ItemTypeSummary.vue | 102 ++++++++++++++++++ src/router/modules/index.js | 2 + src/router/modules/item.js | 21 +++- src/router/modules/itemType.js | 45 ++++++++ src/router/routes.js | 2 + 12 files changed, 577 insertions(+), 2 deletions(-) create mode 100644 src/pages/Item/ItemTypeCreate.vue create mode 100644 src/pages/Item/ItemTypeList.vue create mode 100644 src/pages/ItemType/Card/ItemTypeBasicData.vue create mode 100644 src/pages/ItemType/Card/ItemTypeCard.vue create mode 100644 src/pages/ItemType/Card/ItemTypeDescriptor.vue create mode 100644 src/pages/ItemType/Card/ItemTypeSummary.vue create mode 100644 src/router/modules/itemType.js diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 3ac3d18a0e..2962bbe448 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -1119,6 +1119,8 @@ item: tags: Tags wasteBreakdown: Waste breakdown itemCreate: New item + itemTypeCreate: New item type + family: Item Type descriptor: item: Item buyer: Buyer @@ -1153,6 +1155,23 @@ item: type: Type intrastat: Intrastat origin: Origin +itemType: + pageTitles: + itemType: Item type + basicData: Basic data + summary: Summary + shared: + code: Code + name: Name + worker: Worker + category: Category + temperature: Temperature + summary: + id: id + life: Life + promo: Promo + itemPackingType: Item packing type + isUnconventionalSize: Is unconventional size components: topbar: {} userPanel: diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 4ecbcdbc01..d52240bf41 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -1118,6 +1118,8 @@ item: tags: Etiquetas wasteBreakdown: Deglose de mermas itemCreate: Nuevo artículo + itemTypeCreate: Nueva familia + family: Familia descriptor: item: Artículo buyer: Comprador @@ -1152,6 +1154,28 @@ item: type: Tipo intrastat: Intrastat origin: Origen +itemType: + pageTitles: + itemType: Familia + basicData: Datos básicos + summary: Resumen + shared: + code: Código + name: Nombre + worker: Trabajador + category: Reino + temperature: Temperatura + summary: + id: id + code: Código + name: Nombre + worker: Trabajador + category: Reino + temperature: Temperatura + life: Vida + promo: Promoción + itemPackingType: Tipo de embalaje + isUnconventionalSize: Es de tamaño no convencional components: topbar: {} userPanel: diff --git a/src/pages/Item/ItemTypeCreate.vue b/src/pages/Item/ItemTypeCreate.vue new file mode 100644 index 0000000000..3540fc5afb --- /dev/null +++ b/src/pages/Item/ItemTypeCreate.vue @@ -0,0 +1,91 @@ + + + diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue new file mode 100644 index 0000000000..b0b4de5406 --- /dev/null +++ b/src/pages/Item/ItemTypeList.vue @@ -0,0 +1,86 @@ + + + + + +es: + New item type: Nueva familia + Name: Nombre + Search item type: Buscar familia + Search itemType by id, name or code: Buscar familia por id, nombre o código + diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/ItemType/Card/ItemTypeBasicData.vue new file mode 100644 index 0000000000..ff27198967 --- /dev/null +++ b/src/pages/ItemType/Card/ItemTypeBasicData.vue @@ -0,0 +1,79 @@ + + diff --git a/src/pages/ItemType/Card/ItemTypeCard.vue b/src/pages/ItemType/Card/ItemTypeCard.vue new file mode 100644 index 0000000000..bff91de065 --- /dev/null +++ b/src/pages/ItemType/Card/ItemTypeCard.vue @@ -0,0 +1,26 @@ + + diff --git a/src/pages/ItemType/Card/ItemTypeDescriptor.vue b/src/pages/ItemType/Card/ItemTypeDescriptor.vue new file mode 100644 index 0000000000..cb0cb333b6 --- /dev/null +++ b/src/pages/ItemType/Card/ItemTypeDescriptor.vue @@ -0,0 +1,82 @@ + + + + + +es: + Go to module index: Ir al índice del módulo + diff --git a/src/pages/ItemType/Card/ItemTypeSummary.vue b/src/pages/ItemType/Card/ItemTypeSummary.vue new file mode 100644 index 0000000000..b71b2acf5c --- /dev/null +++ b/src/pages/ItemType/Card/ItemTypeSummary.vue @@ -0,0 +1,102 @@ + + + diff --git a/src/router/modules/index.js b/src/router/modules/index.js index 302ba7fe0b..0b9d10d98d 100644 --- a/src/router/modules/index.js +++ b/src/router/modules/index.js @@ -15,6 +15,7 @@ import Department from './department'; import Entry from './entry'; import roadmap from './roadmap'; import Parking from './parking'; +import ItemType from './itemType'; export default [ Item, @@ -34,4 +35,5 @@ export default [ Entry, roadmap, Parking, + ItemType, ]; diff --git a/src/router/modules/item.js b/src/router/modules/item.js index 41f3ec92a7..17e9b76733 100644 --- a/src/router/modules/item.js +++ b/src/router/modules/item.js @@ -10,7 +10,7 @@ export default { component: RouterView, redirect: { name: 'ItemMain' }, menus: { - main: ['ItemList', 'WasteBreakdown'], + main: ['ItemList', 'WasteBreakdown', 'ItemTypeList'], card: ['ItemBasicData'], }, children: [ @@ -33,7 +33,7 @@ export default { path: 'create', name: 'ItemCreate', meta: { - title: 'create', + title: 'itemCreate', }, component: () => import('src/pages/Item/ItemCreate.vue'), }, @@ -50,6 +50,23 @@ export default { 'https://grafana.verdnatura.es/d/TTNXQAxVk'; }, }, + { + path: 'item-type-list', + name: 'ItemTypeList', + meta: { + title: 'family', + icon: 'contact_support', + }, + component: () => import('src/pages/Item/ItemTypeList.vue'), + }, + { + path: 'item-type-list/create', + name: 'ItemTypeCreate', + meta: { + title: 'itemTypeCreate', + }, + component: () => import('src/pages/Item/ItemTypeCreate.vue'), + }, ], }, { diff --git a/src/router/modules/itemType.js b/src/router/modules/itemType.js new file mode 100644 index 0000000000..0d966c32c5 --- /dev/null +++ b/src/router/modules/itemType.js @@ -0,0 +1,45 @@ +import { RouterView } from 'vue-router'; + +export default { + path: '/item-type', + name: 'ItemType', + meta: { + title: 'itemType', + icon: 'contact_support', + }, + component: RouterView, + redirect: { name: 'ItemTypeCard' }, + menus: { + main: [], + card: ['ItemTypeBasicData'], + }, + children: [ + { + name: 'ItemTypeCard', + path: ':id', + component: () => import('src/pages/ItemType/Card/ItemTypeCard.vue'), + redirect: { name: 'ItemTypeSummary' }, + children: [ + { + name: 'ItemTypeSummary', + path: 'summary', + meta: { + title: 'summary', + }, + component: () => + import('src/pages/ItemType/Card/ItemTypeSummary.vue'), + }, + { + name: 'ItemTypeBasicData', + path: 'basic-data', + meta: { + title: 'basicData', + icon: 'vn:settings', + }, + component: () => + import('src/pages/ItemType/Card/ItemTypeBasicData.vue'), + }, + ], + }, + ], +}; diff --git a/src/router/routes.js b/src/router/routes.js index 51e726a62f..198f1d8f2b 100644 --- a/src/router/routes.js +++ b/src/router/routes.js @@ -10,6 +10,7 @@ import supplier from './modules/Supplier'; import route from './modules/route'; import travel from './modules/travel'; import department from './modules/department'; +import ItemType from './modules/itemType'; import shelving from 'src/router/modules/shelving'; import order from 'src/router/modules/order'; import entry from 'src/router/modules/entry'; @@ -71,6 +72,7 @@ const routes = [ roadmap, entry, parking, + ItemType, { path: '/:catchAll(.*)*', name: 'NotFound', From 6018d51642165a13e97ab9bb480d8686bcb62872 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Tue, 23 Apr 2024 07:56:07 +0200 Subject: [PATCH 05/26] perf: #7190 remove comments --- src/composables/useSession.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/composables/useSession.js b/src/composables/useSession.js index f308bf3e2b..bd35328169 100644 --- a/src/composables/useSession.js +++ b/src/composables/useSession.js @@ -90,6 +90,7 @@ export function useSession() { await useRole().fetch(); await useUserConfig().fetch(); await useTokenConfig().fetch(); + startInterval(); } @@ -105,8 +106,6 @@ export function useSession() { const renewPeriod = +sessionStorage.getItem('renewPeriod'); if (!renewPeriod) return; intervalId = setInterval(() => checkValidity(), renewPeriod * 1000); - // JUST FOR TEST - // checkValidity(); } function stopRenewer() { @@ -144,10 +143,8 @@ export function useSession() { //COMMENT THIS IF JUST FOR TEST if (isNaN(renewPeriodInSeconds) || now <= maxDate) { - console.error('Token not renewed'); return (isCheckingToken = false); } - console.error('Token renewed'); await renewToken(); isCheckingToken = false; From 04fe9330d45e97a4ec470abaf088245657ee1840 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Tue, 23 Apr 2024 08:03:09 +0200 Subject: [PATCH 06/26] fix: #7190 tests --- .../__tests__/composables/useSession.spec.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/vitest/__tests__/composables/useSession.spec.js b/test/vitest/__tests__/composables/useSession.spec.js index f9f3dcb80e..8feb5e5719 100644 --- a/test/vitest/__tests__/composables/useSession.spec.js +++ b/test/vitest/__tests__/composables/useSession.spec.js @@ -96,7 +96,11 @@ describe('session', () => { const expectedTokenMultimedia = 'mySessionTokenMultimedia'; const keepLogin = false; - await session.login(expectedToken,expectedTokenMultimedia, keepLogin); + await session.login({ + token: expectedToken, + tokenMultimedia: expectedTokenMultimedia, + keepLogin, + }); const roles = state.getRoles(); const localToken = localStorage.getItem('token'); @@ -106,7 +110,7 @@ describe('session', () => { expect(localToken).toBeNull(); expect(sessionToken).toEqual(expectedToken); - await session.destroy(); // this clears token and user for any other test + await session.destroy(); // this clears token and user for any other test }); it('should fetch the user roles and then set token in the localStorage', async () => { @@ -119,7 +123,11 @@ describe('session', () => { const expectedTokenMultimedia = 'myLocalTokenMultimedia'; const keepLogin = true; - await session.login(expectedToken, expectedTokenMultimedia, keepLogin); + await session.login({ + token: expectedToken, + tokenMultimedia: expectedTokenMultimedia, + keepLogin, + }); const roles = state.getRoles(); const localToken = localStorage.getItem('token'); From 42fe189e9f6b222cb6a1c81d916dfd82cbec5601 Mon Sep 17 00:00:00 2001 From: wbuezas Date: Tue, 23 Apr 2024 16:45:28 -0300 Subject: [PATCH 07/26] Add module name in Item router file --- src/router/modules/item.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/router/modules/item.js b/src/router/modules/item.js index 17e9b76733..70d49c56cb 100644 --- a/src/router/modules/item.js +++ b/src/router/modules/item.js @@ -6,6 +6,7 @@ export default { meta: { title: 'items', icon: 'vn:item', + moduleName: 'Item', }, component: RouterView, redirect: { name: 'ItemMain' }, From a555d11663650f94f64a0619fbdb458f3a2b480e Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 24 Apr 2024 12:15:12 +0200 Subject: [PATCH 08/26] fix: #7190 test --- src/composables/useSession.js | 3 + src/composables/useTokenConfig.js | 1 + .../__tests__/composables/useSession.spec.js | 197 ++++++++++++------ .../composables/useTokenConfig.spec.js | 31 +++ 4 files changed, 167 insertions(+), 65 deletions(-) create mode 100644 test/vitest/__tests__/composables/useTokenConfig.spec.js diff --git a/src/composables/useSession.js b/src/composables/useSession.js index bd35328169..3b38cdad6a 100644 --- a/src/composables/useSession.js +++ b/src/composables/useSession.js @@ -157,5 +157,8 @@ export function useSession() { destroy, login, isLoggedIn, + checkValidity, + setSession, + renewToken, }; } diff --git a/src/composables/useTokenConfig.js b/src/composables/useTokenConfig.js index 6001221997..5cf1b34eef 100644 --- a/src/composables/useTokenConfig.js +++ b/src/composables/useTokenConfig.js @@ -23,5 +23,6 @@ export function useTokenConfig() { return { fetch, + state, }; } diff --git a/test/vitest/__tests__/composables/useSession.spec.js b/test/vitest/__tests__/composables/useSession.spec.js index 8feb5e5719..2292859a97 100644 --- a/test/vitest/__tests__/composables/useSession.spec.js +++ b/test/vitest/__tests__/composables/useSession.spec.js @@ -1,5 +1,5 @@ -import { vi, describe, expect, it } from 'vitest'; -import { axios } from 'app/test/vitest/helper'; +import { vi, describe, expect, it, beforeAll, beforeEach } from 'vitest'; +import { axios, flushPromises } from 'app/test/vitest/helper'; import { useSession } from 'composables/useSession'; import { useState } from 'composables/useState'; @@ -63,81 +63,148 @@ describe('session', () => { }); }); - describe('login', () => { - const expectedUser = { - id: 999, - name: `T'Challa`, - nickname: 'Black Panther', - lang: 'en', - userConfig: { - darkMode: false, - }, - }; - const rolesData = [ - { - role: { - name: 'salesPerson', + describe( + 'login', + () => { + const expectedUser = { + id: 999, + name: `T'Challa`, + nickname: 'Black Panther', + lang: 'en', + userConfig: { + darkMode: false, }, - }, - { - role: { - name: 'admin', + }; + const rolesData = [ + { + role: { + name: 'salesPerson', + }, }, - }, - ]; + { + role: { + name: 'admin', + }, + }, + ]; - it('should fetch the user roles and then set token in the sessionStorage', async () => { - const expectedRoles = ['salesPerson', 'admin']; - vi.spyOn(axios, 'get').mockResolvedValue({ - data: { roles: rolesData, user: expectedUser }, + it('should fetch the user roles and then set token in the sessionStorage', async () => { + const expectedRoles = ['salesPerson', 'admin']; + vi.spyOn(axios, 'get').mockResolvedValue({ + data: { roles: rolesData, user: expectedUser }, + }); + + const expectedToken = 'mySessionToken'; + const expectedTokenMultimedia = 'mySessionTokenMultimedia'; + const keepLogin = false; + + await session.login({ + token: expectedToken, + tokenMultimedia: expectedTokenMultimedia, + keepLogin, + }); + + const roles = state.getRoles(); + const localToken = localStorage.getItem('token'); + const sessionToken = sessionStorage.getItem('token'); + + expect(roles.value).toEqual(expectedRoles); + expect(localToken).toBeNull(); + expect(sessionToken).toEqual(expectedToken); + + await session.destroy(); // this clears token and user for any other test }); - const expectedToken = 'mySessionToken'; - const expectedTokenMultimedia = 'mySessionTokenMultimedia'; - const keepLogin = false; + it('should fetch the user roles and then set token in the localStorage', async () => { + const expectedRoles = ['salesPerson', 'admin']; + vi.spyOn(axios, 'get').mockResolvedValue({ + data: { roles: rolesData, user: expectedUser }, + }); - await session.login({ - token: expectedToken, - tokenMultimedia: expectedTokenMultimedia, - keepLogin, + const expectedToken = 'myLocalToken'; + const expectedTokenMultimedia = 'myLocalTokenMultimedia'; + const keepLogin = true; + + await session.login({ + token: expectedToken, + tokenMultimedia: expectedTokenMultimedia, + keepLogin, + }); + + const roles = state.getRoles(); + const localToken = localStorage.getItem('token'); + const sessionToken = sessionStorage.getItem('token'); + + expect(roles.value).toEqual(expectedRoles); + expect(localToken).toEqual(expectedToken); + expect(sessionToken).toBeNull(); + + await session.destroy(); // this clears token and user for any other test }); + }, + {} + ); - const roles = state.getRoles(); - const localToken = localStorage.getItem('token'); - const sessionToken = sessionStorage.getItem('token'); - - expect(roles.value).toEqual(expectedRoles); - expect(localToken).toBeNull(); - expect(sessionToken).toEqual(expectedToken); - - await session.destroy(); // this clears token and user for any other test + describe('RenewToken', () => { + const expectedToken = 'myToken'; + const expectedTokenMultimedia = 'myTokenMultimedia'; + const currentDate = new Date(); + beforeAll(() => { + const tokenConfig = { + id: 1, + renewPeriod: 21600, + courtesyTime: 60, + renewInterval: 300, + }; + state.setTokenConfig(tokenConfig); + sessionStorage.setItem('renewPeriod', 1); }); - - it('should fetch the user roles and then set token in the localStorage', async () => { - const expectedRoles = ['salesPerson', 'admin']; - vi.spyOn(axios, 'get').mockResolvedValue({ - data: { roles: rolesData, user: expectedUser }, - }); - - const expectedToken = 'myLocalToken'; - const expectedTokenMultimedia = 'myLocalTokenMultimedia'; - const keepLogin = true; - - await session.login({ + it('NOT Should renewToken', async () => { + const data = { token: expectedToken, tokenMultimedia: expectedTokenMultimedia, - keepLogin, - }); + keepLogin: false, + ttl: 1, + created: Date.now(), + }; + session.setSession(data); + expect(sessionStorage.getItem('keepLogin')).toBeFalsy(); + expect(sessionStorage.getItem('created')).toBeDefined(); + expect(sessionStorage.getItem('ttl')).toEqual(1); + await session.checkValidity(); + expect(sessionStorage.getItem('token')).toEqual(expectedToken); + expect(sessionStorage.getItem('tokenMultimedia')).toEqual( + expectedTokenMultimedia + ); + }); + it('Should renewToken', async () => { + currentDate.setMinutes(currentDate.getMinutes() - 100); + const data = { + token: expectedToken, + tokenMultimedia: expectedTokenMultimedia, + keepLogin: false, + ttl: 1, + created: currentDate, + }; + session.setSession(data); - const roles = state.getRoles(); - const localToken = localStorage.getItem('token'); - const sessionToken = sessionStorage.getItem('token'); - - expect(roles.value).toEqual(expectedRoles); - expect(localToken).toEqual(expectedToken); - expect(sessionToken).toBeNull(); - - await session.destroy(); // this clears token and user for any other test + vi.spyOn(axios, 'post') + .mockResolvedValueOnce({ + data: { id: '' }, + }) + .mockResolvedValueOnce({ + data: { + id: '', + }, + }); + expect(sessionStorage.getItem('keepLogin')).toBeFalsy(); + expect(sessionStorage.getItem('created')).toBeDefined(); + expect(sessionStorage.getItem('ttl')).toEqual(1); + await session.checkValidity(); + expect(sessionStorage.getItem('token')).not.toEqual(expectedToken); + expect(sessionStorage.getItem('tokenMultimedia')).not.toEqual( + expectedTokenMultimedia + ); }); }); }); diff --git a/test/vitest/__tests__/composables/useTokenConfig.spec.js b/test/vitest/__tests__/composables/useTokenConfig.spec.js new file mode 100644 index 0000000000..a25a4abb1e --- /dev/null +++ b/test/vitest/__tests__/composables/useTokenConfig.spec.js @@ -0,0 +1,31 @@ +import { vi, describe, expect, it } from 'vitest'; +import { axios, flushPromises } from 'app/test/vitest/helper'; +import { useTokenConfig } from 'composables/useTokenConfig'; +const tokenConfig = useTokenConfig(); + +describe('useTokenConfig', () => { + describe('fetch', () => { + it('should call setTokenConfig of the state with the expected data', async () => { + const data = { + id: 1, + renewPeriod: 21600, + courtesyTime: 60, + renewInterval: 300, + }; + vi.spyOn(axios, 'get').mockResolvedValueOnce({ + data, + }); + + vi.spyOn(tokenConfig.state, 'setTokenConfig'); + + tokenConfig.fetch(); + + await flushPromises(); + + expect(tokenConfig.state.setTokenConfig).toHaveBeenCalledWith(data); + + const renewPeriod = sessionStorage.getItem('renewPeriod'); + expect(renewPeriod).toEqual(data.renewPeriod); + }); + }); +}); From e6c9a692f54b610016d32b68db20e1c42185bf6c Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 24 Apr 2024 14:33:10 +0200 Subject: [PATCH 09/26] feat moduleName --- src/router/modules/department.js | 1 + src/router/modules/itemType.js | 1 + src/router/modules/roadmap.js | 1 + 3 files changed, 3 insertions(+) diff --git a/src/router/modules/department.js b/src/router/modules/department.js index aaffc34602..dfd5e64bac 100644 --- a/src/router/modules/department.js +++ b/src/router/modules/department.js @@ -6,6 +6,7 @@ export default { meta: { title: 'department', icon: 'vn:greuge', + moduleName: 'Department', }, component: RouterView, redirect: { name: 'DepartmentCard' }, diff --git a/src/router/modules/itemType.js b/src/router/modules/itemType.js index 0d966c32c5..32bd636909 100644 --- a/src/router/modules/itemType.js +++ b/src/router/modules/itemType.js @@ -6,6 +6,7 @@ export default { meta: { title: 'itemType', icon: 'contact_support', + moduleName: 'ItemType', }, component: RouterView, redirect: { name: 'ItemTypeCard' }, diff --git a/src/router/modules/roadmap.js b/src/router/modules/roadmap.js index 02edf94bee..6b2aa6a13e 100644 --- a/src/router/modules/roadmap.js +++ b/src/router/modules/roadmap.js @@ -6,6 +6,7 @@ export default { meta: { title: 'roadmap', icon: 'vn:troncales', + moduleName: 'Roadmap', }, component: RouterView, redirect: { name: 'RouteMain' }, From e1f50f5daf3d017c6b8a04470822f061368b1340 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 24 Apr 2024 15:05:10 +0200 Subject: [PATCH 10/26] fix: minorchanges --- src/i18n/locale/es.yml | 2 +- src/pages/ItemType/Card/ItemTypeSummary.vue | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 6d2e5c8c2b..6634b6f07b 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -1234,7 +1234,7 @@ itemType: life: Vida promo: Promoción itemPackingType: Tipo de embalaje - isUnconventionalSize: Es de tamaño no convencional + isUnconventionalSize: Es de tamaño poco convencional components: topbar: {} itemsFilterPanel: diff --git a/src/pages/ItemType/Card/ItemTypeSummary.vue b/src/pages/ItemType/Card/ItemTypeSummary.vue index b71b2acf5c..393675a30e 100644 --- a/src/pages/ItemType/Card/ItemTypeSummary.vue +++ b/src/pages/ItemType/Card/ItemTypeSummary.vue @@ -93,6 +93,7 @@ async function setItemTypeData(data) { :value="itemType.itemPackingType?.description" /> @@ -100,3 +101,9 @@ async function setItemTypeData(data) { + + From 127567898da40f416db6d3fcc3da7231dba59a3d Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Fri, 26 Apr 2024 13:27:58 +0200 Subject: [PATCH 11/26] fix: add filter panel --- src/pages/Item/ItemTypeList.vue | 22 +++++++++++- src/pages/ItemType/ItemTypeFilter.vue | 49 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/pages/ItemType/ItemTypeFilter.vue diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index b0b4de5406..5b106916d6 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -6,6 +6,7 @@ import VnPaginate from 'src/components/ui/VnPaginate.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import CardList from 'src/components/ui/CardList.vue'; import ItemTypeSummary from 'src/pages/ItemType/Card/ItemTypeSummary.vue'; +import ItemTypeFilter from 'src/pages/ItemType/ItemTypeFilter.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import { useStateStore } from 'stores/useStateStore'; @@ -31,12 +32,31 @@ const redirectToCreateView = () => { + +
+ + + {{ t('globals.collapseMenu') }} + + +
+
+ + + + +
+import { useI18n } from 'vue-i18n'; +import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; +import VnInput from 'src/components/common/VnInput.vue'; +const { t } = useI18n(); + +const props = defineProps({ + dataKey: { + type: String, + required: true, + }, +}); + + + + + +en: + params: + name: Name + code: Code +es: + params: + name: Nombre + code: Código + Name: Nombre + Code: Código + From c1ed4ab03280b8125c92e8587e9a8bd4ca626abd Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Fri, 26 Apr 2024 14:49:54 +0200 Subject: [PATCH 12/26] fix: add exprBuilder --- src/components/ui/VnFilterPanel.vue | 3 ++- src/components/ui/VnSearchbar.vue | 2 ++ src/pages/Item/ItemTypeList.vue | 17 +++++++++++++++-- src/pages/ItemType/ItemTypeFilter.vue | 8 +++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index 96d0971910..cde48e9b39 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -145,7 +145,8 @@ const customTags = computed(() => ); async function remove(key) { - userParams.value[key] = null; + // userParams.value[key] = null; + if (userParams.value[key]) delete userParams.value[key]; await search(); emit('remove', key); } diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue index fc8475ace7..da8d43c950 100644 --- a/src/components/ui/VnSearchbar.vue +++ b/src/components/ui/VnSearchbar.vue @@ -81,8 +81,10 @@ async function search() { const staticParams = Object.entries(store.userParams).filter( ([key, value]) => value && (props.staticParams || []).includes(key) ); + // const filter =props?.where? { where: JSON.parse(props.where) }: {} await arrayData.applyFilter({ params: { + // filter , ...Object.fromEntries(staticParams), search: searchText.value, }, diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index 5b106916d6..1e21c17192 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -24,6 +24,17 @@ const redirectToItemTypeSummary = (id) => { const redirectToCreateView = () => { router.push({ name: 'ItemTypeCreate' }); }; +const exprBuilder = (param, value) => { + switch (param) { + case 'name': + case 'code': + case 'search': + return { + name: { like: `%${value}%` }, + code: { like: `%${value}%` }, + }; + } +}; - + diff --git a/src/pages/ItemType/ItemTypeFilter.vue b/src/pages/ItemType/ItemTypeFilter.vue index 33238545fd..2a86795c2e 100644 --- a/src/pages/ItemType/ItemTypeFilter.vue +++ b/src/pages/ItemType/ItemTypeFilter.vue @@ -10,10 +10,16 @@ const props = defineProps({ required: true, }, }); + +const emit = defineEmits(['search']);