2021-05-25 13:58:01 +00:00
|
|
|
<template>
|
|
|
|
<q-form @submit="save" @reset="reset">
|
|
|
|
<q-card-section class="fit row justify-start content-center items-center">
|
|
|
|
<q-input
|
|
|
|
v-model="userModel.firstname"
|
|
|
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
|
|
|
label="Vorname"
|
|
|
|
:rules="[notEmpty]"
|
|
|
|
autocomplete="given-name"
|
|
|
|
filled
|
|
|
|
/>
|
|
|
|
<q-input
|
|
|
|
v-model="userModel.lastname"
|
|
|
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
|
|
|
label="Nachname"
|
|
|
|
:rules="[notEmpty]"
|
|
|
|
autocomplete="family-name"
|
|
|
|
filled
|
|
|
|
/>
|
|
|
|
<q-input
|
|
|
|
v-model="userModel.display_name"
|
|
|
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
|
|
|
label="Angezeigter Name"
|
|
|
|
:rules="[notEmpty]"
|
|
|
|
autocomplete="nickname"
|
|
|
|
filled
|
|
|
|
/>
|
|
|
|
<q-input
|
|
|
|
v-model="userModel.mail"
|
|
|
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
|
|
|
label="E-Mail"
|
|
|
|
:rules="[isEmail, notEmpty]"
|
|
|
|
autocomplete="email"
|
|
|
|
filled
|
|
|
|
/>
|
|
|
|
<q-input
|
|
|
|
v-model="userModel.userid"
|
|
|
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
|
|
|
label="Benutzername"
|
|
|
|
:readonly="!newUser"
|
|
|
|
:rules="newUser ? [isFreeUID, notEmpty] : []"
|
|
|
|
autocomplete="username"
|
|
|
|
filled
|
|
|
|
/>
|
|
|
|
<q-select
|
|
|
|
v-model="userModel.roles"
|
|
|
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
|
|
|
label="Rollen"
|
|
|
|
filled
|
|
|
|
multiple
|
|
|
|
use-chips
|
|
|
|
:readonly="!canSetRoles"
|
|
|
|
:options="allRoles"
|
|
|
|
option-label="name"
|
|
|
|
option-value="name"
|
|
|
|
/>
|
|
|
|
<IsoDateInput
|
|
|
|
v-model="userModel.birthday"
|
|
|
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
|
|
|
label="Geburtstag"
|
|
|
|
autocomplete="bday"
|
|
|
|
/>
|
|
|
|
<q-file
|
|
|
|
v-model="avatar"
|
|
|
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
|
|
|
filled
|
|
|
|
label="Avatar"
|
|
|
|
accept=".jpg, image/*"
|
2021-11-20 22:01:28 +00:00
|
|
|
hint="Bilddateien nur JPEG"
|
|
|
|
:clearable="avatar.name !== '' || deleteAvatar"
|
|
|
|
@update:model-value="imagePreview"
|
|
|
|
@clear="clear"
|
2021-05-25 13:58:01 +00:00
|
|
|
@rejected="onAvatarRejected"
|
|
|
|
>
|
2021-11-20 22:01:28 +00:00
|
|
|
<template #file="{ file }">
|
|
|
|
<div class="full-width row justify-center">
|
|
|
|
<q-avatar size="265px">
|
|
|
|
<q-img v-if="!avatarError || file.name !== ''" :src="image" @error="error" />
|
|
|
|
<q-img v-else src="no-image.svg" />
|
|
|
|
</q-avatar>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<template #append v-if="!avatarError && !deleteAvatar">
|
|
|
|
<q-btn round color="negative" icon="mdi-trash-can" size="xs" @click="toDeleteAvatar" />
|
2021-05-25 13:58:01 +00:00
|
|
|
</template>
|
|
|
|
</q-file>
|
|
|
|
</q-card-section>
|
|
|
|
<q-separator v-if="!newUser" />
|
|
|
|
<q-card-section v-if="!newUser" class="fit row justify-start content-center items-center">
|
|
|
|
<PasswordInput
|
|
|
|
v-if="isCurrentUser"
|
|
|
|
v-model="password"
|
|
|
|
:rules="[notEmpty]"
|
|
|
|
filled
|
|
|
|
label="Passwort"
|
|
|
|
autocomplete="current-password"
|
|
|
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
|
|
|
hint="Passwort muss immer eingetragen werden"
|
|
|
|
/>
|
|
|
|
<PasswordInput
|
|
|
|
v-model="newPassword"
|
|
|
|
filled
|
|
|
|
label="Neues Password"
|
|
|
|
autocomplete="new-password"
|
|
|
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
|
|
|
/>
|
|
|
|
</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 { Notify } from 'quasar';
|
|
|
|
import { IsoDateInput, PasswordInput } from '@flaschengeist/api/components';
|
|
|
|
import { defineComponent, computed, ref, onBeforeMount, PropType, watchEffect } from 'vue';
|
2021-11-20 22:01:28 +00:00
|
|
|
import {
|
|
|
|
hasPermission,
|
|
|
|
notEmpty,
|
|
|
|
isEmail,
|
|
|
|
useMainStore,
|
|
|
|
useUserStore,
|
|
|
|
api,
|
|
|
|
} from '@flaschengeist/api';
|
2021-05-25 13:58:01 +00:00
|
|
|
|
|
|
|
export default defineComponent({
|
|
|
|
name: 'MainUserSettings',
|
|
|
|
components: { IsoDateInput, PasswordInput },
|
|
|
|
props: {
|
|
|
|
user: {
|
|
|
|
required: true,
|
|
|
|
type: Object as PropType<FG.User>,
|
|
|
|
},
|
|
|
|
newUser: { type: Boolean, default: false },
|
|
|
|
},
|
|
|
|
emits: {
|
|
|
|
'update:user': (payload: FG.User) => !!payload,
|
|
|
|
},
|
|
|
|
setup(props, { emit }) {
|
|
|
|
const userStore = useUserStore();
|
|
|
|
const mainStore = useMainStore();
|
|
|
|
|
|
|
|
onBeforeMount(() => {
|
|
|
|
void userStore.getRoles(false);
|
|
|
|
});
|
|
|
|
|
|
|
|
const password = ref('');
|
|
|
|
const newPassword = ref('');
|
2021-11-20 22:01:28 +00:00
|
|
|
const avatar = ref<File>(new File([], '', {}));
|
|
|
|
const avatarError = ref(false);
|
2021-05-25 13:58:01 +00:00
|
|
|
const userModel = ref(Object.assign({}, props.user));
|
|
|
|
|
|
|
|
const canSetRoles = computed(() => hasPermission('users_set_roles'));
|
|
|
|
const allRoles = computed(() => userStore.roles.map((role) => role.name));
|
|
|
|
const isCurrentUser = computed(() => userModel.value.userid === mainStore.currentUser.userid);
|
|
|
|
|
2021-11-20 22:01:28 +00:00
|
|
|
const _deleteAvatar = ref(false);
|
|
|
|
const deleteAvatar = computed({
|
|
|
|
get: () => _deleteAvatar.value,
|
|
|
|
set: (val: boolean) => (_deleteAvatar.value = val),
|
|
|
|
});
|
|
|
|
|
2021-05-25 13:58:01 +00:00
|
|
|
/* Reset model if props changed */
|
2021-11-15 21:52:25 +00:00
|
|
|
watchEffect(() => {
|
|
|
|
if (props.user.userid && props.user.userid !== userModel.value.userid) reset();
|
|
|
|
});
|
2021-05-25 13:58:01 +00:00
|
|
|
|
|
|
|
function onAvatarRejected() {
|
|
|
|
Notify.create({
|
|
|
|
group: false,
|
|
|
|
type: 'negative',
|
|
|
|
message: 'Datei zu groß oder keine gültige Bilddatei.',
|
|
|
|
timeout: 10000,
|
|
|
|
progress: true,
|
|
|
|
actions: [{ icon: 'mdi-close', color: 'white' }],
|
|
|
|
});
|
2021-11-20 22:01:28 +00:00
|
|
|
avatar.value = new File([], '', {});
|
2021-05-25 13:58:01 +00:00
|
|
|
}
|
|
|
|
|
2021-11-20 22:01:28 +00:00
|
|
|
async function save() {
|
|
|
|
let changed: FG.User = userModel.value;
|
2021-05-25 13:58:01 +00:00
|
|
|
if (typeof changed.birthday === 'string') changed.birthday = new Date(changed.birthday);
|
|
|
|
changed = Object.assign(changed, {
|
|
|
|
password: password.value,
|
|
|
|
});
|
|
|
|
if (newPassword.value != '') {
|
|
|
|
changed = Object.assign(changed, {
|
|
|
|
new_password: newPassword.value,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
emit('update:user', changed);
|
|
|
|
|
2021-11-20 22:01:28 +00:00
|
|
|
if (avatar.value instanceof File && avatar.value.name)
|
|
|
|
await userStore
|
2021-05-25 13:58:01 +00:00
|
|
|
.uploadAvatar(changed, avatar.value instanceof File ? avatar.value : avatar.value[0])
|
|
|
|
.catch((response: Response) => {
|
|
|
|
if (response && response.status == 400) {
|
|
|
|
onAvatarRejected();
|
|
|
|
}
|
|
|
|
});
|
2021-11-20 22:01:28 +00:00
|
|
|
if (deleteAvatar.value) {
|
|
|
|
await userStore.deleteAvatar(changed);
|
|
|
|
}
|
|
|
|
password.value = '';
|
2021-05-25 13:58:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function reset() {
|
|
|
|
userModel.value = Object.assign({}, props.user);
|
|
|
|
password.value = '';
|
|
|
|
newPassword.value = '';
|
2021-11-20 22:01:28 +00:00
|
|
|
imgsrc.value = undefined;
|
|
|
|
deleteAvatar.value = false;
|
2021-05-25 13:58:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function isFreeUID(val: string) {
|
|
|
|
return (
|
|
|
|
userStore.users.findIndex((user) => user.userid === val) === -1 ||
|
|
|
|
'Benutzername ist schon vergeben'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-11-20 22:01:28 +00:00
|
|
|
const imgsrc = ref();
|
|
|
|
|
|
|
|
const image = computed(() => {
|
|
|
|
if (imgsrc.value) {
|
|
|
|
return <string>imgsrc.value;
|
|
|
|
}
|
|
|
|
if (props.user.avatar_url && !deleteAvatar.value) {
|
|
|
|
return `${api.defaults.baseURL || ''}${props.user.avatar_url}`;
|
|
|
|
}
|
|
|
|
return 'no-image.svg';
|
|
|
|
});
|
|
|
|
|
|
|
|
function imagePreview() {
|
|
|
|
if (avatar.value && avatar.value instanceof File) {
|
|
|
|
let reader = new FileReader();
|
|
|
|
|
|
|
|
reader.onload = (e) => {
|
|
|
|
imgsrc.value = e.target?.result;
|
|
|
|
};
|
|
|
|
|
|
|
|
reader.readAsDataURL(avatar.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function clear(val: File) {
|
|
|
|
avatar.value = new File([], '', {});
|
|
|
|
imgsrc.value = undefined;
|
|
|
|
deleteAvatar.value = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
function toDeleteAvatar() {
|
|
|
|
avatar.value = new File([], '', {});
|
|
|
|
imgsrc.value = undefined;
|
|
|
|
deleteAvatar.value = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function error() {
|
|
|
|
avatarError.value = true;
|
|
|
|
}
|
|
|
|
|
2021-05-25 13:58:01 +00:00
|
|
|
return {
|
|
|
|
allRoles,
|
|
|
|
avatar,
|
|
|
|
canSetRoles,
|
|
|
|
isCurrentUser,
|
|
|
|
isEmail,
|
|
|
|
isFreeUID,
|
|
|
|
newPassword,
|
|
|
|
notEmpty,
|
|
|
|
onAvatarRejected,
|
|
|
|
password,
|
|
|
|
reset,
|
|
|
|
save,
|
|
|
|
userModel,
|
2021-11-20 22:01:28 +00:00
|
|
|
image,
|
|
|
|
imagePreview,
|
|
|
|
clear,
|
|
|
|
deleteAvatar,
|
|
|
|
avatarError,
|
|
|
|
toDeleteAvatar,
|
|
|
|
error,
|
2021-05-25 13:58:01 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
});
|
|
|
|
</script>
|