release v2.0.0 #4

Merged
crimsen merged 481 commits from develop into master 2024-01-18 15:15:08 +00:00
29 changed files with 95 additions and 55 deletions
Showing only changes of commit 1b478d7680 - Show all commits

View File

@ -2,7 +2,7 @@ import config from 'src/config';
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { LocalStorage, Notify } from 'quasar'; import { LocalStorage, Notify } from 'quasar';
import axios, { AxiosError } from 'axios'; import axios, { AxiosError } from 'axios';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
const api = axios.create(); const api = axios.create();

View File

@ -1,5 +1,5 @@
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { hasPermissions } from 'src/utils/permission'; import { hasPermissions } from 'src/utils/permission';
import { RouteRecord } from 'vue-router'; import { RouteRecord } from 'vue-router';

View File

@ -5,6 +5,7 @@ import { api } from 'boot/axios';
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
import { Notify } from 'quasar'; import { Notify } from 'quasar';
import { notEmpty } from 'src/utils/validators';
const config: { [key: string]: Array<string> } = { const config: { [key: string]: Array<string> } = {
// Do not change required Modules !! // Do not change required Modules !!
@ -56,6 +57,9 @@ interface Backend {
} }
export { Backend }; export { Backend };
// Handle Notifications
export const translateNotification = (note: FG.Notification): FG_Plugin.Notification => note;
// Combine routes, shortcuts and widgets from plugins // Combine routes, shortcuts and widgets from plugins
/** /**
@ -288,6 +292,7 @@ function loadPlugin(
loadedPlugins.plugins.push({ loadedPlugins.plugins.push({
name: plugin.name, name: plugin.name,
version: plugin.version, version: plugin.version,
notification: plugin.notification?.bind({}) || translateNotification,
}); });
return plugin; return plugin;

View File

@ -1,6 +1,6 @@
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
export default boot(({ app }) => { export default boot(({ app }) => {
app.use(createPinia()); app.use(createPinia());

View File

@ -1,7 +1,7 @@
<template> <template>
<q-card bordered class="row q-ma-xs q-pa-xs" style="position: relative"> <q-card bordered class="row q-ma-xs q-pa-xs" style="position: relative">
<div class="col-12 text-weight-light">{{ dateString }}</div> <div class="col-12 text-weight-light">{{ dateString }}</div>
<div class="col-12">{{ modelValue.text }}</div> <div :id="`ntfctn-${modelValue.id}`" class="col-12" @click="click">{{ modelValue.text }}</div>
<q-btn <q-btn
round round
dense dense
@ -12,32 +12,58 @@
style="position: absolute; top: 0; right: 0" style="position: absolute; top: 0; right: 0"
@click="remove" @click="remove"
/> />
<q-btn
v-if="modelValue.accept !== undefined"
round
dense
icon="close"
size="sm"
color="positive"
class="q-ma-xs"
style="position: absolute; top: 50px; right: 0"
@click="accept"
/>
</q-card> </q-card>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, computed } from 'vue'; import { defineComponent, PropType, computed } from 'vue';
import { formatDateTime } from 'src/utils/datetime'; import { formatDateTime } from 'src/utils/datetime';
import { FG_Plugin } from 'src/plugins';
import { useRouter } from 'vue-router';
export default defineComponent({ export default defineComponent({
name: 'Notification', name: 'Notification',
props: { props: {
modelValue: { modelValue: {
required: true, required: true,
type: Object as PropType<FG.Notification>, type: Object as PropType<FG_Plugin.Notification>,
}, },
}, },
emits: { emits: {
remove: (id: number) => !!id, remove: (id: number) => !!id,
}, },
setup(props, { emit }) { setup(props, { emit }) {
const router = useRouter();
const dateString = computed(() => formatDateTime(props.modelValue.time, true, true)); const dateString = computed(() => formatDateTime(props.modelValue.time, true, true));
async function click() {
if (props.modelValue.link) await router.push(props.modelValue.link);
}
function accept() {
if (props.modelValue.accept)
void props.modelValue.accept().then(() => emit('remove', props.modelValue.id));
else emit('remove', props.modelValue.id);
}
function remove() { function remove() {
if (props.modelValue.reject)
void props.modelValue.reject().then(() => emit('remove', props.modelValue.id));
emit('remove', props.modelValue.id); emit('remove', props.modelValue.id);
} }
return { dateString, remove }; return { accept, click, dateString, remove };
}, },
}); });
</script> </script>

View File

@ -106,7 +106,7 @@ import ShortcutLink from 'src/components/navigation/ShortcutLink.vue';
import Notification from 'src/components/Notification.vue'; import Notification from 'src/components/Notification.vue';
import { Screen } from 'quasar'; import { Screen } from 'quasar';
import { defineComponent, ref, inject, computed, onBeforeMount } from 'vue'; import { defineComponent, ref, inject, computed, onBeforeMount } from 'vue';
import { useMainStore } from 'src/store'; 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';

View File

@ -54,7 +54,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { Loading, Notify } from 'quasar'; import { Loading, Notify } from 'quasar';
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';

View File

@ -37,7 +37,7 @@
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { Loading, Notify } from 'quasar'; import { Loading, Notify } from 'quasar';
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
export default defineComponent({ export default defineComponent({
// name: 'PageName' // name: 'PageName'

19
src/plugins.d.ts vendored
View File

@ -1,4 +1,4 @@
import { RouteRecordRaw, RouteRecordName } from 'vue-router'; import { RouteLocationRaw, RouteRecordRaw, RouteRecordName } from 'vue-router';
import { Component, ComputedRef } from 'vue'; import { Component, ComputedRef } from 'vue';
declare namespace FG_Plugin { declare namespace FG_Plugin {
@ -19,6 +19,8 @@ declare namespace FG_Plugin {
outerRoutes?: MenuRoute[]; outerRoutes?: MenuRoute[];
/** Routes without menu links, for internal usage */ /** Routes without menu links, for internal usage */
internalRoutes?: NamedRouteRecordRaw[]; internalRoutes?: NamedRouteRecordRaw[];
/** Handle notifications, defaults to boot/plugins.ts:translateNotification() */
notification?(msg: FG.Notification): FG_Plugin.Notification;
} }
/** /**
@ -39,12 +41,27 @@ declare namespace FG_Plugin {
widgets: Widget[]; widgets: Widget[];
} }
/**
* Interface for a frontend notification
*/
interface Notification extends FG.Notification {
/** If set a button for accepting will be shown, this function will get called before deleting the notification */
accept?(): Promise<void>;
/** If set this function is called before the notification gets deleted */
reject?(): Promise<void>;
/** If set the notification text is interpreted as a link to this location */
link?: RouteLocationRaw;
/** If set this icon is used */
icon?: string;
}
/** /**
* Loaded Flaschengeist plugin * Loaded Flaschengeist plugin
*/ */
interface LoadedPlugin { interface LoadedPlugin {
name: string; name: string;
version: string; version: string;
notification(msg: FG.Notification): FG_Plugin.Notification;
} }
/** /**

View File

@ -62,7 +62,7 @@ import { hasPermission } from 'src/utils/permission';
import BalanceHeader from '../components/BalanceHeader.vue'; import BalanceHeader from '../components/BalanceHeader.vue';
import PERMISSIONS from '../permissions'; import PERMISSIONS from '../permissions';
import { useBalanceStore } from '../store'; import { useBalanceStore } from '../store';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
export default defineComponent({ export default defineComponent({
name: 'BalanceAdd', name: 'BalanceAdd',

View File

@ -20,7 +20,7 @@
import { computed, defineComponent, onBeforeMount, PropType } from 'vue'; import { computed, defineComponent, onBeforeMount, PropType } from 'vue';
import UserSelector from 'src/plugins/user/components/UserSelector.vue'; import UserSelector from 'src/plugins/user/components/UserSelector.vue';
import { useBalanceStore } from '../store'; import { useBalanceStore } from '../store';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
export default defineComponent({ export default defineComponent({
name: 'BalanceHeader', name: 'BalanceHeader',

View File

@ -29,7 +29,7 @@ import UserSelector from 'src/plugins/user/components/UserSelector.vue';
import BalanceHeader from '../components/BalanceHeader.vue'; import BalanceHeader from '../components/BalanceHeader.vue';
import PERMISSIONS from '../permissions'; import PERMISSIONS from '../permissions';
import { useBalanceStore } from '../store'; import { useBalanceStore } from '../store';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
export default defineComponent({ export default defineComponent({
name: 'BalanceTransfer', name: 'BalanceTransfer',

View File

@ -33,7 +33,7 @@
import { ref, computed, defineComponent, onUnmounted, onMounted, PropType } from 'vue'; import { ref, computed, defineComponent, onUnmounted, onMounted, PropType } from 'vue';
import { hasPermission } from 'src/utils/permission'; import { hasPermission } from 'src/utils/permission';
import { formatDateTime } from 'src/utils/datetime'; import { formatDateTime } from 'src/utils/datetime';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { useUserStore } from 'src/plugins/user/store'; import { useUserStore } from 'src/plugins/user/store';
import { useBalanceStore } from '../store'; import { useBalanceStore } from '../store';

View File

@ -7,7 +7,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { useBalanceStore } from '../store'; import { useBalanceStore } from '../store';
import { computed, defineComponent, onBeforeMount } from 'vue'; import { computed, defineComponent, onBeforeMount } from 'vue';

View File

@ -73,7 +73,7 @@ import BalanceAdd from '../components/BalanceAdd.vue';
import BalanceTransfer from '../components/BalanceTransfer.vue'; import BalanceTransfer from '../components/BalanceTransfer.vue';
import Transaction from '../components/Transaction.vue'; import Transaction from '../components/Transaction.vue';
import { useBalanceStore } from '../store'; import { useBalanceStore } from '../store';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
export default defineComponent({ export default defineComponent({
name: 'BalanceManage', name: 'BalanceManage',

View File

@ -35,7 +35,7 @@
import { computed, defineComponent, onMounted, ref } from 'vue'; import { computed, defineComponent, onMounted, ref } from 'vue';
import { formatDateTime } from 'src/utils/datetime'; import { formatDateTime } from 'src/utils/datetime';
import { useBalanceStore } from '../store'; import { useBalanceStore } from '../store';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { useUserStore } from 'src/plugins/user/store'; import { useUserStore } from 'src/plugins/user/store';
export default defineComponent({ export default defineComponent({

View File

@ -17,7 +17,7 @@ export interface TransactionsResponse {
} }
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import { Notify } from 'quasar'; import { Notify } from 'quasar';

View File

@ -326,7 +326,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onBeforeMount, ComputedRef, computed, ref } from 'vue'; import { defineComponent, onBeforeMount, ComputedRef, computed, ref } from 'vue';
import DrinkPriceVolumesTable from 'src/plugins/pricelist/components/CalculationTable/DrinkPriceVolumesTable.vue'; import DrinkPriceVolumesTable from 'src/plugins/pricelist/components/CalculationTable/DrinkPriceVolumesTable.vue';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { Drink, usePricelistStore } from 'src/plugins/pricelist/store'; import { Drink, usePricelistStore } from 'src/plugins/pricelist/store';
import MinPriceSetting from 'src/plugins/pricelist/components/MinPriceSetting.vue'; import MinPriceSetting from 'src/plugins/pricelist/components/MinPriceSetting.vue';
import NewDrink from 'src/plugins/pricelist/components/CalculationTable/NewDrink.vue'; import NewDrink from 'src/plugins/pricelist/components/CalculationTable/NewDrink.vue';

View File

@ -39,7 +39,7 @@ import { defineComponent, onBeforeMount, computed, PropType } from 'vue';
import { Notify } from 'quasar'; import { Notify } from 'quasar';
import { asHour } from 'src/utils/datetime'; import { asHour } from 'src/utils/datetime';
import { useUserStore } from 'src/plugins/user/store'; import { useUserStore } from 'src/plugins/user/store';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { useScheduleStore } from 'src/plugins/schedule/store'; import { useScheduleStore } from 'src/plugins/schedule/store';
export default defineComponent({ export default defineComponent({

View File

@ -14,7 +14,7 @@
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';
import UserSelector from '../components/UserSelector.vue'; import UserSelector from '../components/UserSelector.vue';
import MainUserSettings from '../components/settings/MainUserSettings.vue'; import MainUserSettings from '../components/settings/MainUserSettings.vue';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { useUserStore } from '../store'; import { useUserStore } from '../store';
export default defineComponent({ export default defineComponent({

View File

@ -26,7 +26,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { computed, defineComponent, onMounted, ref } from 'vue'; import { computed, defineComponent, onMounted, ref } from 'vue';
import { useUserStore } from '../store'; import { useUserStore } from '../store';

View File

@ -112,7 +112,7 @@ import { hasPermission } from 'src/utils/permission';
import IsoDateInput from 'src/components/utils/IsoDateInput.vue'; import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
import { defineComponent, computed, ref, onBeforeMount, PropType } from 'vue'; import { defineComponent, computed, ref, onBeforeMount, PropType } from 'vue';
import { useUserStore } from '../../store'; import { useUserStore } from '../../store';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
export default defineComponent({ export default defineComponent({
name: 'MainUserSettings', name: 'MainUserSettings',

View File

@ -48,7 +48,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, computed, PropType } from 'vue'; import { defineComponent, ref, computed, PropType } from 'vue';
import { formatDateTime } from 'src/utils/datetime'; import { formatDateTime } from 'src/utils/datetime';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { useSessionStore } from '../../store'; import { useSessionStore } from '../../store';
export default defineComponent({ export default defineComponent({

View File

@ -22,7 +22,7 @@
import { defineComponent, onBeforeMount, ref } from 'vue'; import { defineComponent, onBeforeMount, ref } from 'vue';
import Session from '../components/settings/Session.vue'; import Session from '../components/settings/Session.vue';
import MainUserSettings from '../components/settings/MainUserSettings.vue'; import MainUserSettings from '../components/settings/MainUserSettings.vue';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { useSessionStore } from '../store'; import { useSessionStore } from '../store';
import { useUserStore } from '../store'; import { useUserStore } from '../store';

View File

@ -1,5 +1,5 @@
import { FG_Plugin } from 'src/plugins'; import { FG_Plugin } from 'src/plugins';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
import { computed } from 'vue'; import { computed } from 'vue';
const mainRoutes: FG_Plugin.MenuRoute[] = [ const mainRoutes: FG_Plugin.MenuRoute[] = [

View File

@ -1,7 +1,7 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { api } from 'src/boot/axios'; import { api } from 'src/boot/axios';
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
export const useUserStore = defineStore({ export const useUserStore = defineStore({
id: 'users', id: 'users',

View File

@ -1,27 +1,11 @@
import { store } from 'quasar/wrappers';
import { createStore } from 'vuex';
export interface StateInterface {
[key: string]: unknown;
}
export default store(function (/* { ssrContext } */) {
const Store = createStore({
modules: {},
// enable strict mode (adds overhead!)
// for dev mode and --debug builds only
strict: !!process.env.DEBUGGING,
});
return Store;
});
import { defineStore } from 'pinia';
import { api } from 'src/boot/axios';
import { AxiosResponse } from 'axios';
import { LocalStorage, SessionStorage } from 'quasar';
import { useUserStore, useSessionStore } from 'src/plugins/user/store'; import { useUserStore, useSessionStore } from 'src/plugins/user/store';
import { translateNotification } from 'src/boot/plugins';
import { LocalStorage, SessionStorage } from 'quasar';
import { FG_Plugin } from 'src/plugins';
import { AxiosResponse } from 'axios';
import { api } from 'src/boot/axios';
import { defineStore } from 'pinia';
import { inject } from 'vue';
function loadCurrentSession() { function loadCurrentSession() {
const session = LocalStorage.getItem<FG.Session>('session'); const session = LocalStorage.getItem<FG.Session>('session');
@ -41,7 +25,7 @@ export const useMainStore = defineStore({
state: () => ({ state: () => ({
session: loadCurrentSession(), session: loadCurrentSession(),
user: loadUser(), user: loadUser(),
notifications: [] as Array<FG.Notification>, notifications: [] as Array<FG_Plugin.Notification>,
}), }),
getters: { getters: {
@ -126,6 +110,7 @@ export const useMainStore = defineStore({
}, },
async loadNotifications() { async loadNotifications() {
const flaschengeist = inject<FG_Plugin.Flaschengeist>('flaschengeist');
const params = const params =
this.notifications.length > 0 this.notifications.length > 0
? { from: this.notifications[this.notifications.length - 1].time } ? { from: this.notifications[this.notifications.length - 1].time }
@ -133,12 +118,17 @@ export const useMainStore = defineStore({
const { data } = await api.get<FG.Notification[]>('/notifications', { params: params }); const { data } = await api.get<FG.Notification[]>('/notifications', { params: params });
data.forEach((n) => { data.forEach((n) => {
n.time = new Date(n.time); n.time = new Date(n.time);
const notif = (
flaschengeist?.plugins.filter((p) => p.name === n.plugin)[0]?.notification ||
translateNotification
)(n);
this.notifications.push(notif);
if (window.Notification.permission === 'granted') if (window.Notification.permission === 'granted')
new window.Notification(n.text, { new window.Notification(notif.text, {
timestamp: n.time.getTime(), timestamp: notif.time.getTime(),
}); });
}); });
this.notifications.push(...data);
}, },
async removeNotification(id: number) { async removeNotification(id: number) {
@ -154,3 +144,5 @@ export const useMainStore = defineStore({
}, },
}, },
}); });
export default () => useMainStore;

View File

@ -1,4 +1,4 @@
import { useMainStore } from 'src/store'; import { useMainStore } from 'src/stores';
export function hasPermission(permission: string) { export function hasPermission(permission: string) {
const store = useMainStore(); const store = useMainStore();