Compare commits
	
		
			12 Commits
		
	
	
		
			v1.0.0-alp
			...
			main
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | b7735e2924 | |
|  | d35cc8e8d1 | |
|  | d34898e1e9 | |
|  | 93669d66dc | |
|  | ec5458bf7e | |
|  | efc7c49a0b | |
|  | b1e4879881 | |
|  | ee7e03ce28 | |
|  | 2928c241ad | |
|  | fe9ec96ce1 | |
|  | 417689b725 | |
|  | 48972f84e1 | 
|  | @ -1,6 +1,6 @@ | |||
| { | ||||
|   "license": "MIT", | ||||
|   "version": "1.0.0-alpha.8", | ||||
|   "version": "1.0.0", | ||||
|   "name": "@flaschengeist/api", | ||||
|   "author": "Tim Gröger <flaschengeist@wu5.de>", | ||||
|   "homepage": "https://flaschengeist.dev/Flaschengeist", | ||||
|  | @ -11,11 +11,11 @@ | |||
|   "main": "./src/index.ts", | ||||
|   "peerDependencies": { | ||||
|     "@quasar/app-webpack": "^3.7.2", | ||||
|     "flaschengeist": "^2.0.0-alpha.1", | ||||
|     "flaschengeist": "^2.0.0", | ||||
|     "pinia": "^2.0.8" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@flaschengeist/types": "^1.0.0-alpha.10", | ||||
|     "@flaschengeist/types": "^1.0.0", | ||||
|     "@types/node": "^14.18.0", | ||||
|     "typescript": "^4.5.4" | ||||
|   }, | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import { defineStore } from 'pinia'; | ||||
| import { api } from '../internal'; | ||||
| import { isAxiosError, useMainStore } from '.'; | ||||
| import { DisplayNameMode } from '@flaschengeist/users'; | ||||
| 
 | ||||
| export function fixUser(u?: FG.User) { | ||||
|   return !u ? u : Object.assign(u, { birthday: u.birthday ? new Date(u.birthday) : undefined }); | ||||
|  | @ -22,6 +23,7 @@ export const useUserStore = defineStore({ | |||
|   state: () => ({ | ||||
|     roles: [] as FG.Role[], | ||||
|     permissions: [] as FG.Permission[], | ||||
|     userSettings: {} as FG.UserSettings, | ||||
|     // list of all users, include deleted ones, use `users` getter for list of active ones
 | ||||
|     _users: [] as FG.User[], | ||||
|     // Internal flags for deciding if lists need to force-loaded
 | ||||
|  | @ -31,7 +33,42 @@ export const useUserStore = defineStore({ | |||
| 
 | ||||
|   getters: { | ||||
|     users(state) { | ||||
|       return state._users.filter((u) => !u.deleted); | ||||
|       const u = state._users.filter((u) => !u.deleted); | ||||
| 
 | ||||
|       switch (this.userSettings['display_name']) { | ||||
|         case DisplayNameMode.FIRSTNAME_LASTNAME || DisplayNameMode.FIRSTNAME: | ||||
|           u.sort((a, b) => { | ||||
|             const a_lastname = a.lastname.toLowerCase(); | ||||
|             const b_lastname = b.lastname.toLowerCase(); | ||||
|             const a_firstname = a.firstname.toLowerCase(); | ||||
|             const b_firstname = b.firstname.toLowerCase(); | ||||
|             if (a_firstname === b_firstname) { | ||||
|               return a_lastname < b_lastname ? -1 : 1; | ||||
|             } | ||||
|             return a_firstname < b_firstname ? -1 : 1; | ||||
|           }); | ||||
|           break; | ||||
|         case <string>DisplayNameMode.DISPLAYNAME: | ||||
|           u.sort((a, b) => { | ||||
|             const a_displayname = a.display_name.toLowerCase(); | ||||
|             const b_displayname = b.display_name.toLowerCase(); | ||||
|             return a_displayname < b_displayname ? -1 : 1; | ||||
|           }); | ||||
|           break; | ||||
|         default: | ||||
|           u.sort((a, b) => { | ||||
|             const a_lastname = a.lastname.toLowerCase(); | ||||
|             const b_lastname = b.lastname.toLowerCase(); | ||||
|             const a_firstname = a.firstname.toLowerCase(); | ||||
|             const b_firstname = b.firstname.toLowerCase(); | ||||
|             if (a_lastname === b_lastname) { | ||||
|               return a_firstname < b_firstname ? -1 : 1; | ||||
|             } | ||||
|             return a_lastname < b_lastname ? -1 : 1; | ||||
|           }); | ||||
|       } | ||||
| 
 | ||||
|       return u; | ||||
|     }, | ||||
|   }, | ||||
| 
 | ||||
|  | @ -207,5 +244,35 @@ export const useUserStore = defineStore({ | |||
|       await api.delete(`/roles/${role}`); | ||||
|       this.roles = this.roles.filter((r) => r.id !== role); | ||||
|     }, | ||||
| 
 | ||||
|     /** Get Settings for display name mode | ||||
|      * @param force If set to true a fresh list is loaded from backend even when a local copy is available | ||||
|      * @throws Probably an AxiosError if request failed | ||||
|      * @returns Settings for display name mode | ||||
|      */ | ||||
|     async getDisplayNameModeSetting(force = false): Promise<string> { | ||||
|       const mainStore = useMainStore(); | ||||
|       if (force) { | ||||
|         const { data } = await api.get<{ data: string }>( | ||||
|           `users/${mainStore.currentUser.userid}/setting/display_name_mode` | ||||
|         ); | ||||
|         this.userSettings['display_name'] = data.data; | ||||
|       } | ||||
|       return this.userSettings['display_name']; | ||||
|     }, | ||||
| 
 | ||||
|     /** Set Settings for display name mode | ||||
|      * @param mode New display name mode | ||||
|      * @throws Probably an AxiosError if request failed | ||||
|      * @returns Settings for display name mode | ||||
|      */ | ||||
|     async setDisplayNameModeSetting(mode: string): Promise<string> { | ||||
|       const mainStore = useMainStore(); | ||||
|       await api.put(`users/${mainStore.currentUser.userid}/setting/display_name_mode`, { | ||||
|         data: mode, | ||||
|       }); | ||||
|       this.userSettings['display_name'] = mode; | ||||
|       return mode; | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
|  |  | |||
							
								
								
									
										22
									
								
								package.json
								
								
								
								
							
							
						
						
									
										22
									
								
								package.json
								
								
								
								
							|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "private": true, | ||||
|   "license": "MIT", | ||||
|   "version": "2.0.0-alpha.2", | ||||
|   "version": "2.1.0", | ||||
|   "productName": "flaschengeist-frontend", | ||||
|   "name": "flaschengeist", | ||||
|   "author": "Tim Gröger <flaschengeist@wu5.de>", | ||||
|  | @ -15,12 +15,12 @@ | |||
|     "lint": "eslint --ext .js,.ts,.vue ./src ./api" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@flaschengeist/api": "^1.0.0-alpha.8", | ||||
|     "@flaschengeist/balance": "^1.0.0-alpha.2", | ||||
|     "@flaschengeist/pricelist-old": "^1.0.0-alpha.1", | ||||
|     "@flaschengeist/schedule": "^1.0.0-alpha.8", | ||||
|     "@flaschengeist/users": "^1.0.0-alpha.4", | ||||
|     "axios": "^0.24.0", | ||||
|     "@flaschengeist/api": "^1.0.0", | ||||
|     "@flaschengeist/balance": "^1.0.0", | ||||
|     "@flaschengeist/pricelist-old": "^1.0.0", | ||||
|     "@flaschengeist/schedule": "^1.0.0", | ||||
|     "@flaschengeist/users": "^1.0.0", | ||||
|     "axios": "^1.4.0", | ||||
|     "pinia": "^2.0.8", | ||||
|     "quasar": "^2.11.10", | ||||
|     "vue": "^3.0.0", | ||||
|  | @ -29,7 +29,7 @@ | |||
|   "devDependencies": { | ||||
|     "@capacitor/core": "^5.0.0", | ||||
|     "@capacitor/preferences": "^5.0.0", | ||||
|     "@flaschengeist/types": "^1.0.0-alpha.10", | ||||
|     "@flaschengeist/types": "^1.0.0", | ||||
|     "@quasar/app-webpack": "^3.7.2", | ||||
|     "@quasar/extras": "^1.16.3", | ||||
|     "@types/node": "^14.18.0", | ||||
|  | @ -41,9 +41,9 @@ | |||
|     "eslint": "^8.5.0", | ||||
|     "eslint-config-prettier": "^8.3.0", | ||||
|     "eslint-plugin-prettier": "^4.0.0", | ||||
|     "eslint-plugin-vue": "^8.2.0", | ||||
|     "eslint-webpack-plugin": "^3.1.1", | ||||
|     "modify-source-webpack-plugin": "^3.0.0", | ||||
|     "eslint-plugin-vue": "^9.14.1", | ||||
|     "eslint-webpack-plugin": "^4.0.1", | ||||
|     "modify-source-webpack-plugin": "^4.1.0", | ||||
|     "prettier": "^2.5.1", | ||||
|     "typescript": "^4.5.4", | ||||
|     "vuedraggable": "^4.1.0" | ||||
|  |  | |||
|  | @ -3,4 +3,4 @@ module.exports = [ | |||
| //  '@flaschengeist/balance',
 | ||||
| //  '@flaschengeist/schedule',
 | ||||
| //  '@flaschengeist/pricelist',
 | ||||
| ] | ||||
|   '@flaschengeist/schedule', | ||||
|  |  | |||
|  | @ -9,9 +9,23 @@ | |||
| /* eslint-env node */ | ||||
| /* eslint-disable @typescript-eslint/no-var-requires */ | ||||
| const ESLintPlugin = require('eslint-webpack-plugin'); | ||||
| const { ModifySourcePlugin } = require('modify-source-webpack-plugin'); | ||||
| const { ModifySourcePlugin, ReplaceOperation } = require('modify-source-webpack-plugin'); | ||||
| const { configure } = require('quasar/wrappers'); | ||||
| 
 | ||||
| const operation = () => { | ||||
|   const custom_plgns = require('./plugin.config.js'); | ||||
|   const required_plgns = require('./src/vendor-plugin.config.js'); | ||||
|   const plugins = [...custom_plgns, ...required_plgns].map( | ||||
|     (v) => `import("${v}").catch(() => "${v}")` | ||||
|   ); | ||||
|   const replace = new ReplaceOperation( | ||||
|     'all', | ||||
|     `\\/\\* *INSERT_PLUGIN_LIST *\\*\\/`, | ||||
|     `${plugins.join(', ')}` | ||||
|   ); | ||||
|   return replace; | ||||
| }; | ||||
| 
 | ||||
| module.exports = configure(function (/* ctx */) { | ||||
|   return { | ||||
|     // https://quasar.dev/quasar-cli/supporting-ts
 | ||||
|  | @ -42,7 +56,7 @@ module.exports = configure(function (/* ctx */) { | |||
|       // 'ionicons-v5',
 | ||||
|       // 'line-awesome',
 | ||||
|       // 'material-icons',
 | ||||
|       'mdi-v6', | ||||
|       'mdi-v7', | ||||
|       // 'themify',
 | ||||
| 
 | ||||
|       // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
 | ||||
|  | @ -52,7 +66,7 @@ module.exports = configure(function (/* ctx */) { | |||
|     // Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build
 | ||||
|     build: { | ||||
|       vueRouterMode: 'history', // available values: 'hash', 'history'
 | ||||
| 
 | ||||
|       //publicPath: 'flaschengeist2',
 | ||||
|       // transpile: false,
 | ||||
| 
 | ||||
|       // Add dependencies for transpiling with Babel (Array of string/regex)
 | ||||
|  | @ -81,16 +95,7 @@ module.exports = configure(function (/* ctx */) { | |||
|             rules: [ | ||||
|               { | ||||
|                 test: /plugins\.ts$/, | ||||
|                 modify: (src, filename) => { | ||||
|                   const custom_plgns = require('./plugin.config.js'); | ||||
|                   const required_plgns = require('./src/vendor-plugin.config.js'); | ||||
|                   return src.replace( | ||||
|                     /\/\* *INSERT_PLUGIN_LIST *\*\//, | ||||
|                     [...custom_plgns, ...required_plgns] | ||||
|                       .map((v) => `import("${v}").catch(() => "${v}")`) | ||||
|                       .join(',') | ||||
|                   ); | ||||
|                 }, | ||||
|                 operations: [operation()], | ||||
|               }, | ||||
|             ], | ||||
|           }, | ||||
|  | @ -215,5 +220,8 @@ module.exports = configure(function (/* ctx */) { | |||
|         // chainWebpack also available besides this extendWebpack
 | ||||
|       }, | ||||
|     }, | ||||
|     bin: { | ||||
|       linuxAndroidStudio: '/home/crimsen/.local/share/JetBrains/Toolbox/scripts/studio', | ||||
|     }, | ||||
|   }; | ||||
| }); | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| { | ||||
|   "name": "flaschengeist", | ||||
|   "version": "2.0.0-alpha.1", | ||||
|   "version": "2.0.0", | ||||
|   "description": "Modular student club administration system", | ||||
|   "author": "Tim Gröger <flaschengeist@wu5.de>", | ||||
|   "private": true, | ||||
|  | @ -13,4 +13,4 @@ | |||
|     "@capacitor/preferences": "^5.0.0", | ||||
|     "@capacitor/splash-screen": "^5.0.0" | ||||
|   } | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -234,6 +234,12 @@ function loadPlugin( | |||
|     Array.prototype.push.apply(loadedPlugins.widgets, plugin.widgets); | ||||
|   } | ||||
| 
 | ||||
|   if (!plugin.settingWidgets) plugin.settingWidgets = []; | ||||
|   if (plugin.settingWidgets.length > 0) { | ||||
|     plugin.settingWidgets.forEach((widget) => (widget.name = plugin.id + '.' + widget.name)); | ||||
|     Array.prototype.push.apply(loadedPlugins.settingWidgets, plugin.settingWidgets); | ||||
|   } | ||||
| 
 | ||||
|   loadedPlugins.plugins.push({ | ||||
|     id: plugin.id, | ||||
|     name: plugin.name, | ||||
|  | @ -252,6 +258,7 @@ export async function loadPlugins(backend: FG.Backend, baseRoutes: RouteRecordRa | |||
|     shortcuts: [], | ||||
|     outerShortcuts: [], | ||||
|     widgets: [], | ||||
|     settingWidgets: [], | ||||
|   }; | ||||
| 
 | ||||
|   // Wait for all plugins to be loaded
 | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
|       class="q-ma-xs" | ||||
|       title="Löschen" | ||||
|       style="position: absolute; top: 0; right: 0; z-index: 999" | ||||
|       @click="dismiss" | ||||
|       @click.stop.prevent="dismiss" | ||||
|     /> | ||||
|     <q-card-section class="q-pa-xs"> | ||||
|       <div class="text-overline">{{ dateString }}</div> | ||||
|  | @ -34,7 +34,7 @@ | |||
|         flat | ||||
|         dense | ||||
|         size="sm" | ||||
|         @click="accept" | ||||
|         @click.stop.prevent="accept" | ||||
|       /> | ||||
|       <q-btn | ||||
|         v-if="modelValue.reject" | ||||
|  | @ -44,7 +44,7 @@ | |||
|         flat | ||||
|         dense | ||||
|         size="sm" | ||||
|         @click="reject" | ||||
|         @click.stop.prevent="reject" | ||||
|       /> | ||||
|     </q-card-actions> | ||||
|   </q-card> | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
|   <q-layout view="hHh Lpr lff"> | ||||
|     <q-header elevated class="bg-primary text-white"> | ||||
|       <q-toolbar> | ||||
|         <q-btn dense flat round icon="mdi-menu" @click="openMenu" /> | ||||
|         <q-btn dense flat round icon="mdi-menu" @click="openMenu(true)" /> | ||||
| 
 | ||||
|         <q-toolbar-title> | ||||
|           <router-link :to="{ name: 'dashboard' }" style="text-decoration: none; color: inherit"> | ||||
|  | @ -56,32 +56,44 @@ | |||
|       side="left" | ||||
|       bordered | ||||
|       :mini="leftDrawerMini" | ||||
|       @click.capture="openMenu" | ||||
|       @click.capture="openMenuMini" | ||||
|     > | ||||
|       <!-- Plugins --> | ||||
|       <essential-expansion-link | ||||
|         v-for="(entry, index) in mainLinks" | ||||
|         :key="'plugin' + index" | ||||
|         :entry="entry" | ||||
|         @add-short-cut="addShortcut" | ||||
|       /> | ||||
|       <q-separator /> | ||||
|       <essential-link | ||||
|         v-for="(entry, index) in essentials" | ||||
|         :key="'essential' + index" | ||||
|         :entry="entry" | ||||
|       /> | ||||
|       <div v-if="platform.is.capacitor"> | ||||
|       <q-scroll-area class="fit"> | ||||
|         <essential-expansion-link | ||||
|           v-for="(entry, index) in mainLinks" | ||||
|           :key="'plugin' + index" | ||||
|           :entry="entry" | ||||
|           @add-short-cut="addShortcut" | ||||
|         /> | ||||
|         <q-separator /> | ||||
|         <q-item clickable tag="a" target="self" @click="logout"> | ||||
|           <q-item-section avatar> | ||||
|             <q-icon name="mdi-exit-to-app" /> | ||||
|           </q-item-section> | ||||
|         <essential-link | ||||
|           v-for="(entry, index) in essentials" | ||||
|           :key="'essential' + index" | ||||
|           :entry="entry" | ||||
|         /> | ||||
|         <div v-if="platform.is.capacitor"> | ||||
|           <q-separator /> | ||||
|           <q-item clickable tag="a" target="self" @click="logout"> | ||||
|             <q-item-section avatar> | ||||
|               <q-icon name="mdi-exit-to-app" /> | ||||
|             </q-item-section> | ||||
| 
 | ||||
|           <q-item-section> | ||||
|             <q-item-label>Logout</q-item-label> | ||||
|           </q-item-section> | ||||
|         </q-item> | ||||
|             <q-item-section> | ||||
|               <q-item-label>Logout</q-item-label> | ||||
|             </q-item-section> | ||||
|           </q-item> | ||||
|         </div> | ||||
|       </q-scroll-area> | ||||
|       <div class="q-mini-drawer-hide absolute" style="top: 15px; right: -17px"> | ||||
|         <q-btn | ||||
|           dense | ||||
|           round | ||||
|           unelevated | ||||
|           color="accent" | ||||
|           icon="mdi-chevron-left" | ||||
|           @click="openMenuMini(true)" | ||||
|         /> | ||||
|       </div> | ||||
|     </q-drawer> | ||||
|     <q-page-container> | ||||
|  | @ -125,7 +137,7 @@ export default defineComponent({ | |||
|     const mainStore = useMainStore(); | ||||
|     const flaschengeist = inject<FG_Plugin.Flaschengeist>('flaschengeist'); | ||||
|     const leftDrawer = ref(!Platform.is.mobile); | ||||
|     const leftDrawerMini = ref(false); | ||||
|     const leftDrawerMini = ref(true); | ||||
|     const mainLinks = flaschengeist?.menuLinks || []; | ||||
|     const notifications = computed(() => mainStore.notifications.slice().reverse()); | ||||
|     const polling = ref(NaN); | ||||
|  | @ -138,9 +150,11 @@ export default defineComponent({ | |||
|       void mainStore.getShortcuts(); | ||||
|     }); | ||||
|     onBeforeUnmount(() => window.clearInterval(polling.value)); | ||||
| 
 | ||||
|     /* | ||||
|     function openMenu(event: { target: HTMLInputElement }) { | ||||
|       if (event.target.nodeName === 'DIV') leftDrawerMini.value = false; | ||||
|       console.log(event.target.nodeName); | ||||
|       if (event.target.nodeName === 'DIV' || event.target.nodeName === 'I') | ||||
|         leftDrawerMini.value = false; | ||||
|       else { | ||||
|         if (!leftDrawer.value || leftDrawerMini.value) { | ||||
|           leftDrawer.value = true; | ||||
|  | @ -151,7 +165,13 @@ export default defineComponent({ | |||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
| */ | ||||
|     function openMenu(value = !leftDrawer.value) { | ||||
|       leftDrawer.value = value; | ||||
|     } | ||||
|     function openMenuMini(value = !leftDrawerMini.value) { | ||||
|       leftDrawerMini.value = value; | ||||
|     } | ||||
|     function logout() { | ||||
|       void router.push({ name: 'login', params: { logout: 'logout' } }); | ||||
|       void mainStore.logout(); | ||||
|  | @ -214,6 +234,7 @@ export default defineComponent({ | |||
|       notifications, | ||||
|       noPermission, | ||||
|       openMenu, | ||||
|       openMenuMini, | ||||
|       remove, | ||||
|       requestPermission, | ||||
|       useNative, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue