[Vue3] Fixed users plugin and some basic stuff, still broken.

This commit is contained in:
Ferdinand Thiessen 2021-01-30 08:38:44 +01:00
parent 117d8256be
commit 897c98c53a
24 changed files with 184 additions and 187 deletions

View File

@ -29,7 +29,7 @@ module.exports = configure(function (/* ctx */) {
// app boot file (/src/boot) // app boot file (/src/boot)
// --> boot files are part of "main.js" // --> boot files are part of "main.js"
// https://quasar.dev/quasar-cli/boot-files // https://quasar.dev/quasar-cli/boot-files
boot: ['axios', 'plugins', 'loading', 'filter', 'login'], boot: ['axios', 'plugins', 'loading', 'login'],
// https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css
css: ['app.scss'], css: ['app.scss'],

View File

@ -1,6 +1,6 @@
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { Loading } from 'quasar'; import { Loading } from 'quasar';
import DarkCircularProgress from 'components/loading/DarkCircularProgress.vue'; //import DarkCircularProgress from 'components/loading/DarkCircularProgress.vue';
// "async" is optional; // "async" is optional;
// more info on params: https://quasar.dev/quasar-cli/cli-documentation/boot-files#Anatomy-of-a-boot-file // more info on params: https://quasar.dev/quasar-cli/cli-documentation/boot-files#Anatomy-of-a-boot-file
@ -8,6 +8,7 @@ export default boot(() => {
Loading.setDefaults({ Loading.setDefaults({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
spinner: DarkCircularProgress, // spinner: DarkCircularProgress,
// TODO : Das funktioniert wohl erstmal nicht mehr... gibt ne exception
}); });
}); });

View File

