Header & userPanel componentization
This commit is contained in:
parent
44b823e376
commit
fade50dd2a
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<q-header class="bg-darker" color="white" elevated>
|
||||
<q-toolbar class="q-py-sm q-px-md">
|
||||
<q-btn flat @click="drawer = !drawer" round dense icon="menu" />
|
||||
<q-btn flat @click="$emit('on-toggle-drawer')" round dense icon="menu" />
|
||||
<router-link to="/">
|
||||
<q-btn flat round class="q-ml-xs" v-if="$q.screen.gt.xs">
|
||||
<q-avatar square size="md"><img src="@/assets/logo_icon.svg" alt="Logo" /></q-avatar>
|
||||
|
@ -9,74 +9,40 @@
|
|||
</router-link>
|
||||
<q-toolbar-title shrink class="text-weight-bold"> Salix </q-toolbar-title>
|
||||
<q-space></q-space>
|
||||
<q-btn dense flat no-wrap>
|
||||
<q-avatar size="lg">
|
||||
<q-img
|
||||
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
|
||||
spinner-color="white"
|
||||
/>
|
||||
</q-avatar>
|
||||
<q-tooltip>Account</q-tooltip>
|
||||
<q-icon name="arrow_drop_down" size="s" />
|
||||
|
||||
<q-menu>
|
||||
<div class="row no-wrap q-pa-md">
|
||||
<div class="column">
|
||||
<div class="text-h6 q-mb-md">Settings</div>
|
||||
<q-toggle
|
||||
:label="$t(`globals.lang['${language}']`)"
|
||||
icon="public"
|
||||
color="orange"
|
||||
false-value="es"
|
||||
true-value="en"
|
||||
v-model="language"
|
||||
/>
|
||||
<q-toggle
|
||||
v-model="mode"
|
||||
checked-icon="dark_mode"
|
||||
color="orange"
|
||||
unchecked-icon="light_mode"
|
||||
/>
|
||||
|
||||
<q-btn color="orange" outline size="sm" label="Settings" icon="settings" />
|
||||
</div>
|
||||
|
||||
<q-separator vertical inset class="q-mx-lg" />
|
||||
|
||||
<div class="column items-center" style="min-width: 150px">
|
||||
<q-avatar size="80px">
|
||||
<q-img
|
||||
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
|
||||
spinner-color="white"
|
||||
/>
|
||||
</q-avatar>
|
||||
|
||||
<div class="text-subtitle1 q-mt-md">
|
||||
<strong>{{ user.nickname }}</strong>
|
||||
</div>
|
||||
<div class="text-subtitle3 text-grey-7 q-mb-xs">@{{ user.username }}</div>
|
||||
|
||||
<q-btn
|
||||
color="orange"
|
||||
flat
|
||||
label="Log Out"
|
||||
size="sm"
|
||||
icon="logout"
|
||||
@click="logout()"
|
||||
v-close-popup
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
<div class="q-pl-sm q-gutter-sm row items-center no-wrap">
|
||||
<q-btn v-if="$q.screen.gt.xs" dense flat size="md" icon="add">
|
||||
<q-icon name="arrow_drop_down" size="s" />
|
||||
<q-menu>
|
||||
<q-list style="min-width: 150px">
|
||||
<q-item clickable>
|
||||
<q-item-section>New customer</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable>
|
||||
<q-item-section>New ticket</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
<q-btn v-if="$q.screen.gt.xs" dense flat round size="md" icon="notifications" />
|
||||
<q-btn dense flat no-wrap>
|
||||
<q-avatar size="lg">
|
||||
<q-img
|
||||
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
|
||||
spinner-color="white"
|
||||
/>
|
||||
</q-avatar>
|
||||
<q-tooltip>Account</q-tooltip>
|
||||
<q-icon name="arrow_drop_down" size="s" />
|
||||
<UserPanel />
|
||||
</q-btn>
|
||||
</div>
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component';
|
||||
import axios from 'axios';
|
||||
import { Dark } from 'quasar';
|
||||
import UserPanel from '@/components/UserPanel.vue';
|
||||
|
||||
interface UserProfile {
|
||||
id: number;
|
||||
|
@ -86,38 +52,11 @@ interface UserProfile {
|
|||
}
|
||||
|
||||
@Options({
|
||||
props: {
|
||||
drawer: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
components: {
|
||||
UserPanel,
|
||||
},
|
||||
})
|
||||
export default class Topbar extends Vue {
|
||||
drawer?: boolean;
|
||||
private lang = 'es';
|
||||
|
||||
mounted(): void {
|
||||
axios
|
||||
.get('/api/accounts/acl')
|
||||
.then((response) => {
|
||||
this.$store.dispatch('updateUserData', response.data);
|
||||
})
|
||||
.catch(() => {
|
||||
this.$q.notify({
|
||||
message: this.$t('globals.accessDenied'),
|
||||
type: 'negative',
|
||||
});
|
||||
this.$store.dispatch('logOut');
|
||||
this.$router.push('/login');
|
||||
});
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.$store.dispatch('logOut');
|
||||
this.$router.push('/login');
|
||||
}
|
||||
|
||||
get user(): UserProfile {
|
||||
return this.$store.state.user;
|
||||
}
|
||||
|
@ -125,22 +64,5 @@ export default class Topbar extends Vue {
|
|||
get token(): string {
|
||||
return this.$store.getters.token;
|
||||
}
|
||||
|
||||
get mode(): boolean {
|
||||
return Dark.isActive;
|
||||
}
|
||||
|
||||
set mode(value: boolean) {
|
||||
Dark.set(value);
|
||||
}
|
||||
|
||||
get language(): string {
|
||||
return this.lang;
|
||||
}
|
||||
|
||||
set language(value: string) {
|
||||
this.lang = value;
|
||||
this.$i18n.locale = value;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<template>
|
||||
<q-menu>
|
||||
<div class="row no-wrap q-pa-md">
|
||||
<div class="column">
|
||||
<div class="text-h6 q-mb-md">{{ $t('components.userPanel.settings') }}</div>
|
||||
<q-toggle
|
||||
:label="$t(`globals.lang['${language}']`)"
|
||||
icon="public"
|
||||
color="orange"
|
||||
false-value="es"
|
||||
true-value="en"
|
||||
v-model="language"
|
||||
/>
|
||||
<q-toggle v-model="mode" checked-icon="dark_mode" color="orange" unchecked-icon="light_mode" />
|
||||
|
||||
<q-btn color="orange" outline size="sm" label="Settings" icon="settings" />
|
||||
</div>
|
||||
|
||||
<q-separator vertical inset class="q-mx-lg" />
|
||||
|
||||
<div class="column items-center" style="min-width: 150px">
|
||||
<q-avatar size="80px">
|
||||
<q-img
|
||||
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
|
||||
spinner-color="white"
|
||||
/>
|
||||
</q-avatar>
|
||||
|
||||
<div class="text-subtitle1 q-mt-md">
|
||||
<strong>{{ user.nickname }}</strong>
|
||||
</div>
|
||||
<div class="text-subtitle3 text-grey-7 q-mb-xs">@{{ user.username }}</div>
|
||||
|
||||
<q-btn color="orange" flat label="Log Out" size="sm" icon="logout" @click="logout()" v-close-popup />
|
||||
</div>
|
||||
</div>
|
||||
</q-menu>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component';
|
||||
import axios from 'axios';
|
||||
import { Dark } from 'quasar';
|
||||
|
||||
interface UserProfile {
|
||||
id: number;
|
||||
username: string;
|
||||
nickname: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
@Options({})
|
||||
export default class UserPanel extends Vue {
|
||||
private lang = 'es';
|
||||
|
||||
mounted(): void {
|
||||
axios
|
||||
.get('/api/accounts/acl')
|
||||
.then((response) => {
|
||||
this.$store.dispatch('updateUserData', response.data);
|
||||
})
|
||||
.catch(() => {
|
||||
this.$q.notify({
|
||||
message: this.$t('errors.statusUnauthorized'),
|
||||
type: 'negative',
|
||||
});
|
||||
this.$store.dispatch('logOut');
|
||||
this.$router.push('/login');
|
||||
});
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.$store.dispatch('logOut');
|
||||
this.$router.push('/login');
|
||||
}
|
||||
|
||||
get user(): UserProfile {
|
||||
return this.$store.state.user;
|
||||
}
|
||||
|
||||
get token(): string {
|
||||
return this.$store.getters.token;
|
||||
}
|
||||
|
||||
get mode(): boolean {
|
||||
return Dark.isActive;
|
||||
}
|
||||
|
||||
set mode(value: boolean) {
|
||||
Dark.set(value);
|
||||
}
|
||||
|
||||
get language(): string {
|
||||
return this.lang;
|
||||
}
|
||||
|
||||
set language(value: string) {
|
||||
this.lang = value;
|
||||
this.$i18n.locale = value;
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,5 @@
|
|||
import toLowerCase from './toLowerCase';
|
||||
|
||||
export default {
|
||||
toLowerCase,
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
export default function toLowerCase(value: string): string {
|
||||
return value.toLowerCase();
|
||||
}
|
|
@ -5,6 +5,10 @@
|
|||
"en": "English"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"statusUnauthorized": "Access denied",
|
||||
"statusInternalServerError": "An internal server error has ocurred"
|
||||
},
|
||||
"login": {
|
||||
"title": "Login",
|
||||
"username": "Username",
|
||||
|
@ -16,7 +20,10 @@
|
|||
},
|
||||
"customer": {},
|
||||
"components": {
|
||||
"dialog": {},
|
||||
"calendar": {}
|
||||
"topbar": {},
|
||||
"userPanel": {
|
||||
"settings": "Settings",
|
||||
"logOut": "Log Out"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,9 +3,11 @@
|
|||
"lang": {
|
||||
"es": "Español",
|
||||
"en": "Inglés"
|
||||
},
|
||||
"accessDenied": "Acceso denegado",
|
||||
"internalServerError": "Ha ocurrido un error interno del servidor"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"statusUnauthorized": "Acceso denegado",
|
||||
"statusInternalServerError": "Ha ocurrido un error interno del servidor"
|
||||
},
|
||||
"login": {
|
||||
"title": "Iniciar sesión",
|
||||
|
@ -18,7 +20,10 @@
|
|||
},
|
||||
"customer": {},
|
||||
"components": {
|
||||
"dialog": {},
|
||||
"calendar": {}
|
||||
"topbar": {},
|
||||
"userPanel": {
|
||||
"settings": "Configuración",
|
||||
"logOut": "Cerrar sesión"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,8 +5,13 @@ import store from './store';
|
|||
import { Quasar } from 'quasar';
|
||||
import quasarUserOptions from './quasar-user-options';
|
||||
import i18n from './i18n';
|
||||
import filters from './core/filters';
|
||||
|
||||
createApp(App).use(i18n).use(Quasar, quasarUserOptions).use(store).use(router).mount('#app');
|
||||
const app = createApp(App).use(i18n).use(Quasar, quasarUserOptions).use(store).use(router);
|
||||
|
||||
app.config.globalProperties.$filters = filters;
|
||||
|
||||
app.mount('#app');
|
||||
|
||||
import axios from 'axios';
|
||||
axios.interceptors.request.use(
|
||||
|
|
|
@ -10,22 +10,23 @@ const routes: Array<RouteRecordRaw> = [
|
|||
{
|
||||
path: '/',
|
||||
name: 'Main',
|
||||
component: () => import('../views/Main/Main.vue'),
|
||||
component: () => import('../views/Layout/Main.vue'),
|
||||
redirect: { name: 'Dashboard' },
|
||||
children: [
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
component: () => import('../views/Main/Dashboard.vue'),
|
||||
component: () => import('../views/Dashboard/Dashboard.vue'),
|
||||
},
|
||||
{
|
||||
path: '/customer',
|
||||
name: 'Customer',
|
||||
component: () => import('../views/Customer/Main.vue'),
|
||||
component: () => import('../views/Customer/Customer.vue'),
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'NotFound',
|
||||
component: () => import('../views/Main/NotFound.vue'),
|
||||
component: () => import('../views/Layout/NotFound.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -3,5 +3,6 @@ import { Store } from '@/store'; // path to store file
|
|||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$store: Store;
|
||||
$filters: Record<string, T>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<template>
|
||||
<q-layout>
|
||||
<q-page class="q-pa-md">
|
||||
<q-card class="q-pa-md"> Dashboard page.. </q-card>
|
||||
</q-page>
|
||||
</q-layout>
|
||||
<q-page class="q-pa-md">
|
||||
<q-card class="q-pa-md"> Customer page.. </q-card>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<q-card class="q-pa-md">
|
||||
Dashboard page..
|
||||
{{ $filters.toLowerCase('HELLO WORLD') }}
|
||||
</q-card>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component';
|
||||
|
||||
@Options({})
|
||||
export default class Dashboard extends Vue {
|
||||
get whatever(): string {
|
||||
return this.$filters.toLowerCase('HELLO WORLD');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<q-layout>
|
||||
<Topbar :drawer="drawer" />
|
||||
<q-layout view="hHh lpR fFf">
|
||||
<Topbar @on-toggle-drawer="onToggleDrawer()" />
|
||||
<q-drawer
|
||||
v-model="drawer"
|
||||
show-if-above
|
||||
|
@ -71,6 +71,10 @@ import Topbar from '@/components/Topbar.vue';
|
|||
export default class Main extends Vue {
|
||||
drawer = false;
|
||||
miniState = true;
|
||||
|
||||
onToggleDrawer(): void {
|
||||
this.drawer = !this.drawer;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -111,7 +111,7 @@ export default class Login extends Vue {
|
|||
message: this.$t('login.loginSuccess'),
|
||||
type: 'positive',
|
||||
});
|
||||
this.$router.push('/');
|
||||
this.$router.push('/dashboard');
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorCode = error.response.status;
|
||||
|
@ -122,7 +122,7 @@ export default class Login extends Vue {
|
|||
});
|
||||
} else {
|
||||
this.$q.notify({
|
||||
message: this.$t('globals.internalServerError'),
|
||||
message: this.$t('errors.statusInternalServerError'),
|
||||
type: 'negative',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<template>
|
||||
<q-layout>
|
||||
<q-page class="q-pa-md">
|
||||
<q-card class="q-pa-md"> Dashboard page.. </q-card>
|
||||
</q-page>
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component';
|
||||
|
||||
@Options({})
|
||||
export default class Dashboard extends Vue {}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
Loading…
Reference in New Issue