Compare commits
	
		
			24 Commits
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						b7735e2924 | |
| 
							
							
								
									
								
								 | 
						d35cc8e8d1 | |
| 
							
							
								
									
								
								 | 
						d34898e1e9 | |
| 
							
							
								
									
								
								 | 
						93669d66dc | |
| 
							
							
								
									
								
								 | 
						ec5458bf7e | |
| 
							
							
								
									
								
								 | 
						efc7c49a0b | |
| 
							
							
								
									
								
								 | 
						b1e4879881 | |
| 
							
							
								
									
								
								 | 
						ee7e03ce28 | |
| 
							
							
								
									
								
								 | 
						2928c241ad | |
| 
							
							
								
									
								
								 | 
						fe9ec96ce1 | |
| 
							
							
								
									
								
								 | 
						417689b725 | |
| 
							
							
								
									
								
								 | 
						847e923265 | |
| 
							
							
								
									
								
								 | 
						4cb0362bb7 | |
| 
							
							
								
									
								
								 | 
						a46c41cb5b | |
| 
							
							
								
									
								
								 | 
						3d55f2d2ae | |
| 
							
							
								
									
								
								 | 
						3689da810c | |
| 
							
							
								
									
								
								 | 
						e6d9054256 | |
| 
							
							
								
									
								
								 | 
						ab45bf3667 | |
| 
							
							
								
									
								
								 | 
						857d07040b | |
| 
							
							
								
								 | 
						e07df08822 | |
| 
							
							
								
								 | 
						ec28857af5 | |
| 
							
							
								
								 | 
						1c452e23fe | |
| 
							
							
								
								 | 
						195593ddc5 | |
| 
							
							
								
									
								
								 | 
						48972f84e1 | 
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					yarn-error.log
 | 
				
			||||||
 | 
					.woodpecker/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					pipeline:
 | 
				
			||||||
 | 
					  deploy:
 | 
				
			||||||
 | 
					    when:
 | 
				
			||||||
 | 
					      event: tag
 | 
				
			||||||
 | 
					      tag: "@flaschengeist/api-v*"
 | 
				
			||||||
 | 
					    image: node:lts-alpine
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - cd api
 | 
				
			||||||
 | 
					      - echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" > .npmrc
 | 
				
			||||||
 | 
					      - yarn publish --non-interactive
 | 
				
			||||||
 | 
					    secrets: [ node_auth_token ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					depends_on:
 | 
				
			||||||
 | 
					  - lint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					pipeline:
 | 
				
			||||||
 | 
					  lint:
 | 
				
			||||||
 | 
					    when:
 | 
				
			||||||
 | 
					      branch: [main, develop]
 | 
				
			||||||
 | 
					    image: node:lts-alpine
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - yarn install
 | 
				
			||||||
 | 
					      - yarn lint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,6 @@
 | 
				
			||||||
# Flaschengeist (frontend)
 | 
					# Flaschengeist (frontend)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Modular student club administration system, licensed under the MIT license.
 | 
					Modular student club administration system, licensed under the MIT license.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <q-avatar>
 | 
					  <q-avatar>
 | 
				
			||||||
    <slot :avatarURL="avatarURL(modelValue)">
 | 
					    <slot :avatar-u-r-l="avatarURL(modelValue)">
 | 
				
			||||||
      <q-img :src="avatarURL(modelValue)" style="min-width: 100%; min-height: 100%">
 | 
					      <q-img :src="avatarURL(modelValue)" style="min-width: 100%; min-height: 100%">
 | 
				
			||||||
        <template #error>
 | 
					        <template #error>
 | 
				
			||||||
          <img :src="fallback" style="height: 100%" />
 | 
					          <img :src="fallback" style="height: 100%" />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "license": "MIT",
 | 
					  "license": "MIT",
 | 
				
			||||||
  "version": "1.0.0-alpha.7",
 | 
					  "version": "1.0.0",
 | 
				
			||||||
  "name": "@flaschengeist/api",
 | 
					  "name": "@flaschengeist/api",
 | 
				
			||||||
  "author": "Tim Gröger <flaschengeist@wu5.de>",
 | 
					  "author": "Tim Gröger <flaschengeist@wu5.de>",
 | 
				
			||||||
  "homepage": "https://flaschengeist.dev/Flaschengeist",
 | 
					  "homepage": "https://flaschengeist.dev/Flaschengeist",
 | 
				
			||||||
| 
						 | 
					@ -10,14 +10,14 @@
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "main": "./src/index.ts",
 | 
					  "main": "./src/index.ts",
 | 
				
			||||||
  "peerDependencies": {
 | 
					  "peerDependencies": {
 | 
				
			||||||
    "@quasar/app": "^3.2.4",
 | 
					    "@quasar/app-webpack": "^3.7.2",
 | 
				
			||||||
    "flaschengeist": "^2.0.0-alpha.1",
 | 
					    "flaschengeist": "^2.0.0",
 | 
				
			||||||
    "pinia": "^2.0.6"
 | 
					    "pinia": "^2.0.8"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@flaschengeist/types": "^1.0.0-alpha.10",
 | 
					    "@flaschengeist/types": "^1.0.0",
 | 
				
			||||||
    "@types/node": "^14.18.00",
 | 
					    "@types/node": "^14.18.0",
 | 
				
			||||||
    "typescript": "^4.5.2"
 | 
					    "typescript": "^4.5.4"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "prettier": {
 | 
					  "prettier": {
 | 
				
			||||||
    "singleQuote": true,
 | 
					    "singleQuote": true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import { AxiosResponse } from 'axios';
 | 
				
			||||||
import { api } from '../internal';
 | 
					import { api } from '../internal';
 | 
				
			||||||
import { defineStore } from 'pinia';
 | 
					import { defineStore } from 'pinia';
 | 
				
			||||||
import { PersistentStorage } from '../utils/persistent';
 | 
					import { PersistentStorage } from '../utils/persistent';
 | 
				
			||||||
 | 
					import { LocalStorage, SessionStorage } from 'quasar';
 | 
				
			||||||
function reviveSession() {
 | 
					function reviveSession() {
 | 
				
			||||||
  return PersistentStorage.get<FG.Session>('fg_session').then((s) => fixSession(s || undefined));
 | 
					  return PersistentStorage.get<FG.Session>('fg_session').then((s) => fixSession(s || undefined));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -154,6 +154,8 @@ export const useMainStore = defineStore({
 | 
				
			||||||
    handleLoggedOut() {
 | 
					    handleLoggedOut() {
 | 
				
			||||||
      this.$reset();
 | 
					      this.$reset();
 | 
				
			||||||
      void clearPersistant();
 | 
					      void clearPersistant();
 | 
				
			||||||
 | 
					      LocalStorage.clear();
 | 
				
			||||||
 | 
					      SessionStorage.clear();
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
import { defineStore } from 'pinia';
 | 
					import { defineStore } from 'pinia';
 | 
				
			||||||
import { api } from '../internal';
 | 
					import { api } from '../internal';
 | 
				
			||||||
import { isAxiosError, useMainStore } from '.';
 | 
					import { isAxiosError, useMainStore } from '.';
 | 
				
			||||||
 | 
					import { DisplayNameMode } from '@flaschengeist/users';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function fixUser(u?: FG.User) {
 | 
					export function fixUser(u?: FG.User) {
 | 
				
			||||||
  return !u ? u : Object.assign(u, { birthday: u.birthday ? new Date(u.birthday) : undefined });
 | 
					  return !u ? u : Object.assign(u, { birthday: u.birthday ? new Date(u.birthday) : undefined });
 | 
				
			||||||
| 
						 | 
					@ -22,6 +23,7 @@ export const useUserStore = defineStore({
 | 
				
			||||||
  state: () => ({
 | 
					  state: () => ({
 | 
				
			||||||
    roles: [] as FG.Role[],
 | 
					    roles: [] as FG.Role[],
 | 
				
			||||||
    permissions: [] as FG.Permission[],
 | 
					    permissions: [] as FG.Permission[],
 | 
				
			||||||
 | 
					    userSettings: {} as FG.UserSettings,
 | 
				
			||||||
    // list of all users, include deleted ones, use `users` getter for list of active ones
 | 
					    // list of all users, include deleted ones, use `users` getter for list of active ones
 | 
				
			||||||
    _users: [] as FG.User[],
 | 
					    _users: [] as FG.User[],
 | 
				
			||||||
    // Internal flags for deciding if lists need to force-loaded
 | 
					    // Internal flags for deciding if lists need to force-loaded
 | 
				
			||||||
| 
						 | 
					@ -31,7 +33,42 @@ export const useUserStore = defineStore({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getters: {
 | 
					  getters: {
 | 
				
			||||||
    users(state) {
 | 
					    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}`);
 | 
					      await api.delete(`/roles/${role}`);
 | 
				
			||||||
      this.roles = this.roles.filter((r) => r.id !== 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;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,35 +1,35 @@
 | 
				
			||||||
import { LocalStorage, Platform } from 'quasar';
 | 
					import { LocalStorage, Platform } from 'quasar';
 | 
				
			||||||
import { Storage } from '@capacitor/storage';
 | 
					import { Preferences } from '@capacitor/preferences';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
					// eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
				
			||||||
type PersitentTypes = Date | RegExp | number | boolean | string | object;
 | 
					type PersitentTypes = Date | RegExp | number | boolean | string | object;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class PersistentStorage {
 | 
					export class PersistentStorage {
 | 
				
			||||||
  static clear() {
 | 
					  static clear() {
 | 
				
			||||||
    if (Platform.is.capacitor) return Storage.clear();
 | 
					    if (Platform.is.capacitor) return Preferences.clear();
 | 
				
			||||||
    else return Promise.resolve(LocalStorage.clear());
 | 
					    else return Promise.resolve(LocalStorage.clear());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static remove(key: string) {
 | 
					  static remove(key: string) {
 | 
				
			||||||
    if (Platform.is.capacitor) return Storage.remove({ key: key });
 | 
					    if (Platform.is.capacitor) return Preferences.remove({ key: key });
 | 
				
			||||||
    else return Promise.resolve(LocalStorage.remove(key));
 | 
					    else return Promise.resolve(LocalStorage.remove(key));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static set(key: string, value: PersitentTypes) {
 | 
					  static set(key: string, value: PersitentTypes) {
 | 
				
			||||||
    if (Platform.is.capacitor) return Storage.set({ key, value: JSON.stringify(value) });
 | 
					    if (Platform.is.capacitor) return Preferences.set({ key, value: JSON.stringify(value) });
 | 
				
			||||||
    else return Promise.resolve(LocalStorage.set(key, value));
 | 
					    else return Promise.resolve(LocalStorage.set(key, value));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static get<T extends PersitentTypes>(key: string) {
 | 
					  static get<T extends PersitentTypes>(key: string) {
 | 
				
			||||||
    if (Platform.is.capacitor)
 | 
					    if (Platform.is.capacitor)
 | 
				
			||||||
      return Storage.get({ key }).then((v) =>
 | 
					      return Preferences.get({ key }).then((v) =>
 | 
				
			||||||
        v.value === null ? null : (JSON.parse(v.value) as T)
 | 
					        v.value === null ? null : (JSON.parse(v.value) as T)
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    else return Promise.resolve(LocalStorage.getItem<T>(key));
 | 
					    else return Promise.resolve(LocalStorage.getItem<T>(key));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static keys() {
 | 
					  static keys() {
 | 
				
			||||||
    if (Platform.is.capacitor) return Storage.keys().then((v) => v.keys);
 | 
					    if (Platform.is.capacitor) return Preferences.keys().then((v) => v.keys);
 | 
				
			||||||
    else return Promise.resolve(LocalStorage.getAllKeys());
 | 
					    else return Promise.resolve(LocalStorage.getAllKeys());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,14 +3,7 @@
 | 
				
			||||||
  "target": "esnext",
 | 
					  "target": "esnext",
 | 
				
			||||||
  "compilerOptions": {
 | 
					  "compilerOptions": {
 | 
				
			||||||
    "baseUrl": "./",
 | 
					    "baseUrl": "./",
 | 
				
			||||||
    "lib": [
 | 
					    "lib": ["es2020", "dom"],
 | 
				
			||||||
      "es2020",
 | 
					    "types": ["@flaschengeist/types", "@quasar/app", "node"]
 | 
				
			||||||
      "dom"
 | 
					  }
 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    "types": [
 | 
					 | 
				
			||||||
      "@flaschengeist/types",
 | 
					 | 
				
			||||||
      "@quasar/app",
 | 
					 | 
				
			||||||
      "node"
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										42
									
								
								package.json
								
								
								
								
							
							
						
						
									
										42
									
								
								package.json
								
								
								
								
							| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "private": true,
 | 
					  "private": true,
 | 
				
			||||||
  "license": "MIT",
 | 
					  "license": "MIT",
 | 
				
			||||||
  "version": "2.0.0-alpha.1",
 | 
					  "version": "2.1.0",
 | 
				
			||||||
  "productName": "flaschengeist-frontend",
 | 
					  "productName": "flaschengeist-frontend",
 | 
				
			||||||
  "name": "flaschengeist",
 | 
					  "name": "flaschengeist",
 | 
				
			||||||
  "author": "Tim Gröger <flaschengeist@wu5.de>",
 | 
					  "author": "Tim Gröger <flaschengeist@wu5.de>",
 | 
				
			||||||
| 
						 | 
					@ -15,31 +15,37 @@
 | 
				
			||||||
    "lint": "eslint --ext .js,.ts,.vue ./src ./api"
 | 
					    "lint": "eslint --ext .js,.ts,.vue ./src ./api"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@flaschengeist/api": "file:./api",
 | 
					    "@flaschengeist/api": "^1.0.0",
 | 
				
			||||||
    "@flaschengeist/users": "^1.0.0-alpha.3",
 | 
					    "@flaschengeist/balance": "^1.0.0",
 | 
				
			||||||
    "axios": "^0.24.0",
 | 
					    "@flaschengeist/pricelist-old": "^1.0.0",
 | 
				
			||||||
    "pinia": "^2.0.6",
 | 
					    "@flaschengeist/schedule": "^1.0.0",
 | 
				
			||||||
    "quasar": "^2.3.3"
 | 
					    "@flaschengeist/users": "^1.0.0",
 | 
				
			||||||
 | 
					    "axios": "^1.4.0",
 | 
				
			||||||
 | 
					    "pinia": "^2.0.8",
 | 
				
			||||||
 | 
					    "quasar": "^2.11.10",
 | 
				
			||||||
 | 
					    "vue": "^3.0.0",
 | 
				
			||||||
 | 
					    "vue-router": "^4.0.0"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@capacitor/core": "^3.3.2",
 | 
					    "@capacitor/core": "^5.0.0",
 | 
				
			||||||
    "@capacitor/storage": "^1.2.3",
 | 
					    "@capacitor/preferences": "^5.0.0",
 | 
				
			||||||
    "@flaschengeist/types": "^1.0.0-alpha.10",
 | 
					    "@flaschengeist/types": "^1.0.0",
 | 
				
			||||||
    "@quasar/app": "^3.2.4",
 | 
					    "@quasar/app-webpack": "^3.7.2",
 | 
				
			||||||
    "@quasar/extras": "^1.12.2",
 | 
					    "@quasar/extras": "^1.16.3",
 | 
				
			||||||
    "@types/node": "^14.18.0",
 | 
					    "@types/node": "^14.18.0",
 | 
				
			||||||
    "@types/webpack": "^5.28.0",
 | 
					    "@types/webpack": "^5.28.0",
 | 
				
			||||||
    "@types/webpack-env": "^1.16.3",
 | 
					    "@types/webpack-env": "^1.16.3",
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "^5.5.0",
 | 
					    "@typescript-eslint/eslint-plugin": "^5.8.0",
 | 
				
			||||||
    "@typescript-eslint/parser": "^5.5.0",
 | 
					    "@typescript-eslint/parser": "^5.8.0",
 | 
				
			||||||
    "eslint": "^8.4.0",
 | 
					    "@vue/devtools": "^6.5.0",
 | 
				
			||||||
 | 
					    "eslint": "^8.5.0",
 | 
				
			||||||
    "eslint-config-prettier": "^8.3.0",
 | 
					    "eslint-config-prettier": "^8.3.0",
 | 
				
			||||||
    "eslint-plugin-prettier": "^4.0.0",
 | 
					    "eslint-plugin-prettier": "^4.0.0",
 | 
				
			||||||
    "eslint-plugin-vue": "^8.1.1",
 | 
					    "eslint-plugin-vue": "^9.14.1",
 | 
				
			||||||
    "eslint-webpack-plugin": "^3.1.1",
 | 
					    "eslint-webpack-plugin": "^4.0.1",
 | 
				
			||||||
    "modify-source-webpack-plugin": "^3.0.0",
 | 
					    "modify-source-webpack-plugin": "^4.1.0",
 | 
				
			||||||
    "prettier": "^2.5.1",
 | 
					    "prettier": "^2.5.1",
 | 
				
			||||||
    "typescript": "^4.5.2",
 | 
					    "typescript": "^4.5.4",
 | 
				
			||||||
    "vuedraggable": "^4.1.0"
 | 
					    "vuedraggable": "^4.1.0"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "prettier": {
 | 
					  "prettier": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,4 +3,4 @@ module.exports = [
 | 
				
			||||||
//  '@flaschengeist/balance',
 | 
					//  '@flaschengeist/balance',
 | 
				
			||||||
//  '@flaschengeist/schedule',
 | 
					//  '@flaschengeist/schedule',
 | 
				
			||||||
//  '@flaschengeist/pricelist',
 | 
					//  '@flaschengeist/pricelist',
 | 
				
			||||||
]
 | 
					  '@flaschengeist/schedule',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,9 +9,23 @@
 | 
				
			||||||
/* eslint-env node */
 | 
					/* eslint-env node */
 | 
				
			||||||
/* eslint-disable @typescript-eslint/no-var-requires */
 | 
					/* eslint-disable @typescript-eslint/no-var-requires */
 | 
				
			||||||
const ESLintPlugin = require('eslint-webpack-plugin');
 | 
					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 { 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 */) {
 | 
					module.exports = configure(function (/* ctx */) {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    // https://quasar.dev/quasar-cli/supporting-ts
 | 
					    // https://quasar.dev/quasar-cli/supporting-ts
 | 
				
			||||||
| 
						 | 
					@ -42,7 +56,7 @@ module.exports = configure(function (/* ctx */) {
 | 
				
			||||||
      // 'ionicons-v5',
 | 
					      // 'ionicons-v5',
 | 
				
			||||||
      // 'line-awesome',
 | 
					      // 'line-awesome',
 | 
				
			||||||
      // 'material-icons',
 | 
					      // 'material-icons',
 | 
				
			||||||
      'mdi-v6',
 | 
					      'mdi-v7',
 | 
				
			||||||
      // 'themify',
 | 
					      // 'themify',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
 | 
					      // '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
 | 
					    // Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build
 | 
				
			||||||
    build: {
 | 
					    build: {
 | 
				
			||||||
      vueRouterMode: 'history', // available values: 'hash', 'history'
 | 
					      vueRouterMode: 'history', // available values: 'hash', 'history'
 | 
				
			||||||
 | 
					      //publicPath: 'flaschengeist2',
 | 
				
			||||||
      // transpile: false,
 | 
					      // transpile: false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Add dependencies for transpiling with Babel (Array of string/regex)
 | 
					      // Add dependencies for transpiling with Babel (Array of string/regex)
 | 
				
			||||||
| 
						 | 
					@ -81,16 +95,7 @@ module.exports = configure(function (/* ctx */) {
 | 
				
			||||||
            rules: [
 | 
					            rules: [
 | 
				
			||||||
              {
 | 
					              {
 | 
				
			||||||
                test: /plugins\.ts$/,
 | 
					                test: /plugins\.ts$/,
 | 
				
			||||||
                modify: (src, filename) => {
 | 
					                operations: [operation()],
 | 
				
			||||||
                  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(',')
 | 
					 | 
				
			||||||
                  );
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
| 
						 | 
					@ -215,5 +220,8 @@ module.exports = configure(function (/* ctx */) {
 | 
				
			||||||
        // chainWebpack also available besides this extendWebpack
 | 
					        // chainWebpack also available besides this extendWebpack
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    bin: {
 | 
				
			||||||
 | 
					      linuxAndroidStudio: '/home/crimsen/.local/share/JetBrains/Toolbox/scripts/studio',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,9 @@
 | 
				
			||||||
  "bundledWebRuntime": false,
 | 
					  "bundledWebRuntime": false,
 | 
				
			||||||
  "npmClient": "yarn",
 | 
					  "npmClient": "yarn",
 | 
				
			||||||
  "webDir": "www",
 | 
					  "webDir": "www",
 | 
				
			||||||
 | 
					  "android": {
 | 
				
			||||||
 | 
					    "minWebViewVersion": 71
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  "ios": {
 | 
					  "ios": {
 | 
				
			||||||
    "allowsLinkPreview": false
 | 
					    "allowsLinkPreview": false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					  <head>
 | 
				
			||||||
 | 
					    <title>Quasar</title>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <meta charset="utf-8">
 | 
				
			||||||
 | 
					    <meta name="description" content="Quasar Capacitor App">
 | 
				
			||||||
 | 
					    <meta name="format-detection" content="telephone=no">
 | 
				
			||||||
 | 
					    <meta name="msapplication-tap-highlight" content="no">
 | 
				
			||||||
 | 
					    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, viewport-fit=cover">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style>
 | 
				
			||||||
 | 
					      .page {
 | 
				
			||||||
 | 
					        display: flex;
 | 
				
			||||||
 | 
					        flex-direction: column;
 | 
				
			||||||
 | 
					        align-items: center;
 | 
				
			||||||
 | 
					        justify-content: center;
 | 
				
			||||||
 | 
					        height: 100vh;
 | 
				
			||||||
 | 
					        text-align: center;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					  </head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <body>
 | 
				
			||||||
 | 
					    <div class="page">
 | 
				
			||||||
 | 
					      <div>
 | 
				
			||||||
 | 
					        This file will be auto-generated. Do not edit.
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <div>
 | 
				
			||||||
 | 
					        Run "quasar dev" or "quasar build" with Capacitor mode.
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
| 
						 | 
					@ -1,16 +1,16 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "name": "flaschengeist",
 | 
					  "name": "flaschengeist",
 | 
				
			||||||
  "version": "2.0.0-alpha.1",
 | 
					  "version": "2.0.0",
 | 
				
			||||||
  "description": "Modular student club administration system",
 | 
					  "description": "Modular student club administration system",
 | 
				
			||||||
  "author": "Tim Gröger <flaschengeist@wu5.de>",
 | 
					  "author": "Tim Gröger <flaschengeist@wu5.de>",
 | 
				
			||||||
  "private": true,
 | 
					  "private": true,
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@capacitor/android": "^3.3.2",
 | 
					    "@capacitor/android": "^5.0.0-beta.0",
 | 
				
			||||||
    "@capacitor/app": "^1.0.0",
 | 
					    "@capacitor/app": "^5.0.0",
 | 
				
			||||||
    "@capacitor/cli": "^3.0.0",
 | 
					    "@capacitor/cli": "^5.0.0",
 | 
				
			||||||
    "@capacitor/core": "^3.0.0",
 | 
					    "@capacitor/core": "^5.0.0",
 | 
				
			||||||
    "@capacitor/ios": "^3.0.0-beta.0",
 | 
					    "@capacitor/ios": "^5.0.0",
 | 
				
			||||||
    "@capacitor/splash-screen": "^1.0.0",
 | 
					    "@capacitor/preferences": "^5.0.0",
 | 
				
			||||||
    "@capacitor/storage": "^1.2.3"
 | 
					    "@capacitor/splash-screen": "^5.0.0"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -21,8 +21,8 @@ async function loadBaseUrl() {
 | 
				
			||||||
    console.warn('Could not load BaseURL', e);
 | 
					    console.warn('Could not load BaseURL', e);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					// eslint-disable-next-line
 | 
				
			||||||
class BackendError extends Error {}
 | 
					class BackendError extends Error { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Loading backend information
 | 
					 * Loading backend information
 | 
				
			||||||
| 
						 | 
					@ -68,8 +68,18 @@ export default boot(async ({ app, router }) => {
 | 
				
			||||||
    // Handle errors from loading the backend information
 | 
					    // Handle errors from loading the backend information
 | 
				
			||||||
    if (error instanceof BackendError || isAxiosError(error)) {
 | 
					    if (error instanceof BackendError || isAxiosError(error)) {
 | 
				
			||||||
      router.isReady().finally(() => {
 | 
					      router.isReady().finally(() => {
 | 
				
			||||||
        if (Platform.is.capacitor) void router.push({ name: 'setup_backend' });
 | 
					        // if (Platform.is.capacitor) void router.push({ name: 'setup_backend' });
 | 
				
			||||||
        else void router.push({ name: 'offline', params: { refresh: 1 } });
 | 
					        if (Platform.is.capacitor) {
 | 
				
			||||||
 | 
					          //void router.push({ name: 'setup_backend' })
 | 
				
			||||||
 | 
					          Notify.create({
 | 
				
			||||||
 | 
					            type: 'negative',
 | 
				
			||||||
 | 
					            message:
 | 
				
			||||||
 | 
					              'Backend nicht erreichbar! Prüfe deine Internetverbindung oder probiere es später nochmal.',
 | 
				
			||||||
 | 
					            timeout: 0,
 | 
				
			||||||
 | 
					            icon: 'mdi-alert-circle-outline',
 | 
				
			||||||
 | 
					            closeBtn: true,
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        } else void router.push({ name: 'offline', params: { refresh: 1 } });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    } else if (typeof error === 'string') {
 | 
					    } else if (typeof error === 'string') {
 | 
				
			||||||
      // Handle plugin not found errors
 | 
					      // Handle plugin not found errors
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -234,6 +234,12 @@ function loadPlugin(
 | 
				
			||||||
    Array.prototype.push.apply(loadedPlugins.widgets, plugin.widgets);
 | 
					    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({
 | 
					  loadedPlugins.plugins.push({
 | 
				
			||||||
    id: plugin.id,
 | 
					    id: plugin.id,
 | 
				
			||||||
    name: plugin.name,
 | 
					    name: plugin.name,
 | 
				
			||||||
| 
						 | 
					@ -252,6 +258,7 @@ export async function loadPlugins(backend: FG.Backend, baseRoutes: RouteRecordRa
 | 
				
			||||||
    shortcuts: [],
 | 
					    shortcuts: [],
 | 
				
			||||||
    outerShortcuts: [],
 | 
					    outerShortcuts: [],
 | 
				
			||||||
    widgets: [],
 | 
					    widgets: [],
 | 
				
			||||||
 | 
					    settingWidgets: [],
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Wait for all plugins to be loaded
 | 
					  // Wait for all plugins to be loaded
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
      class="q-ma-xs"
 | 
					      class="q-ma-xs"
 | 
				
			||||||
      title="Löschen"
 | 
					      title="Löschen"
 | 
				
			||||||
      style="position: absolute; top: 0; right: 0; z-index: 999"
 | 
					      style="position: absolute; top: 0; right: 0; z-index: 999"
 | 
				
			||||||
      @click="dismiss"
 | 
					      @click.stop.prevent="dismiss"
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
    <q-card-section class="q-pa-xs">
 | 
					    <q-card-section class="q-pa-xs">
 | 
				
			||||||
      <div class="text-overline">{{ dateString }}</div>
 | 
					      <div class="text-overline">{{ dateString }}</div>
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@
 | 
				
			||||||
        flat
 | 
					        flat
 | 
				
			||||||
        dense
 | 
					        dense
 | 
				
			||||||
        size="sm"
 | 
					        size="sm"
 | 
				
			||||||
        @click="accept"
 | 
					        @click.stop.prevent="accept"
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      <q-btn
 | 
					      <q-btn
 | 
				
			||||||
        v-if="modelValue.reject"
 | 
					        v-if="modelValue.reject"
 | 
				
			||||||
| 
						 | 
					@ -44,7 +44,7 @@
 | 
				
			||||||
        flat
 | 
					        flat
 | 
				
			||||||
        dense
 | 
					        dense
 | 
				
			||||||
        size="sm"
 | 
					        size="sm"
 | 
				
			||||||
        @click="reject"
 | 
					        @click.stop.prevent="reject"
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
    </q-card-actions>
 | 
					    </q-card-actions>
 | 
				
			||||||
  </q-card>
 | 
					  </q-card>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
  <q-layout view="hHh Lpr lff">
 | 
					  <q-layout view="hHh Lpr lff">
 | 
				
			||||||
    <q-header elevated class="bg-primary text-white">
 | 
					    <q-header elevated class="bg-primary text-white">
 | 
				
			||||||
      <q-toolbar>
 | 
					      <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>
 | 
					        <q-toolbar-title>
 | 
				
			||||||
          <router-link :to="{ name: 'dashboard' }" style="text-decoration: none; color: inherit">
 | 
					          <router-link :to="{ name: 'dashboard' }" style="text-decoration: none; color: inherit">
 | 
				
			||||||
| 
						 | 
					@ -56,32 +56,44 @@
 | 
				
			||||||
      side="left"
 | 
					      side="left"
 | 
				
			||||||
      bordered
 | 
					      bordered
 | 
				
			||||||
      :mini="leftDrawerMini"
 | 
					      :mini="leftDrawerMini"
 | 
				
			||||||
      @click.capture="openMenu"
 | 
					      @click.capture="openMenuMini"
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <!-- Plugins -->
 | 
					      <!-- Plugins -->
 | 
				
			||||||
      <essential-expansion-link
 | 
					      <q-scroll-area class="fit">
 | 
				
			||||||
        v-for="(entry, index) in mainLinks"
 | 
					        <essential-expansion-link
 | 
				
			||||||
        :key="'plugin' + index"
 | 
					          v-for="(entry, index) in mainLinks"
 | 
				
			||||||
        :entry="entry"
 | 
					          :key="'plugin' + index"
 | 
				
			||||||
        @add-short-cut="addShortcut"
 | 
					          :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-separator />
 | 
					        <q-separator />
 | 
				
			||||||
        <q-item clickable tag="a" target="self" @click="logout">
 | 
					        <essential-link
 | 
				
			||||||
          <q-item-section avatar>
 | 
					          v-for="(entry, index) in essentials"
 | 
				
			||||||
            <q-icon name="mdi-exit-to-app" />
 | 
					          :key="'essential' + index"
 | 
				
			||||||
          </q-item-section>
 | 
					          :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-section>
 | 
				
			||||||
            <q-item-label>Logout</q-item-label>
 | 
					              <q-item-label>Logout</q-item-label>
 | 
				
			||||||
          </q-item-section>
 | 
					            </q-item-section>
 | 
				
			||||||
        </q-item>
 | 
					          </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>
 | 
					      </div>
 | 
				
			||||||
    </q-drawer>
 | 
					    </q-drawer>
 | 
				
			||||||
    <q-page-container>
 | 
					    <q-page-container>
 | 
				
			||||||
| 
						 | 
					@ -125,7 +137,7 @@ export default defineComponent({
 | 
				
			||||||
    const mainStore = useMainStore();
 | 
					    const mainStore = useMainStore();
 | 
				
			||||||
    const flaschengeist = inject<FG_Plugin.Flaschengeist>('flaschengeist');
 | 
					    const flaschengeist = inject<FG_Plugin.Flaschengeist>('flaschengeist');
 | 
				
			||||||
    const leftDrawer = ref(!Platform.is.mobile);
 | 
					    const leftDrawer = ref(!Platform.is.mobile);
 | 
				
			||||||
    const leftDrawerMini = ref(false);
 | 
					    const leftDrawerMini = ref(true);
 | 
				
			||||||
    const mainLinks = flaschengeist?.menuLinks || [];
 | 
					    const mainLinks = flaschengeist?.menuLinks || [];
 | 
				
			||||||
    const notifications = computed(() => mainStore.notifications.slice().reverse());
 | 
					    const notifications = computed(() => mainStore.notifications.slice().reverse());
 | 
				
			||||||
    const polling = ref(NaN);
 | 
					    const polling = ref(NaN);
 | 
				
			||||||
| 
						 | 
					@ -138,9 +150,11 @@ export default defineComponent({
 | 
				
			||||||
      void mainStore.getShortcuts();
 | 
					      void mainStore.getShortcuts();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    onBeforeUnmount(() => window.clearInterval(polling.value));
 | 
					    onBeforeUnmount(() => window.clearInterval(polling.value));
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
    function openMenu(event: { target: HTMLInputElement }) {
 | 
					    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 {
 | 
					      else {
 | 
				
			||||||
        if (!leftDrawer.value || leftDrawerMini.value) {
 | 
					        if (!leftDrawer.value || leftDrawerMini.value) {
 | 
				
			||||||
          leftDrawer.value = true;
 | 
					          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() {
 | 
					    function logout() {
 | 
				
			||||||
      void router.push({ name: 'login', params: { logout: 'logout' } });
 | 
					      void router.push({ name: 'login', params: { logout: 'logout' } });
 | 
				
			||||||
      void mainStore.logout();
 | 
					      void mainStore.logout();
 | 
				
			||||||
| 
						 | 
					@ -214,6 +234,7 @@ export default defineComponent({
 | 
				
			||||||
      notifications,
 | 
					      notifications,
 | 
				
			||||||
      noPermission,
 | 
					      noPermission,
 | 
				
			||||||
      openMenu,
 | 
					      openMenu,
 | 
				
			||||||
 | 
					      openMenuMini,
 | 
				
			||||||
      remove,
 | 
					      remove,
 | 
				
			||||||
      requestPermission,
 | 
					      requestPermission,
 | 
				
			||||||
      useNative,
 | 
					      useNative,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,9 +2,9 @@
 | 
				
			||||||
  <q-page
 | 
					  <q-page
 | 
				
			||||||
    padding
 | 
					    padding
 | 
				
			||||||
    style="grid-auto-rows: 1fr"
 | 
					    style="grid-auto-rows: 1fr"
 | 
				
			||||||
    class="fit row justify-around items-start q-col-gutter-sm"
 | 
					    class="row justify-center content-center items-center q-col-gutter-lg"
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
    <div v-for="(item, index) in widgets" :key="index" class="col-4 full-height col-sm-6 col-xs-12">
 | 
					    <div v-for="(item, index) in widgets" :key="index" class="full-height col-sm-6 col-xs-12">
 | 
				
			||||||
      <component :is="item.widget" />
 | 
					      <component :is="item.widget" />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </q-page>
 | 
					  </q-page>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -121,11 +121,24 @@ export default defineComponent({
 | 
				
			||||||
        if (quasar.platform.is.capacitor)
 | 
					        if (quasar.platform.is.capacitor)
 | 
				
			||||||
          await sessionStore.updateSession(14 * 24 * 60 * 60, mainStore.currentSession.token);
 | 
					          await sessionStore.updateSession(14 * 24 * 60 * 60, mainStore.currentSession.token);
 | 
				
			||||||
        // Redirect user to previous page, if any.
 | 
					        // Redirect user to previous page, if any.
 | 
				
			||||||
        const redirect =
 | 
					        // there are different redirects possible:
 | 
				
			||||||
          router.currentRoute.value.redirectedFrom || 'redirect' in router.currentRoute.value.query
 | 
					        // 1. when explicitely entered
 | 
				
			||||||
            ? { path: router.currentRoute.value.query.redirect as string }
 | 
					        //    a) http://localhost:8080/ -> should be redirected to mainRoute
 | 
				
			||||||
            : mainRoute;
 | 
					        //    b) http://localhost:8080/in/user/settings -> should be redirected to in/user/settings
 | 
				
			||||||
 | 
					        // 2. when automatically logged out:
 | 
				
			||||||
 | 
					        //    http://localhost:8080/login?redirect=/in/user/settings
 | 
				
			||||||
 | 
					        //    -> should be redirected to in/user/settings
 | 
				
			||||||
 | 
					        var redirect;
 | 
				
			||||||
 | 
					        if (router.currentRoute.value.redirectedFrom) {
 | 
				
			||||||
 | 
					          redirect = router.currentRoute.value.redirectedFrom.path;
 | 
				
			||||||
 | 
					          if (redirect === '/') {
 | 
				
			||||||
 | 
					            redirect = mainRoute;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else if ('redirect' in router.currentRoute.value.query) {
 | 
				
			||||||
 | 
					          redirect = { path: router.currentRoute.value.query.redirect as string };
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          redirect = mainRoute;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        void router.push(redirect);
 | 
					        void router.push(redirect);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        // Login failed, notify and reset form
 | 
					        // Login failed, notify and reset form
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "extends": "@quasar/app/tsconfig-preset",
 | 
					  "extends": "@quasar/app-webpack/tsconfig-preset",
 | 
				
			||||||
  "target": "esnext",
 | 
					  "target": "esnext",
 | 
				
			||||||
  "compilerOptions": {
 | 
					  "compilerOptions": {
 | 
				
			||||||
    "baseUrl": ".",
 | 
					    "baseUrl": ".",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue