Merge remote-tracking branch 'origin/develop' into feature/pricelist
|
@ -10,12 +10,11 @@
|
|||
"lint": "eslint --ext .js,.ts,.vue ./src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.9.19",
|
||||
"axios": "^0.21.1",
|
||||
"cordova": "^10.0.0",
|
||||
"core-js": "^3.9.1",
|
||||
"pinia": "^2.0.0-alpha.7",
|
||||
"quasar": "^2.0.0-beta.9"
|
||||
"quasar": "^2.0.0-beta.11"
|
||||
},
|
||||
"prettier": {
|
||||
"singleQuote": true,
|
||||
|
@ -24,7 +23,8 @@
|
|||
"arrowParens": "always"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/app": "^3.0.0-beta.9",
|
||||
"@quasar/app": "^3.0.0-beta.10",
|
||||
"@quasar/extras": "^1.10.0",
|
||||
"@quasar/quasar-app-extension-qcalendar": "file:deps/quasar-ui-qcalendar/app-extension",
|
||||
"@types/node": "^12.20.6",
|
||||
"@types/webpack": "^4.41.26",
|
||||
|
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 913 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 370 B |
Before Width: | Height: | Size: 715 B After Width: | Height: | Size: 702 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1022 B |
Before Width: | Height: | Size: 866 B After Width: | Height: | Size: 801 B |
Before Width: | Height: | Size: 1017 B After Width: | Height: | Size: 969 B |
Before Width: | Height: | Size: 566 B After Width: | Height: | Size: 529 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1015 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 715 B After Width: | Height: | Size: 702 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 923 B After Width: | Height: | Size: 885 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 996 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 8.5 KiB |
|
@ -313,7 +313,7 @@ async function getBackend() {
|
|||
*/
|
||||
export default boot(async ({ router, app }) => {
|
||||
const backend = await getBackend();
|
||||
if (!backend) {
|
||||
if (backend === null) {
|
||||
void router.push({ name: 'error' });
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<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">{{ modelValue.text }}</div>
|
||||
<q-btn
|
||||
round
|
||||
dense
|
||||
icon="close"
|
||||
size="sm"
|
||||
color="negative"
|
||||
class="q-ma-xs"
|
||||
style="position: absolute; top: 0; right: 0"
|
||||
@click="remove"
|
||||
/>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed } from 'vue';
|
||||
import { formatDateTime } from 'src/utils/datetime';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Notification',
|
||||
props: {
|
||||
modelValue: {
|
||||
required: true,
|
||||
type: Object as PropType<FG.Notification>,
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
remove: (id: number) => !!id,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const dateString = computed(() => formatDateTime(props.modelValue.time, true, true));
|
||||
|
||||
function remove() {
|
||||
emit('remove', props.modelValue.id);
|
||||
}
|
||||
|
||||
return { dateString, remove };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,11 +1,10 @@
|
|||
declare namespace FG {
|
||||
interface Session {
|
||||
expires: Date;
|
||||
token: string;
|
||||
lifetime: number;
|
||||
browser: string;
|
||||
platform: string;
|
||||
userid: string;
|
||||
interface Notification {
|
||||
id: number;
|
||||
plugin: string;
|
||||
text: string;
|
||||
data?: unknown;
|
||||
time: Date;
|
||||
}
|
||||
interface User {
|
||||
userid: string;
|
||||
|
@ -18,6 +17,14 @@ declare namespace FG {
|
|||
permissions?: Array<string>;
|
||||
avatar_url?: string;
|
||||
}
|
||||
interface Session {
|
||||
expires: Date;
|
||||
token: string;
|
||||
lifetime: number;
|
||||
browser: string;
|
||||
platform: string;
|
||||
userid: string;
|
||||
}
|
||||
type Permission = string;
|
||||
interface Role {
|
||||
id: number;
|
||||
|
@ -69,6 +76,7 @@ declare namespace FG {
|
|||
}
|
||||
interface Service {
|
||||
userid: string;
|
||||
is_backup: boolean;
|
||||
value: number;
|
||||
}
|
||||
interface Drink {
|
||||
|
@ -77,8 +85,8 @@ declare namespace FG {
|
|||
package_size?: number;
|
||||
name: string;
|
||||
volume?: number;
|
||||
cost_price_pro_volume?: number;
|
||||
cost_price_package_netto?: number;
|
||||
cost_per_volume?: number;
|
||||
cost_per_package?: number;
|
||||
tags?: Array<Tag>;
|
||||
type?: DrinkType;
|
||||
volumes: Array<DrinkPriceVolume>;
|
||||
|
@ -88,7 +96,7 @@ declare namespace FG {
|
|||
interface DrinkIngredient {
|
||||
id: number;
|
||||
volume: number;
|
||||
drink_ingredient_id: number;
|
||||
ingredient_id: number;
|
||||
}
|
||||
interface DrinkPrice {
|
||||
id: number;
|
||||
|
|
|
@ -21,6 +21,25 @@
|
|||
</q-toolbar-title>
|
||||
|
||||
<!-- Hier kommen die Shortlinks hin -->
|
||||
<q-btn icon="message" flat dense
|
||||
><q-badge color="negative" floating>{{ notifications.length }}</q-badge>
|
||||
<q-menu style="max-height: 400px; overflow: auto">
|
||||
<q-btn
|
||||
v-if="noPermission"
|
||||
label="Benachrichtigungen erlauben"
|
||||
@click="requestPermission"
|
||||
/>
|
||||
<template v-if="notifications.length > 0">
|
||||
<Notification
|
||||
v-for="(notification, index) in notifications"
|
||||
:key="index"
|
||||
:model-value="notification"
|
||||
@remove="remove"
|
||||
/>
|
||||
</template>
|
||||
<div v-else class="q-pa-sm">Keine neuen Benachrichtigungen</div>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
<shortcut-link
|
||||
v-for="(shortcut, index) in shortcuts"
|
||||
:key="'shortcut' + index"
|
||||
|
@ -84,8 +103,9 @@
|
|||
<script lang="ts">
|
||||
import EssentialLink from 'src/components/navigation/EssentialLink.vue';
|
||||
import ShortcutLink from 'src/components/navigation/ShortcutLink.vue';
|
||||
import Notification from 'src/components/Notification.vue';
|
||||
import { Screen } from 'quasar';
|
||||
import { defineComponent, ref, inject, computed } from 'vue';
|
||||
import { defineComponent, ref, inject, computed, onBeforeMount } from 'vue';
|
||||
import { useMainStore } from 'src/store';
|
||||
import { FG_Plugin } from 'src/plugins';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
@ -100,7 +120,7 @@ const essentials: FG_Plugin.MenuLink[] = [
|
|||
|
||||
export default defineComponent({
|
||||
name: 'MainLayout',
|
||||
components: { EssentialLink, ShortcutLink },
|
||||
components: { EssentialLink, ShortcutLink, Notification },
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const mainStore = useMainStore();
|
||||
|
@ -108,6 +128,16 @@ export default defineComponent({
|
|||
const leftDrawer = ref(false);
|
||||
const shortcuts = flaschengeist?.shortcuts;
|
||||
const mainLinks = flaschengeist?.menuLinks;
|
||||
const notifications = computed(() => mainStore.notifications.slice().reverse());
|
||||
const noPermission = ref(window.Notification.permission !== 'granted');
|
||||
|
||||
function requestPermission() {
|
||||
void window.Notification.requestPermission().then(
|
||||
(p) => (noPermission.value = p !== 'granted')
|
||||
);
|
||||
}
|
||||
|
||||
onBeforeMount(() => window.setInterval(() => void mainStore.loadNotifications(), 30000));
|
||||
|
||||
const leftDrawerOpen = computed({
|
||||
get: () => (leftDrawer.value || Screen.gt.sm ? true : false),
|
||||
|
@ -131,6 +161,10 @@ export default defineComponent({
|
|||
void mainStore.logout();
|
||||
}
|
||||
|
||||
async function remove(id: number) {
|
||||
await mainStore.removeNotification(id);
|
||||
}
|
||||
|
||||
return {
|
||||
essentials,
|
||||
leftDrawerOpen,
|
||||
|
@ -138,6 +172,10 @@ export default defineComponent({
|
|||
leftDrawerClicker,
|
||||
logout,
|
||||
mainLinks,
|
||||
notifications,
|
||||
noPermission,
|
||||
remove,
|
||||
requestPermission,
|
||||
shortcuts,
|
||||
subLinks,
|
||||
};
|
||||
|
|
|
@ -181,7 +181,7 @@ export default defineComponent({
|
|||
id: -1,
|
||||
drink_ingredient: {
|
||||
id: -1,
|
||||
drink_ingredient_id: newIngredient.value.id,
|
||||
ingredient_id: newIngredient.value.id,
|
||||
volume: newIngredientVolume.value,
|
||||
},
|
||||
extra_ingredient: undefined,
|
||||
|
|
|
@ -41,14 +41,14 @@
|
|||
type="number"
|
||||
/>
|
||||
<q-input
|
||||
v-model.number="newDrink.cost_price_package_netto"
|
||||
v-model.number="newDrink.cost_per_package"
|
||||
class="col-sm-4 col-xs-6 q-pa-sm"
|
||||
filled
|
||||
label="Preis Netto/Gebinde"
|
||||
type="number"
|
||||
/>
|
||||
<q-input
|
||||
v-model="cost_price_pro_volume"
|
||||
v-model.number="cost_per_volume"
|
||||
class="col-sm-4 col-xs-6 q-pa-sm"
|
||||
filled
|
||||
label="Preis mit 19%/Liter"
|
||||
|
@ -78,8 +78,8 @@ export default defineComponent({
|
|||
package_size: undefined,
|
||||
name: '',
|
||||
volume: undefined,
|
||||
cost_price_pro_volume: undefined,
|
||||
cost_price_package_netto: undefined,
|
||||
cost_per_volume: undefined,
|
||||
cost_per_package: undefined,
|
||||
tags: [],
|
||||
type: undefined,
|
||||
volumes: [],
|
||||
|
@ -88,24 +88,24 @@ export default defineComponent({
|
|||
|
||||
const calc_price_pro_volume = computed(
|
||||
() =>
|
||||
!!newDrink.value.cost_price_package_netto &&
|
||||
!!newDrink.value.cost_per_package &&
|
||||
!!newDrink.value.volume &&
|
||||
!!newDrink.value.package_size
|
||||
);
|
||||
const cost_price_pro_volume = computed({
|
||||
const cost_per_volume = computed({
|
||||
get: () => {
|
||||
if (calc_price_pro_volume.value) {
|
||||
const retVal =
|
||||
((newDrink.value.cost_price_package_netto || 0) /
|
||||
((newDrink.value.cost_per_package || 0) /
|
||||
((newDrink.value.volume || 0) * (newDrink.value.package_size || 0))) *
|
||||
1.19;
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
newDrink.value.cost_price_pro_volume = Math.round(retVal * 1000) / 1000;
|
||||
newDrink.value.cost_per_volume = Math.round(retVal * 1000) / 1000;
|
||||
}
|
||||
return newDrink.value.cost_price_pro_volume;
|
||||
return newDrink.value.cost_per_volume;
|
||||
},
|
||||
set: (val) => {
|
||||
newDrink.value.cost_price_pro_volume = val;
|
||||
newDrink.value.cost_per_volume = val;
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -124,7 +124,7 @@ export default defineComponent({
|
|||
drinkTypes: computed(() => store.drinkTypes),
|
||||
newDrink,
|
||||
calc_price_pro_volume,
|
||||
cost_price_pro_volume,
|
||||
cost_per_volume,
|
||||
addDrink,
|
||||
cancelAddDrink,
|
||||
notEmpty,
|
||||
|
|
|
@ -45,7 +45,7 @@ export default defineComponent({
|
|||
volume.value.ingredients.forEach((ingredient) => {
|
||||
if (ingredient.drink_ingredient) {
|
||||
const _drink = store.drinks.find(
|
||||
(a) => a.id === ingredient.drink_ingredient?.drink_ingredient_id
|
||||
(a) => a.id === ingredient.drink_ingredient?.ingredient_id
|
||||
);
|
||||
retVal +=
|
||||
ingredient.drink_ingredient.volume *
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<q-page padding>
|
||||
<q-card>
|
||||
<q-table title="Getränke" :columns="columns_drinks" :rows="drinks" row-key="name">
|
||||
<template #body-cell-volumes="props">
|
||||
<q-td :props="props">
|
||||
<q-table
|
||||
:columns="columns_volumes"
|
||||
:rows="props.row.volumes"
|
||||
hide-header
|
||||
:hide-bottom="props.row.volumes.length < 5"
|
||||
flat
|
||||
>
|
||||
<template #body-cell-prices="props_volumes">
|
||||
<q-td :props="props_volumes">
|
||||
<q-table
|
||||
:columns="columns_prices"
|
||||
:rows="props_volumes.row.prices"
|
||||
hide-header
|
||||
:hide-bottom="props_volumes.row.prices.length < 5"
|
||||
flat
|
||||
>
|
||||
<template #body-cell-public="props_prices">
|
||||
<q-td :props="props_prices">
|
||||
<q-toggle v-model="props_prices.row.public" disable />
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-card>
|
||||
</q-page>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onBeforeMount, computed } from 'vue';
|
||||
import { usePricelistStore } from '../store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Pricelist',
|
||||
setup() {
|
||||
const store = usePricelistStore();
|
||||
|
||||
onBeforeMount(() => {
|
||||
void store.getDrinks();
|
||||
});
|
||||
const drinks = computed(() => store.drinks);
|
||||
const columns_drinks = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Getränk',
|
||||
field: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'volumes',
|
||||
label: 'Preise',
|
||||
field: 'volumes',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
const columns_volumes = [
|
||||
{
|
||||
name: 'volume',
|
||||
label: 'Inhalt',
|
||||
field: 'volume',
|
||||
format: (val: number) => `${val.toFixed(3)}L`,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'prices',
|
||||
label: 'Preise',
|
||||
field: 'prices',
|
||||
},
|
||||
];
|
||||
const columns_prices = [
|
||||
{
|
||||
name: 'price',
|
||||
label: 'Preis',
|
||||
field: 'price',
|
||||
format: (val: number) => `${val.toFixed(2)}€`,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Beschreibung',
|
||||
field: 'description',
|
||||
},
|
||||
{
|
||||
name: 'public',
|
||||
label: 'Öffentlich',
|
||||
field: 'public',
|
||||
},
|
||||
];
|
||||
return { columns_drinks, columns_volumes, columns_prices, drinks };
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -1,15 +0,0 @@
|
|||
<template>
|
||||
<Pricelist />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import Pricelist from '../components/Pricelist.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PricelistPage',
|
||||
components: { Pricelist },
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -19,7 +19,7 @@ export const innerRoutes: FG_Plugin.MenuRoute[] = [
|
|||
route: {
|
||||
path: 'pricelist',
|
||||
name: 'drinks-pricelist',
|
||||
component: () => import('../pages/PricelistP.vue'),
|
||||
component: () => import('../pages/Pricelist.vue'),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -53,8 +53,8 @@ class Drink {
|
|||
package_size,
|
||||
name,
|
||||
volume,
|
||||
cost_price_pro_volume,
|
||||
cost_price_package_netto,
|
||||
cost_per_volume,
|
||||
cost_per_package,
|
||||
tags,
|
||||
type,
|
||||
uuid,
|
||||
|
@ -65,15 +65,13 @@ class Drink {
|
|||
this.package_size = package_size;
|
||||
this.name = name;
|
||||
this.volume = volume;
|
||||
this.cost_price_package_netto = cost_price_package_netto;
|
||||
this._cost_price_pro_volume = cost_price_pro_volume;
|
||||
this.cost_per_package = cost_per_package;
|
||||
this.cost_per_volume = cost_per_volume;
|
||||
this.cost_price_pro_volume = computed({
|
||||
get: () => {
|
||||
if (!!this.volume && !!this.package_size && !!this.cost_price_package_netto) {
|
||||
if (!!this.volume && !!this.package_size && !!this.cost_per_package) {
|
||||
const retVal =
|
||||
((this.cost_price_package_netto || 0) /
|
||||
((this.volume || 0) * (this.package_size || 0))) *
|
||||
1.19;
|
||||
((this.cost_per_package || 0) / ((this.volume || 0) * (this.package_size || 0))) * 1.19;
|
||||
this._cost_price_pro_volume = Math.round(retVal * 1000) / 1000;
|
||||
}
|
||||
|
||||
|
@ -289,7 +287,7 @@ export const usePricelistStore = defineStore({
|
|||
volume.ingredients.forEach((ingredient) => {
|
||||
if (ingredient.drink_ingredient) {
|
||||
const _drink = usePricelistStore().drinks.find(
|
||||
(a) => a.id === ingredient.drink_ingredient?.drink_ingredient_id
|
||||
(a) => a.id === ingredient.drink_ingredient?.ingredient_id
|
||||
);
|
||||
retVal +=
|
||||
ingredient.drink_ingredient.volume *
|
||||
|
|
|
@ -15,32 +15,30 @@
|
|||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-page padding>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-table title="Veranstaltungstypen" :data="rows" row-key="jobid" :columns="columns">
|
||||
<template #top-right>
|
||||
<q-input v-model="newEventType" dense placeholder="Neuer Typ" />
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-table title="Veranstaltungstypen" :rows="rows" row-key="jobid" :columns="columns">
|
||||
<template #top-right>
|
||||
<q-input v-model="newEventType" dense placeholder="Neuer Typ" />
|
||||
|
||||
<div></div>
|
||||
<q-btn color="primary" icon="mdi-plus" label="Hinzufügen" @click="addType" />
|
||||
</template>
|
||||
<template #body-cell-actions="props">
|
||||
<!-- <q-btn :label="item"> -->
|
||||
<!-- {{ item.row.name }} -->
|
||||
<q-td :props="props" align="right" :auto-width="true">
|
||||
<q-btn
|
||||
round
|
||||
icon="mdi-pencil"
|
||||
@click="editType({ id: props.row.id, name: props.row.name })"
|
||||
/>
|
||||
<q-btn round icon="mdi-delete" @click="deleteType(props.row.id)" />
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-page>
|
||||
<div></div>
|
||||
<q-btn color="primary" icon="mdi-plus" label="Hinzufügen" @click="addType" />
|
||||
</template>
|
||||
<template #body-cell-actions="props">
|
||||
<!-- <q-btn :label="item"> -->
|
||||
<!-- {{ item.row.name }} -->
|
||||
<q-td :props="props" align="right" :auto-width="true">
|
||||
<q-btn
|
||||
round
|
||||
icon="mdi-pencil"
|
||||
@click="editType({ id: props.row.id, name: props.row.name })"
|
||||
/>
|
||||
<q-btn round icon="mdi-delete" @click="deleteType(props.row.id)" />
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -59,7 +57,7 @@ export default defineComponent({
|
|||
const actualEvent = ref(emptyEvent);
|
||||
const newEventName = ref('');
|
||||
|
||||
onBeforeMount(() => store.getEventTypes());
|
||||
onBeforeMount(async () => await store.getEventTypes());
|
||||
|
||||
const rows = computed(() => store.eventTypes);
|
||||
|
||||
|
|
|
@ -15,32 +15,30 @@
|
|||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-page padding>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-table title="Diensttypen" :data="rows" row-key="jobid" :columns="columns">
|
||||
<template #top-right>
|
||||
<q-input v-model="newJob" dense placeholder="Neuer Typ" />
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-table title="Diensttypen" :rows="rows" row-key="jobid" :columns="columns">
|
||||
<template #top-right>
|
||||
<q-input v-model="newJob" dense placeholder="Neuer Typ" />
|
||||
|
||||
<div></div>
|
||||
<q-btn color="primary" icon="mdi-plus" label="Hinzufügen" @click="addType" />
|
||||
</template>
|
||||
<template #body-cell-actions="props">
|
||||
<!-- <q-btn :label="item"> -->
|
||||
<!-- {{ item.row.name }} -->
|
||||
<q-td :props="props" align="right" :auto-width="true">
|
||||
<q-btn
|
||||
round
|
||||
icon="mdi-pencil"
|
||||
@click="editType({ id: props.row.id, name: props.row.name })"
|
||||
/>
|
||||
<q-btn round icon="mdi-delete" @click="deleteType(props.row.id)" />
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-page>
|
||||
<div></div>
|
||||
<q-btn color="primary" icon="mdi-plus" label="Hinzufügen" @click="addType" />
|
||||
</template>
|
||||
<template #body-cell-actions="props">
|
||||
<!-- <q-btn :label="item"> -->
|
||||
<!-- {{ item.row.name }} -->
|
||||
<q-td :props="props" align="right" :auto-width="true">
|
||||
<q-btn
|
||||
round
|
||||
icon="mdi-pencil"
|
||||
@click="editType({ id: props.row.id, name: props.row.name })"
|
||||
/>
|
||||
<q-btn round icon="mdi-delete" @click="deleteType(props.row.id)" />
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ export default defineComponent({
|
|||
async function enrollForJob() {
|
||||
const newService: FG.Service = {
|
||||
userid: mainStore.currentUser.userid,
|
||||
is_backup: false,
|
||||
value: 1,
|
||||
};
|
||||
try {
|
||||
|
@ -104,6 +105,7 @@ export default defineComponent({
|
|||
async function signOutFromJob() {
|
||||
const newService: FG.Service = {
|
||||
userid: mainStore.currentUser.userid,
|
||||
is_backup: false,
|
||||
value: -1,
|
||||
};
|
||||
try {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
export const PERMISSIONS = {
|
||||
// Can create events
|
||||
CREATE: 'schedule_create',
|
||||
CREATE: 'events_create',
|
||||
// Can edit events
|
||||
EDIT: 'schedule_edit',
|
||||
EDIT: 'events_edit',
|
||||
// Can delete events
|
||||
DELETE: 'schedule_delete',
|
||||
DELETE: 'events_delete',
|
||||
// Can create and edit EventTypes
|
||||
EVENT_TYPE: 'schedule_event_type',
|
||||
EVENT_TYPE: 'events_event_type',
|
||||
// Can create and edit JobTypes
|
||||
JOB_TYPE: 'schedule_job_type',
|
||||
JOB_TYPE: 'events_job_type',
|
||||
// Can self assign to jobs
|
||||
ASSIGN: 'schedule_assign',
|
||||
ASSIGN: 'events_assign',
|
||||
// Can assign other users to jobs
|
||||
ASSIGN_OTHER: 'schedule_assign_other',
|
||||
ASSIGN_OTHER: 'events_assign_other',
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@ const plugin: FG_Plugin.Plugin = {
|
|||
name: 'User',
|
||||
innerRoutes: routes,
|
||||
requiredModules: [],
|
||||
requiredBackendModules: ['auth'],
|
||||
requiredBackendModules: ['auth', 'users', 'roles'],
|
||||
version: '0.0.1',
|
||||
widgets: [
|
||||
{
|
||||
|
|
|
@ -41,6 +41,7 @@ export const useMainStore = defineStore({
|
|||
state: () => ({
|
||||
session: loadCurrentSession(),
|
||||
user: loadUser(),
|
||||
notifications: [] as Array<FG.Notification>,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
|
@ -123,5 +124,33 @@ export const useMainStore = defineStore({
|
|||
response && 'status' in response ? (<AxiosResponse>response).status : false
|
||||
);
|
||||
},
|
||||
|
||||
async loadNotifications() {
|
||||
const params =
|
||||
this.notifications.length > 0
|
||||
? { from: this.notifications[this.notifications.length - 1].time }
|
||||
: {};
|
||||
const { data } = await api.get<FG.Notification[]>('/notifications', { params: params });
|
||||
data.forEach((n) => {
|
||||
n.time = new Date(n.time);
|
||||
if (window.Notification.permission === 'granted')
|
||||
new window.Notification(n.text, {
|
||||
timestamp: n.time.getTime(),
|
||||
});
|
||||
});
|
||||
this.notifications.push(...data);
|
||||
},
|
||||
|
||||
async removeNotification(id: number) {
|
||||
const idx = this.notifications.findIndex((n) => n.id === id);
|
||||
if (idx >= 0)
|
||||
try {
|
||||
this.notifications.splice(idx, 1);
|
||||
await api.delete(`/notifications/${id}`);
|
||||
} catch (error) {
|
||||
if (this.notifications.length > idx)
|
||||
this.notifications.splice(idx, this.notifications.length - idx - 1);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
67
yarn.lock
|
@ -1027,10 +1027,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@positron/stack-trace/-/stack-trace-1.0.0.tgz#14fcc712a530038ef9be1ce6952315a839f466a8"
|
||||
integrity sha1-FPzHEqUwA475vhzmlSMVqDn0Zqg=
|
||||
|
||||
"@quasar/app@^3.0.0-beta.9":
|
||||
version "3.0.0-beta.9"
|
||||
resolved "https://registry.yarnpkg.com/@quasar/app/-/app-3.0.0-beta.9.tgz#8d5fb2056aaf5a03919b63c9c655933281e8beed"
|
||||
integrity sha512-Wt12LLWTyp1GMw17xVVScvjom9EILIlnuV4xrgZ7WTfwwZ18jcqtf3pTs2NMeOgIwohlP2qLsI5JQW9wLylQzw==
|
||||
"@quasar/app@^3.0.0-beta.10":
|
||||
version "3.0.0-beta.10"
|
||||
resolved "https://registry.yarnpkg.com/@quasar/app/-/app-3.0.0-beta.10.tgz#f4401fa64f9f94b7e643560fb8968f3f7250578d"
|
||||
integrity sha512-KC5dmW7hOJ0x634vgyb98cjjia2Mj0BySgq1JghMGiCqq3noF6E9DS9UoF3ru9HUVRUtxfLe73Su8V0s1UoQZg==
|
||||
dependencies:
|
||||
"@quasar/babel-preset-app" "2.0.1"
|
||||
"@quasar/fastclick" "1.1.4"
|
||||
|
@ -1041,7 +1041,7 @@
|
|||
"@types/terser-webpack-plugin" "3.0.0"
|
||||
"@types/webpack" "4.41.26"
|
||||
"@types/webpack-bundle-analyzer" "3.9.1"
|
||||
"@types/webpack-dev-server" "3.11.1"
|
||||
"@types/webpack-dev-server" "3.11.2"
|
||||
"@vue/compiler-sfc" "3.0.6"
|
||||
"@vue/server-renderer" "3.0.6"
|
||||
archiver "5.2.0"
|
||||
|
@ -1135,10 +1135,10 @@
|
|||
core-js "^3.6.5"
|
||||
core-js-compat "^3.6.5"
|
||||
|
||||
"@quasar/extras@^1.9.19":
|
||||
version "1.9.19"
|
||||
resolved "https://registry.yarnpkg.com/@quasar/extras/-/extras-1.9.19.tgz#8e511117ccd8ec5bbed7038b9dad1749052376b8"
|
||||
integrity sha512-A1IO0dzfUtRkyKq3QC7ZQNvhLeJey2ET8MvRX7oV0Qe+g30jy/4pr1ZhO7GylkDAoLT3C9eY8t2/5Anrl0AHdA==
|
||||
"@quasar/extras@^1.10.0":
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@quasar/extras/-/extras-1.10.0.tgz#1cf13d299e80e1396b2f982e1663b89dd54f3e8e"
|
||||
integrity sha512-H71Y/6pxunwiEN+oo9OBGM96ncM2QreVdnb2t2iStVHduju3nnypw9euBCshhYxKE/ORHZoOBRDoiddUOyaUdA==
|
||||
|
||||
"@quasar/fastclick@1.1.4":
|
||||
version "1.1.4"
|
||||
|
@ -1148,7 +1148,7 @@
|
|||
"@quasar/quasar-app-extension-qcalendar@file:deps/quasar-ui-qcalendar/app-extension":
|
||||
version "4.0.0-alpha.1"
|
||||
dependencies:
|
||||
"@quasar/quasar-ui-qcalendar" "link:../../../../../.cache/yarn/v6/npm-@quasar-quasar-app-extension-qcalendar-4.0.0-alpha.1-0eac22e6-3d93-4296-9ecf-77ea02e6ad1a-1616339967660/node_modules/@quasar/ui"
|
||||
"@quasar/quasar-ui-qcalendar" "link:../../../../../.cache/yarn/v6/npm-@quasar-quasar-app-extension-qcalendar-4.0.0-alpha.1-3c86cc72-2d95-4dfe-9e1e-2f553d66f91e-1616893646708/node_modules/@quasar/ui"
|
||||
|
||||
"@quasar/quasar-ui-qcalendar@link:deps/quasar-ui-qcalendar/ui":
|
||||
version "0.0.0"
|
||||
|
@ -1266,13 +1266,6 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50"
|
||||
integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==
|
||||
|
||||
"@types/http-proxy-middleware@*":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/http-proxy-middleware/-/http-proxy-middleware-1.0.0.tgz#4370a52766782e9c4f0be2ef79c3dd47aef5f428"
|
||||
integrity sha512-/s8lFX6rT43hSPqjjD8KNuu0SkPKY7uIdR6u9DCxVqCRhAvfKxGbVOixJsAT2mdpSnCyrGFAGoB39KFh6tmRxw==
|
||||
dependencies:
|
||||
http-proxy-middleware "*"
|
||||
|
||||
"@types/http-proxy@^1.17.4":
|
||||
version "1.17.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.5.tgz#c203c5e6e9dc6820d27a40eb1e511c70a220423d"
|
||||
|
@ -1365,16 +1358,16 @@
|
|||
dependencies:
|
||||
"@types/webpack" "*"
|
||||
|
||||
"@types/webpack-dev-server@3.11.1":
|
||||
version "3.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz#f8f4dac1da226d530bd15a1d5dc34b23ba766ccb"
|
||||
integrity sha512-rIb+LtUkKnh7+oIJm3WiMJONd71Q0lZuqGLcSqhZ5qjN9gV/CNmZe7Bai+brnBPZ/KVYOsr+4bFLiNZwjBicLw==
|
||||
"@types/webpack-dev-server@3.11.2":
|
||||
version "3.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz#73915a7d9e0a9b5e010a2388a46f68ab3f770ef8"
|
||||
integrity sha512-13w1VhaghN+G1rYjkBPgN/GFRoHd9uI2fwK9cSKvLutdmZ22L9iicFEvt69by40DP2I6uNcClaGTyPY6nYhIgQ==
|
||||
dependencies:
|
||||
"@types/connect-history-api-fallback" "*"
|
||||
"@types/express" "*"
|
||||
"@types/http-proxy-middleware" "*"
|
||||
"@types/serve-static" "*"
|
||||
"@types/webpack" "*"
|
||||
http-proxy-middleware "^1.0.0"
|
||||
|
||||
"@types/webpack-env@^1.16.0":
|
||||
version "1.16.0"
|
||||
|
@ -5235,17 +5228,6 @@ http-proxy-agent@^4.0.1:
|
|||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
http-proxy-middleware@*:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.0.6.tgz#0618557722f450375d3796d701a8ac5407b3b94e"
|
||||
integrity sha512-NyL6ZB6cVni7pl+/IT2W0ni5ME00xR0sN27AQZZrpKn1b+qRh+mLbBxIq9Cq1oGfmTc7BUq4HB77mxwCaxAYNg==
|
||||
dependencies:
|
||||
"@types/http-proxy" "^1.17.4"
|
||||
http-proxy "^1.18.1"
|
||||
is-glob "^4.0.1"
|
||||
lodash "^4.17.20"
|
||||
micromatch "^4.0.2"
|
||||
|
||||
http-proxy-middleware@0.19.1:
|
||||
version "0.19.1"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a"
|
||||
|
@ -5256,6 +5238,17 @@ http-proxy-middleware@0.19.1:
|
|||
lodash "^4.17.11"
|
||||
micromatch "^3.1.10"
|
||||
|
||||
http-proxy-middleware@^1.0.0:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.0.6.tgz#0618557722f450375d3796d701a8ac5407b3b94e"
|
||||
integrity sha512-NyL6ZB6cVni7pl+/IT2W0ni5ME00xR0sN27AQZZrpKn1b+qRh+mLbBxIq9Cq1oGfmTc7BUq4HB77mxwCaxAYNg==
|
||||
dependencies:
|
||||
"@types/http-proxy" "^1.17.4"
|
||||
http-proxy "^1.18.1"
|
||||
is-glob "^4.0.1"
|
||||
lodash "^4.17.20"
|
||||
micromatch "^4.0.2"
|
||||
|
||||
http-proxy@^1.17.0, http-proxy@^1.18.1:
|
||||
version "1.18.1"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
|
||||
|
@ -8236,10 +8229,10 @@ qs@~6.5.2:
|
|||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||
|
||||
quasar@^2.0.0-beta.9:
|
||||
version "2.0.0-beta.9"
|
||||
resolved "https://registry.yarnpkg.com/quasar/-/quasar-2.0.0-beta.9.tgz#0247a3512f3bdffd29f8f2a3f33fc91e62a91697"
|
||||
integrity sha512-xqYKBQMF5ntM9RUr/eHFsSM6onXTzpwTcoBnM0YL6/QknBlSBHXltjG7pJmKMSm62ZRq1YeOdLsC9nchHgSrCQ==
|
||||
quasar@^2.0.0-beta.11:
|
||||
version "2.0.0-beta.11"
|
||||
resolved "https://registry.yarnpkg.com/quasar/-/quasar-2.0.0-beta.11.tgz#78f21abe94caa78fe17ad17ce945d09dfda9e5d2"
|
||||
integrity sha512-YG+iVkd1LNbo0MFSrPl1npEW02FfVeD4+/98nYPz3pfZEdOy1kiOF7N9Ij7RDC8x0/+9Ans9mgBvs0xjg9YxyA==
|
||||
|
||||
query-string@^4.1.0:
|
||||
version "4.3.4"
|
||||
|
|