forked from verdnatura/salix-front
feat(navBar): refs #7632 isLoading
This commit is contained in:
parent
5c5393db6d
commit
175ebbc343
|
@ -2,9 +2,11 @@ import axios from 'axios';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import { Router } from 'src/router';
|
import { Router } from 'src/router';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import { useStateQueryStore } from 'src/stores/useStateQueryStore';
|
||||||
|
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
const stateQuery = useStateQueryStore();
|
||||||
const baseUrl = '/api/';
|
const baseUrl = '/api/';
|
||||||
|
|
||||||
axios.defaults.baseURL = baseUrl;
|
axios.defaults.baseURL = baseUrl;
|
||||||
|
@ -15,7 +17,7 @@ const onRequest = (config) => {
|
||||||
if (token.length && !config.headers.Authorization) {
|
if (token.length && !config.headers.Authorization) {
|
||||||
config.headers.Authorization = token;
|
config.headers.Authorization = token;
|
||||||
}
|
}
|
||||||
|
stateQuery.add(config);
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,9 +26,10 @@ const onRequestError = (error) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onResponse = (response) => {
|
const onResponse = (response) => {
|
||||||
const { method } = response.config;
|
const config = response.config;
|
||||||
|
stateQuery.remove(config);
|
||||||
|
|
||||||
const isSaveRequest = method === 'patch';
|
const isSaveRequest = config.method === 'patch';
|
||||||
if (isSaveRequest) {
|
if (isSaveRequest) {
|
||||||
notify('globals.dataSaved', 'positive');
|
notify('globals.dataSaved', 'positive');
|
||||||
}
|
}
|
||||||
|
@ -35,6 +38,8 @@ const onResponse = (response) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onResponseError = (error) => {
|
const onResponseError = (error) => {
|
||||||
|
stateQuery.remove(error.config);
|
||||||
|
|
||||||
let message = '';
|
let message = '';
|
||||||
|
|
||||||
const response = error.response;
|
const response = error.response;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { onMounted, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useStateQueryStore } from 'src/stores/useStateQueryStore';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import PinnedModules from './PinnedModules.vue';
|
import PinnedModules from './PinnedModules.vue';
|
||||||
import UserPanel from 'components/UserPanel.vue';
|
import UserPanel from 'components/UserPanel.vue';
|
||||||
|
@ -12,6 +13,7 @@ import VnAvatar from './ui/VnAvatar.vue';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
const stateQuery = useStateQueryStore();
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const user = state.getUser();
|
const user = state.getUser();
|
||||||
const appName = 'Lilium';
|
const appName = 'Lilium';
|
||||||
|
@ -50,6 +52,14 @@ const pinnedModulesRef = ref();
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<VnBreadcrumbs v-if="$q.screen.gt.sm" />
|
<VnBreadcrumbs v-if="$q.screen.gt.sm" />
|
||||||
|
<QSpinner
|
||||||
|
color="primary"
|
||||||
|
class="q-ml-md"
|
||||||
|
:class="{
|
||||||
|
'no-visible': !stateQuery.isLoading().value,
|
||||||
|
}"
|
||||||
|
size="xs"
|
||||||
|
/>
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<div id="searchbar" class="searchbar"></div>
|
<div id="searchbar" class="searchbar"></div>
|
||||||
<QSpace />
|
<QSpace />
|
||||||
|
@ -104,6 +114,9 @@ const pinnedModulesRef = ref();
|
||||||
.q-header {
|
.q-header {
|
||||||
background-color: var(--vn-section-color);
|
background-color: var(--vn-section-color);
|
||||||
}
|
}
|
||||||
|
.no-visible {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<i18n>
|
<i18n>
|
||||||
en:
|
en:
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
||||||
|
});
|
|
@ -7,41 +7,46 @@ vi.mock('src/composables/useSession', () => ({
|
||||||
getToken: () => 'DEFAULT_TOKEN',
|
getToken: () => 'DEFAULT_TOKEN',
|
||||||
isLoggedIn: () => vi.fn(),
|
isLoggedIn: () => vi.fn(),
|
||||||
destroy: () => vi.fn(),
|
destroy: () => vi.fn(),
|
||||||
})
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('src/stores/useStateQueryStore', () => ({
|
||||||
|
useStateQueryStore: () => ({
|
||||||
|
add: () => vi.fn(),
|
||||||
|
remove: () => vi.fn(),
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('Axios boot', () => {
|
describe('Axios boot', () => {
|
||||||
|
|
||||||
describe('onRequest()', async () => {
|
describe('onRequest()', async () => {
|
||||||
it('should set the "Authorization" property on the headers', async () => {
|
it('should set the "Authorization" property on the headers', async () => {
|
||||||
const config = { headers: {} };
|
const config = { headers: {} };
|
||||||
|
|
||||||
const resultConfig = onRequest(config);
|
const resultConfig = onRequest(config);
|
||||||
|
|
||||||
expect(resultConfig).toEqual(expect.objectContaining({
|
expect(resultConfig).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'DEFAULT_TOKEN'
|
Authorization: 'DEFAULT_TOKEN',
|
||||||
}
|
},
|
||||||
}));
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('onResponseError()', async () => {
|
describe('onResponseError()', async () => {
|
||||||
it('should call to the Notify plugin with a message error for an status code "500"', 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 = {
|
const error = {
|
||||||
response: {
|
response: {
|
||||||
status: 500
|
status: 500,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = onResponseError(error);
|
const result = onResponseError(error);
|
||||||
|
|
||||||
|
expect(result).rejects.toEqual(expect.objectContaining(error));
|
||||||
expect(result).rejects.toEqual(
|
|
||||||
expect.objectContaining(error)
|
|
||||||
);
|
|
||||||
expect(Notify.create).toHaveBeenCalledWith(
|
expect(Notify.create).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
message: 'An internal server error has ocurred',
|
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 () => {
|
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 = {
|
const error = {
|
||||||
response: {
|
response: {
|
||||||
status: 401,
|
status: 401,
|
||||||
data: {
|
data: {
|
||||||
error: {
|
error: {
|
||||||
message: 'Invalid user or password'
|
message: 'Invalid user or password',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = onResponseError(error);
|
const result = onResponseError(error);
|
||||||
|
|
||||||
|
expect(result).rejects.toEqual(expect.objectContaining(error));
|
||||||
expect(result).rejects.toEqual(
|
|
||||||
expect.objectContaining(error)
|
|
||||||
);
|
|
||||||
expect(Notify.create).toHaveBeenCalledWith(
|
expect(Notify.create).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
message: 'Invalid user or password',
|
message: 'Invalid user or password',
|
||||||
|
@ -77,5 +79,5 @@ describe('Axios boot', () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
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' };
|
||||||
|
const secondQuery = { 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 hash', async () => {
|
||||||
|
add(firstQuery);
|
||||||
|
add(secondQuery);
|
||||||
|
|
||||||
|
const beforeCount = getQueries().size;
|
||||||
|
const hash = add();
|
||||||
|
expect(getQueries().has(hash)).toBeTruthy();
|
||||||
|
|
||||||
|
remove(hash);
|
||||||
|
expect(getQueries().has(hash)).toBeFalsy();
|
||||||
|
expect(getQueries().size).toBe(beforeCount);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue