forked from verdnatura/salix-front
Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix-front into test
This commit is contained in:
commit
f68812eec1
70
src/App.vue
70
src/App.vue
|
@ -1,16 +1,10 @@
|
|||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const router = useRouter();
|
||||
const session = useSession();
|
||||
const { t, availableLocales, locale, fallbackLocale } = useI18n();
|
||||
const { isLoggedIn } = session;
|
||||
const { availableLocales, locale, fallbackLocale } = useI18n();
|
||||
|
||||
onMounted(() => {
|
||||
let userLang = window.navigator.language;
|
||||
|
@ -39,68 +33,6 @@ quasar.iconMapFn = (iconName) => {
|
|||
content: iconName,
|
||||
};
|
||||
};
|
||||
|
||||
function responseError(error) {
|
||||
let message = error.message;
|
||||
let logOut = false;
|
||||
|
||||
const response = error.response;
|
||||
if (response && response.data.error) {
|
||||
message = response.data.error.message;
|
||||
}
|
||||
|
||||
switch (error.response?.status) {
|
||||
case 401:
|
||||
message = 'login.loginError';
|
||||
|
||||
if (isLoggedIn()) {
|
||||
message = 'errors.statusUnauthorized';
|
||||
logOut = true;
|
||||
}
|
||||
break;
|
||||
case 403:
|
||||
message = 'errors.statusUnauthorized';
|
||||
break;
|
||||
case 500:
|
||||
message = 'errors.statusInternalServerError';
|
||||
break;
|
||||
case 502:
|
||||
message = 'errors.statusBadGateway';
|
||||
break;
|
||||
case 504:
|
||||
message = 'errors.statusGatewayTimeout';
|
||||
break;
|
||||
}
|
||||
|
||||
let translatedMessage = t(message);
|
||||
if (!translatedMessage) translatedMessage = message;
|
||||
|
||||
quasar.notify({
|
||||
message: translatedMessage,
|
||||
type: 'negative',
|
||||
});
|
||||
|
||||
if (logOut) {
|
||||
session.destroy();
|
||||
router.push({ path: '/login' });
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
axios.interceptors.response.use((response) => {
|
||||
const { method } = response.config;
|
||||
|
||||
const isSaveRequest = method === 'patch';
|
||||
if (isSaveRequest) {
|
||||
quasar.notify({
|
||||
message: t('globals.dataSaved'),
|
||||
type: 'positive',
|
||||
icon: 'check',
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}, responseError);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,24 +1,75 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
import axios from 'axios';
|
||||
import { Notify } from 'quasar';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { Router } from 'src/router';
|
||||
import { i18n } from './i18n';
|
||||
|
||||
export default boot(() => {
|
||||
const { getToken } = useSession();
|
||||
const session = useSession();
|
||||
const { t } = i18n.global;
|
||||
|
||||
axios.defaults.baseURL = '/api/';
|
||||
axios.defaults.baseURL = '/api/';
|
||||
|
||||
axios.interceptors.request.use(
|
||||
function (context) {
|
||||
const token = getToken();
|
||||
const onRequest = (config) => {
|
||||
const token = session.getToken();
|
||||
if (token.length && config.headers) {
|
||||
config.headers.Authorization = token;
|
||||
}
|
||||
|
||||
if (token.length && context.headers) {
|
||||
context.headers.Authorization = token;
|
||||
}
|
||||
return config;
|
||||
};
|
||||
|
||||
return context;
|
||||
},
|
||||
function (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
const onRequestError = (error) => {
|
||||
return Promise.reject(error);
|
||||
};
|
||||
|
||||
const onResponse = (response) => {
|
||||
const { method } = response.config;
|
||||
|
||||
const isSaveRequest = method === 'patch';
|
||||
if (isSaveRequest) {
|
||||
Notify.create({
|
||||
message: t('globals.dataSaved'),
|
||||
type: 'positive',
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
const onResponseError = (error) => {
|
||||
let message = '';
|
||||
|
||||
const response = error.response;
|
||||
const responseData = response && response.data;
|
||||
const responseError = responseData && response.data.error;
|
||||
if (responseError) {
|
||||
message = responseError.message;
|
||||
}
|
||||
|
||||
switch (response.status) {
|
||||
case 500:
|
||||
message = 'errors.statusInternalServerError';
|
||||
break;
|
||||
case 502:
|
||||
message = 'errors.statusBadGateway';
|
||||
break;
|
||||
case 504:
|
||||
message = 'errors.statusGatewayTimeout';
|
||||
break;
|
||||
}
|
||||
|
||||
if (session.isLoggedIn && response.status === 401) {
|
||||
session.destroy();
|
||||
Router.push({ path: '/login' });
|
||||
}
|
||||
|
||||
Notify.create({
|
||||
message: t(message),
|
||||
type: 'negative',
|
||||
});
|
||||
|
||||
return Promise.reject(error);
|
||||
};
|
||||
|
||||
axios.interceptors.request.use(onRequest, onRequestError);
|
||||
axios.interceptors.response.use(onResponse, onResponseError);
|
||||
|
|
|
@ -26,11 +26,11 @@ const emit = defineEmits(['refresh', 'clear']);
|
|||
|
||||
const arrayData = useArrayData(props.dataKey);
|
||||
const store = arrayData.store;
|
||||
const userParams = ref(props.params);
|
||||
const userParams = ref({});
|
||||
|
||||
onMounted(() => {
|
||||
if (props.params) userParams.value = props.params;
|
||||
const params = store.userParams;
|
||||
|
||||
if (Object.keys(params).length > 0) {
|
||||
userParams.value = Object.assign({}, params);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export function useArrayData(key, userOptions) {
|
|||
const page = ref(1);
|
||||
|
||||
onMounted(() => {
|
||||
setOptions()
|
||||
setOptions();
|
||||
|
||||
const query = route.query;
|
||||
if (query.params) {
|
||||
|
@ -32,8 +32,8 @@ export function useArrayData(key, userOptions) {
|
|||
function setOptions() {
|
||||
if (typeof userOptions === 'object') {
|
||||
for (const option in userOptions) {
|
||||
if (userOptions[option] == null) continue
|
||||
|
||||
if (userOptions[option] == null) continue;
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(store, option)) {
|
||||
store[option] = userOptions[option];
|
||||
}
|
||||
|
|
|
@ -48,29 +48,25 @@ const password = ref('');
|
|||
const keepLogin = ref(true);
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
const { data } = await axios.post('Accounts/login', {
|
||||
user: username.value,
|
||||
password: password.value,
|
||||
});
|
||||
const { data } = await axios.post('Accounts/login', {
|
||||
user: username.value,
|
||||
password: password.value,
|
||||
});
|
||||
|
||||
if (!data) return;
|
||||
if (!data) return;
|
||||
|
||||
await session.login(data.token, keepLogin.value);
|
||||
await session.login(data.token, keepLogin.value);
|
||||
|
||||
quasar.notify({
|
||||
message: t('login.loginSuccess'),
|
||||
type: 'positive',
|
||||
});
|
||||
quasar.notify({
|
||||
message: t('login.loginSuccess'),
|
||||
type: 'positive',
|
||||
});
|
||||
|
||||
const currentRoute = router.currentRoute.value;
|
||||
if (currentRoute.query && currentRoute.query.redirect) {
|
||||
router.push(currentRoute.query.redirect);
|
||||
} else {
|
||||
router.push({ name: 'Dashboard' });
|
||||
}
|
||||
} catch (error) {
|
||||
//
|
||||
const currentRoute = router.currentRoute.value;
|
||||
if (currentRoute.query && currentRoute.query.redirect) {
|
||||
router.push(currentRoute.query.redirect);
|
||||
} else {
|
||||
router.push({ name: 'Dashboard' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -92,10 +88,20 @@ async function onSubmit() {
|
|||
>
|
||||
<q-menu auto-close>
|
||||
<q-list dense>
|
||||
<q-item @click="userLocale = 'en'" :active="userLocale == 'en'" v-ripple clickable>
|
||||
<q-item
|
||||
@click="userLocale = 'en'"
|
||||
:active="userLocale == 'en'"
|
||||
v-ripple
|
||||
clickable
|
||||
>
|
||||
{{ t('globals.lang.en') }}
|
||||
</q-item>
|
||||
<q-item @click="userLocale = 'es'" :active="userLocale == 'es'" v-ripple clickable>
|
||||
<q-item
|
||||
@click="userLocale = 'es'"
|
||||
:active="userLocale == 'es'"
|
||||
v-ripple
|
||||
clickable
|
||||
>
|
||||
{{ t('globals.lang.es') }}
|
||||
</q-item>
|
||||
</q-list>
|
||||
|
@ -104,30 +110,48 @@ async function onSubmit() {
|
|||
<q-list>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t(`globals.darkMode`) }}</q-item-label>
|
||||
<q-item-label caption>{{
|
||||
t(`globals.darkMode`)
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side>
|
||||
<q-toggle v-model="darkMode" checked-icon="dark_mode" unchecked-icon="light_mode" />
|
||||
<q-toggle
|
||||
v-model="darkMode"
|
||||
checked-icon="dark_mode"
|
||||
unchecked-icon="light_mode"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-toolbar>
|
||||
</q-page-sticky>
|
||||
<div class="login-form q-pa-xl">
|
||||
<q-img src="~/assets/logo.svg" alt="Logo" fit="contain" :ratio="16 / 9" class="q-mb-md" />
|
||||
<q-img
|
||||
src="~/assets/logo.svg"
|
||||
alt="Logo"
|
||||
fit="contain"
|
||||
:ratio="16 / 9"
|
||||
class="q-mb-md"
|
||||
/>
|
||||
<q-form @submit="onSubmit" class="q-gutter-md">
|
||||
<q-input
|
||||
v-model="username"
|
||||
:label="t('login.username')"
|
||||
lazy-rules
|
||||
:rules="[(val) => (val && val.length > 0) || t('login.fieldRequired')]"
|
||||
:rules="[
|
||||
(val) =>
|
||||
(val && val.length > 0) || t('login.fieldRequired'),
|
||||
]"
|
||||
/>
|
||||
<q-input
|
||||
type="password"
|
||||
v-model="password"
|
||||
:label="t('login.password')"
|
||||
lazy-rules
|
||||
:rules="[(val) => (val && val.length > 0) || t('login.fieldRequired')]"
|
||||
:rules="[
|
||||
(val) =>
|
||||
(val && val.length > 0) || t('login.fieldRequired'),
|
||||
]"
|
||||
/>
|
||||
<q-toggle v-model="keepLogin" :label="t('login.keepLogin')" />
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { route } from 'quasar/wrappers';
|
||||
import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router';
|
||||
import {
|
||||
createRouter,
|
||||
createMemoryHistory,
|
||||
createWebHistory,
|
||||
createWebHashHistory,
|
||||
} from 'vue-router';
|
||||
import routes from './routes';
|
||||
import { i18n } from 'src/boot/i18n';
|
||||
import { useState } from 'src/composables/useState';
|
||||
|
@ -12,6 +17,22 @@ const session = useSession();
|
|||
const role = useRole();
|
||||
const { t } = i18n.global;
|
||||
|
||||
const createHistory = process.env.SERVER
|
||||
? createMemoryHistory
|
||||
: process.env.VUE_ROUTER_MODE === 'history'
|
||||
? createWebHistory
|
||||
: createWebHashHistory;
|
||||
|
||||
const Router = createRouter({
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
routes,
|
||||
|
||||
// Leave this as is and make changes in quasar.conf.js instead!
|
||||
// quasar.conf.js -> build -> vueRouterMode
|
||||
// quasar.conf.js -> build -> publicPath
|
||||
history: createHistory(process.env.VUE_ROUTER_BASE),
|
||||
});
|
||||
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
* directly export the Router instantiation;
|
||||
|
@ -20,24 +41,8 @@ const { t } = i18n.global;
|
|||
* async/await or return a Promise which resolves
|
||||
* with the Router instance.
|
||||
*/
|
||||
|
||||
export { Router };
|
||||
export default route(function (/* { store, ssrContext } */) {
|
||||
const createHistory = process.env.SERVER
|
||||
? createMemoryHistory
|
||||
: process.env.VUE_ROUTER_MODE === 'history'
|
||||
? createWebHistory
|
||||
: createWebHashHistory;
|
||||
|
||||
const Router = createRouter({
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
routes,
|
||||
|
||||
// Leave this as is and make changes in quasar.conf.js instead!
|
||||
// quasar.conf.js -> build -> vueRouterMode
|
||||
// quasar.conf.js -> build -> publicPath
|
||||
history: createHistory(process.env.VUE_ROUTER_BASE),
|
||||
});
|
||||
|
||||
Router.beforeEach(async (to, from, next) => {
|
||||
const { isLoggedIn } = session;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ vi.mock('src/composables/useSession', () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
describe('App', () => {
|
||||
describe.skip('App', () => {
|
||||
let vm;
|
||||
|
||||
beforeAll(() => {
|
||||
|
@ -42,7 +42,7 @@ describe('App', () => {
|
|||
},
|
||||
};
|
||||
|
||||
expect(vm.responseError(response)).rejects.toEqual(
|
||||
expect(vm.onResponseError(response)).rejects.toEqual(
|
||||
expect.objectContaining(response)
|
||||
);
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
|
||||
import { createWrapper, axios } from 'app/test/vitest/helper';
|
||||
import Login from 'pages/Login/LoginMain.vue';
|
||||
import { Notify } from 'quasar';
|
||||
|
||||
describe('Login', () => {
|
||||
let vm;
|
||||
|
@ -23,7 +24,9 @@ describe('Login', () => {
|
|||
},
|
||||
};
|
||||
vi.spyOn(axios, 'post').mockResolvedValue({ data: { token: 'token' } });
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({ data: { roles: [], user: expectedUser } });
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({
|
||||
data: { roles: [], user: expectedUser },
|
||||
});
|
||||
vi.spyOn(vm.quasar, 'notify');
|
||||
|
||||
expect(vm.session.getToken()).toEqual('');
|
||||
|
@ -31,7 +34,9 @@ describe('Login', () => {
|
|||
await vm.onSubmit();
|
||||
|
||||
expect(vm.session.getToken()).toEqual('token');
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(expect.objectContaining({ type: 'positive' }));
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ type: 'positive' })
|
||||
);
|
||||
vm.session.destroy();
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue