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