diff --git a/src/boot/axios.js b/src/boot/axios.js index 99a163cca..3bd80f487 100644 --- a/src/boot/axios.js +++ b/src/boot/axios.js @@ -2,9 +2,11 @@ import axios from 'axios'; import { useSession } from 'src/composables/useSession'; import { Router } from 'src/router'; import useNotify from 'src/composables/useNotify.js'; +import { useStateQueryStore } from 'src/stores/useStateQueryStore'; const session = useSession(); const { notify } = useNotify(); +const stateQuery = useStateQueryStore(); const baseUrl = '/api/'; axios.defaults.baseURL = baseUrl; @@ -15,7 +17,7 @@ const onRequest = (config) => { if (token.length && !config.headers.Authorization) { config.headers.Authorization = token; } - + stateQuery.add(config); return config; }; @@ -24,10 +26,10 @@ const onRequestError = (error) => { }; const onResponse = (response) => { - const { method } = response.config; + const config = response.config; + stateQuery.remove(config); - const isSaveRequest = method === 'patch'; - if (isSaveRequest) { + if (config.method === 'patch') { notify('globals.dataSaved', 'positive'); } @@ -35,6 +37,8 @@ const onResponse = (response) => { }; const onResponseError = (error) => { + stateQuery.remove(error.config); + let message = ''; const response = error.response; diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue index 00faaebc2..9b0393489 100644 --- a/src/components/NavBar.vue +++ b/src/components/NavBar.vue @@ -3,6 +3,7 @@ import { onMounted, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { useState } from 'src/composables/useState'; import { useStateStore } from 'stores/useStateStore'; +import { useStateQueryStore } from 'src/stores/useStateQueryStore'; import { useQuasar } from 'quasar'; import PinnedModules from './PinnedModules.vue'; import UserPanel from 'components/UserPanel.vue'; @@ -12,6 +13,7 @@ import VnAvatar from './ui/VnAvatar.vue'; const { t } = useI18n(); const stateStore = useStateStore(); const quasar = useQuasar(); +const stateQuery = useStateQueryStore(); const state = useState(); const user = state.getUser(); const appName = 'Lilium'; @@ -50,6 +52,14 @@ const pinnedModulesRef = ref(); + diff --git a/src/css/app.scss b/src/css/app.scss index 905934d4c..d4c76ad6b 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -288,3 +288,7 @@ input::-webkit-inner-spin-button { color: $info; } } + +.no-visible { + visibility: hidden; +} diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 8a41bbe04..82c3b48e0 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -229,7 +229,7 @@ onBeforeMount(() => { > @@ -251,7 +251,7 @@ onBeforeMount(() => { @@ -292,7 +292,7 @@ onBeforeMount(() => { diff --git a/src/pages/Item/ItemRequestFilter.vue b/src/pages/Item/ItemRequestFilter.vue index 0e81ff5a5..64bc0e575 100644 --- a/src/pages/Item/ItemRequestFilter.vue +++ b/src/pages/Item/ItemRequestFilter.vue @@ -174,6 +174,16 @@ const decrement = (paramsObj, key) => { + + + + + @@ -274,11 +284,11 @@ en: to: To mine: For me state: State + myTeam: My team dateFiltersTooltip: Cannot choose a range of dates and days onward at the same time denied: Denied accepted: Accepted pending: Pending - es: params: search: Búsqueda general @@ -291,6 +301,7 @@ es: to: Hasta mine: Para mi state: Estado + myTeam: Mi equipo dateFiltersTooltip: No se puede seleccionar un rango de fechas y días en adelante a la vez denied: Denegada accepted: Aceptada diff --git a/src/stores/useStateQueryStore.js b/src/stores/useStateQueryStore.js new file mode 100644 index 000000000..d25dbb921 --- /dev/null +++ b/src/stores/useStateQueryStore.js @@ -0,0 +1,31 @@ +import { ref, computed } from 'vue'; +import { defineStore } from 'pinia'; + +export const useStateQueryStore = defineStore('stateQueryStore', () => { + const queries = ref(new Set()); + + function add(query) { + queries.value.add(query); + return query; + } + + function isLoading() { + return computed(() => queries.value.size); + } + + function remove(query) { + queries.value.delete(query); + } + + function reset() { + queries.value = new Set(); + } + + return { + add, + isLoading, + remove, + queries, + reset, + }; +}); diff --git a/test/vitest/__tests__/boot/axios.spec.js b/test/vitest/__tests__/boot/axios.spec.js index feb0d93ea..7a802b4d2 100644 --- a/test/vitest/__tests__/boot/axios.spec.js +++ b/test/vitest/__tests__/boot/axios.spec.js @@ -7,41 +7,46 @@ vi.mock('src/composables/useSession', () => ({ getToken: () => 'DEFAULT_TOKEN', isLoggedIn: () => vi.fn(), destroy: () => vi.fn(), - }) + }), +})); + +vi.mock('src/stores/useStateQueryStore', () => ({ + useStateQueryStore: () => ({ + add: () => vi.fn(), + remove: () => vi.fn(), + }), })); describe('Axios boot', () => { - describe('onRequest()', async () => { it('should set the "Authorization" property on the headers', async () => { const config = { headers: {} }; const resultConfig = onRequest(config); - expect(resultConfig).toEqual(expect.objectContaining({ - headers: { - Authorization: 'DEFAULT_TOKEN' - } - })); + expect(resultConfig).toEqual( + expect.objectContaining({ + headers: { + Authorization: 'DEFAULT_TOKEN', + }, + }) + ); }); - }) + }); describe('onResponseError()', async () => { it('should call to the Notify plugin with a message error for an status code "500"', async () => { - Notify.create = vi.fn() + Notify.create = vi.fn(); const error = { response: { - status: 500 - } + status: 500, + }, }; const result = onResponseError(error); - - expect(result).rejects.toEqual( - expect.objectContaining(error) - ); + expect(result).rejects.toEqual(expect.objectContaining(error)); expect(Notify.create).toHaveBeenCalledWith( expect.objectContaining({ message: 'An internal server error has ocurred', @@ -51,25 +56,22 @@ describe('Axios boot', () => { }); it('should call to the Notify plugin with a message from the response property', async () => { - Notify.create = vi.fn() + Notify.create = vi.fn(); const error = { response: { status: 401, data: { error: { - message: 'Invalid user or password' - } - } - } + message: 'Invalid user or password', + }, + }, + }, }; const result = onResponseError(error); - - expect(result).rejects.toEqual( - expect.objectContaining(error) - ); + expect(result).rejects.toEqual(expect.objectContaining(error)); expect(Notify.create).toHaveBeenCalledWith( expect.objectContaining({ message: 'Invalid user or password', @@ -77,5 +79,5 @@ describe('Axios boot', () => { }) ); }); - }) + }); }); diff --git a/test/vitest/__tests__/stores/useStateQueryStore.spec.js b/test/vitest/__tests__/stores/useStateQueryStore.spec.js new file mode 100644 index 000000000..ab3afb007 --- /dev/null +++ b/test/vitest/__tests__/stores/useStateQueryStore.spec.js @@ -0,0 +1,58 @@ +import { describe, expect, it, beforeEach, beforeAll } from 'vitest'; +import { createWrapper } from 'app/test/vitest/helper'; + +import { useStateQueryStore } from 'src/stores/useStateQueryStore'; + +describe('useStateQueryStore', () => { + beforeAll(() => { + createWrapper({}, {}); + }); + + const stateQueryStore = useStateQueryStore(); + const { add, isLoading, remove, reset } = useStateQueryStore(); + const firstQuery = { url: 'myQuery' }; + + function getQueries() { + return stateQueryStore.queries; + } + + beforeEach(() => { + reset(); + expect(getQueries().size).toBeFalsy(); + }); + + it('should add two queries', async () => { + expect(getQueries().size).toBeFalsy(); + add(firstQuery); + + expect(getQueries().size).toBeTruthy(); + expect(getQueries().has(firstQuery)).toBeTruthy(); + + add(); + expect(getQueries().size).toBe(2); + }); + + it('should add and remove loading state', async () => { + expect(isLoading().value).toBeFalsy(); + add(firstQuery); + expect(isLoading().value).toBeTruthy(); + remove(firstQuery); + expect(isLoading().value).toBeFalsy(); + }); + + it('should add and remove query', async () => { + const secondQuery = { ...firstQuery }; + const thirdQuery = { ...firstQuery }; + + add(firstQuery); + add(secondQuery); + + const beforeCount = getQueries().size; + add(thirdQuery); + expect(getQueries().has(thirdQuery)).toBeTruthy(); + + remove(thirdQuery); + expect(getQueries().has(thirdQuery)).toBeFalsy(); + expect(getQueries().size).toBe(beforeCount); + }); +});