Compare commits
	
		
			No commits in common. "3f4f8a5fd4a926b59f65cf9c1f20d3644e9964dc" and "94c45fe3f4336a98389f935b16cdbe6dea687f4d" have entirely different histories.
		
	
	
		
			3f4f8a5fd4
			...
			94c45fe3f4
		
	
		|  | @ -1,6 +1,6 @@ | |||
| { | ||||
|   "license": "MIT", | ||||
|   "version": "1.1.0", | ||||
|   "version": "1.0.0", | ||||
|   "name": "@flaschengeist/users", | ||||
|   "author": "Ferdinand Thiessen <rpm@fthiessen.de>", | ||||
|   "homepage": "https://flaschengeist.dev/Flaschengeist", | ||||
|  |  | |||
|  | @ -1,66 +0,0 @@ | |||
| <template> | ||||
|   <q-card> | ||||
|     <q-card-section class="text-h6"> Einstellungen Benutzer </q-card-section> | ||||
|     <q-card-section> | ||||
|       <q-select | ||||
|         v-model="displayNameMode" | ||||
|         :options="options" | ||||
|         label="Anzeige des Namens" | ||||
|         emit-value | ||||
|         map-options | ||||
|         input-debounce="0" | ||||
|         filled | ||||
|       /> | ||||
|     </q-card-section> | ||||
|   </q-card> | ||||
| </template> | ||||
| <script lang="ts"> | ||||
| import { defineComponent, onBeforeMount, computed } from 'vue'; | ||||
| import { useUserStore } from '@flaschengeist/api'; | ||||
| import { DisplayNameMode } from '../models'; | ||||
| export default defineComponent({ | ||||
|   name: 'SettingWidget', | ||||
|   setup() { | ||||
|     const store = useUserStore(); | ||||
|     onBeforeMount(() => { | ||||
|       void store.getDisplayNameModeSetting(true); | ||||
|     }); | ||||
| 
 | ||||
|     const displayNameMode = computed({ | ||||
|       get: () => store.userSettings.display_name || DisplayNameMode.DISPLAYNAME, | ||||
|       set: (val) => { | ||||
|         console.log('set', val); | ||||
|         void store.setDisplayNameModeSetting(val); | ||||
|       }, | ||||
|     }); | ||||
| 
 | ||||
|     const options = [ | ||||
|       { | ||||
|         label: 'Anzeigename', | ||||
|         value: DisplayNameMode.DISPLAYNAME, | ||||
|       }, | ||||
|       { | ||||
|         label: 'Vorname', | ||||
|         value: DisplayNameMode.FIRSTNAME, | ||||
|       }, | ||||
|       { | ||||
|         label: 'Nachname', | ||||
|         value: DisplayNameMode.LASTNAME, | ||||
|       }, | ||||
|       { | ||||
|         label: 'Vor- und Nachname', | ||||
|         value: DisplayNameMode.FIRSTNAME_LASTNAME, | ||||
|       }, | ||||
|       { | ||||
|         label: 'Nachname, Vorname', | ||||
|         value: DisplayNameMode.LASTNAME_FIRSTNAME, | ||||
|       }, | ||||
|     ]; | ||||
| 
 | ||||
|     return { | ||||
|       displayNameMode, | ||||
|       options, | ||||
|     }; | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
|  | @ -4,19 +4,15 @@ | |||
|     filled | ||||
|     :label="label" | ||||
|     :options="users" | ||||
|     :option-label="showName" | ||||
|     option-label="display_name" | ||||
|     option-value="userid" | ||||
|     map-options | ||||
|     use-input | ||||
|     input-debounce="0" | ||||
|     @filter="filterFn" | ||||
|   /> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { computed, defineComponent, PropType, onBeforeMount, ref } from 'vue'; | ||||
| import { computed, defineComponent, PropType, onBeforeMount } from 'vue'; | ||||
| import { useUserStore } from '@flaschengeist/api'; | ||||
| import { DisplayNameMode } from '../models'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
|   name: 'UserSelector', | ||||
|  | @ -30,64 +26,17 @@ export default defineComponent({ | |||
| 
 | ||||
|     onBeforeMount(() => { | ||||
|       void userStore.getUsers(false); | ||||
|       void userStore.getDisplayNameModeSetting(true); | ||||
|     }); | ||||
| 
 | ||||
|     const users = computed(() => | ||||
|       userStore.users.filter((user) => { | ||||
|         let names = filter.value.toLowerCase().split(' '); | ||||
|         if (names.length < 1) { | ||||
|           return true; | ||||
|         } | ||||
|         if (names.length === 1) { | ||||
|           let name = names[0]; | ||||
|           return ( | ||||
|             user.lastname.toLowerCase().includes(name) || | ||||
|             user.firstname.toLowerCase().includes(name) | ||||
|           ); | ||||
|         } | ||||
|         if (names.length === 2) { | ||||
|           let name1 = names[0]; | ||||
|           let name2 = names[1]; | ||||
|           return ( | ||||
|             (user.lastname.toLowerCase().includes(name1) && | ||||
|               user.firstname.toLowerCase().includes(name2)) || | ||||
|             (user.lastname.toLowerCase().includes(name2) && | ||||
|               user.firstname.toLowerCase().includes(name1)) | ||||
|           ); | ||||
|         } | ||||
|         return true; | ||||
|       }) | ||||
|     ); | ||||
|     const filter = ref<string>(''); | ||||
|     const filterFn = (val: string, update: () => void) => { | ||||
|       filter.value = val; | ||||
|       update(); | ||||
|     }; | ||||
|     const users = computed(() => userStore.users); | ||||
|     const selected = computed({ | ||||
|       get: () => props.modelValue, | ||||
|       set: (value: FG.User | undefined) => (value ? emit('update:modelValue', value) : undefined), | ||||
|     }); | ||||
|     function showName(user: FG.User) { | ||||
|       switch (userStore.userSettings.display_name) { | ||||
|         case DisplayNameMode.DISPLAYNAME: | ||||
|           return user.display_name; | ||||
|         case DisplayNameMode.FIRSTNAME: | ||||
|           return user.firstname; | ||||
|         case DisplayNameMode.LASTNAME: | ||||
|           return user.lastname; | ||||
|         case DisplayNameMode.FIRSTNAME_LASTNAME: | ||||
|           return `${user.firstname} ${user.lastname}`; | ||||
|         case DisplayNameMode.LASTNAME_FIRSTNAME: | ||||
|           return `${user.lastname}, ${user.firstname}`; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return { | ||||
|       selected, | ||||
|       users, | ||||
|       filterFn, | ||||
|       showName, | ||||
|     }; | ||||
|   }, | ||||
| }); | ||||
|  |  | |||
							
								
								
									
										10
									
								
								src/index.ts
								
								
								
								
							
							
						
						
									
										10
									
								
								src/index.ts
								
								
								
								
							|  | @ -1,7 +1,6 @@ | |||
| import { FG_Plugin } from '@flaschengeist/types'; | ||||
| import { defineAsyncComponent } from 'vue'; | ||||
| import routes from './routes'; | ||||
| import { DisplayNameMode } from './models'; | ||||
| 
 | ||||
| const plugin: FG_Plugin.Plugin = { | ||||
|   id: 'users', | ||||
|  | @ -17,15 +16,6 @@ const plugin: FG_Plugin.Plugin = { | |||
|       widget: defineAsyncComponent(() => import('./components/Widget.vue')), | ||||
|     }, | ||||
|   ], | ||||
|   settingWidgets: [ | ||||
|     { | ||||
|       priority: 1, | ||||
|       name: 'userSettings', | ||||
|       permissions: [], | ||||
|       widget: defineAsyncComponent(() => import('./components/SettingWidget.vue')), | ||||
|     }, | ||||
|   ], | ||||
| }; | ||||
| 
 | ||||
| export default plugin; | ||||
| export { DisplayNameMode }; | ||||
|  |  | |||
|  | @ -12,12 +12,3 @@ export interface LoginResponse { | |||
| export interface CurrentUserResponse extends FG.User { | ||||
|   permissions: FG.Permission[]; | ||||
| } | ||||
| 
 | ||||
| export enum DisplayNameMode { | ||||
|   FIRSTNAME = 'firstname', | ||||
|   LASTNAME = 'lastname', | ||||
|   FULLNAME = 'fullname', | ||||
|   DISPLAYNAME = 'display_name', | ||||
|   FIRSTNAME_LASTNAME = 'firstname_lastname', | ||||
|   LASTNAME_FIRSTNAME = 'lastname_firstname', | ||||
| } | ||||
|  |  | |||
|  | @ -1,153 +0,0 @@ | |||
| <template> | ||||
|   <q-page> | ||||
|     <q-table | ||||
|       :columns="cols" | ||||
|       :rows="users" | ||||
|       :filter="filter" | ||||
|       :grid="grid" | ||||
|       :pagination="initialPagination" | ||||
|     > | ||||
|       <template v-slot:body-cell-avatar="props"> | ||||
|         <q-td :key="props.key" :props="props"> | ||||
|           <user-avatar v-model="props.row" /> | ||||
|         </q-td> | ||||
|       </template> | ||||
|       <template v-slot:top-right="props"> | ||||
|         <q-input v-model="filter" filled dense debounce="300" placeholder="Suche"> | ||||
|           <template v-slot:append> | ||||
|             <q-icon name="mdi-magnify" /> | ||||
|           </template> | ||||
|         </q-input> | ||||
|         <q-btn | ||||
|           flat | ||||
|           round | ||||
|           dense | ||||
|           :icon="grid ? 'mdi-land-rows-horizontal' : 'mdi-card-account-details'" | ||||
|           @click="grid = !grid" | ||||
|         /> | ||||
|       </template> | ||||
|       <template v-slot:item="props"> | ||||
|         <div class="q-pa-xs col-xs-12 col-sm-6 col-md-4"> | ||||
|           <q-card bordered> | ||||
|             <div class="row justify-center"> | ||||
|               <q-img :src="avatarURL(props.row.userid)"> | ||||
|                 <template #error> | ||||
|                   <div | ||||
|                     class="row fit justify-center items-center" | ||||
|                     style="background-color: transparent" | ||||
|                   > | ||||
|                     <img src="no-image.svg" style="object-fit: contain; height: 100%" /> | ||||
|                   </div> | ||||
|                 </template> | ||||
|               </q-img> | ||||
|             </div> | ||||
|             <q-card-section> | ||||
|               <div class="text-h6">{{ props.row.firstname }} {{ props.row.lastname }}</div> | ||||
|               <div class="text-caption">{{ props.row.display_name }}</div> | ||||
|             </q-card-section> | ||||
|             <q-card-section> | ||||
|               <div class="row items-center"> | ||||
|                 <q-btn | ||||
|                   flat | ||||
|                   dense | ||||
|                   icon="mdi-email" | ||||
|                   :href="'mailto:' + props.row.mail" | ||||
|                   class="q-mr-xs" | ||||
|                   no-caps | ||||
|                 /> | ||||
|                 <div class="text-caption">{{ props.row.mail }}</div> | ||||
|               </div> | ||||
|               <div class="row items-center"> | ||||
|                 <q-btn | ||||
|                   flat | ||||
|                   dense | ||||
|                   icon="mdi-calendar" | ||||
|                   class="q-mr-xs" | ||||
|                   no-caps | ||||
|                   v-if="props.row.birthday" | ||||
|                 /> | ||||
|                 <div class="text-caption" v-if="props.row.birthday"> | ||||
|                   {{ props.row.birthday.toLocaleDateString() }} | ||||
|                 </div> | ||||
|               </div> | ||||
|             </q-card-section> | ||||
|           </q-card> | ||||
|         </div> | ||||
|       </template> | ||||
|     </q-table> | ||||
|   </q-page> | ||||
| </template> | ||||
| <script lang="ts"> | ||||
| import { defineComponent, onBeforeMount, computed, ref } from 'vue'; | ||||
| import { useUserStore, avatarURL } from '@flaschengeist/api'; | ||||
| import { UserAvatar } from '@flaschengeist/api/components'; | ||||
| 
 | ||||
| const cols = [ | ||||
|   { | ||||
|     name: 'avatar', | ||||
|     label: 'Profilbild', | ||||
|     sortable: false, | ||||
|   }, | ||||
|   { | ||||
|     name: 'firstName', | ||||
|     label: 'Vorname', | ||||
|     field: 'firstname', | ||||
|     sortable: true, | ||||
|   }, | ||||
|   { | ||||
|     name: 'lastName', | ||||
|     label: 'Nachname', | ||||
|     field: 'lastname', | ||||
|     sortable: true, | ||||
|   }, | ||||
|   { | ||||
|     name: 'displayName', | ||||
|     label: 'Anzeigename', | ||||
|     field: 'display_name', | ||||
|     sortable: true, | ||||
|   }, | ||||
|   { | ||||
|     name: 'mail', | ||||
|     label: 'E-Mail', | ||||
|     field: 'mail', | ||||
|     sortable: true, | ||||
|   }, | ||||
|   { | ||||
|     name: 'birthday', | ||||
|     label: 'Geburtstag', | ||||
|     field: 'birthday', | ||||
|     sortable: true, | ||||
|     format: (val: Date | undefined) => { | ||||
|       return val?.toLocaleDateString(); | ||||
|     }, | ||||
|   }, | ||||
| ]; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
|   components: { UserAvatar }, | ||||
|   setup() { | ||||
|     const userStore = useUserStore(); | ||||
|     onBeforeMount(() => { | ||||
|       void userStore.getUsers(true); | ||||
|     }); | ||||
|     const filter = ref(''); | ||||
| 
 | ||||
|     const users = computed(() => userStore.users); | ||||
|     const grid = ref(false); | ||||
|     return { | ||||
|       users, | ||||
|       cols, | ||||
|       filter, | ||||
|       grid, | ||||
|       avatarURL, | ||||
|       initialPagination: { | ||||
|         sortBy: 'lastName', | ||||
|         descending: false, | ||||
|         page: 1, | ||||
|         rowsPerPage: 0, | ||||
|         // rowsNumber: xx if getting data from a server | ||||
|       }, | ||||
|     }; | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
|  | @ -7,9 +7,6 @@ | |||
|         </q-card-section> | ||||
|         <MainUserSettings :user="currentUser" @update:user="updateUser" /> | ||||
|       </q-card> | ||||
|       <div v-for="(item, index) in widgets" :key="index" class="full-height col-12"> | ||||
|         <component :is="item.widget" /> | ||||
|       </div> | ||||
|       <div class="col-12 text-left text-h6">Aktive Sessions:</div> | ||||
|       <user-session | ||||
|         v-for="(session, index) in sessions" | ||||
|  | @ -17,17 +14,15 @@ | |||
|         v-model="sessions[index]" | ||||
|         @delete="removeSession(session)" | ||||
|       /> | ||||
|       <q-btn label="List Widgets" @click="listWidgets" /> | ||||
|     </q-page> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { useMainStore, useUserStore, useSessionStore, hasPermissions } from '@flaschengeist/api'; | ||||
| import { useMainStore, useUserStore, useSessionStore } from '@flaschengeist/api'; | ||||
| import MainUserSettings from '../components/settings/MainUserSettings.vue'; | ||||
| import { defineComponent, onBeforeMount, ref, computed, inject } from 'vue'; | ||||
| import { defineComponent, onBeforeMount, ref } from 'vue'; | ||||
| import UserSession from '../components/settings/UserSession.vue'; | ||||
| import { FG_Plugin } from '@flaschengeist/types'; | ||||
| import { Notify } from 'quasar'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
|  | @ -52,29 +47,17 @@ export default defineComponent({ | |||
|         progress: true, | ||||
|         actions: [{ icon: 'mdi-close', color: 'white' }], | ||||
|       }); | ||||
|       console.log(widgets); | ||||
|     } | ||||
| 
 | ||||
|     function removeSession(s: FG.Session) { | ||||
|       sessions.value = sessions.value.filter((ss) => ss.token !== s.token); | ||||
|     } | ||||
| 
 | ||||
|     const flaschengeist = inject<FG_Plugin.Flaschengeist>('flaschengeist'); | ||||
|     const widgets = computed(() => | ||||
|       flaschengeist?.settingWidgets.filter((widget) => hasPermissions(widget.permissions)) | ||||
|     ); | ||||
| 
 | ||||
|     function listWidgets() { | ||||
|       console.log(widgets); | ||||
|     } | ||||
| 
 | ||||
|     return { | ||||
|       currentUser, | ||||
|       sessions, | ||||
|       updateUser, | ||||
|       removeSession, | ||||
|       widgets, | ||||
|       listWidgets, | ||||
|     }; | ||||
|   }, | ||||
| }); | ||||
|  |  | |||
|  | @ -10,16 +10,6 @@ const mainRoutes: FG_Plugin.MenuRoute[] = [ | |||
|     permissions: ['user'], | ||||
|     route: { path: 'user', name: 'user', redirect: { name: 'user-settings' } }, | ||||
|     children: [ | ||||
|       { | ||||
|         title: 'Mitglieder', | ||||
|         icon: 'mdi-account-multiple', | ||||
|         shortcut: true, | ||||
|         route: { | ||||
|           path: 'members', | ||||
|           name: 'user-members', | ||||
|           component: () => import('../pages/Members.vue'), | ||||
|         }, | ||||
|       }, | ||||
|       { | ||||
|         title: 'Einstellungen', | ||||
|         icon: 'mdi-account-edit', | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue