release v2.0.0 #4

Merged
crimsen merged 481 commits from develop into master 2024-01-18 15:15:08 +00:00
12 changed files with 435 additions and 300 deletions
Showing only changes of commit 8689e84d47 - Show all commits

View File

@ -5,16 +5,8 @@ import { Store } from 'vuex';
export default boot<Store<StateInterface>>(({ router, store }) => { export default boot<Store<StateInterface>>(({ router, store }) => {
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
const user = store.state.user.currentUser;
const session = store.state.session.currentSession; const session = store.state.session.currentSession;
let permissions: string[] = [];
if (user) {
user.roles.forEach(role => {
permissions = permissions.concat(role.permissions);
});
}
if (to.name != 'login') { if (to.name != 'login') {
if (!session || session.expires <= new Date()) { if (!session || session.expires <= new Date()) {
store.dispatch('session/logout').catch(error => { store.dispatch('session/logout').catch(error => {
@ -32,7 +24,7 @@ export default boot<Store<StateInterface>>(({ router, store }) => {
return (<{ permissions: FG.Permission[] }>( return (<{ permissions: FG.Permission[] }>(
record.meta record.meta
)).permissions.every((permission: string) => { )).permissions.every((permission: string) => {
return permissions.includes(permission); return store.state.user.currentPermissions.includes(permission);
}); });
} }
} }

View File

@ -23,6 +23,7 @@
import { computed, defineComponent } from '@vue/composition-api'; import { computed, defineComponent } from '@vue/composition-api';
import { Store } from 'vuex'; import { Store } from 'vuex';
import { StateInterface } from 'src/store'; import { StateInterface } from 'src/store';
import { hasPermissions } from 'src/components/permission';
export default defineComponent({ export default defineComponent({
name: 'EssentialLink', name: 'EssentialLink',
@ -65,17 +66,9 @@ export default defineComponent({
return props.title; return props.title;
}); });
const hasPermissions = computed(() => { const isGranted = computed(() =>
let permissions = props.permissions; hasPermissions(props.permissions || [], root.$store)
if (permissions) { );
return (<string[]>permissions).every(permission => {
return (<{ 'user/permissions': string[] }>(
(<Store<StateInterface>>root.$store).getters
))['user/permissions'].includes(permission);
});
}
return true;
});
return { realTitle: title, hasPermissions }; return { realTitle: title, hasPermissions };
} }

View File

@ -1,11 +1,12 @@
<template> <template>
<q-btn flat dense :icon="icon" :to="{ name: link }" v-if="hasPermissions" /> <q-btn flat dense :icon="icon" :to="{ name: link }" v-if="isGranted" />
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from '@vue/composition-api'; import { computed, defineComponent } from '@vue/composition-api';
import { Store } from 'vuex'; import { Store } from 'vuex';
import { StateInterface } from 'src/store'; import { StateInterface } from 'src/store';
import { hasPermissions } from 'src/components/permission';
export default defineComponent({ export default defineComponent({
name: 'ShortCutLink', name: 'ShortCutLink',
@ -23,18 +24,10 @@ export default defineComponent({
} }
}, },
setup(props, { root }) { setup(props, { root }) {
const hasPermissions = computed(() => { const isGranted = computed(() =>
let permissions = props.permissions; hasPermissions(props.permissions || [], root.$store)
if (permissions) { );
return (<string[]>permissions).every(permission => { return { isGranted };
return (<{ 'user/permissions': string[] }>(
(<Store<StateInterface>>root.$store).getters
))['user/permissions'].includes(permission);
});
}
return true;
});
return { hasPermissions };
} }
}); });
</script> </script>

View File

@ -0,0 +1,14 @@
import { Store } from 'vuex';
import { StateInterface } from 'src/store';
export function hasPermission(
permission: string,
store: Store<StateInterface>
) {
return store.state.user.currentPermissions.includes(permission);
}
export function hasPermissions(needed: string[], store: Store<StateInterface>) {
const permissions = store.state.user.currentPermissions;
return needed.every(value => permissions.includes(value));
}

View File

@ -0,0 +1,41 @@
<template>
<q-select
filled
label="Benutzer"
@input="updated"
v-model="user"
:options="users"
option-label="display_name"
option-value="userid"
map-options
/>
</template>
<script lang="ts">
import { computed, defineComponent, ref } from '@vue/composition-api';
import { Store } from 'vuex';
import { StateInterface } from 'src/store';
interface Props {
user: FG.User;
}
export default defineComponent({
name: 'UserSelector',
props: ['user'],
setup(props: Props, { root, emit }) {
const store = <Store<StateInterface>>root.$store;
const users = computed(() => store.state.user.users);
const user = ref(props.user);
const updated = (value: FG.User) => {
emit('update:user', value);
};
return {
user,
updated,
users
};
}
});
</script>

View File

@ -1,191 +0,0 @@
<template>
<q-card class="col-12">
<q-linear-progress indeterminate rounded color="primary" v-if="loading" />
<q-form @submit="save" @reset="reset">
<q-card-section class="fit row justify-start content-center items-center">
<q-input
class="col-xs-12 col-sm-6 q-pa-sm"
label="Vorname"
:rules="[notEmpty]"
v-model="firstname"
filled
/>
<q-input
class="col-xs-12 col-sm-6 q-pa-sm"
label="Nachname"
:rules="[notEmpty]"
v-model="lastname"
filled
/>
<q-input
class="col-xs-12 col-sm-6 q-pa-sm"
label="Benutzername"
readonly
:value="user.userid"
filled
/>
<q-input
class="col-xs-12 col-sm-6 q-pa-sm"
label="E-Mail"
:rules="[isEmail, notEmpty]"
v-model="mail"
filled
/>
<q-input
class="col-xs-12 col-sm-6 q-pa-sm"
label="Display Name"
:rules="[notEmpty]"
v-model="display_name"
filled
/>
<q-select
class="col-xs-12 col-sm-6 q-pa-sm"
label="Rollen"
readonly
v-model="user.roles"
:options="user.roles"
filled
>
<template v-slot:selected-item="scope">
<q-chip v-for="(item, index) in scope.opt" :key="'item' + index">
{{ item.name }}
</q-chip>
</template>
</q-select>
</q-card-section>
<q-separator />
<q-card-section class="fit row justify-start content-center items-center">
<q-input
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
label="Password"
type="password"
hint="Password muss immer eingetragen werden"
:rules="[notEmpty]"
v-model="password"
filled
/>
<q-input
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
label="Neues Password"
type="password"
v-model="new_password"
filled
/>
<q-input
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
label="Wiederhole neues Password"
type="password"
:disable="new_password.length == 0"
:rules="[samePassword]"
v-model="new_password2"
filled
/>
</q-card-section>
<q-card-actions align="right">
<q-btn label="test" @click="$store.dispatch('user/getUser')" />
<q-btn label="Reset" type="reset" />
<q-btn color="primary" type="submit" label="Speichern" />
</q-card-actions>
</q-form>
</q-card>
</template>
<script lang="ts">
import { computed, defineComponent, ref } from '@vue/composition-api';
import { Store } from 'vuex';
import { StateInterface } from 'src/store';
export default defineComponent({
name: 'Main',
setup(_, { root }) {
const store = <Store<StateInterface>>root.$store;
const user = computed(() => <FG.User>store.state.user.currentUser);
const firstname = ref(user.value?.firstname);
const lastname = ref(user.value?.lastname);
const mail = ref(user.value?.mail);
const display_name = ref(user.value?.display_name);
const password = ref('');
const new_password = ref('');
const new_password2 = ref('');
function save() {
let change_values: { [index: string]: string } = {
firstname: firstname.value,
lastname: lastname.value,
mail: mail.value,
display_name: display_name.value
};
Object.keys(change_values).forEach(key => {
if (
change_values[key] === (<{ [index: string]: any }>user.value)[key]
) {
delete change_values[key];
}
});
change_values = Object.assign(change_values, {
password: password.value
});
if (new_password.value != '') {
change_values = Object.assign(change_values, {
new_password: new_password.value
});
}
store.dispatch('user/updateUser', change_values).catch(error => {
console.warn(error);
});
}
function reset() {
firstname.value = user.value.firstname;
lastname.value = user.value.lastname;
mail.value = user.value.mail;
display_name.value = user.value.display_name;
password.value = '';
new_password.value = '';
new_password2.value = '';
}
function samePassword(val: string) {
return val == new_password.value || 'Passwörter sind nicht identisch!';
}
function notEmpty(val: string) {
return !!val || 'Feld darf nicht leer sein!';
}
function isEmail(val: string | null) {
return (
!val ||
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w\w+)+$/.test(val) ||
'E-Mail ist nicht valide.'
);
}
const loading = computed(() => {
return (
store.state.user.getUserLoading || store.state.user.updateUserLoading
);
});
return {
user,
firstname,
lastname,
mail,
display_name,
password,
new_password,
new_password2,
samePassword,
isEmail,
notEmpty,
save,
reset,
loading
};
}
});
</script>

View File

@ -0,0 +1,194 @@
<template>
<q-form @submit="save" @reset="reset">
<q-linear-progress indeterminate rounded color="primary" v-if="loading" />
<q-card-section class="fit row justify-start content-center items-center">
<q-input
class="col-xs-12 col-sm-6 q-pa-sm"
label="Vorname"
:rules="[notEmpty]"
v-model="props.user.firstname"
filled
/>
<q-input
class="col-xs-12 col-sm-6 q-pa-sm"
label="Nachname"
:rules="[notEmpty]"
v-model="props.user.lastname"
filled
/>
<q-input
class="col-xs-12 col-sm-6 q-pa-sm"
label="Benutzername"
readonly
:value="props.user.userid"
filled
/>
<q-input
class="col-xs-12 col-sm-6 q-pa-sm"
label="E-Mail"
:rules="[isEmail, notEmpty]"
v-model="props.user.mail"
filled
/>
<q-input
class="col-xs-12 col-sm-6 q-pa-sm"
label="Display Name"
:rules="[notEmpty]"
v-model="props.user.display_name"
filled
/>
<q-select
class="col-xs-12 col-sm-6 q-pa-sm"
label="Rollen"
filled
multiple
use-chips
v-model="props.user.roles"
:readonly="() => canSetRoles()"
:options="allRoles"
option-label="name"
option-value="name"
/>
</q-card-section>
<q-separator />
<q-card-section class="fit row justify-start content-center items-center">
<q-input
v-if="isCurrentUser"
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
label="Password"
type="password"
hint="Password muss immer eingetragen werden"
:rules="[notEmpty]"
v-model="password"
filled
/>
<q-input
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
label="Neues Password"
type="password"
v-model="new_password"
filled
/>
<q-input
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
label="Wiederhole neues Password"
type="password"
:disable="new_password.length == 0"
:rules="[samePassword]"
v-model="new_password2"
filled
/>
</q-card-section>
<q-card-actions align="right">
<q-btn label="Reset" type="reset" />
<q-btn color="primary" type="submit" label="Speichern" />
</q-card-actions>
</q-form>
</template>
<script lang="ts">
import {
computed,
defineComponent,
ref,
onBeforeMount
} from '@vue/composition-api';
import { Store } from 'vuex';
import { StateInterface } from 'src/store';
import { hasPermission } from 'src/components/permission';
interface Props {
user?: FG.User;
}
export default defineComponent({
name: 'MainUserSettings',
props: ['user'],
setup(props: Props, { root }) {
const store = <Store<StateInterface>>root.$store;
onBeforeMount(() => {
store.dispatch('user/getRoles', false).catch(error => {
console.warn(error);
});
});
const isCurrentUser = computed(
() => props.user?.userid === store.state.user.currentUser?.userid
);
const canSetRoles = computed(() => hasPermission('users_set_roles', store));
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 allRoles = computed(() =>
store.state.user.roles.map(role => role.name)
);
const password = ref('');
const new_password = ref('');
const new_password2 = ref('');
function save() {
let changed = <FG.User>props.user;
changed = Object.assign(changed, {
password: password.value
});
if (new_password.value != '') {
changed = Object.assign(changed, {
new_password: new_password.value
});
}
store.dispatch('user/updateUser', changed).catch(error => {
console.warn(error);
});
}
function reset() {
props.user = oldUser.value;
password.value = '';
new_password.value = '';
new_password2.value = '';
}
function samePassword(val: string) {
return val == new_password.value || 'Passwörter sind nicht identisch!';
}
function notEmpty(val: string) {
return !!val || 'Feld darf nicht leer sein!';
}
function isEmail(val: string | null) {
return (
!val ||
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w\w+)+$/.test(val) ||
'E-Mail ist nicht valide.'
);
}
const loading = computed(() => store.state.user.loading > 0);
return {
props,
allRoles,
canSetRoles,
password,
new_password,
new_password2,
samePassword,
isCurrentUser,
isEmail,
notEmpty,
save,
reset,
loading
};
}
});
</script>

View File

@ -0,0 +1,55 @@
<template>
<div>
<q-page
padding
class="fit row justify-center content-center items-center q-gutter-sm"
>
<q-card class="col-12">
<q-card-section
class="fit row justify-start content-center items-center"
>
<div class="col-xs-12 col-sm-6 text-center text-h6">
Benutzereinstellungen
</div>
<div class="col-xs-12 col-sm-6 q-pa-sm">
<UserSelector :user="user" @update:user="userUpdated" />
</div>
</q-card-section>
<MainUserSettings :user="user" />
</q-card>
</q-page>
</div>
</template>
<script lang="ts">
import { defineComponent, onBeforeMount, ref } from '@vue/composition-api';
import UserSelector from '../components/UserSelector.vue';
import MainUserSettings from '../components/settings/MainUserSettings.vue';
import { Store } from 'vuex';
import { StateInterface } from 'src/store';
export default defineComponent({
name: 'AdminSettings',
components: { UserSelector, MainUserSettings },
setup(_, { root }) {
const store = <Store<StateInterface>>root.$store;
onBeforeMount(() => {
store.dispatch('user/getUsers').catch(error => console.warn(error));
});
const user = ref(<FG.User>store.state.user.currentUser);
// can be dropped with VUE3
const userUpdated = (value: FG.User) => {
user.value = value;
console.log(value);
};
return {
user,
userUpdated
};
}
});
</script>

View File

@ -3,42 +3,42 @@
<q-page <q-page
padding padding
class="fit row justify-center content-center items-center q-gutter-sm" class="fit row justify-center content-center items-center q-gutter-sm"
>
<div
class="fit row justify-center content-center items-center q-gutter-sm"
> >
<circular-progress v-if="sessionsLoading" /> <circular-progress v-if="sessionsLoading" />
<div class="col-12 text-left text-h6"> <q-card class="col-12">
Allgemeine Einstellungen: <q-card-section
</div> class="fit row justify-start content-center items-center"
<Main /> >
<div class="col-12 text-left text-h6"> <div class="col-12 text-center text-h6">Benutzereinstellungen</div>
Aktive Sessions: </q-card-section>
</div> <MainUserSettings :user="currentUser" />
</q-card>
<div class="col-12 text-left text-h6">Aktive Sessions:</div>
<sessions <sessions
v-for="(session, index) in sessions" v-for="(session, index) in sessions"
:key="'session' + index" :key="'session' + index"
:session="session" :session="session"
/> />
</div>
<div class="row">
<q-btn label="show sessions" @click="showRootGetters" />
</div>
</q-page> </q-page>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, onBeforeMount } from '@vue/composition-api'; import {
computed,
defineComponent,
onBeforeMount,
ref
} from '@vue/composition-api';
import CircularProgress from 'components/loading/CircularProgress.vue'; import CircularProgress from 'components/loading/CircularProgress.vue';
import Sessions from '../components/settings/Sessions.vue'; import Sessions from '../components/settings/Sessions.vue';
import Main from '../components/settings/Main.vue'; import MainUserSettings from '../components/settings/MainUserSettings.vue';
import { Store } from 'vuex'; import { Store } from 'vuex';
import { StateInterface } from 'src/store'; import { StateInterface } from 'src/store';
export default defineComponent({ export default defineComponent({
// name: 'PageName' // name: 'PageName'
components: { CircularProgress, Sessions, Main }, components: { CircularProgress, Sessions, MainUserSettings },
setup(_, { root }) { setup(_, { root }) {
const store = <Store<StateInterface>>root.$store; const store = <Store<StateInterface>>root.$store;
@ -47,21 +47,13 @@ export default defineComponent({
console.warn(error); console.warn(error);
}); });
}); });
const currentUser = ref(<FG.User>store.state.user.currentUser);
const sessions = computed(() => store.state.session.sessions); const sessions = computed(() => store.state.session.sessions);
const sessionsLoading = computed(() => store.state.session.loading);
function showRootGetters() {
console.log(sessions.value);
}
const sessionsLoading = computed(
() =>
store.state.session.loading ||
store.state.user.getUserLoading ||
store.state.user.updateUserLoading
);
return { return {
showRootGetters, currentUser,
sessionsLoading, sessionsLoading,
sessions sessions
}; };

View File

@ -25,6 +25,15 @@ const mainRoutes: FG_Plugin.PluginRouteConfig[] = [
shortcut: true, shortcut: true,
meta: { permissions: ['user'] }, meta: { permissions: ['user'] },
component: () => import('../pages/Settings.vue') component: () => import('../pages/Settings.vue')
},
{
title: 'Admin',
icon: 'mdi-cog',
path: 'admin',
name: 'admin-settings',
shortcut: false,
meta: { permissions: ['users_edit_other'] },
component: () => import('../pages/AdminSettings.vue')
} }
] ]
} }

View File

@ -2,7 +2,7 @@ import { Module, MutationTree, ActionTree, GetterTree } from 'vuex';
import { LoginData, LoginResponse } from 'src/plugins/user/models'; import { LoginData, LoginResponse } from 'src/plugins/user/models';
import { StateInterface } from 'src/store'; import { StateInterface } from 'src/store';
import { axios } from 'src/boot/axios'; import { axios } from 'src/boot/axios';
import { AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import { Router } from 'src/router'; import { Router } from 'src/router';
import { LocalStorage, Loading } from 'quasar'; import { LocalStorage, Loading } from 'quasar';
@ -12,10 +12,15 @@ export interface SessionInterface {
loading: boolean; loading: boolean;
} }
function loadFromLocal() {
const session = LocalStorage.getItem<FG.Session>('currentSession');
if (session) session.expires = new Date(session.expires);
return session;
}
const state: SessionInterface = { const state: SessionInterface = {
sessions: [], sessions: [],
currentSession: currentSession: loadFromLocal() || undefined,
LocalStorage.getItem<FG.Session>('currentSession') || undefined,
loading: false loading: false
}; };
@ -47,12 +52,13 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
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('user/setCurrentUser', response.data.user, { root: true });
void Router.push({ name: 'user-main' }); commit('user/setCurrentPermissions', response.data.permissions, {
}) root: true
.catch(error => { });
console.exception(error);
}) })
.catch(error => console.warn(error))
.finally(() => { .finally(() => {
void Router.push({ name: 'user-main' });
Loading.hide(); Loading.hide();
}); });
}, },
@ -70,7 +76,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
/** /**
* Delete a given session * Delete a given session
*/ */
deleteSession({ commit, rootState }, token: string | null) { deleteSession({ commit, dispatch, rootState }, token: string | null) {
if (token === null) return; if (token === null) return;
commit('setLoading', true); commit('setLoading', true);
@ -78,17 +84,23 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
.delete(`/auth/${token}`) .delete(`/auth/${token}`)
.then(() => { .then(() => {
if (token === rootState.session.currentSession?.token) { if (token === rootState.session.currentSession?.token) {
commit('clearCurrentSession'); void dispatch('clearup');
commit('user/clearCurrentUser', null, { root: true });
void Router.push({ name: 'login' });
} else { } else {
commit('getSessions'); commit('getSessions');
} }
}) })
.catch((error: AxiosError) => {
if (!error.response || error.response.status != 401) throw error;
})
.finally(() => { .finally(() => {
commit('setLoading', false); commit('setLoading', false);
}); });
}, },
clearup({ commit }) {
commit('clearCurrentSession');
commit('user/clearCurrentUser', null, { root: true });
void Router.push({ name: 'login' });
},
/** /**
* Get all sessions from current User * Get all sessions from current User
*/ */

View File

@ -3,19 +3,25 @@ import { StateInterface } from 'src/store';
import { axios } from 'boot/axios'; import { axios } from 'boot/axios';
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import { SessionStorage } from 'quasar'; import { SessionStorage } from 'quasar';
import { CurrentUserResponse } from 'src/plugins/user/models';
export interface UserStateInterface { export interface UserStateInterface {
updateUserLoading: boolean;
getUserLoading: boolean;
currentUser?: FG.User; currentUser?: FG.User;
currentPermissions: FG.Permission[];
users: FG.User[]; users: FG.User[];
roles: FG.Role[];
permissions: FG.Permission[];
loading: number;
} }
const state: UserStateInterface = { const state: UserStateInterface = {
users: [], users: [],
roles: [],
permissions: [],
currentUser: SessionStorage.getItem<FG.User>('currentUser') || undefined, currentUser: SessionStorage.getItem<FG.User>('currentUser') || undefined,
updateUserLoading: false, currentPermissions:
getUserLoading: false SessionStorage.getItem<FG.Permission[]>('currentPermissions') || [],
loading: 0
}; };
const mutations: MutationTree<UserStateInterface> = { const mutations: MutationTree<UserStateInterface> = {
@ -23,35 +29,46 @@ const mutations: MutationTree<UserStateInterface> = {
SessionStorage.set('currentUser', data); SessionStorage.set('currentUser', data);
state.currentUser = data; state.currentUser = data;
}, },
setCurrentPermissions(state, data: FG.Permission[]) {
SessionStorage.set('currentPermissions', data);
state.currentPermissions = data;
},
clearCurrentUser(state) { clearCurrentUser(state) {
SessionStorage.remove('currentUser'); SessionStorage.remove('currentUser');
SessionStorage.remove('currentPermissions');
state.currentUser = undefined; state.currentUser = undefined;
state.currentPermissions = [];
}, },
setUsers(state, data: FG.User[]) { setUsers(state, data: FG.User[]) {
state.users = data; state.users = data;
}, },
setLoading( setRoles(state, data: FG.Role[]) {
state, state.roles = data;
data: { key: 'updateUserLoading' | 'getUserLoading'; data: boolean } },
) { setPermissions(state, data: FG.Permission[]) {
state[data.key] = data.data; state.permissions = data;
},
setLoading(state, data = true) {
if (data) state.loading += 1;
else state.loading -= 1;
} }
}; };
const actions: ActionTree<UserStateInterface, StateInterface> = { const actions: ActionTree<UserStateInterface, StateInterface> = {
getCurrentUser({ commit, rootState }) { getCurrentUser({ commit, rootState }) {
if (rootState.session.currentSession) { if (rootState.session.currentSession) {
commit('setLoading', { key: 'getUserLoading', data: true }); commit('setLoading');
axios axios
.get(`/users/${rootState.session.currentSession.userid}`) .get(`/users/${rootState.session.currentSession.userid}`)
.then((response: AxiosResponse<FG.User>) => { .then((response: AxiosResponse<CurrentUserResponse>) => {
commit('setCurrentUser', response.data); commit('setCurrentUser', response.data);
commit('setCurrentPermissions', response.data.permissions);
}) })
.catch(err => { .catch(err => {
console.warn(err); console.warn(err);
}) })
.finally(() => { .finally(() => {
commit('setLoading', { key: 'getUserLoading', data: false }); commit('setLoading', false);
}); });
} else { } else {
console.debug('User not logged in, can not get current_user.'); console.debug('User not logged in, can not get current_user.');
@ -60,7 +77,7 @@ const actions: ActionTree<UserStateInterface, StateInterface> = {
getUsers({ commit }) { getUsers({ commit }) {
axios axios
.get(`/users`) .get('/users')
.then((response: AxiosResponse<FG.User[]>) => { .then((response: AxiosResponse<FG.User[]>) => {
commit('setUsers', response.data); commit('setUsers', response.data);
}) })
@ -69,20 +86,43 @@ const actions: ActionTree<UserStateInterface, StateInterface> = {
}); });
}, },
updateUser({ commit, state, dispatch }, data) { updateUser({ commit, state, dispatch }, data: FG.User) {
commit('setLoading', { key: 'updateUserLoading', data: true }); commit('setLoading');
if (!state.currentUser) throw 'Not logged in';
axios axios
.put(`/users/${state.currentUser.userid}`, data) .put(`/users/${data.userid}`, data)
.then(() => { .then(() => {
if (state.currentUser && state.currentUser.userid === data.userid)
void dispatch('getCurrentUser'); void dispatch('getCurrentUser');
else void dispatch('getUsers');
}) })
.catch(error => { .catch(error => {
console.log(error); console.log(error);
}) })
.finally(() => { .finally(() => {
commit('setLoading', { key: 'updateUserLoading', data: false }); commit('setLoading', false);
}); });
},
getRoles({ commit, state }, force = true) {
if (!force && state.roles.length > 0) return;
commit('setLoading');
axios
.get('/roles')
.then((response: AxiosResponse<FG.Role[]>) => {
commit('setRoles', response.data);
})
.finally(() => commit('setLoading', false));
},
getPermissions({ commit, state }, force = true) {
if (!force && state.permissions.length > 0) return;
commit('setLoading');
axios
.get('/roles')
.then((response: AxiosResponse<FG.Permission[]>) => {
commit('setPermissions', response.data);
})
.finally(() => commit('setLoading', false));
} }
}; };
@ -93,20 +133,11 @@ const getters: GetterTree<UserStateInterface, StateInterface> = {
users({ users }) { users({ users }) {
return users; return users;
}, },
loading({ updateUserLoading, getUserLoading }) { loading({ loading }) {
return updateUserLoading || getUserLoading; return loading > 0;
}, },
displayName({ currentUser }) { displayName({ currentUser }) {
return currentUser?.display_name; return currentUser?.display_name;
},
permissions({ currentUser }) {
let permissions: string[] = [];
if (currentUser) {
currentUser.roles.forEach(role => {
permissions = permissions.concat(role.permissions);
});
}
return permissions;
} }
}; };