Added Dashboard as start page
* Plugins can register widgets on the dashboard * Added dummy widget for schedule and user ("greeting") * Added simple widget for balance
This commit is contained in:
parent
31620f9681
commit
cfc46dddd3
|
@ -1829,6 +1829,11 @@
|
|||
"integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/crypto-js": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.0.1.tgz",
|
||||
"integrity": "sha512-6+OPzqhKX/cx5xh+yO8Cqg3u3alrkhoxhE5ZOdSEv0DOzJ13lwJ6laqGU0Kv6+XDMFmlnGId04LtY22PsFLQUw=="
|
||||
},
|
||||
"@types/electron-packager": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/electron-packager/-/electron-packager-14.0.0.tgz",
|
||||
|
@ -4402,6 +4407,11 @@
|
|||
"randomfill": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"crypto-js": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
|
||||
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
|
||||
},
|
||||
"css": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.9.10",
|
||||
"@types/crypto-js": "^4.0.1",
|
||||
"@vue/composition-api": "^0.6.4",
|
||||
"axios": "^0.18.1",
|
||||
"core-js": "^3.7.0",
|
||||
"crypto-js": "^4.0.0",
|
||||
"quasar": "^1.14.3",
|
||||
"vue-router": "3.3.2"
|
||||
},
|
||||
|
|
|
@ -37,7 +37,7 @@ export default boot<Store<StateInterface>>(({ router, store }) => {
|
|||
} else {
|
||||
if (store.state.user.currentUser && !to.params['logout']) {
|
||||
// Called login while already logged in
|
||||
void next({ name: 'user-main' });
|
||||
void next({ name: 'dashboard' });
|
||||
} else {
|
||||
// Not logged in or from logout
|
||||
next();
|
||||
|
|
|
@ -9,7 +9,7 @@ const config = {
|
|||
// Do not change required Modules !!
|
||||
requiredModules: ['User'],
|
||||
// here you can import plugins.
|
||||
loadModules: ['Balance']
|
||||
loadModules: ['Balance', 'Schedule']
|
||||
};
|
||||
|
||||
// do not change anything here !!
|
||||
|
@ -164,6 +164,9 @@ function loadPlugin(
|
|||
plugin.outRoutes
|
||||
);
|
||||
}
|
||||
if (plugin.widget) {
|
||||
loadedPlugins.widgets.push(plugin.widget);
|
||||
}
|
||||
if (plugin.store) {
|
||||
console.log(plugin.store);
|
||||
console.log(plugin.store.keys());
|
||||
|
@ -176,7 +179,7 @@ function loadPlugin(
|
|||
version: plugin.version
|
||||
});
|
||||
} else {
|
||||
console.exception(`Don't find required Plugin ${requiredModule}`);
|
||||
console.exception(`Could not find required Plugin ${requiredModule}`);
|
||||
}
|
||||
});
|
||||
return loadedPlugins;
|
||||
|
@ -191,7 +194,8 @@ export default boot<Store<StateInterface>>(({ Vue, router, store }) => {
|
|||
plugins: [],
|
||||
mainLinks: [],
|
||||
shortcuts: [],
|
||||
shortcutsOut: []
|
||||
shortcutsOut: [],
|
||||
widgets: []
|
||||
};
|
||||
|
||||
// get all plugins
|
||||
|
@ -210,7 +214,7 @@ export default boot<Store<StateInterface>>(({ Vue, router, store }) => {
|
|||
);
|
||||
loadedPlugins = loadPlugin(loadedPlugins, config.loadModules, plugins, store);
|
||||
|
||||
console.log(loadedPlugins.routes);
|
||||
loadedPlugins.widgets.sort((a, b) => b.priority - a.priority);
|
||||
|
||||
// add new routes for plugins
|
||||
router.addRoutes(loadedPlugins.routes);
|
||||
|
|
|
@ -13,12 +13,17 @@
|
|||
/>
|
||||
|
||||
<q-toolbar-title>
|
||||
<q-avatar>
|
||||
<img src="logo.svg" />
|
||||
</q-avatar>
|
||||
<span class="gt-xs">
|
||||
Flaschengeist
|
||||
</span>
|
||||
<router-link
|
||||
:to="{ name: 'dashboard' }"
|
||||
style="text-decoration: none; color: inherit;"
|
||||
>
|
||||
<q-avatar>
|
||||
<img src="logo.svg" />
|
||||
</q-avatar>
|
||||
<span class="gt-xs">
|
||||
Flaschengeist
|
||||
</span>
|
||||
</router-link>
|
||||
</q-toolbar-title>
|
||||
|
||||
<!-- Hier kommen die Shortlinks hin -->
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<q-page
|
||||
padding
|
||||
style="grid-auto-rows: 1fr;"
|
||||
class="fit row justify-around items-start q-col-gutter-sm"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in widgets"
|
||||
:key="index"
|
||||
class="col-4 full-height col-sm-6 col-xs-12"
|
||||
>
|
||||
<component v-bind:is="item" />
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref } from '@vue/composition-api';
|
||||
import { AsyncComponentPromise } from 'vue/types/options';
|
||||
export default defineComponent({
|
||||
name: 'Dashboard',
|
||||
setup(_, { root }) {
|
||||
const widgets = ref<Array<AsyncComponentPromise>>([]);
|
||||
onMounted(() => {
|
||||
console.log('mounted!');
|
||||
root.$flaschengeistPlugins.widgets.forEach(widget =>
|
||||
widgets.value.push(widget.widget)
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
widgets
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -40,7 +40,7 @@ import { Loading } from 'quasar';
|
|||
export default defineComponent({
|
||||
// name: 'PageName'
|
||||
setup(_, { root }) {
|
||||
const mainRoute = { name: 'user-main' };
|
||||
const mainRoute = { name: 'dashboard' };
|
||||
|
||||
/* Stuff for the real login page */
|
||||
const userid = ref('');
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { RouteConfig } from 'vue-router';
|
||||
import { Module } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
import { AsyncComponentPromise } from 'vue/types/options';
|
||||
|
||||
declare namespace FG_Plugin {
|
||||
interface ShortCutLink {
|
||||
link: string;
|
||||
|
@ -21,6 +23,7 @@ declare namespace FG_Plugin {
|
|||
mainRoutes?: PluginRouteConfig[];
|
||||
outRoutes?: PluginRouteConfig[];
|
||||
store?: Map<string, Module<any, StateInterface>>;
|
||||
widget?: Widget;
|
||||
requiredModules: string[];
|
||||
version: string;
|
||||
}
|
||||
|
@ -42,11 +45,17 @@ declare namespace FG_Plugin {
|
|||
version: string;
|
||||
}
|
||||
|
||||
interface Widget {
|
||||
widget: AsyncComponentPromise;
|
||||
priority: number;
|
||||
}
|
||||
|
||||
interface LoadedPlugins {
|
||||
plugins: LoadedPlugin[];
|
||||
routes: RouteConfig[];
|
||||
mainLinks: PluginMainLink[];
|
||||
shortcuts: ShortCutLink[];
|
||||
shortcutsOut: ShortCutLink[];
|
||||
widgets: Widget[];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<q-card style="text-align: center;">
|
||||
<q-card-section>
|
||||
<div class="text-h6">Gerücht: {{ balance.toFixed(2) }} €</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onBeforeMount } from '@vue/composition-api';
|
||||
import { BalanceInterface } from 'src/plugins/balance/store/balance';
|
||||
import { Store } from 'vuex';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BalanceWidget',
|
||||
setup(_, { root }) {
|
||||
onBeforeMount(() => {
|
||||
store.dispatch('balance/getBalance').catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
});
|
||||
|
||||
const store: Store<{ balance: BalanceInterface }> = <
|
||||
Store<{ balance: BalanceInterface }>
|
||||
>root.$store;
|
||||
|
||||
const balance = computed<number>(() => {
|
||||
return store.state.balance.balance;
|
||||
});
|
||||
|
||||
return { balance };
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -11,7 +11,11 @@ const plugin: FG_Plugin.Plugin = {
|
|||
version: '0.0.1',
|
||||
store: new Map<string, Module<BalanceInterface, StateInterface>>([
|
||||
['balance', balance]
|
||||
])
|
||||
]),
|
||||
widget: {
|
||||
widget: () => import('./components/Widget.vue'),
|
||||
priority: 0
|
||||
}
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<q-card class="row justify-center content-center" style="text-align: center;">
|
||||
<q-card-section>
|
||||
<div class="text-h6 col-12">Dienste diesen Monat: {{ jobs }}</div>
|
||||
<div class="text-h6 col-12">Nächster Dienst: {{ nextJob | date }}</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onBeforeMount } from '@vue/composition-api';
|
||||
import { BalanceInterface } from 'src/plugins/balance/store/balance';
|
||||
import { Store } from 'vuex';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DummyWidget',
|
||||
setup(_, { root }) {
|
||||
function randomNumber(start: number, end: number) {
|
||||
return start + Math.floor(Math.random() * Math.floor(end));
|
||||
}
|
||||
const jobs = randomNumber(0, 5);
|
||||
const nextJob = new Date(2021, randomNumber(1, 12), randomNumber(1, 31));
|
||||
return { jobs, nextJob };
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,13 @@
|
|||
import { FG_Plugin } from 'src/plugins';
|
||||
|
||||
const plugin: FG_Plugin.Plugin = {
|
||||
name: 'Schedule',
|
||||
requiredModules: [],
|
||||
version: '0.0.1',
|
||||
widget: {
|
||||
widget: () => import('./components/Widget.vue'),
|
||||
priority: 0
|
||||
}
|
||||
};
|
||||
|
||||
export default plugin;
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<q-card style="text-align: center;">
|
||||
<q-card-section class="row justify-center content-stretch">
|
||||
<div class="col-4">
|
||||
<q-avatar style="width: 100%; height: auto;">
|
||||
<img :src="avatarLink" />
|
||||
</q-avatar>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<span class="text-h6">{{ name }}</span
|
||||
><br />
|
||||
<span>Andere Infos wo ich aber nicht weiß was</span>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from '@vue/composition-api';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
import MD5 from 'crypto-js/md5';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DummyWidget',
|
||||
setup(_, { root }) {
|
||||
const store = <Store<StateInterface>>root.$store;
|
||||
const avatarLink = computed(() => {
|
||||
const hash = MD5(store.state.user.currentUser?.mail.trim());
|
||||
return `https://www.gravatar.com/avatar/${hash}?s=500&d=robohash`;
|
||||
});
|
||||
|
||||
const name = ref(store.state.user.currentUser?.display_name);
|
||||
|
||||
return { avatarLink, name };
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -1,37 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<q-page padding class="fit row justify-center content-center items-center">
|
||||
<q-card class="col-4" height="">
|
||||
<q-card-section>
|
||||
Name: {{ userObj.firstname }} {{ userObj.lastname }}<br />
|
||||
E-Mail: {{ userObj.mail }}<br />
|
||||
Roles:
|
||||
<ul v-for="(role, index) in userObj.roles" :key="'role' + index">
|
||||
<li>{{ role }}</li>
|
||||
</ul>
|
||||
<br />
|
||||
Token expires: {{ sessionObj.expires | dateTime }}
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-page>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from '@vue/composition-api';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'PageName'
|
||||
setup(_, { root }) {
|
||||
const store = <Store<StateInterface>>root.$store;
|
||||
|
||||
const userObj = computed(() => store.state.user.currentUser);
|
||||
|
||||
const sessionObj = computed(() => store.state.session.currentSession);
|
||||
|
||||
return { userObj, sessionObj };
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -13,7 +13,11 @@ const plugin: FG_Plugin.Plugin = {
|
|||
store: new Map<string, Module<any, StateInterface>>([
|
||||
['user', userStore],
|
||||
['session', sessionsStore]
|
||||
])
|
||||
]),
|
||||
widget: {
|
||||
priority: 1,
|
||||
widget: () => import('./components/Widget.vue')
|
||||
}
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
|
|
|
@ -8,18 +8,9 @@ const mainRoutes: FG_Plugin.PluginRouteConfig[] = [
|
|||
component: () => import('../pages/MainPage.vue'),
|
||||
meta: { permissions: ['user'] },
|
||||
children: [
|
||||
{
|
||||
title: 'Hauptkanal',
|
||||
icon: 'mdi-account-hard-hat',
|
||||
path: 'user-main',
|
||||
name: 'user-main',
|
||||
shortcut: false,
|
||||
meta: { permissions: ['user'] },
|
||||
component: () => import('../pages/User.vue')
|
||||
},
|
||||
{
|
||||
title: 'Einstellungen',
|
||||
icon: 'mdi-cog',
|
||||
icon: 'mdi-account-edit',
|
||||
path: 'settings',
|
||||
name: 'user-settings',
|
||||
shortcut: true,
|
||||
|
|
|
@ -20,9 +20,15 @@ const routes: RouteConfig[] = [
|
|||
},
|
||||
{
|
||||
path: '/main',
|
||||
redirect: 'user',
|
||||
redirect: 'dashboard',
|
||||
component: () => import('layouts/MainLayout.vue'),
|
||||
children: [
|
||||
{
|
||||
name: 'dashboard',
|
||||
path: 'dashboard',
|
||||
meta: { permission: 'user' },
|
||||
component: () => import('pages/Dashboard.vue')
|
||||
},
|
||||
{
|
||||
name: 'about',
|
||||
path: 'about',
|
||||
|
|
Loading…
Reference in New Issue