release v2.0.0 #4
|
@ -16,7 +16,7 @@ module.exports = {
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
project: resolve(__dirname, './tsconfig.json'),
|
project: resolve(__dirname, './tsconfig.json'),
|
||||||
tsconfigRootDir: __dirname,
|
tsconfigRootDir: __dirname,
|
||||||
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
|
ecmaVersion: 2019, // Allows for the parsing of modern ECMAScript features
|
||||||
sourceType: 'module' // Allows for the use of imports
|
sourceType: 'module' // Allows for the use of imports
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -11,5 +11,6 @@
|
||||||
"typescript.format.placeOpenBraceOnNewLineForControlBlocks": false,
|
"typescript.format.placeOpenBraceOnNewLineForControlBlocks": false,
|
||||||
"typescript.format.placeOpenBraceOnNewLineForFunctions": false,
|
"typescript.format.placeOpenBraceOnNewLineForFunctions": false,
|
||||||
"vetur.format.defaultFormatter.html": "js-beautify-html",
|
"vetur.format.defaultFormatter.html": "js-beautify-html",
|
||||||
"vetur.format.defaultFormatter.js": "vscode-typescript"
|
"vetur.format.defaultFormatter.js": "esbenp.prettier-vscode",
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
}
|
}
|
|
@ -17,6 +17,7 @@ declare namespace FG {
|
||||||
}
|
}
|
||||||
type Permission = string;
|
type Permission = string;
|
||||||
interface Role {
|
interface Role {
|
||||||
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
permissions: Array<Permission>;
|
permissions: Array<Permission>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, ref } 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';
|
||||||
|
|
||||||
|
@ -26,13 +26,11 @@ export default defineComponent({
|
||||||
setup(props: Props, { root, emit }) {
|
setup(props: Props, { root, emit }) {
|
||||||
const store = <Store<StateInterface>>root.$store;
|
const store = <Store<StateInterface>>root.$store;
|
||||||
const users = computed(() => store.state.user.users);
|
const users = computed(() => store.state.user.users);
|
||||||
const user = ref(props.user);
|
|
||||||
const updated = (value: FG.User) => {
|
const updated = (value: FG.User) => {
|
||||||
emit('update:user', value);
|
emit('update:user', value);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
|
||||||
updated,
|
updated,
|
||||||
users
|
users
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
<template>
|
||||||
|
<q-card class="col-12">
|
||||||
|
<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">
|
||||||
|
<span class="col-xs-12 col-sm-6 text-center text-h6">
|
||||||
|
Rollen und Berechtigungen
|
||||||
|
</span>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
use-input
|
||||||
|
label="Rolle"
|
||||||
|
input-debounce="0"
|
||||||
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
|
:value="role"
|
||||||
|
:options="roles"
|
||||||
|
option-label="name"
|
||||||
|
option-value="name"
|
||||||
|
map-options
|
||||||
|
@new-value="createRole"
|
||||||
|
@input="updateRole"
|
||||||
|
/>
|
||||||
|
</q-card-section>
|
||||||
|
<q-separator />
|
||||||
|
<q-card-section
|
||||||
|
v-if="role"
|
||||||
|
class="fit row justify-start content-center items-center"
|
||||||
|
>
|
||||||
|
<q-scroll-area style="height: 20em; width: 100%;">
|
||||||
|
<q-option-group
|
||||||
|
:value="role.permissions"
|
||||||
|
@input="updatePermissions"
|
||||||
|
:options="permissions"
|
||||||
|
color="primary"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
</q-scroll-area>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-actions v-if="role" align="right">
|
||||||
|
<q-btn label="Löschen" color="negative" @click="remove" />
|
||||||
|
<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,
|
||||||
|
onBeforeMount
|
||||||
|
} from '@vue/composition-api';
|
||||||
|
import { Store } from 'vuex';
|
||||||
|
import { StateInterface } from 'src/store';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'RoleSettings',
|
||||||
|
setup(_, { root }) {
|
||||||
|
const store = <Store<StateInterface>>root.$store;
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
store.dispatch('user/getRoles').catch(error => {
|
||||||
|
console.warn(error);
|
||||||
|
});
|
||||||
|
store.dispatch('user/getPermissions').catch(error => {
|
||||||
|
console.warn(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const loading = computed(() => store.state.user.loading > 0);
|
||||||
|
const role = ref<FG.Role | null>(null);
|
||||||
|
const roles = computed(() => store.state.user.roles);
|
||||||
|
const permissions = computed(() =>
|
||||||
|
store.state.user.permissions.map(perm => {
|
||||||
|
return {
|
||||||
|
value: perm,
|
||||||
|
label: perm
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
function createRole(
|
||||||
|
name: string,
|
||||||
|
done: (arg0: string, arg1: string) => void
|
||||||
|
): void {
|
||||||
|
role.value = { name: name, permissions: [], id: -1 };
|
||||||
|
done(name, 'add-unique');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePermissions(permissions: string[]) {
|
||||||
|
if (role.value) {
|
||||||
|
role.value.permissions = permissions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRole(rl: FG.Role | string) {
|
||||||
|
if (typeof rl === 'string') return;
|
||||||
|
role.value = {
|
||||||
|
id: rl.id,
|
||||||
|
name: rl.name,
|
||||||
|
permissions: Array.from(rl.permissions)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
if (role.value) {
|
||||||
|
if (role.value.id === -1)
|
||||||
|
void store.dispatch('user/newRole', role.value);
|
||||||
|
else void store.dispatch('user/updateRole', role.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
if (role.value && role.value.id !== -1) {
|
||||||
|
const original = roles.value.find(
|
||||||
|
value => value.name === role.value?.name
|
||||||
|
);
|
||||||
|
if (original) updateRole(original);
|
||||||
|
} else {
|
||||||
|
role.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove() {
|
||||||
|
if (role.value) {
|
||||||
|
if (role.value.id === -1) {
|
||||||
|
role.value = null;
|
||||||
|
} else {
|
||||||
|
store
|
||||||
|
.dispatch('user/deleteRole', role.value)
|
||||||
|
.then(() => (role.value = null))
|
||||||
|
.catch(error => console.warn(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
roles,
|
||||||
|
role,
|
||||||
|
createRole,
|
||||||
|
permissions,
|
||||||
|
updateRole,
|
||||||
|
updatePermissions,
|
||||||
|
save,
|
||||||
|
reset,
|
||||||
|
remove,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -17,20 +17,29 @@
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<MainUserSettings :user="user" />
|
<MainUserSettings :user="user" />
|
||||||
</q-card>
|
</q-card>
|
||||||
|
<RoleSettings v-if="canEditRoles" />
|
||||||
</q-page>
|
</q-page>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onBeforeMount, ref } from '@vue/composition-api';
|
import {
|
||||||
|
computed,
|
||||||
|
defineComponent,
|
||||||
|
onBeforeMount,
|
||||||
|
ref
|
||||||
|
} from '@vue/composition-api';
|
||||||
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';
|
||||||
|
import RoleSettings from '../components/settings/RoleSettings.vue';
|
||||||
import { Store } from 'vuex';
|
import { Store } from 'vuex';
|
||||||
import { StateInterface } from 'src/store';
|
import { StateInterface } from 'src/store';
|
||||||
|
import { hasPermission } from 'src/components/permission';
|
||||||
|
import { PERMISSIONS } from '../permissions';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'AdminSettings',
|
name: 'AdminSettings',
|
||||||
components: { UserSelector, MainUserSettings },
|
components: { UserSelector, MainUserSettings, RoleSettings },
|
||||||
setup(_, { root }) {
|
setup(_, { root }) {
|
||||||
const store = <Store<StateInterface>>root.$store;
|
const store = <Store<StateInterface>>root.$store;
|
||||||
|
|
||||||
|
@ -38,6 +47,9 @@ export default defineComponent({
|
||||||
store.dispatch('user/getUsers').catch(error => console.warn(error));
|
store.dispatch('user/getUsers').catch(error => console.warn(error));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const canEditRoles = computed(() =>
|
||||||
|
hasPermission(PERMISSIONS.ROLES_EDIT, store)
|
||||||
|
);
|
||||||
const user = ref(<FG.User>store.state.user.currentUser);
|
const user = ref(<FG.User>store.state.user.currentUser);
|
||||||
|
|
||||||
// can be dropped with VUE3
|
// can be dropped with VUE3
|
||||||
|
@ -48,7 +60,8 @@ export default defineComponent({
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
userUpdated
|
userUpdated,
|
||||||
|
canEditRoles
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
export const PERMISSIONS = {
|
||||||
|
// Kann andere Nutzer bearbeiten
|
||||||
|
EDIT_OTHER: 'users_edit_other',
|
||||||
|
// Kann Rollen von Nutzern setzen
|
||||||
|
SET_ROLES: 'users_set_roles',
|
||||||
|
// Kann Nutzer löschen
|
||||||
|
DELETE: 'users_delete_other',
|
||||||
|
// Kann neue Nutzer hinzufügen
|
||||||
|
REGISTER: 'users_register',
|
||||||
|
// Kann Rollen löschen oder bearbeiten, z.b. Rechte hinzufügen etc
|
||||||
|
ROLES_EDIT: 'roles_edit'
|
||||||
|
};
|
|
@ -103,7 +103,7 @@ const actions: ActionTree<UserStateInterface, StateInterface> = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getRoles({ commit, state }, force = true) {
|
getRoles({ commit, state }, force = false) {
|
||||||
if (!force && state.roles.length > 0) return;
|
if (!force && state.roles.length > 0) return;
|
||||||
commit('setLoading');
|
commit('setLoading');
|
||||||
axios
|
axios
|
||||||
|
@ -114,12 +114,50 @@ const actions: ActionTree<UserStateInterface, StateInterface> = {
|
||||||
.finally(() => commit('setLoading', false));
|
.finally(() => commit('setLoading', false));
|
||||||
},
|
},
|
||||||
|
|
||||||
getPermissions({ commit, state }, force = true) {
|
updateRole({ commit, state }, data: FG.Role) {
|
||||||
if (!force && state.permissions.length > 0) return;
|
|
||||||
commit('setLoading');
|
commit('setLoading');
|
||||||
axios
|
axios
|
||||||
.get('/roles')
|
.put(`/roles/${data.id}`, data)
|
||||||
.then((response: AxiosResponse<FG.Permission[]>) => {
|
.then(() => {
|
||||||
|
const idx = state.roles.findIndex(role => role.id === data.id);
|
||||||
|
if (idx) {
|
||||||
|
state.roles[idx].name = data.name;
|
||||||
|
state.roles[idx].permissions = data.permissions;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
commit('setLoading', false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
newRole({ commit }, data: FG.Role) {
|
||||||
|
commit('setLoading');
|
||||||
|
axios
|
||||||
|
.post('/roles', data)
|
||||||
|
.then(() => commit('getRoles'))
|
||||||
|
.finally(() => {
|
||||||
|
commit('setLoading', false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteRole({ commit, state }, data: FG.Role) {
|
||||||
|
commit('setLoading');
|
||||||
|
axios
|
||||||
|
.delete(`/roles/${data.id}`)
|
||||||
|
.then(() => {
|
||||||
|
state.roles = state.roles.filter(value => value.id !== data.id);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
commit('setLoading', false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getPermissions({ commit, state }, force = false) {
|
||||||
|
if (!force && state.roles.length > 0) return;
|
||||||
|
commit('setLoading');
|
||||||
|
axios
|
||||||
|
.get('/roles/permissions')
|
||||||
|
.then((response: AxiosResponse<FG.Role[]>) => {
|
||||||
commit('setPermissions', response.data);
|
commit('setPermissions', response.data);
|
||||||
})
|
})
|
||||||
.finally(() => commit('setLoading', false));
|
.finally(() => commit('setLoading', false));
|
||||||
|
@ -138,6 +176,9 @@ const getters: GetterTree<UserStateInterface, StateInterface> = {
|
||||||
},
|
},
|
||||||
displayName({ currentUser }) {
|
displayName({ currentUser }) {
|
||||||
return currentUser?.display_name;
|
return currentUser?.display_name;
|
||||||
|
},
|
||||||
|
roles({ roles }): string[] {
|
||||||
|
return roles.map(role => role.name).flat();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue