Router improvements and load roles on login #8
14
cypress.json
14
cypress.json
|
@ -1,15 +1,15 @@
|
||||||
{
|
{
|
||||||
"baseUrl": "http://localhost:8080/",
|
"baseUrl": "http://localhost:8080/",
|
||||||
"fixturesFolder": "test/cypress/fixtures",
|
"fixturesFolder": "tests/cypress/fixtures",
|
||||||
"integrationFolder": "test/cypress/integration",
|
"integrationFolder": "tests/cypress/integration",
|
||||||
"pluginsFile": "test/cypress/plugins/index.js",
|
"pluginsFile": "tests/cypress/plugins/index.js",
|
||||||
"screenshotsFolder": "test/cypress/screenshots",
|
"screenshotsFolder": "tests/cypress/screenshots",
|
||||||
"supportFile": "test/cypress/support/index.js",
|
"supportFile": "tests/cypress/support/index.js",
|
||||||
"videosFolder": "test/cypress/videos",
|
"videosFolder": "tests/cypress/videos",
|
||||||
"video": true,
|
"video": true,
|
||||||
"component": {
|
"component": {
|
||||||
"componentFolder": "src",
|
"componentFolder": "src",
|
||||||
"testFiles": "**/*.spec.js",
|
"testFiles": "**/*.spec.js",
|
||||||
"supportFile": "test/cypress/support/unit.js"
|
"supportFile": "tests/cypress/support/unit.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ defineEmits(['toggle-drawer']);
|
||||||
<q-icon name="arrow_drop_down" size="s" />
|
<q-icon name="arrow_drop_down" size="s" />
|
||||||
<q-menu>
|
<q-menu>
|
||||||
<q-list style="min-width: 150px">
|
<q-list style="min-width: 150px">
|
||||||
<q-item clickable>
|
<q-item :to="{ path: '/customer/create' }" clickable>
|
||||||
<q-item-section>New customer</q-item-section>
|
<q-item-section>New customer</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item clickable>
|
<q-item clickable>
|
||||||
|
|
|
@ -3,14 +3,15 @@ import { onMounted, computed } from 'vue';
|
||||||
import { Dark, useQuasar } from 'quasar';
|
import { Dark, useQuasar } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
import { useRole } from 'src/composables/useRole';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
|
const role = useRole();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
|
|
||||||
|
@ -28,11 +29,10 @@ const token = session.getToken();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get('/api/accounts/acl');
|
const stateRoles = state.getRoles().value
|
||||||
const roles = data.roles.map(userRoles => userRoles.role.name);
|
if (stateRoles.length === 0) {
|
||||||
|
await role.fetch();
|
||||||
state.setUser(data.user);
|
}
|
||||||
state.setRoles(roles);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
message: t('errors.statusUnauthorized'),
|
message: t('errors.statusUnauthorized'),
|
||||||
|
|
|
@ -12,7 +12,7 @@ jest.mock('vue-router', () => ({
|
||||||
|
|
||||||
describe('UserPanel', () => {
|
describe('UserPanel', () => {
|
||||||
describe('onMounted', () => {
|
describe('onMounted', () => {
|
||||||
it('should define the user into state', async () => {
|
it.only('should define the user into state', async () => {
|
||||||
const userMock = {
|
const userMock = {
|
||||||
user: {
|
user: {
|
||||||
id: 1,
|
id: 1,
|
||||||
|
@ -30,7 +30,7 @@ describe('UserPanel', () => {
|
||||||
expect(vm.state.getUser().value).toEqual(expectedUser);
|
expect(vm.state.getUser().value).toEqual(expectedUser);
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('should logout and notify the expected error', async () => {
|
it('should logout and notify the expected error', async () => {
|
||||||
jest.spyOn(axios, 'get').mockRejectedValue(new Error('error'));
|
jest.spyOn(axios, 'get').mockRejectedValue(new Error('error'));
|
||||||
|
|
||||||
const { vm } = createWrapper(UserPanel);
|
const { vm } = createWrapper(UserPanel);
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
import { useState } from './useState';
|
import { useState } from './useState';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const state = useState();
|
||||||
|
|
||||||
export function useRole() {
|
export function useRole() {
|
||||||
|
async function fetch() {
|
||||||
|
const { data } = await axios.get('/api/accounts/acl');
|
||||||
|
const roles = data.roles.map(userRoles => userRoles.role.name);
|
||||||
|
|
||||||
|
state.setUser(data.user);
|
||||||
|
state.setRoles(roles);
|
||||||
|
}
|
||||||
|
|
||||||
function hasAny(roles) {
|
function hasAny(roles) {
|
||||||
const { getRoles } = useState();
|
const roleStore = state.getRoles();
|
||||||
const roleStore = getRoles();
|
|
||||||
|
|
||||||
for (const role of roles) {
|
for (const role of roles) {
|
||||||
if (roleStore.value.indexOf(role) !== -1) return true;
|
if (roleStore.value.indexOf(role) !== -1) return true;
|
||||||
|
@ -13,6 +23,7 @@ export function useRole() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
fetch,
|
||||||
hasAny,
|
hasAny,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { useState } from './useState';
|
import { useState } from './useState';
|
||||||
|
import { useRole } from './useRole';
|
||||||
|
|
||||||
export function useSession() {
|
export function useSession() {
|
||||||
function getToken() {
|
function getToken() {
|
||||||
|
@ -29,6 +30,14 @@ export function useSession() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function login(token, keepLogin) {
|
||||||
|
const { fetch } = useRole();
|
||||||
|
|
||||||
|
setToken({ token, keepLogin });
|
||||||
|
|
||||||
|
await fetch();
|
||||||
|
}
|
||||||
|
|
||||||
function isLoggedIn() {
|
function isLoggedIn() {
|
||||||
const localToken = localStorage.getItem('token');
|
const localToken = localStorage.getItem('token');
|
||||||
const sessionToken = sessionStorage.getItem('token');
|
const sessionToken = sessionStorage.getItem('token');
|
||||||
|
@ -40,6 +49,7 @@ export function useSession() {
|
||||||
getToken,
|
getToken,
|
||||||
setToken,
|
setToken,
|
||||||
destroy,
|
destroy,
|
||||||
|
login,
|
||||||
isLoggedIn,
|
isLoggedIn,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
<template>
|
<template>
|
||||||
<q-card>asd</q-card>
|
<q-card>Basic Data</q-card>
|
||||||
</template>
|
</template>
|
|
@ -4,7 +4,7 @@
|
||||||
<q-card square>
|
<q-card square>
|
||||||
<router-link :to="{ path: '/customer/list' }">
|
<router-link :to="{ path: '/customer/list' }">
|
||||||
<q-icon name="arrow_back" size="md" color="primary" />
|
<q-icon name="arrow_back" size="md" color="primary" />
|
||||||
</router-link>
|
</router-link>Descriptor
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useRole } from '/src/composables/useRole';
|
|
||||||
|
|
||||||
const slide = ref('style');
|
const slide = ref('style');
|
||||||
const slideText = 'Description text';
|
const slideText = 'Description text';
|
||||||
|
|
||||||
const { hasAny } = useRole();
|
|
||||||
|
|
||||||
const isSalesPerson = computed(() => hasAny(['salesPerson']));
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -19,9 +15,8 @@ const isSalesPerson = computed(() => hasAny(['salesPerson']));
|
||||||
rounded
|
rounded
|
||||||
class="bg-orange text-white q-mb-lg"
|
class="bg-orange text-white q-mb-lg"
|
||||||
>
|
>
|
||||||
{{ isSalesPerson }}
|
Employee notification message
|
||||||
<template #action>
|
<template #action>
|
||||||
<q-btn flat label="Turn ON Wifi" />
|
|
||||||
<q-btn flat label="Dismiss" />
|
<q-btn flat label="Dismiss" />
|
||||||
</template>
|
</template>
|
||||||
</q-banner>
|
</q-banner>
|
||||||
|
|
|
@ -32,17 +32,19 @@ async function onSubmit() {
|
||||||
password: password.value,
|
password: password.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
session.setToken({
|
await session.login(data.token, keepLogin.value);
|
||||||
token: data.token,
|
|
||||||
keepLogin: keepLogin.value,
|
|
||||||
});
|
|
||||||
|
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
message: t('login.loginSuccess'),
|
message: t('login.loginSuccess'),
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
});
|
});
|
||||||
|
|
||||||
await router.push({ path: '/dashboard' });
|
const currentRoute = router.currentRoute.value;
|
||||||
|
if (currentRoute.query && currentRoute.query.redirect) {
|
||||||
|
router.push(currentRoute.query.redirect);
|
||||||
|
} else {
|
||||||
|
router.push({ name: 'Dashboard' });
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
const errorCode = error.response && error.response.status;
|
const errorCode = error.response && error.response.status;
|
||||||
|
|
|
@ -7,6 +7,7 @@ const mockPush = jest.fn();
|
||||||
jest.mock('vue-router', () => ({
|
jest.mock('vue-router', () => ({
|
||||||
useRouter: () => ({
|
useRouter: () => ({
|
||||||
push: mockPush,
|
push: mockPush,
|
||||||
|
currentRoute: { value: 'myCurrentRoute' }
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ describe('Login', () => {
|
||||||
|
|
||||||
it('should successfully set the token into session', async () => {
|
it('should successfully set the token into session', async () => {
|
||||||
jest.spyOn(axios, 'post').mockResolvedValue({ data: { token: 'token' } });
|
jest.spyOn(axios, 'post').mockResolvedValue({ data: { token: 'token' } });
|
||||||
|
jest.spyOn(axios, 'get').mockResolvedValue({ data: { roles: [], user: { id: 1 } } });
|
||||||
jest.spyOn(vm.quasar, 'notify')
|
jest.spyOn(vm.quasar, 'notify')
|
||||||
|
|
||||||
expect(vm.session.getToken()).toEqual('');
|
expect(vm.session.getToken()).toEqual('');
|
||||||
|
|
|
@ -36,18 +36,18 @@ export default route(function (/* { store, ssrContext } */) {
|
||||||
Router.beforeEach((to, from, next) => {
|
Router.beforeEach((to, from, next) => {
|
||||||
const { isLoggedIn } = session;
|
const { isLoggedIn } = session;
|
||||||
|
|
||||||
if (!isLoggedIn && to.name !== 'Login') {
|
if (!isLoggedIn() && to.name !== 'Login') {
|
||||||
next({ path: '/login', query: { redirect: to.fullPath } });
|
next({ name: 'Login', query: { redirect: to.fullPath } });
|
||||||
} else {
|
} else {
|
||||||
const pathRoutes = to.matched;
|
const matches = to.matched;
|
||||||
const droles = pathRoutes.every(route => {
|
const hasRequiredRoles = matches.every(route => {
|
||||||
const meta = route.meta;
|
const meta = route.meta;
|
||||||
if (meta.roles)
|
if (meta && meta.roles)
|
||||||
return role.hasAny(meta.roles)
|
return role.hasAny(meta.roles)
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (droles) {
|
if (hasRequiredRoles) {
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
next({ path: '/' });
|
next({ path: '/' });
|
||||||
|
|
|
@ -6,7 +6,7 @@ const routes = [
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
meta: { title: 'logIn' },
|
meta: { title: 'logIn' },
|
||||||
component: () => import('../pages/Login/Login.vue'),
|
component: () => import('../pages/Login/Login.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
|
|
Loading…
Reference in New Issue