211 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Vue
		
	
	
	
		
		
			
		
	
	
			211 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Vue
		
	
	
	
|  | <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/*" | ||
|  |         max-file-size="204800" | ||
|  |         hint="Bilddateien, max. 200 KiB" | ||
|  |         @rejected="onAvatarRejected" | ||
|  |       > | ||
|  |         <template #append> | ||
|  |           <q-icon name="mdi-file-image" @click.stop /> | ||
|  |         </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 } 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 | FileList | string[]>(); | ||
|  |     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); | ||
|  | 
 | ||
|  |     /* 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 = undefined; | ||
|  |     } | ||
|  | 
 | ||
|  |     function save() { | ||
|  |       let changed = 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) | ||
|  |         userStore | ||
|  |           .uploadAvatar(changed, avatar.value instanceof File ? avatar.value : avatar.value[0]) | ||
|  |           .catch((response: Response) => { | ||
|  |             if (response && response.status == 400) { | ||
|  |               onAvatarRejected(); | ||
|  |             } | ||
|  |           }); | ||
|  |     } | ||
|  | 
 | ||
|  |     function reset() { | ||
|  |       userModel.value = Object.assign({}, props.user); | ||
|  |       password.value = ''; | ||
|  |       newPassword.value = ''; | ||
|  |     } | ||
|  | 
 | ||
|  |     function isFreeUID(val: string) { | ||
|  |       return ( | ||
|  |         userStore.users.findIndex((user) => user.userid === val) === -1 || | ||
|  |         'Benutzername ist schon vergeben' | ||
|  |       ); | ||
|  |     } | ||
|  | 
 | ||
|  |     return { | ||
|  |       allRoles, | ||
|  |       avatar, | ||
|  |       canSetRoles, | ||
|  |       isCurrentUser, | ||
|  |       isEmail, | ||
|  |       isFreeUID, | ||
|  |       newPassword, | ||
|  |       notEmpty, | ||
|  |       onAvatarRejected, | ||
|  |       password, | ||
|  |       reset, | ||
|  |       save, | ||
|  |       userModel, | ||
|  |     }; | ||
|  |   }, | ||
|  | }); | ||
|  | </script> |