@ -2,12 +2,12 @@ import { boot } from 'quasar/wrappers';
import { Store } from 'vuex'; import { Store } from 'vuex';
import { FG_Plugin } from 'src/plugins'; import { FG_Plugin } from 'src/plugins';
import routes from 'src/router/routes'; import routes from 'src/router/routes';
import { axios } from 'boot/axios'; import { api } from 'boot/axios';
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import { Router, RouteRecordRaw } from 'vue-router'; import { Router, RouteRecordRaw } from 'vue-router';
import { UserSessionState } from 'src/plugins/user/store'; import { UserSessionState } from 'src/plugins/user/store';
const config = { const config: { [key: string]: Array<string> } = {
// Do not change required Modules !! // Do not change required Modules !!
requiredModules: ['User'], requiredModules: ['User'],
// here you can import plugins. // here you can import plugins.
@ -39,43 +39,49 @@ function setPermissions(object: FG_Plugin.PluginRouteConfig) {
object.route.meta['permissions'] = object.permissions; object.route.meta['permissions'] = object.permissions;
} }
function convertRoutes(parent: RouteRecordRaw, children?: FG_Plugin.PluginRouteConfig[]) {
if (children === undefined) return;
children.forEach((child) => {
setPermissions(child);
convertRoutes(child.route, child.children);
if (parent.children === undefined) parent.children = [];
parent.children.push(child.route);
});
}
function combineRoutes( function combineRoutes(
target: RouteRecordRaw[], target: RouteRecordRaw[],
source: FG_Plugin.PluginRouteConfig[], source: FG_Plugin.PluginRouteConfig[],
mainPath: '/' | '/main' = '/' mainPath: '/' | '/main' = '/'
): RouteRecordRaw[] { ): RouteRecordRaw[] {
// Search parent
target.forEach((target) => { target.forEach((target) => {
if (target.path === mainPath) { if (target.path === mainPath) {
// Parent found = target
source.forEach((sourceMainConfig: FG_Plugin.PluginRouteConfig) => { source.forEach((sourceMainConfig: FG_Plugin.PluginRouteConfig) => {
// Check if source is already in target
const targetMainConfig = target.children?.find((targetMainConfig: RouteRecordRaw) => { const targetMainConfig = target.children?.find((targetMainConfig: RouteRecordRaw) => {
return sourceMainConfig.route.path === targetMainConfig.path; return sourceMainConfig.route.path === targetMainConfig.path;
}); });
// Already in target routes, add only children
if (targetMainConfig) { if (targetMainConfig) {
const sourceChildren: RouteRecordRaw[] = []; convertRoutes(targetMainConfig, sourceMainConfig.children);
sourceMainConfig.children?.forEach((child) => {
setPermissions(child);
sourceChildren.push(child.route);
});
if (targetMainConfig.children) {
targetMainConfig.children = Object.assign(targetMainConfig.children, sourceChildren);
} else {
targetMainConfig.children = sourceChildren;
}
} else { } else {
// Append to target
if (target.children === undefined) { if (target.children === undefined) {
target.children = []; target.children = [];
} }
convertRoutes(sourceMainConfig.route, sourceMainConfig.children);
if ( if (
sourceMainConfig.children && sourceMainConfig.children &&
sourceMainConfig.children.length > 0 && sourceMainConfig.children.length > 0 &&
!sourceMainConfig.route.component !sourceMainConfig.route.component
) )
target.children.push({ Object.assign(sourceMainConfig.route, {
component: () => import('src/components/navigation/EmptyParent.vue'), component: () => import('src/components/navigation/EmptyParent.vue'),
name: sourceMainConfig.route.name,
path: sourceMainConfig.route.path,
}); });
else target.children.push(sourceMainConfig.route); target.children.push(sourceMainConfig.route);
} }
}); });
} }
@ -168,6 +174,7 @@ function loadPlugin(
loadedPlugins.mainLinks = combineMainLinks(loadedPlugins.mainLinks, route); loadedPlugins.mainLinks = combineMainLinks(loadedPlugins.mainLinks, route);
}); });
loadedPlugins.shortcuts = loadShortCuts(loadedPlugins.shortcuts, plugin.mainRoutes); loadedPlugins.shortcuts = loadShortCuts(loadedPlugins.shortcuts, plugin.mainRoutes);
console.log(loadedPlugins);
} }
if (plugin.outRoutes) { if (plugin.outRoutes) {
loadedPlugins.routes = combineRoutes(loadedPlugins.routes, plugin.outRoutes); loadedPlugins.routes = combineRoutes(loadedPlugins.routes, plugin.outRoutes);
@ -199,7 +206,7 @@ function loadPlugin(
async function getBackend(): Promise<Backend | null> { async function getBackend(): Promise<Backend | null> {
let backend: Backend | null = null; let backend: Backend | null = null;
try { try {
const response: AxiosResponse<Backend> = await axios.get('/'); const response: AxiosResponse<Backend> = await api.get('/');
backend = response.data; backend = response.data;
} catch (e) { } catch (e) {
console.log(e); console.log(e);

View File

@ -33,7 +33,7 @@ export default defineComponent({
link: { link: {
type: String, type: String,
default: 'home', default: 'dashboard',
}, },
icon: { icon: {

View File

@ -38,14 +38,6 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref } from 'vue'; import { computed, defineComponent, ref } from 'vue';
import { date } from 'quasar'; import { date } from 'quasar';
import { exception } from 'console';
interface Props {
value?: Date;
label?: string;
readonly: boolean;
type: string;
rules: Array<string>;
}
export default defineComponent({ export default defineComponent({
name: 'IsoDateInput', name: 'IsoDateInput',
@ -107,7 +99,7 @@ export default defineComponent({
case 'datetime': case 'datetime':
return 'YYYY-MM-DD HH:mm'; return 'YYYY-MM-DD HH:mm';
} }
throw exception; throw 'Invalid type given';
}); });
function dateChanged(dateString: string) { function dateChanged(dateString: string) {

View File

@ -24,7 +24,7 @@
<!-- Hier kommen die Shortlinks hin --> <!-- Hier kommen die Shortlinks hin -->
<div> <div>
<short-cut-link <short-cut-link
v-for="(shortcut, index) in $flaschengeistPlugins.shortcuts" v-for="(shortcut, index) in plugins.shortcuts"
:key="'shortcut' + index" :key="'shortcut' + index"
:link="shortcut.link" :link="shortcut.link"
:icon="shortcut.icon" :icon="shortcut.icon"
@ -46,7 +46,7 @@
<!-- Plugins --> <!-- Plugins -->
<q-list> <q-list>
<essential-link <essential-link
v-for="(link, index) in $flaschengeistPlugins.mainLinks" v-for="(link, index) in plugins.mainLinks"
:key="'plugin' + index" :key="'plugin' + index"
:title="link.title" :title="link.title"
:link="link.link" :link="link.link"
@ -172,7 +172,7 @@ export default defineComponent({
function logout() { function logout() {
Loading.show({ message: 'Session wird abgemeldet' }); Loading.show({ message: 'Session wird abgemeldet' });
store.dispatch('session/logout').finally(() => { store.dispatch('sessions/logout').finally(() => {
Loading.hide(); Loading.hide();
}); });
} }
@ -185,6 +185,7 @@ export default defineComponent({
pluginChildLinks, pluginChildLinks,
shortcuts, shortcuts,
logout, logout,
plugins,
}; };
}, },
}); });

View File

@ -10,7 +10,7 @@
</q-toolbar-title> </q-toolbar-title>
<div> <div>
<short-cut-link <short-cut-link
v-for="(shortcut, index) in $flaschengeistPlugins.shortcutsOut" v-for="(shortcut, index) in plugins.shortcutsOut"
:key="'shortcut' + index" :key="'shortcut' + index"
:link="shortcut.link" :link="shortcut.link"
:icon="shortcut.icon" :icon="shortcut.icon"
@ -42,11 +42,16 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { FG_Plugin } from 'src/plugins';
import { defineComponent, inject } from 'vue';
import ShortCutLink from 'components/navigation/ShortCutLink.vue'; import ShortCutLink from 'components/navigation/ShortCutLink.vue';
export default defineComponent({ export default defineComponent({
name: 'OutLayout', name: 'OutLayout',
components: { ShortCutLink }, components: { ShortCutLink },
setup() {
const plugins = inject<FG_Plugin.LoadedPlugins>('flaschengeistPlugins');
return { plugins };
},
}); });
</script> </script>

View File

@ -11,18 +11,18 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, defineComponent, onMounted, inject, ref } from 'vue'; import { Component, defineComponent, onMounted, inject, markRaw } from 'vue';
import { hasPermissions } from 'src/utils/permission'; import { hasPermissions } from 'src/utils/permission';
import { FG_Plugin } from 'src/plugins'; import { FG_Plugin } from 'src/plugins';
export default defineComponent({ export default defineComponent({
name: 'Dashboard', name: 'Dashboard',
setup() { setup() {
const widgets = ref<Array<Component>>([]); const widgets = markRaw<Array<Component>>([]);
const flaschengeistPlugins = inject<FG_Plugin.LoadedPlugins>('flaschengeistPlugins'); const flaschengeistPlugins = inject<FG_Plugin.LoadedPlugins>('flaschengeistPlugins');
onMounted(() => { onMounted(() => {
flaschengeistPlugins?.widgets.forEach((widget) => { flaschengeistPlugins?.widgets.forEach((widget) => {
if (hasPermissions(widget.permissions)) widgets.value.push(widget.widget); if (hasPermissions(widget.permissions)) widgets.push(widget.widget);
}); });
}); });

View File

@ -90,7 +90,7 @@ export default defineComponent({
message: 'Du wirst angemeldet', message: 'Du wirst angemeldet',
}); });
store store
.dispatch('session/login', { .dispatch('sessions/login', {
userid: userid.value, userid: userid.value,
password: password.value, password: password.value,
}) })
@ -129,7 +129,7 @@ export default defineComponent({
return; return;
} }
void store void store
.dispatch('session/requestPasswordReset', { .dispatch('sessions/requestPasswordReset', {
userid: userid.value, userid: userid.value,
}) })
.then(() => { .then(() => {

View File

@ -72,7 +72,7 @@ export default defineComponent({
message: 'Das Passwort wird zurückgesetzt', message: 'Das Passwort wird zurückgesetzt',
}); });
store store
.dispatch('session/resetPassword', { .dispatch('sessions/resetPassword', {
password: password.value, password: password.value,
token: router.currentRoute.value.query.token, token: router.currentRoute.value.query.token,
}) })

1
src/plugins.d.ts vendored
View File

@ -33,6 +33,7 @@ declare namespace FG_Plugin {
requiredBackendModules: string[]; requiredBackendModules: string[];
mainRoutes?: PluginRouteConfig[]; mainRoutes?: PluginRouteConfig[];
outRoutes?: PluginRouteConfig[]; outRoutes?: PluginRouteConfig[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
store?: Map<string, Module<any, any>>; store?: Map<string, Module<any, any>>;
} }

View File

@ -12,13 +12,14 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';
import MainUserSettings from 'src/plugins/user/components/settings/MainUserSettings.vue'; import MainUserSettings from 'src/plugins/user/components/settings/MainUserSettings.vue';
import { Store } from 'vuex'; import { useStore } from 'vuex';
import { StateInterface } from 'src/store'; import { UserSessionState } from '../store';
export default defineComponent({ export default defineComponent({
name: 'NewUser', name: 'NewUser',
components: { MainUserSettings }, components: { MainUserSettings },
setup(_, { root }) { setup() {
const store = <Store<StateInterface>>root.$store; const store = useStore<UserSessionState>();
const user = ref<FG.User>({ const user = ref<FG.User>({
userid: '', userid: '',
display_name: '', display_name: '',
@ -28,7 +29,7 @@ export default defineComponent({
roles: [], roles: [],
}); });
function setUser(value: FG.User) { function setUser(value: FG.User) {
store.dispatch('user/setUser', value).catch((error) => { store.dispatch('users/setUser', value).catch((error) => {
console.warn(error); console.warn(error);
}); });
} }

View File

@ -11,18 +11,18 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { useStore } from 'vuex';
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';
import { Store } from 'vuex'; import { UserSessionState } from '../store';
import { StateInterface } from '../../../store';
import UserSelector from '../components/UserSelector.vue'; import UserSelector from '../components/UserSelector.vue';
import MainUserSettings from '../components/settings/MainUserSettings.vue'; import MainUserSettings from '../components/settings/MainUserSettings.vue';
export default defineComponent({ export default defineComponent({
name: 'UpdateUser', name: 'UpdateUser',
components: { UserSelector, MainUserSettings }, components: { UserSelector, MainUserSettings },
setup(_, { root }) { setup() {
const store = <Store<StateInterface>>root.$store; const store = useStore<UserSessionState>();
const user = ref(<FG.User>store.state.user.currentUser); const user = ref(<FG.User>store.state.users.currentUser);
// can be dropped with VUE3 // can be dropped with VUE3
const userUpdated = (value: FG.User) => { const userUpdated = (value: FG.User) => {
@ -31,7 +31,7 @@ export default defineComponent({
}; };
function updateUser(value: FG.User) { function updateUser(value: FG.User) {
store.dispatch('user/updateUser', value).catch((error) => { store.dispatch('users/updateUser', value).catch((error) => {
console.warn(error); console.warn(error);
}); });
} }

View File

@ -13,27 +13,22 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, onBeforeMount } from 'vue'; import { computed, defineComponent, onBeforeMount } from 'vue';
import { Store } from 'vuex'; import { useStore } from 'vuex';
import { StateInterface } from 'src/store'; import { UserSessionState } from '../store';
interface Props {
user: FG.User;
label: string;
}
export default defineComponent({ export default defineComponent({
name: 'UserSelector', name: 'UserSelector',
props: { user: { required: true }, label: { type: String, default: 'Benutzer' } }, props: { user: { required: true, type: Object }, label: { type: String, default: 'Benutzer' } },
setup(props: Props, { root, emit }) { setup(_, { emit }) {
const store = <Store<StateInterface>>root.$store; const store = useStore<UserSessionState>();
onBeforeMount(() => { onBeforeMount(() => {
store.dispatch('user/getUsers').catch((error) => { store.dispatch('users/getUsers').catch((error) => {
console.error(error); console.error(error);
}); });
}); });
const users = computed(() => store.state.user.users); const users = computed(() => store.state.users.users);
const updated = (value: FG.User) => { const updated = (value: FG.User) => {
emit('update:user', value); emit('update:user', value);
}; };

View File

@ -27,18 +27,18 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, onMounted, ref } from 'vue'; import { computed, defineComponent, onMounted, ref } from 'vue';
import { Store } from 'vuex'; import { useStore } from 'vuex';
import { StateInterface } from 'src/store'; import { UserSessionState } from '../store';
export default defineComponent({ export default defineComponent({
name: 'Greeting', name: 'Greeting',
setup(_, { root }) { setup() {
const store = <Store<StateInterface>>root.$store; const store = useStore<UserSessionState>();
onMounted(() => store.dispatch('user/getUsers', false)); onMounted(() => store.dispatch('users/getUsers', false));
const name = ref(store.state.user.currentUser?.display_name); const name = ref(store.state.users.currentUser?.display_name);
const avatarLink = ref(store.state.user.currentUser?.avatar_url); const avatarLink = ref(store.state.users.currentUser?.avatar_url);
function userHasBirthday(user: FG.User) { function userHasBirthday(user: FG.User) {
const today = new Date(); const today = new Date();
@ -50,13 +50,13 @@ export default defineComponent({
} }
const hasBirthday = computed(() => { const hasBirthday = computed(() => {
return userHasBirthday(<FG.User>store.state.user.currentUser); return userHasBirthday(<FG.User>store.state.users.currentUser);
}); });
const birthday = computed(() => const birthday = computed(() =>
store.state.user.users store.state.users.users
.filter(userHasBirthday) .filter(userHasBirthday)
.filter((user) => user.userid !== store.state.user.currentUser?.userid) .filter((user) => user.userid !== store.state.users.currentUser?.userid)
); );
return { avatarLink, name, hasBirthday, birthday }; return { avatarLink, name, hasBirthday, birthday };

View File

@ -5,14 +5,14 @@
class="col-xs-12 col-sm-6 q-pa-sm" class="col-xs-12 col-sm-6 q-pa-sm"
label="Vorname" label="Vorname"
:rules="[notEmpty]" :rules="[notEmpty]"
v-model="props.user.firstname" v-model="user_model.firstname"
filled filled
/> />
<q-input <q-input
class="col-xs-12 col-sm-6 q-pa-sm" class="col-xs-12 col-sm-6 q-pa-sm"
label="Nachname" label="Nachname"
:rules="[notEmpty]" :rules="[notEmpty]"
v-model="props.user.lastname" v-model="user_model.lastname"
filled filled
/> />
@ -20,21 +20,21 @@
class="col-xs-12 col-sm-6 q-pa-sm" class="col-xs-12 col-sm-6 q-pa-sm"
label="Angezeigter Name" label="Angezeigter Name"
:rules="[notEmpty]" :rules="[notEmpty]"
v-model="props.user.display_name" v-model="user_model.display_name"
filled filled
/> />
<q-input <q-input
class="col-xs-12 col-sm-6 q-pa-sm" class="col-xs-12 col-sm-6 q-pa-sm"
label="E-Mail" label="E-Mail"
:rules="[isEmail, notEmpty]" :rules="[isEmail, notEmpty]"
v-model="props.user.mail" v-model="user_model.mail"
filled filled
/> />
<q-input <q-input
class="col-xs-12 col-sm-6 q-pa-sm" class="col-xs-12 col-sm-6 q-pa-sm"
label="Benutzername" label="Benutzername"
:readonly="!props.newUser" :readonly="!newUser"
v-model="props.user.userid" v-model="user_model.userid"
:rules="[isUseridUsed, notEmpty]" :rules="[isUseridUsed, notEmpty]"
filled filled
/> />
@ -44,7 +44,7 @@
filled filled
multiple multiple
use-chips use-chips
v-model="props.user.roles" v-model="user_model.roles"
:readonly="!canSetRoles" :readonly="!canSetRoles"
:options="allRoles" :options="allRoles"
option-label="name" option-label="name"
@ -52,7 +52,7 @@
/> />
<IsoDateInput <IsoDateInput
class="col-xs-12 col-sm-6 q-pa-sm" class="col-xs-12 col-sm-6 q-pa-sm"
v-model="props.user.birthday" v-model="user_model.birthday"
label="Geburtstag" label="Geburtstag"
/> />
<q-file <q-file
@ -70,8 +70,8 @@
</template> </template>
</q-file> </q-file>
</q-card-section> </q-card-section>
<q-separator v-if="!props.newUser" /> <q-separator v-if="!newUser" />
<q-card-section class="fit row justify-start content-center items-center" v-if="!props.newUser"> <q-card-section class="fit row justify-start content-center items-center" v-if="!newUser">
<q-input <q-input
v-if="isCurrentUser" v-if="isCurrentUser"
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm" class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
@ -107,51 +107,37 @@
</template> </template>
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { computed, defineComponent, ref, onBeforeMount } from 'vue'; import { computed, defineComponent, ref, onBeforeMount } from 'vue';
import { Store } from 'vuex'; import { useStore } from 'vuex';
import { StateInterface } from 'src/store';
import { hasPermission } from 'src/utils/permission'; import { hasPermission } from 'src/utils/permission';
import IsoDateInput from 'src/components/utils/IsoDateInput.vue'; import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
import { Notify } from 'quasar'; import { Notify } from 'quasar';
import { UserSessionState } from '../../store';
interface Props {
user?: FG.User;
newUser?: boolean;
}
export default defineComponent({ export default defineComponent({
name: 'MainUserSettings', name: 'MainUserSettings',
components: { IsoDateInput: IsoDateInput }, components: { IsoDateInput: IsoDateInput },
props: { props: {
user: { newUser: { type: Boolean, default: false },
required: true, user: { type: Object, required: true },
},
newUser: {
default: false,
},
}, },
setup(props: Props, { root, emit }) { setup(props, { emit }) {
const store = <Store<StateInterface>>root.$store; const store = useStore<UserSessionState>();
const user_model = ref<FG.User>(<FG.User>props.user);
onBeforeMount(() => { onBeforeMount(() => {
store.dispatch('user/getRoles', false).catch((error) => { store.dispatch('users/getRoles', false).catch((error) => {
console.warn(error); console.warn(error);
}); });
}); });
const isCurrentUser = computed( const isCurrentUser = computed(
() => props.user?.userid === store.state.user.currentUser?.userid () => user_model.value.userid === store.state.users.currentUser?.userid
); );
const canSetRoles = computed(() => hasPermission('users_set_roles', store)); const canSetRoles = computed(() => hasPermission('users_set_roles'));
const oldUser = computed(() => {
if (isCurrentUser.value) return <FG.User>store.state.user.currentUser;
else
return store.state.user.users.filter((user) => {
user.userid === props.user?.userid;
})[0];
});
const avatar = ref([]); const avatar = ref([]);
function onAvatarRejected() { function onAvatarRejected() {
@ -166,13 +152,13 @@ export default defineComponent({
avatar.value = []; avatar.value = [];
} }
const allRoles = computed(() => store.state.user.roles.map((role) => role.name)); const allRoles = computed(() => store.state.users.roles.map((role) => role.name));
const password = ref(''); const password = ref('');
const new_password = ref(''); const new_password = ref('');
const new_password2 = ref(''); const new_password2 = ref('');
function save() { function save() {
let changed = <FG.User>props.user; let changed = user_model.value;
if (typeof changed.birthday === 'string') changed.birthday = new Date(changed.birthday); if (typeof changed.birthday === 'string') changed.birthday = new Date(changed.birthday);
changed = Object.assign(changed, { changed = Object.assign(changed, {
password: password.value, password: password.value,
@ -183,11 +169,12 @@ export default defineComponent({
}); });
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
emit('update:user', changed); emit('update:user', changed);
if (avatar.value && (avatar.value.length > 0 || avatar.value instanceof File)) if (avatar.value && (avatar.value.length > 0 || avatar.value instanceof File))
store store
.dispatch('user/uploadAvatar', { .dispatch('users/uploadAvatar', {
user: changed, user: changed,
file: avatar.value, file: avatar.value,
}) })
@ -200,9 +187,8 @@ export default defineComponent({
} }
function reset() { function reset() {
//TODO: hier müssen wir uns etwas anderes einfallen lassen! // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
// eslint-disable-next-line vue/no-mutating-props user_model.value = <FG.User>props.user;
props.user = oldUser.value;
password.value = ''; password.value = '';
new_password.value = ''; new_password.value = '';
new_password2.value = ''; new_password2.value = '';
@ -224,7 +210,7 @@ export default defineComponent({
function isUseridUsed(val: string) { function isUseridUsed(val: string) {
return ( return (
!store.state.user.users.find((user: FG.User) => { !store.state.users.users.find((user: FG.User) => {
return user.userid == val; return user.userid == val;
}) || }) ||
!props.newUser || !props.newUser ||
@ -234,8 +220,8 @@ export default defineComponent({
return { return {
avatar, avatar,
user_model,
onAvatarRejected, onAvatarRejected,
props,
allRoles, allRoles,
canSetRoles, canSetRoles,
password, password,

View File

@ -45,28 +45,28 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { useStore } from 'vuex';
import { UserSessionState } from '../../store';
import { computed, defineComponent, ref, onBeforeMount } from 'vue'; import { computed, defineComponent, ref, onBeforeMount } from 'vue';
import { Store } from 'vuex';
import { StateInterface } from 'src/store';
export default defineComponent({ export default defineComponent({
name: 'RoleSettings', name: 'RoleSettings',
setup(_, { root }) { setup() {
const store = <Store<StateInterface>>root.$store; const store = useStore<UserSessionState>();
onBeforeMount(() => { onBeforeMount(() => {
store.dispatch('user/getRoles').catch((error) => { store.dispatch('users/getRoles').catch((error) => {
console.warn(error); console.warn(error);
}); });
store.dispatch('user/getPermissions').catch((error) => { store.dispatch('users/getPermissions').catch((error) => {
console.warn(error); console.warn(error);
}); });
}); });
const role = ref<FG.Role | null>(null); const role = ref<FG.Role | null>(null);
const roles = computed(() => store.state.user.roles); const roles = computed(() => store.state.users.roles);
const permissions = computed(() => const permissions = computed(() =>
store.state.user.permissions.map((perm) => { store.state.users.permissions.map((perm) => {
return { return {
value: perm, value: perm,
label: perm, label: perm,
@ -103,14 +103,14 @@ export default defineComponent({
function save() { function save() {
if (role.value) { if (role.value) {
if (role.value.id === -1) if (role.value.id === -1)
void store.dispatch('user/newRole', role.value).then((createdRole: FG.Role) => { void store.dispatch('users/newRole', role.value).then((createdRole: FG.Role) => {
console.log(createdRole); console.log(createdRole);
role.value = createdRole; role.value = createdRole;
}); });
else { else {
if (newRoleName.value !== '') role.value.name = newRoleName.value; if (newRoleName.value !== '') role.value.name = newRoleName.value;
console.log(role.value); console.log(role.value);
void store.dispatch('user/updateRole', role.value); void store.dispatch('users/updateRole', role.value);
} }
} }
} }
@ -130,7 +130,7 @@ export default defineComponent({
role.value = null; role.value = null;
} else { } else {
store store
.dispatch('user/deleteRole', role.value) .dispatch('users/deleteRole', role.value)
.then(() => (role.value = null)) .then(() => (role.value = null))
.catch((error) => console.warn(error)); .catch((error) => console.warn(error));
} }

View File

@ -21,7 +21,7 @@
Lebenszeit: Lebenszeit:
{{ session.lifetime }} {{ session.lifetime }}
</div> </div>
<div class="col-xs-12 col-sm-6">Läuft aus: {{ session.expires | dateTime(true) }}</div> <div class="col-xs-12 col-sm-6">Läuft aus: {{ dateTime(session.expires) }}</div>
</div> </div>
<div class="row q-my-sm" v-else> <div class="row q-my-sm" v-else>
<q-input <q-input
@ -46,19 +46,24 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { useStore } from 'vuex';
import { defineComponent, ref, computed } from 'vue'; import { defineComponent, ref, computed } from 'vue';
import { Store } from 'vuex'; import { UserSessionState } from '../../store';
import { StateInterface } from 'src/store'; import { formatDateTime } from 'src/utils/datetime';
export default defineComponent({ export default defineComponent({
name: 'Sessions', name: 'Sessions',
props: { props: {
session: { session: {
required: true, required: true,
type: Object,
}, },
}, },
setup(props: { session: FG.Session }, { root }) { setup(props) {
const store = <Store<StateInterface>>root.$store; const store = useStore<UserSessionState>();
const dateTime = (date: Date) => formatDateTime(date, true);
const options = ref(['Minuten', 'Stunden', 'Tage']); const options = ref(['Minuten', 'Stunden', 'Tage']);
const option = ref<string>(options.value[0]); const option = ref<string>(options.value[0]);
const lifetime = ref(0); const lifetime = ref(0);
@ -87,12 +92,12 @@ export default defineComponent({
} }
function deleteSession(token: string) { function deleteSession(token: string) {
store.dispatch('session/deleteSession', token).catch((error) => { store.dispatch('sessions/deleteSession', token).catch((error) => {
console.warn(error); console.warn(error);
}); });
} }
function isThisSession(token: string) { function isThisSession(token: string) {
return store.state.session.currentSession?.token === token; return store.state.sessions.currentSession?.token === token;
} }
const isEdit = ref(false); const isEdit = ref(false);
@ -107,6 +112,7 @@ export default defineComponent({
case options.value[2]: case options.value[2]:
return (lifetime.value / (60 * 60 * 24)).toFixed(2); return (lifetime.value / (60 * 60 * 24)).toFixed(2);
} }
throw 'Invalid option';
}, },
set: (val) => { set: (val) => {
if (val) { if (val) {
@ -126,7 +132,7 @@ export default defineComponent({
}); });
function edit(value: boolean) { function edit(value: boolean) {
lifetime.value = props.session.lifetime; lifetime.value = (<FG.Session>props.session).lifetime;
isEdit.value = value; isEdit.value = value;
} }
@ -134,7 +140,10 @@ export default defineComponent({
console.log(lifetime.value); console.log(lifetime.value);
isEdit.value = false; isEdit.value = false;
void store void store
.dispatch('session/updateSession', { lifetime: lifetime.value, token: props.session.token }) .dispatch('sessions/updateSession', {
lifetime: lifetime.value,
token: (<FG.Session>props.session).token,
})
.catch((error) => { .catch((error) => {
console.log(error); console.log(error);
}); });
@ -146,6 +155,7 @@ export default defineComponent({
isThisSession, isThisSession,
deleteSession, deleteSession,
isEdit, isEdit,
dateTime,
edit, edit,
options, options,
option, option,

View File

@ -46,23 +46,19 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref } from 'vue';
import RoleSettings from '../components/settings/RoleSettings.vue';
import UpdateUser from '../components/UpdateUser.vue';
import NewUser from '../components/NewUser.vue';
import { Store } from 'vuex';
import { StateInterface } from 'src/store';
import { hasPermission } from 'src/utils/permission';
import { PERMISSIONS } from '../permissions';
import { Screen } from 'quasar'; import { Screen } from 'quasar';
import { PERMISSIONS } from '../permissions';
import NewUser from '../components/NewUser.vue';
import { computed, defineComponent, ref } from 'vue';
import { hasPermission } from 'src/utils/permission';
import UpdateUser from '../components/UpdateUser.vue';
import RoleSettings from '../components/settings/RoleSettings.vue';
export default defineComponent({ export default defineComponent({
name: 'AdminSettings', name: 'AdminSettings',
components: { RoleSettings, UpdateUser, NewUser }, components: { RoleSettings, UpdateUser, NewUser },
setup(_, { root }) { setup() {
const store = <Store<StateInterface>>root.$store; const canEditRoles = computed(() => hasPermission(PERMISSIONS.ROLES_EDIT));
const canEditRoles = computed(() => hasPermission(PERMISSIONS.ROLES_EDIT, store));
interface Tab { interface Tab {
name: string; name: string;

View File

@ -10,7 +10,7 @@
:title="route.title" :title="route.title"
:icon="route.icon" :icon="route.icon"
:link="route.name" :link="route.name"
:permissions="route.meta.permissions" :permissions="route.permissions"
/> />
</q-list> </q-list>
</q-card-section> </q-card-section>
@ -21,15 +21,19 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { useRoute } from 'vue-router';
import { computed, defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import EssentialLink from 'src/components/navigation/EssentialLink.vue';
import mainRoutes from 'src/plugins/user/routes'; import mainRoutes from 'src/plugins/user/routes';
import EssentialLink from 'src/components/navigation/EssentialLink.vue';
export default defineComponent({ export default defineComponent({
// name: 'PageName' // name: 'PageName'
components: { EssentialLink }, components: { EssentialLink },
setup(_, { root }) { setup() {
const route = useRoute();
const checkMain = computed(() => { const checkMain = computed(() => {
return root.$route.matched.length == 2; return route.matched.length == 2;
}); });
return { checkMain, mainRoutes }; return { checkMain, mainRoutes };
}, },

View File

@ -17,27 +17,27 @@
import { computed, defineComponent, onBeforeMount, ref } from 'vue'; import { computed, defineComponent, onBeforeMount, ref } from 'vue';
import Sessions from '../components/settings/Sessions.vue'; import Sessions from '../components/settings/Sessions.vue';
import MainUserSettings from '../components/settings/MainUserSettings.vue'; import MainUserSettings from '../components/settings/MainUserSettings.vue';
import { Store } from 'vuex'; import { useStore } from 'vuex';
import { StateInterface } from 'src/store';
import setLoadingBar from 'src/utils/loading'; import setLoadingBar from 'src/utils/loading';
import { UserSessionState } from '../store';
export default defineComponent({ export default defineComponent({
// name: 'PageName' // name: 'PageName'
components: { Sessions, MainUserSettings }, components: { Sessions, MainUserSettings },
setup(_, _, { setup() {
const store = <Store<StateInterface>>root.$store; const store = useStore<UserSessionState>();
onBeforeMount(() => { onBeforeMount(() => {
store.dispatch('session/getSessions').catch((error) => { store.dispatch('sessions/getSessions').catch((error) => {
console.warn(error); console.warn(error);
}); });
}); });
const currentUser = ref(<FG.User>store.state.user.currentUser); const currentUser = ref(<FG.User>store.state.users.currentUser);
const sessions = computed(() => store.state.session.sessions); const sessions = computed(() => store.state.sessions.sessions);
const loading = computed(() => store.state.session.loading || store.state.user.loading > 0); const loading = computed(() => store.state.sessions.loading || store.state.users.loading > 0);
function updateUser(value: FG.User) { function updateUser(value: FG.User) {
store.dispatch('user/updateUser', value).catch((error) => { store.dispatch('users/updateUser', value).catch((error) => {
console.warn(error); console.warn(error);
}); });
} }

View File

@ -1,9 +1,10 @@
import { Module } from 'vuex'; import { Module } from 'vuex';
import userStore, { UserStateInterface } from './store/user';
import sessionsStore, { SessionInterface } from './store/session';
import routes from './routes'; import routes from './routes';
import { StateInterface } from 'src/store';
import { FG_Plugin } from 'src/plugins'; import { FG_Plugin } from 'src/plugins';
import { defineAsyncComponent } from 'vue';
import { UserSessionState } from './store';
import usersStore, { UserStateInterface } from './store/user';
import sessionsStorage, { SessionStateInterface } from './store/session';
const plugin: FG_Plugin.Plugin = { const plugin: FG_Plugin.Plugin = {
name: 'User', name: 'User',
@ -13,17 +14,17 @@ const plugin: FG_Plugin.Plugin = {
version: '0.0.1', version: '0.0.1',
store: new Map< store: new Map<
string, string,
Module<UserStateInterface, StateInterface> | Module<SessionInterface, StateInterface> Module<UserStateInterface, UserSessionState> | Module<SessionStateInterface, UserSessionState>
>([ >([
['user', userStore], ['users', usersStore],
['session', sessionsStore], ['sessions', sessionsStorage],
]), ]),
widgets: [ widgets: [
{ {
priority: 1, priority: 1,
name: 'greeting', name: 'greeting',
permissions: [], permissions: [],
widget: () => import('./components/Widget.vue'), widget: defineAsyncComponent(() => import('./components/Widget.vue')),
}, },
], ],
}; };

View File

@ -1,7 +1,7 @@
import { FG_Plugin } from 'src/plugins'; import { FG_Plugin } from 'src/plugins';
const mainRoutes: FG_Plugin.PluginRouteConfig[] = [ const mainRoutes: FG_Plugin.PluginRouteConfig[] = [
{ {
title: 'loadFromStore("user/displayName")', title: 'loadFromStore("users/displayName")',
icon: 'mdi-account', icon: 'mdi-account',
permissions: ['user'], permissions: ['user'],
route: { path: 'user', name: 'user', component: () => import('../pages/MainPage.vue') }, route: { path: 'user', name: 'user', component: () => import('../pages/MainPage.vue') },

View File

@ -4,7 +4,7 @@ import { api } from 'src/boot/axios';
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import { LocalStorage } from 'quasar'; import { LocalStorage } from 'quasar';
import { UserSessionState } from '.'; import { UserSessionState } from '.';
import { useRouter } from 'vue-router'; import { Router } from 'src/router';
export interface SessionStateInterface { export interface SessionStateInterface {
currentSession?: FG.Session; currentSession?: FG.Session;
@ -63,8 +63,8 @@ const actions: ActionTree<SessionStateInterface, UserSessionState> = {
.then((response: AxiosResponse<LoginResponse>) => { .then((response: AxiosResponse<LoginResponse>) => {
response.data.session.expires = new Date(response.data.session.expires); response.data.session.expires = new Date(response.data.session.expires);
commit('setCurrentSession', response.data.session); commit('setCurrentSession', response.data.session);
commit('user/setCurrentUser', response.data.user, { root: true }); commit('users/setCurrentUser', response.data.user, { root: true });
commit('user/setCurrentPermissions', response.data.permissions, { commit('users/setCurrentPermissions', response.data.permissions, {
root: true, root: true,
}); });
}) })
@ -113,19 +113,16 @@ const actions: ActionTree<SessionStateInterface, UserSessionState> = {
* Clear current session and logged in user * Clear current session and logged in user
*/ */
clearCurrent({ commit }, redirect = true) { clearCurrent({ commit }, redirect = true) {
const router = useRouter(); void Router.push({
void router name: 'login',
.push({ query: redirect ? { redirect: Router.currentRoute.value.fullPath } : {},
name: 'login', params: { logout: 'true' },
query: redirect ? { redirect: router.currentRoute.value.fullPath } : {}, }).then(() => {
params: { logout: 'true' }, commit('clearCurrentSession');
}) commit('users/clearCurrentUser', null, { root: true });
.then(() => { // ensure also volatile store gets cleared by refreshing the site
commit('clearCurrentSession'); Router.go(0);
commit('user/clearCurrentUser', null, { root: true }); });
// ensure also volatile store gets cleared by refreshing the site
router.go(0);
});
}, },
/** /**
* Get all sessions from current User * Get all sessions from current User