release v2.0.0 #4

Merged
crimsen merged 481 commits from develop into master 2024-01-18 15:15:08 +00:00
8 changed files with 233 additions and 14 deletions
Showing only changes of commit 8c1dffc003 - Show all commits

View File

@ -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
}, },

View File

@ -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"
} }

View File

@ -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>;
} }

View File

@ -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
}; };

View File

@ -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>

View File

@ -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
}; };
} }
}); });

View File

@ -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'
};

View File

@ -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();
} }
}; };