<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/*" hint="Bilddateien nur JPEG" :clearable="avatar.name !== '' || deleteAvatar" @update:model-value="imagePreview" @clear="clear" @rejected="onAvatarRejected" > <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" /> </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'; import { hasPermission, notEmpty, isEmail, useMainStore, useUserStore, api, } from '@flaschengeist/api'; 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(''); const avatar = ref<File>(new File([], '', {})); const avatarError = ref(false); 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); const _deleteAvatar = ref(false); const deleteAvatar = computed({ get: () => _deleteAvatar.value, set: (val: boolean) => (_deleteAvatar.value = val), }); /* Reset model if props changed */ watchEffect(() => { if (props.user.userid && props.user.userid !== userModel.value.userid) reset(); }); 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' }], }); avatar.value = new File([], '', {}); } async function save() { let changed: FG.User = userModel.value; 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); if (avatar.value instanceof File && avatar.value.name) await userStore .uploadAvatar(changed, avatar.value instanceof File ? avatar.value : avatar.value[0]) .catch((response: Response) => { if (response && response.status == 400) { onAvatarRejected(); } }); if (deleteAvatar.value) { await userStore.deleteAvatar(changed); } password.value = ''; } function reset() { userModel.value = Object.assign({}, props.user); password.value = ''; newPassword.value = ''; imgsrc.value = undefined; deleteAvatar.value = false; avatarError.value = false; } function isFreeUID(val: string) { return ( userStore.users.findIndex((user) => user.userid === val) === -1 || 'Benutzername ist schon vergeben' ); } 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; } return { allRoles, avatar, canSetRoles, isCurrentUser, isEmail, isFreeUID, newPassword, notEmpty, onAvatarRejected, password, reset, save, userModel, image, imagePreview, clear, deleteAvatar, avatarError, toDeleteAvatar, error, }; }, }); </script>