Merge branch 'develop' into feature/balance
This commit is contained in:
		
						commit
						f98b3d72fc
					
				| 
						 | 
					@ -0,0 +1,53 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <q-expansion-item v-if="isGranted(entry)" clickable tag="a" target="self" :label='title' :icon='entry.icon' expand-separator>
 | 
				
			||||||
 | 
					      <q-list class='q-ml-lg'>
 | 
				
			||||||
 | 
					        <div v-for='child in entry.children' :key='child.link'>
 | 
				
			||||||
 | 
					    <q-item v-if='isGranted(child)' clickable :to='{name: child.link}'>
 | 
				
			||||||
 | 
					      <q-menu context-menu>
 | 
				
			||||||
 | 
					        <q-btn v-close-popup label='Verknüpfung erstellen' dense @click='addShortCut(child)'/>
 | 
				
			||||||
 | 
					      </q-menu>
 | 
				
			||||||
 | 
					      <q-item-section avatar>
 | 
				
			||||||
 | 
					        <q-icon :name='child.icon' />
 | 
				
			||||||
 | 
					      </q-item-section>
 | 
				
			||||||
 | 
					      <q-item-section>
 | 
				
			||||||
 | 
					        <q-item-label>
 | 
				
			||||||
 | 
					          {{child.title}}
 | 
				
			||||||
 | 
					        </q-item-label>
 | 
				
			||||||
 | 
					      </q-item-section>
 | 
				
			||||||
 | 
					    </q-item>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </q-list>
 | 
				
			||||||
 | 
					  </q-expansion-item>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { computed, defineComponent, PropType } from 'vue';
 | 
				
			||||||
 | 
					import { hasPermissions } from 'src/utils/permission';
 | 
				
			||||||
 | 
					import { FG_Plugin } from 'src/plugins';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					  name: 'EssentialExpansionLink',
 | 
				
			||||||
 | 
					  components: {  },
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    entry: {
 | 
				
			||||||
 | 
					      type: Object as PropType<FG_Plugin.MenuLink>,
 | 
				
			||||||
 | 
					      required: true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  emits: {
 | 
				
			||||||
 | 
					    addShortCut: (val: FG_Plugin.MenuLink) => val.link,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  setup(props, {emit}) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function isGranted(val: FG_Plugin.MenuLink) { return hasPermissions(val.permissions || [])};
 | 
				
			||||||
 | 
					    const title = computed(() =>
 | 
				
			||||||
 | 
					      typeof props.entry.title === 'object' ? props.entry.title.value : props.entry.title
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    function addShortCut(val: FG_Plugin.MenuLink) {
 | 
				
			||||||
 | 
					      emit('addShortCut', val)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { isGranted, title, addShortCut};
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,9 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <q-btn v-if="isGranted" flat dense :icon="shortcut.icon" :to="{ name: shortcut.link }" />
 | 
					  <q-btn v-if="isGranted" flat dense :icon="shortcut.icon" :to="{ name: shortcut.link }" round>
 | 
				
			||||||
 | 
					    <q-menu v-if="context" context-menu>
 | 
				
			||||||
 | 
					      <q-btn v-close-popup label="Verknüpfung entfernen" @click="deleteShortcut" />
 | 
				
			||||||
 | 
					    </q-menu>
 | 
				
			||||||
 | 
					  </q-btn>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
| 
						 | 
					@ -12,12 +16,22 @@ export default defineComponent({
 | 
				
			||||||
  props: {
 | 
					  props: {
 | 
				
			||||||
    shortcut: {
 | 
					    shortcut: {
 | 
				
			||||||
      required: true,
 | 
					      required: true,
 | 
				
			||||||
      type: Object as PropType<FG_Plugin.Shortcut>,
 | 
					      type: Object as PropType<FG_Plugin.Shortcut | FG_Plugin.MenuLink>,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    context: {
 | 
				
			||||||
 | 
					      type: Boolean,
 | 
				
			||||||
 | 
					      default: false,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  setup(props) {
 | 
					  emits: {
 | 
				
			||||||
 | 
					    deleteShortcut: (val: FG_Plugin.MenuLink | FG_Plugin.Shortcut) => val.link,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  setup(props, { emit }) {
 | 
				
			||||||
    const isGranted = computed(() => hasPermissions(props.shortcut.permissions || []));
 | 
					    const isGranted = computed(() => hasPermissions(props.shortcut.permissions || []));
 | 
				
			||||||
    return { isGranted };
 | 
					    function deleteShortcut() {
 | 
				
			||||||
 | 
					      emit('deleteShortcut', props.shortcut);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return { isGranted, deleteShortcut };
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,8 +14,10 @@
 | 
				
			||||||
        </q-toolbar-title>
 | 
					        </q-toolbar-title>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- Hier kommen die Shortlinks hin -->
 | 
					        <!-- Hier kommen die Shortlinks hin -->
 | 
				
			||||||
        <q-btn icon="mdi-message-bulleted" flat dense
 | 
					        <q-btn icon="mdi-message-bulleted" flat dense round>
 | 
				
			||||||
          ><q-badge color="negative" floating>{{ notifications.length }}</q-badge>
 | 
					          <q-badge color="negative" floating>
 | 
				
			||||||
 | 
					            {{ notifications.length }}
 | 
				
			||||||
 | 
					          </q-badge>
 | 
				
			||||||
          <q-menu style="max-height: 400px; overflow: auto">
 | 
					          <q-menu style="max-height: 400px; overflow: auto">
 | 
				
			||||||
            <q-btn
 | 
					            <q-btn
 | 
				
			||||||
              v-if="useNative && noPermission"
 | 
					              v-if="useNative && noPermission"
 | 
				
			||||||
| 
						 | 
					@ -33,11 +35,11 @@
 | 
				
			||||||
            <div v-else class="q-pa-sm">Keine neuen Benachrichtigungen</div>
 | 
					            <div v-else class="q-pa-sm">Keine neuen Benachrichtigungen</div>
 | 
				
			||||||
          </q-menu>
 | 
					          </q-menu>
 | 
				
			||||||
        </q-btn>
 | 
					        </q-btn>
 | 
				
			||||||
        <shortcut-link
 | 
					        <drag v-model="shortCuts" item-key="link" ghost-class="ghost">
 | 
				
			||||||
          v-for="(shortcut, index) in shortcuts"
 | 
					          <template #item="{ element }">
 | 
				
			||||||
          :key="'shortcut' + index"
 | 
					            <shortcut-link :shortcut="element" context @delete-shortcut="deleteShortcut" />
 | 
				
			||||||
          :shortcut="shortcut"
 | 
					          </template>
 | 
				
			||||||
        />
 | 
					        </drag>
 | 
				
			||||||
        <q-btn flat round dense icon="mdi-exit-to-app" @click="logout()" />
 | 
					        <q-btn flat round dense icon="mdi-exit-to-app" @click="logout()" />
 | 
				
			||||||
      </q-toolbar>
 | 
					      </q-toolbar>
 | 
				
			||||||
    </q-header>
 | 
					    </q-header>
 | 
				
			||||||
| 
						 | 
					@ -51,17 +53,11 @@
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <!-- Plugins -->
 | 
					      <!-- Plugins -->
 | 
				
			||||||
      <q-list>
 | 
					      <q-list>
 | 
				
			||||||
        <essential-link
 | 
					        <essential-expansion-link
 | 
				
			||||||
          v-for="(entry, index) in mainLinks"
 | 
					          v-for="(entry, index) in mainLinks"
 | 
				
			||||||
          :key="'plugin' + index"
 | 
					          :key="'plugin' + index"
 | 
				
			||||||
          :entry="entry"
 | 
					          :entry="entry"
 | 
				
			||||||
        />
 | 
					          @add-short-cut="addShortcut"
 | 
				
			||||||
        <q-separator />
 | 
					 | 
				
			||||||
        <!-- Plugin functions -->
 | 
					 | 
				
			||||||
        <essential-link
 | 
					 | 
				
			||||||
          v-for="(entry, index) in subLinks"
 | 
					 | 
				
			||||||
          :key="'childPlugin' + index"
 | 
					 | 
				
			||||||
          :entry="entry"
 | 
					 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
      </q-list>
 | 
					      </q-list>
 | 
				
			||||||
      <q-separator />
 | 
					      <q-separator />
 | 
				
			||||||
| 
						 | 
					@ -81,13 +77,23 @@
 | 
				
			||||||
import EssentialLink from 'src/components/navigation/EssentialLink.vue';
 | 
					import EssentialLink from 'src/components/navigation/EssentialLink.vue';
 | 
				
			||||||
import ShortcutLink from 'src/components/navigation/ShortcutLink.vue';
 | 
					import ShortcutLink from 'src/components/navigation/ShortcutLink.vue';
 | 
				
			||||||
import Notification from 'src/components/Notification.vue';
 | 
					import Notification from 'src/components/Notification.vue';
 | 
				
			||||||
import { defineComponent, ref, inject, computed, onBeforeMount, onBeforeUnmount } from 'vue';
 | 
					import {
 | 
				
			||||||
 | 
					  defineComponent,
 | 
				
			||||||
 | 
					  ref,
 | 
				
			||||||
 | 
					  inject,
 | 
				
			||||||
 | 
					  computed,
 | 
				
			||||||
 | 
					  onBeforeMount,
 | 
				
			||||||
 | 
					  onBeforeUnmount,
 | 
				
			||||||
 | 
					  ComponentPublicInstance,
 | 
				
			||||||
 | 
					} from 'vue';
 | 
				
			||||||
import { useMainStore } from 'src/stores';
 | 
					import { useMainStore } from 'src/stores';
 | 
				
			||||||
import { FG_Plugin } from 'src/plugins';
 | 
					import { FG_Plugin } from 'src/plugins';
 | 
				
			||||||
import { useRouter } from 'vue-router';
 | 
					import { useRouter } from 'vue-router';
 | 
				
			||||||
import { Screen } from 'quasar';
 | 
					import { Screen } from 'quasar';
 | 
				
			||||||
import config from 'src/config';
 | 
					import config from 'src/config';
 | 
				
			||||||
 | 
					import EssentialExpansionLink from 'components/navigation/EssentialExpansionLink.vue';
 | 
				
			||||||
 | 
					import draggable from 'vuedraggable';
 | 
				
			||||||
 | 
					const drag: ComponentPublicInstance = <ComponentPublicInstance>draggable;
 | 
				
			||||||
const essentials: FG_Plugin.MenuLink[] = [
 | 
					const essentials: FG_Plugin.MenuLink[] = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    title: 'Über Flaschengeist',
 | 
					    title: 'Über Flaschengeist',
 | 
				
			||||||
| 
						 | 
					@ -98,28 +104,23 @@ const essentials: FG_Plugin.MenuLink[] = [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default defineComponent({
 | 
					export default defineComponent({
 | 
				
			||||||
  name: 'MainLayout',
 | 
					  name: 'MainLayout',
 | 
				
			||||||
  components: { EssentialLink, ShortcutLink, Notification },
 | 
					  components: { EssentialExpansionLink, EssentialLink, ShortcutLink, Notification, drag },
 | 
				
			||||||
  setup() {
 | 
					  setup() {
 | 
				
			||||||
    const router = useRouter();
 | 
					    const router = useRouter();
 | 
				
			||||||
    const mainStore = useMainStore();
 | 
					    const mainStore = useMainStore();
 | 
				
			||||||
    const flaschengeist = inject<FG_Plugin.Flaschengeist>('flaschengeist');
 | 
					    const flaschengeist = inject<FG_Plugin.Flaschengeist>('flaschengeist');
 | 
				
			||||||
    const leftDrawer = ref(true);
 | 
					    const leftDrawer = ref(true);
 | 
				
			||||||
    const leftDrawerMini = ref(false);
 | 
					    const leftDrawerMini = ref(false);
 | 
				
			||||||
    const shortcuts = flaschengeist?.shortcuts || [];
 | 
					 | 
				
			||||||
    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);
 | 
				
			||||||
    const useNative = 'Notification' in window && window.Notification !== undefined;
 | 
					    const useNative = 'Notification' in window && window.Notification !== undefined;
 | 
				
			||||||
    const noPermission = ref(!useNative || window.Notification.permission !== 'granted');
 | 
					    const noPermission = ref(!useNative || window.Notification.permission !== 'granted');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const subLinks = computed(() => {
 | 
					 | 
				
			||||||
      const matched = router.currentRoute.value.matched[1];
 | 
					 | 
				
			||||||
      return flaschengeist?.menuLinks.find((link) => matched.name == link.link)?.children;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    onBeforeMount(() => {
 | 
					    onBeforeMount(() => {
 | 
				
			||||||
      polling.value = window.setInterval(() => pollNotification(), config.pollingInterval);
 | 
					      polling.value = window.setInterval(() => pollNotification(), config.pollingInterval);
 | 
				
			||||||
      pollNotification();
 | 
					      pollNotification();
 | 
				
			||||||
 | 
					      void mainStore.getShortcuts();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    onBeforeUnmount(() => window.clearInterval(polling.value));
 | 
					    onBeforeUnmount(() => window.clearInterval(polling.value));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -165,6 +166,30 @@ export default defineComponent({
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const shortCuts = computed({
 | 
				
			||||||
 | 
					      get: () => mainStore.shortcuts,
 | 
				
			||||||
 | 
					      set: (val: Array<FG_Plugin.MenuLink>) => {
 | 
				
			||||||
 | 
					        mainStore.shortcuts = val;
 | 
				
			||||||
 | 
					        void mainStore.setShortcuts();
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function addShortcut(val: FG_Plugin.MenuLink) {
 | 
				
			||||||
 | 
					      const idx = shortCuts.value.findIndex((a: FG_Plugin.MenuLink) => a.link === val.link);
 | 
				
			||||||
 | 
					      if (idx < 0) {
 | 
				
			||||||
 | 
					        shortCuts.value.push(val);
 | 
				
			||||||
 | 
					        void mainStore.setShortcuts();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function deleteShortcut(val: FG_Plugin.MenuLink) {
 | 
				
			||||||
 | 
					      const idx = shortCuts.value.findIndex((a: FG_Plugin.MenuLink) => a.link === val.link);
 | 
				
			||||||
 | 
					      if (idx > -1) {
 | 
				
			||||||
 | 
					        shortCuts.value.splice(idx, 1);
 | 
				
			||||||
 | 
					        void mainStore.setShortcuts();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      essentials,
 | 
					      essentials,
 | 
				
			||||||
      leftDrawer,
 | 
					      leftDrawer,
 | 
				
			||||||
| 
						 | 
					@ -176,10 +201,16 @@ export default defineComponent({
 | 
				
			||||||
      openMenu,
 | 
					      openMenu,
 | 
				
			||||||
      remove,
 | 
					      remove,
 | 
				
			||||||
      requestPermission,
 | 
					      requestPermission,
 | 
				
			||||||
      shortcuts,
 | 
					 | 
				
			||||||
      subLinks,
 | 
					 | 
				
			||||||
      useNative,
 | 
					      useNative,
 | 
				
			||||||
 | 
					      shortCuts,
 | 
				
			||||||
 | 
					      addShortcut,
 | 
				
			||||||
 | 
					      deleteShortcut,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					<style scoped lang="sass">
 | 
				
			||||||
 | 
					.ghost
 | 
				
			||||||
 | 
					  opacity: 0.5
 | 
				
			||||||
 | 
					  background: $accent
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,9 +36,15 @@
 | 
				
			||||||
        </q-form>
 | 
					        </q-form>
 | 
				
			||||||
      </q-card-section>
 | 
					      </q-card-section>
 | 
				
			||||||
      <div class="row justify-end">
 | 
					      <div class="row justify-end">
 | 
				
			||||||
        <q-btn v-if='$q.platform.is.cordova || $q.platform.is.electron' flat round icon="mdi-menu-down"  @click="openServerSettings" />
 | 
					        <q-btn
 | 
				
			||||||
 | 
					          v-if="$q.platform.is.cordova || $q.platform.is.electron"
 | 
				
			||||||
 | 
					          flat
 | 
				
			||||||
 | 
					          round
 | 
				
			||||||
 | 
					          icon="mdi-menu-down"
 | 
				
			||||||
 | 
					          @click="openServerSettings"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <q-slide-transition v-if='$q.platform.is.cordova || $q.platform.is.electron'>
 | 
					      <q-slide-transition v-if="$q.platform.is.cordova || $q.platform.is.electron">
 | 
				
			||||||
        <div v-show="visible">
 | 
					        <div v-show="visible">
 | 
				
			||||||
          <q-separator />
 | 
					          <q-separator />
 | 
				
			||||||
          <q-card-section>
 | 
					          <q-card-section>
 | 
				
			||||||
| 
						 | 
					@ -93,7 +99,7 @@ export default defineComponent({
 | 
				
			||||||
      const status = await mainStore.login(userid.value, password.value);
 | 
					      const status = await mainStore.login(userid.value, password.value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (status === true) {
 | 
					      if (status === true) {
 | 
				
			||||||
        mainStore.user = (await useUserStore().getUser(userid.value)) || undefined;
 | 
					        mainStore.user = (await useUserStore().getUser(userid.value, true)) || undefined;
 | 
				
			||||||
        const x = router.currentRoute.value.query['redirect'];
 | 
					        const x = router.currentRoute.value.query['redirect'];
 | 
				
			||||||
        void router.push(typeof x === 'string' ? { path: x } : mainRoute);
 | 
					        void router.push(typeof x === 'string' ? { path: x } : mainRoute);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ export const useMainStore = defineStore({
 | 
				
			||||||
    session: loadCurrentSession(),
 | 
					    session: loadCurrentSession(),
 | 
				
			||||||
    user: loadUser(),
 | 
					    user: loadUser(),
 | 
				
			||||||
    notifications: [] as Array<FG_Plugin.Notification>,
 | 
					    notifications: [] as Array<FG_Plugin.Notification>,
 | 
				
			||||||
 | 
					    shortcuts: [] as FG_Plugin.MenuLink[],
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getters: {
 | 
					  getters: {
 | 
				
			||||||
| 
						 | 
					@ -139,6 +140,17 @@ export const useMainStore = defineStore({
 | 
				
			||||||
            this.notifications.splice(idx, this.notifications.length - idx - 1);
 | 
					            this.notifications.splice(idx, this.notifications.length - idx - 1);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async getShortcuts() {
 | 
				
			||||||
 | 
					      const { data } = await api.get<Array<FG_Plugin.MenuLink>>(
 | 
				
			||||||
 | 
					        `users/${this.currentUser.userid}/shortcuts`
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      this.shortcuts = data;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async setShortcuts() {
 | 
				
			||||||
 | 
					      await api.put(`users/${this.currentUser.userid}/shortcuts`, this.shortcuts);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue