release v2.0.0 #4
|
@ -24,5 +24,6 @@
|
|||
"sortAttributes": false
|
||||
}
|
||||
},
|
||||
"vetur.format.defaultFormatter.ts": "prettier-tslint"
|
||||
"vetur.format.defaultFormatter.ts": "prettier-tslint",
|
||||
"typescript.format.enable": false
|
||||
}
|
||||
|
|
|
@ -9,6 +9,6 @@ export default defineComponent({
|
|||
name: 'EmptyParent',
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -49,18 +49,14 @@ export default defineComponent({
|
|||
if (props.title.includes('loadFromStore')) {
|
||||
const startIndex = props.title.indexOf('(') + 1;
|
||||
const endIndex = props.title.indexOf(')');
|
||||
const substring = props.title
|
||||
.substring(startIndex, endIndex)
|
||||
.replace(/"/g, '');
|
||||
const substring = props.title.substring(startIndex, endIndex).replace(/"/g, '');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
return <string>root.$store.getters[substring];
|
||||
}
|
||||
return props.title;
|
||||
});
|
||||
|
||||
const isGranted = computed(() =>
|
||||
hasPermissions(props.permissions || [], root.$store)
|
||||
);
|
||||
const isGranted = computed(() => hasPermissions(props.permissions || [], root.$store));
|
||||
|
||||
return { realTitle: title, isGranted };
|
||||
}
|
||||
|
|
|
@ -22,9 +22,7 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
setup(props, { root }) {
|
||||
const isGranted = computed(() =>
|
||||
hasPermissions(props.permissions || [], root.$store)
|
||||
);
|
||||
const isGranted = computed(() => hasPermissions(props.permissions || [], root.$store));
|
||||
return { isGranted };
|
||||
}
|
||||
});
|
||||
|
|
|
@ -49,18 +49,18 @@ export default defineComponent({
|
|||
name: 'IsoDateInput',
|
||||
props: {
|
||||
value: {
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
label: {},
|
||||
readonly: {
|
||||
default: false,
|
||||
default: false
|
||||
},
|
||||
type: {
|
||||
default: 'date',
|
||||
validator: function(value: string) {
|
||||
return ['date', 'time', 'datetime'].indexOf(value) !== -1;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
setup(props: Props, { emit }: { emit: any }) {
|
||||
function getDateTime() {
|
||||
|
@ -183,8 +183,8 @@ export default defineComponent({
|
|||
rules,
|
||||
timeChanged,
|
||||
placeholder,
|
||||
dateTimeChanged,
|
||||
dateTimeChanged
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -112,23 +112,23 @@ const links = [
|
|||
name: 'about',
|
||||
title: 'Über Flaschengeist',
|
||||
link: 'about',
|
||||
icon: 'mdi-information',
|
||||
},
|
||||
icon: 'mdi-information'
|
||||
}
|
||||
];
|
||||
|
||||
const shortcuts = [
|
||||
{
|
||||
link: 'about',
|
||||
icon: 'mdi-information',
|
||||
icon: 'mdi-information'
|
||||
},
|
||||
{
|
||||
link: 'user',
|
||||
icon: 'mdi-account',
|
||||
icon: 'mdi-account'
|
||||
},
|
||||
{
|
||||
link: 'user-plugin1',
|
||||
icon: 'mdi-account-plus',
|
||||
},
|
||||
icon: 'mdi-account-plus'
|
||||
}
|
||||
];
|
||||
|
||||
declare module 'vue/types/vue' {
|
||||
|
@ -146,7 +146,7 @@ export default defineComponent({
|
|||
const leftDrawerOpen = ref(
|
||||
computed({
|
||||
get: () => (leftDrawer.value || Screen.gt.sm ? true : false),
|
||||
set: (val: boolean) => (leftDrawer.value = val),
|
||||
set: (val: boolean) => (leftDrawer.value = val)
|
||||
})
|
||||
);
|
||||
const leftDrawerMini = ref(false);
|
||||
|
@ -177,9 +177,7 @@ export default defineComponent({
|
|||
|
||||
function logout() {
|
||||
Loading.show({ message: 'Session wird abgemeldet' });
|
||||
(<Store<StateInterface>>ctx.root.$store)
|
||||
.dispatch('session/logout')
|
||||
.finally(() => {
|
||||
(<Store<StateInterface>>ctx.root.$store).dispatch('session/logout').finally(() => {
|
||||
Loading.hide();
|
||||
});
|
||||
}
|
||||
|
@ -191,8 +189,8 @@ export default defineComponent({
|
|||
links,
|
||||
pluginChildLinks,
|
||||
shortcuts,
|
||||
logout,
|
||||
logout
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -49,6 +49,6 @@ import ShortCutLink from 'components/navigation/ShortCutLink.vue';
|
|||
|
||||
export default defineComponent({
|
||||
name: 'OutLayout',
|
||||
components: { ShortCutLink },
|
||||
components: { ShortCutLink }
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
style="grid-auto-rows: 1fr;"
|
||||
class="fit row justify-around items-start q-col-gutter-sm"
|
||||
>
|
||||
<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="col-4 full-height col-sm-6 col-xs-12">
|
||||
<component v-bind:is="item" />
|
||||
</div>
|
||||
</q-page>
|
||||
|
@ -25,15 +21,14 @@ export default defineComponent({
|
|||
const widgets = ref<Array<AsyncComponentPromise>>([]);
|
||||
|
||||
onMounted(() => {
|
||||
root.$flaschengeistPlugins.widgets.forEach((widget) => {
|
||||
if (hasPermissions(widget.permissions, root.$store))
|
||||
widgets.value.push(widget.widget);
|
||||
root.$flaschengeistPlugins.widgets.forEach(widget => {
|
||||
if (hasPermissions(widget.permissions, root.$store)) widgets.value.push(widget.widget);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
widgets,
|
||||
widgets
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -12,14 +12,16 @@
|
|||
fill="white"
|
||||
>
|
||||
<g>
|
||||
<circle
|
||||
cx="87.493"
|
||||
cy="25.907"
|
||||
r="25.907"
|
||||
<circle cx="87.493" cy="25.907" r="25.907" />
|
||||
<path
|
||||
d="m194.386,209.531l-42.802-36.895c-1.825-1.573-4.001-2.683-6.345-3.237l-72.533-17.136 47.755,.703v-34.439l-46.525-26.18 50.36,14.829c4.016,1.182 8.356,0.276 11.564-2.414l38.264-32.093c1.1-0.923 2.001-1.994 2.698-3.161 2.656-4.442 2.36-10.26-1.154-14.449-4.437-5.29-12.32-5.981-17.61-1.544l-33.127,27.785-45.605-13.428 41.402,1.292 16.027-13.442-6.021-2.955-3.631,3.945-18.134,2.86h-37.216c-9.665,0-17.501,7.835-17.501,17.501v90.395l.029-.024c0.252,6.569 4.819,12.433 11.527,14.019l68.966,16.292 40.024,34.501c2.834,2.442 6.318,3.639 9.787,3.639 4.213,0 8.402-1.765 11.368-5.206 5.41-6.278 4.708-15.75-1.567-21.158z"
|
||||
/>
|
||||
<path
|
||||
d="m233.888,50.21l-43.49-21.349c-2.17-1.065-4.545-1.612-6.94-1.612-0.861,0-1.724,0.071-2.581,0.213l-13.243,2.2-24.213-11.886c-6.197-3.043-13.688-0.485-16.728,5.713-3.042,6.197-0.484,13.687 5.713,16.729l14.402,7.069 3.539-2.969c4.405-3.694 9.994-5.729 15.738-5.729 7.266,0 14.11,3.192 18.777,8.756 6.702,7.991 7.61,19.371 2.258,28.32-0.529,0.884-1.127,1.717-1.759,2.523l28.035,13.762c0.916,0.45 1.914,0.664 2.967,0.664 5.79,0 13.263-6.495 18.05-16.247 5.66-11.524 5.424-23.236-0.525-26.157z"
|
||||
/>
|
||||
<path
|
||||
d="m102.363,202.426l2.531,6.9-13.835,65.324c-1.716,8.105 3.463,16.065 11.567,17.782 1.048,0.222 2.092,0.328 3.122,0.328 6.936-0.001 13.165-4.839 14.66-11.896l14.265-67.357-5.513-4.752-26.797-6.329z"
|
||||
/>
|
||||
<path d="m194.386,209.531l-42.802-36.895c-1.825-1.573-4.001-2.683-6.345-3.237l-72.533-17.136 47.755,.703v-34.439l-46.525-26.18 50.36,14.829c4.016,1.182 8.356,0.276 11.564-2.414l38.264-32.093c1.1-0.923 2.001-1.994 2.698-3.161 2.656-4.442 2.36-10.26-1.154-14.449-4.437-5.29-12.32-5.981-17.61-1.544l-33.127,27.785-45.605-13.428 41.402,1.292 16.027-13.442-6.021-2.955-3.631,3.945-18.134,2.86h-37.216c-9.665,0-17.501,7.835-17.501,17.501v90.395l.029-.024c0.252,6.569 4.819,12.433 11.527,14.019l68.966,16.292 40.024,34.501c2.834,2.442 6.318,3.639 9.787,3.639 4.213,0 8.402-1.765 11.368-5.206 5.41-6.278 4.708-15.75-1.567-21.158z" />
|
||||
<path d="m233.888,50.21l-43.49-21.349c-2.17-1.065-4.545-1.612-6.94-1.612-0.861,0-1.724,0.071-2.581,0.213l-13.243,2.2-24.213-11.886c-6.197-3.043-13.688-0.485-16.728,5.713-3.042,6.197-0.484,13.687 5.713,16.729l14.402,7.069 3.539-2.969c4.405-3.694 9.994-5.729 15.738-5.729 7.266,0 14.11,3.192 18.777,8.756 6.702,7.991 7.61,19.371 2.258,28.32-0.529,0.884-1.127,1.717-1.759,2.523l28.035,13.762c0.916,0.45 1.914,0.664 2.967,0.664 5.79,0 13.263-6.495 18.05-16.247 5.66-11.524 5.424-23.236-0.525-26.157z" />
|
||||
<path d="m102.363,202.426l2.531,6.9-13.835,65.324c-1.716,8.105 3.463,16.065 11.567,17.782 1.048,0.222 2.092,0.328 3.122,0.328 6.936-0.001 13.165-4.839 14.66-11.896l14.265-67.357-5.513-4.752-26.797-6.329z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
@ -27,7 +29,8 @@
|
|||
Der Admin is über's Kabel gestolpert!
|
||||
</div>
|
||||
<div>
|
||||
Aktuell kann der Backend Server nicht erreicht werden, wir versuchen es in {{reload}} Sekunden erneut.
|
||||
Aktuell kann der Backend Server nicht erreicht werden, wir versuchen es in
|
||||
{{ reload }} Sekunden erneut.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -49,6 +52,6 @@ export default defineComponent({
|
|||
}, 1000);
|
||||
onUnmounted(() => clearInterval(ival));
|
||||
return { reload };
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -2,12 +2,26 @@
|
|||
<div class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center">
|
||||
<div>
|
||||
<div>
|
||||
<svg style="max-width: 400px;" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 292.761 292.761" xmlns:xlink="http://www.w3.org/1999/xlink" enable-background="new 0 0 292.761 292.761" fill="white">
|
||||
<svg
|
||||
style="max-width: 400px;"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 292.761 292.761"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
enable-background="new 0 0 292.761 292.761"
|
||||
fill="white"
|
||||
>
|
||||
<g>
|
||||
<circle cx="87.493" cy="25.907" r="25.907" />
|
||||
<path d="m194.386,209.531l-42.802-36.895c-1.825-1.573-4.001-2.683-6.345-3.237l-72.533-17.136 47.755,.703v-34.439l-46.525-26.18 50.36,14.829c4.016,1.182 8.356,0.276 11.564-2.414l38.264-32.093c1.1-0.923 2.001-1.994 2.698-3.161 2.656-4.442 2.36-10.26-1.154-14.449-4.437-5.29-12.32-5.981-17.61-1.544l-33.127,27.785-45.605-13.428 41.402,1.292 16.027-13.442-6.021-2.955-3.631,3.945-18.134,2.86h-37.216c-9.665,0-17.501,7.835-17.501,17.501v90.395l.029-.024c0.252,6.569 4.819,12.433 11.527,14.019l68.966,16.292 40.024,34.501c2.834,2.442 6.318,3.639 9.787,3.639 4.213,0 8.402-1.765 11.368-5.206 5.41-6.278 4.708-15.75-1.567-21.158z"/>
|
||||
<path d="m233.888,50.21l-43.49-21.349c-2.17-1.065-4.545-1.612-6.94-1.612-0.861,0-1.724,0.071-2.581,0.213l-13.243,2.2-24.213-11.886c-6.197-3.043-13.688-0.485-16.728,5.713-3.042,6.197-0.484,13.687 5.713,16.729l14.402,7.069 3.539-2.969c4.405-3.694 9.994-5.729 15.738-5.729 7.266,0 14.11,3.192 18.777,8.756 6.702,7.991 7.61,19.371 2.258,28.32-0.529,0.884-1.127,1.717-1.759,2.523l28.035,13.762c0.916,0.45 1.914,0.664 2.967,0.664 5.79,0 13.263-6.495 18.05-16.247 5.66-11.524 5.424-23.236-0.525-26.157z"/>
|
||||
<path d="m102.363,202.426l2.531,6.9-13.835,65.324c-1.716,8.105 3.463,16.065 11.567,17.782 1.048,0.222 2.092,0.328 3.122,0.328 6.936-0.001 13.165-4.839 14.66-11.896l14.265-67.357-5.513-4.752-26.797-6.329z"/>
|
||||
<path
|
||||
d="m194.386,209.531l-42.802-36.895c-1.825-1.573-4.001-2.683-6.345-3.237l-72.533-17.136 47.755,.703v-34.439l-46.525-26.18 50.36,14.829c4.016,1.182 8.356,0.276 11.564-2.414l38.264-32.093c1.1-0.923 2.001-1.994 2.698-3.161 2.656-4.442 2.36-10.26-1.154-14.449-4.437-5.29-12.32-5.981-17.61-1.544l-33.127,27.785-45.605-13.428 41.402,1.292 16.027-13.442-6.021-2.955-3.631,3.945-18.134,2.86h-37.216c-9.665,0-17.501,7.835-17.501,17.501v90.395l.029-.024c0.252,6.569 4.819,12.433 11.527,14.019l68.966,16.292 40.024,34.501c2.834,2.442 6.318,3.639 9.787,3.639 4.213,0 8.402-1.765 11.368-5.206 5.41-6.278 4.708-15.75-1.567-21.158z"
|
||||
/>
|
||||
<path
|
||||
d="m233.888,50.21l-43.49-21.349c-2.17-1.065-4.545-1.612-6.94-1.612-0.861,0-1.724,0.071-2.581,0.213l-13.243,2.2-24.213-11.886c-6.197-3.043-13.688-0.485-16.728,5.713-3.042,6.197-0.484,13.687 5.713,16.729l14.402,7.069 3.539-2.969c4.405-3.694 9.994-5.729 15.738-5.729 7.266,0 14.11,3.192 18.777,8.756 6.702,7.991 7.61,19.371 2.258,28.32-0.529,0.884-1.127,1.717-1.759,2.523l28.035,13.762c0.916,0.45 1.914,0.664 2.967,0.664 5.79,0 13.263-6.495 18.05-16.247 5.66-11.524 5.424-23.236-0.525-26.157z"
|
||||
/>
|
||||
<path
|
||||
d="m102.363,202.426l2.531,6.9-13.835,65.324c-1.716,8.105 3.463,16.065 11.567,17.782 1.048,0.222 2.092,0.328 3.122,0.328 6.936-0.001 13.165-4.839 14.66-11.896l14.265-67.357-5.513-4.752-26.797-6.329z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
@ -15,7 +29,8 @@
|
|||
Der Admin war betrunken!!
|
||||
</div>
|
||||
<div>
|
||||
Einige Plugins konnten nicht geladen werden.<br />Sollte diese Seite jemals auftauchen, kontaktiere einen nüchternen Admin.
|
||||
Einige Plugins konnten nicht geladen werden.<br />Sollte diese Seite jemals auftauchen,
|
||||
kontaktiere einen nüchternen Admin.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -26,9 +41,7 @@ import {defineComponent} from '@vue/composition-api';
|
|||
|
||||
export default defineComponent({
|
||||
name: 'PluginError.vue'
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -17,15 +17,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col-12 text-center q-ma-sm" style="max-width: 600px;">
|
||||
Flaschengeist ist ein dynamischen Managementsystem für Studentenclubs.
|
||||
Es ermöglicht unter anderem die Mitgliederverwaltung, Dienstverwaltung,
|
||||
Arbeitsgruppenverwaltung und vieles meher. Es kann fast alles ermöglich
|
||||
werden, wenn ein Plugin dafür geschrieben wird. Jeder Club hat die
|
||||
Möglichkeit sein eigenes Flaschengeist zu hosten. Ziel ist später
|
||||
Flaschengeist ist ein dynamischen Managementsystem für Studentenclubs. Es ermöglicht unter
|
||||
anderem die Mitgliederverwaltung, Dienstverwaltung, Arbeitsgruppenverwaltung und vieles
|
||||
meher. Es kann fast alles ermöglich werden, wenn ein Plugin dafür geschrieben wird. Jeder
|
||||
Club hat die Möglichkeit sein eigenes Flaschengeist zu hosten. Ziel ist später
|
||||
Clubübergreifend dezentralisiert miteinander zu arbeiten.
|
||||
</div>
|
||||
|
||||
|
||||
<q-separator />
|
||||
<div class="col-12 text-h6 q-pa-sm" v-if="$route.name == 'about'">
|
||||
Geladene Plugins:
|
||||
|
@ -49,9 +47,7 @@
|
|||
Entwickler:
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="fit row inline wrap justify-around items-start content-start"
|
||||
>
|
||||
<div class="fit row inline wrap justify-around items-start content-start">
|
||||
<developer
|
||||
v-for="(developer, index) in developers"
|
||||
:key="'dev' + index"
|
||||
|
|
|
@ -1,24 +1,33 @@
|
|||
<template>
|
||||
<q-card>
|
||||
<q-card-actions align="right">
|
||||
<q-card v-bind:class="{ 'bg-grey': isReversed }">
|
||||
<q-card-section class="row items-start justify-between">
|
||||
<div class="col text-center">
|
||||
<div
|
||||
v-bind:class="{ 'text-negative': isNegative() }"
|
||||
class="text-weight-bold"
|
||||
style="font-size: 2em"
|
||||
>
|
||||
<span v-if="isNegative()">-</span>{{ transaction.amount.toFixed(2) }} €
|
||||
</div>
|
||||
<div>{{ text }}</div>
|
||||
<div>{{ timeStr }}</div>
|
||||
</div>
|
||||
<div class="col" style="text-align: right">
|
||||
<q-btn
|
||||
:color="color()"
|
||||
color="negative"
|
||||
aria-label="Reverse transaction"
|
||||
icon="mdi-trash-can"
|
||||
aria-label="Löschen"
|
||||
:disable="disabled()"
|
||||
@click="reverse(transaction)"
|
||||
square
|
||||
:disable="!canReverse"
|
||||
@click="reverse"
|
||||
/>
|
||||
</q-card-actions>
|
||||
<q-card-section>
|
||||
<span>{{ timeStr }}: {{ transaction.amount }}</span>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
// TODO: Better styling
|
||||
|
||||
import { ref, computed, defineComponent, onUnmounted } from '@vue/composition-api';
|
||||
import { ref, computed, defineComponent, onUnmounted, onMounted } from '@vue/composition-api';
|
||||
import { hasPermission } from 'src/utils/permission';
|
||||
import { formatDateTime } from 'src/utils/datetime';
|
||||
import { StateInterfaceBalance } from 'src/plugins/balance/store/balance';
|
||||
|
@ -30,36 +39,60 @@ interface Props {
|
|||
|
||||
export default defineComponent({
|
||||
name: 'Transaction',
|
||||
props: ['transaction'],
|
||||
props: {
|
||||
transaction: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: { transaction: 'refreshText' },
|
||||
setup(props: Props, { root, emit }) {
|
||||
const now = ref(Date.now());
|
||||
const ival = setInterval(() => (now.value = Date.now()), 1000);
|
||||
const store: Store<StateInterfaceBalance> = <Store<StateInterfaceBalance>>root.$store;
|
||||
const text = ref('');
|
||||
|
||||
onUnmounted(() => clearInterval(ival));
|
||||
onMounted(() => refreshText());
|
||||
|
||||
function canReverse(transaction: FG.Transaction) {
|
||||
return (
|
||||
hasPermission('balance_reversal', store) ||
|
||||
(transaction.sender_id === store.state.user.currentUser?.userid &&
|
||||
Date.now() - transaction.time.getTime() < 10000)
|
||||
const isNegative = () => props.transaction.sender_id === store.state.user.currentUser?.userid;
|
||||
|
||||
const refreshText = async () => {
|
||||
if (isNegative()) {
|
||||
text.value = 'Anschreiben';
|
||||
if (props.transaction.receiver_id !== null) {
|
||||
const user = <FG.User>await store.dispatch('user/getUser', {
|
||||
userid: props.transaction.receiver_id
|
||||
});
|
||||
text.value = `Gesendet an ${user.display_name}`;
|
||||
}
|
||||
} else {
|
||||
text.value = 'Gutschrift';
|
||||
if (props.transaction.sender_id !== null) {
|
||||
const user = <FG.User>await store.dispatch('user/getUser', {
|
||||
userid: props.transaction.sender_id
|
||||
});
|
||||
text.value = `Bekommen von ${user.display_name}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const isReversed = computed(() => props.transaction.reversal != undefined);
|
||||
|
||||
const canReverse = computed(
|
||||
() =>
|
||||
!isReversed.value &&
|
||||
(hasPermission('balance_reversal', store) ||
|
||||
(props.transaction.sender_id === store.state.user.currentUser?.userid &&
|
||||
now.value - props.transaction.time.getTime() < 10000))
|
||||
);
|
||||
}
|
||||
|
||||
function color() {
|
||||
return canReverse(props.transaction) ? 'negative' : 'grey';
|
||||
}
|
||||
|
||||
function disabled() {
|
||||
return !canReverse(props.transaction);
|
||||
}
|
||||
|
||||
function reverse(transaction: FG.Transaction) {
|
||||
if (canReverse(transaction))
|
||||
function reverse() {
|
||||
if (canReverse.value)
|
||||
store
|
||||
.dispatch('balance/revert', transaction)
|
||||
.dispatch('balance/revert', props.transaction)
|
||||
.then(() => {
|
||||
emit('reversed', transaction.id);
|
||||
emit('update:transaction', props.transaction);
|
||||
})
|
||||
.catch(error => console.log(error));
|
||||
}
|
||||
|
@ -70,7 +103,7 @@ export default defineComponent({
|
|||
return formatDateTime(props.transaction.time, elapsed > 12 * 60 * 60, true, true) + ' Uhr';
|
||||
});
|
||||
|
||||
return { timeStr, disabled, color, reverse };
|
||||
return { timeStr, reverse, isNegative, text, refreshText, canReverse, isReversed };
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -56,8 +56,9 @@
|
|||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
<div v-for="(transaction, index) in transactions" v-bind:key="index" class="col-sm-4 col-xs-6">
|
||||
<Transaction :transaction="transaction" @reversed="reversed" />
|
||||
<div v-for="(transaction, index) in transactions" v-bind:key="index" class="col-md-4 col-sm-6">
|
||||
<!-- TODO: In Vue3 use v-model:transaction="..." -->
|
||||
<Transaction :transaction.sync="transactions[index]" />
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
@ -80,7 +81,7 @@ export default defineComponent({
|
|||
|
||||
const amount = ref<number>(0);
|
||||
const showAddShortcut = ref(false);
|
||||
const transactions = ref<FG.Transaction[]>([]);
|
||||
const transactions = computed(() => store.state.balance.transactions.slice().reverse());
|
||||
const user = ref(store.state.user.currentUser);
|
||||
const shortCuts = ref(store.state.balance.shortcuts);
|
||||
|
||||
|
@ -89,7 +90,7 @@ export default defineComponent({
|
|||
);
|
||||
|
||||
function addShortcut() {
|
||||
void store.dispatch('balance/addShortcut', amount.value * -1);
|
||||
if (amount.value != 0) void store.dispatch('balance/addShortcut', amount.value * -1);
|
||||
}
|
||||
function removeShortcut(shortcut: number) {
|
||||
void store.dispatch('balance/removeShortcut', shortcut);
|
||||
|
@ -100,19 +101,9 @@ export default defineComponent({
|
|||
function changeBalance(amount: number) {
|
||||
store
|
||||
.dispatch('balance/changeBalance', { amount: amount, user: user.value?.userid })
|
||||
.then((transaction: FG.Transaction) => {
|
||||
if (transactions.value.length > 5) transactions.value.pop();
|
||||
transaction.time = new Date(transaction.time);
|
||||
transactions.value.unshift(transaction);
|
||||
console.log(transactions.value);
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
}
|
||||
|
||||
function reversed(id: number) {
|
||||
transactions.value = transactions.value.filter(t => t.id != id);
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
addShortcut,
|
||||
|
@ -123,7 +114,6 @@ export default defineComponent({
|
|||
amount,
|
||||
showSelector,
|
||||
shortCuts,
|
||||
reversed,
|
||||
userUpdated
|
||||
};
|
||||
}
|
||||
|
|
|
@ -40,7 +40,12 @@ export default defineComponent({
|
|||
align: 'left',
|
||||
sortable: true
|
||||
},
|
||||
{ name: 'balance', label: 'Kontostand', field: 'balance' },
|
||||
{
|
||||
name: 'balance',
|
||||
label: 'Kontostand',
|
||||
field: 'balance',
|
||||
format: (val: number) => val.toFixed(2)
|
||||
},
|
||||
{
|
||||
name: 'limit',
|
||||
label: 'Limit',
|
||||
|
|
|
@ -3,10 +3,7 @@
|
|||
<q-page padding v-if="checkMain">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-list
|
||||
v-for="(mainRoute, index) in mainRoutes"
|
||||
:key="'mainRoute' + index"
|
||||
>
|
||||
<q-list v-for="(mainRoute, index) in mainRoutes" :key="'mainRoute' + index">
|
||||
<essential-link
|
||||
v-for="(route, index2) in mainRoute.children"
|
||||
:key="'route' + index2"
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
</q-card>
|
||||
</div>
|
||||
<div v-for="(transaction, index) in transactions" v-bind:key="index" class="col-sm-4 col-xs-6">
|
||||
<Transaction :transaction="transaction" @reversed="reversed" />
|
||||
<Transaction :transaction.sync="transactions[index]" />
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
@ -56,7 +56,7 @@ export default defineComponent({
|
|||
const sender = ref(store.state.user.currentUser);
|
||||
const receiver = ref<FG.User | undefined>(undefined);
|
||||
const amount = ref<number>(0);
|
||||
const transactions = ref<FG.Transaction[]>([]);
|
||||
const transactions = computed(() => store.state.balance.transactions.slice().reverse());
|
||||
|
||||
const sendDisabled = computed(() => {
|
||||
return !(
|
||||
|
@ -75,10 +75,6 @@ export default defineComponent({
|
|||
receiver.value = selectedUser;
|
||||
}
|
||||
|
||||
function reversed(id: number) {
|
||||
transactions.value = transactions.value.filter(value => value.id != id);
|
||||
}
|
||||
|
||||
function sendAmount() {
|
||||
store
|
||||
.dispatch('balance/changeBalance', {
|
||||
|
@ -86,11 +82,6 @@ export default defineComponent({
|
|||
sender: sender.value?.userid,
|
||||
user: receiver.value?.userid
|
||||
})
|
||||
.then((transaction: FG.Transaction) => {
|
||||
if (transactions.value.length > 5) transactions.value.pop();
|
||||
transaction.time = new Date(transaction.time);
|
||||
transactions.value.unshift(transaction);
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
}
|
||||
|
||||
|
@ -103,8 +94,7 @@ export default defineComponent({
|
|||
showSelector,
|
||||
senderUpdated,
|
||||
receiverUpdated,
|
||||
sendDisabled,
|
||||
reversed
|
||||
sendDisabled
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ export interface UserBalance extends BalanceResponse {
|
|||
export interface BalanceInterface {
|
||||
balances: Map<string, UserBalance>;
|
||||
shortcuts: Array<number>;
|
||||
transactions: Array<FG.Transaction>;
|
||||
loading: number;
|
||||
}
|
||||
|
||||
|
@ -26,6 +27,7 @@ export interface StateInterfaceBalance extends StateInterface {
|
|||
const state: BalanceInterface = {
|
||||
balances: new Map<string, UserBalance>(),
|
||||
shortcuts: [],
|
||||
transactions: [],
|
||||
loading: 0
|
||||
};
|
||||
|
||||
|
@ -53,6 +55,15 @@ const mutations: MutationTree<BalanceInterface> = {
|
|||
},
|
||||
setShortcuts(state, data: Array<number>) {
|
||||
state.shortcuts.splice(0, state.shortcuts.length, ...data);
|
||||
},
|
||||
addTransaction(state, data: FG.Transaction) {
|
||||
state.transactions.push(data);
|
||||
},
|
||||
reverseTransaction(state, data: { transaction: FG.Transaction; reversal: FG.Transaction }) {
|
||||
const idx = state.transactions.findIndex(value => value.id === data.transaction.id);
|
||||
data.transaction.reversal = data.reversal;
|
||||
if (idx > -1) state.transactions[idx] = data.transaction;
|
||||
else state.transactions.push(data.transaction);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -111,8 +122,11 @@ const actions: ActionTree<BalanceInterface, StateInterface> = {
|
|||
})
|
||||
.finally(() => commit('setLoading', false));
|
||||
},
|
||||
revert({ dispatch }, transaction: FG.Transaction) {
|
||||
return axios.delete(`/balance/${transaction.id}`).then(() => {
|
||||
revert({ dispatch, commit }, transaction: FG.Transaction) {
|
||||
return axios
|
||||
.delete(`/balance/${transaction.id}`)
|
||||
.then((response: AxiosResponse<FG.Transaction>) => {
|
||||
commit('reverseTransaction', { transaction: transaction, reversal: response.data });
|
||||
dispatch('getBalance').catch(err => console.warn(err));
|
||||
});
|
||||
},
|
||||
|
@ -121,6 +135,9 @@ const actions: ActionTree<BalanceInterface, StateInterface> = {
|
|||
return axios
|
||||
.put(`/users/${data.user}/balance`, data)
|
||||
.then((response: AxiosResponse<FG.Transaction>) => {
|
||||
const transaction = response.data;
|
||||
transaction.time = new Date(transaction.time);
|
||||
commit('addTransaction', transaction);
|
||||
commit(state.balances.has(data.user) ? 'changeBalance' : 'setBalance', {
|
||||
userid: data.user,
|
||||
amount: data.amount
|
||||
|
@ -130,12 +147,12 @@ const actions: ActionTree<BalanceInterface, StateInterface> = {
|
|||
userid: data.sender,
|
||||
amount: -1 * data.amount
|
||||
});
|
||||
return response.data;
|
||||
return transaction;
|
||||
})
|
||||
.catch(err => {
|
||||
console.debug(err);
|
||||
// Maybe Balance changed
|
||||
dispatch('getBalance').catch(err => console.warn(err));
|
||||
console.warn(err);
|
||||
return dispatch('getBalance', data.sender ? data.sender : data.user);
|
||||
})
|
||||
.finally(() => commit('setLoading', false));
|
||||
}
|
||||
|
|
|
@ -5,16 +5,10 @@
|
|||
Benutzereinstellungen
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6 q-pa-sm">
|
||||
<UserSelector
|
||||
:user="user"
|
||||
@update:user="userUpdated"
|
||||
/>
|
||||
<UserSelector :user="user" @update:user="userUpdated" />
|
||||
</div>
|
||||
</q-card-section>
|
||||
<MainUserSettings
|
||||
:user="user"
|
||||
@update:user="updateUser"
|
||||
/>
|
||||
<MainUserSettings :user="user" @update:user="updateUser" />
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
|
@ -39,7 +33,7 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
function updateUser(value: FG.User) {
|
||||
store.dispatch('user/updateUser', value).catch((error) => {
|
||||
store.dispatch('user/updateUser', value).catch(error => {
|
||||
console.warn(error);
|
||||
});
|
||||
}
|
||||
|
@ -47,9 +41,9 @@ export default defineComponent({
|
|||
return {
|
||||
user,
|
||||
userUpdated,
|
||||
updateUser,
|
||||
updateUser
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -9,12 +9,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<span class="text-h6">Hallo {{ name }}</span><br />
|
||||
<span class="text-h6">Hallo {{ name }}</span
|
||||
><br />
|
||||
<span v-if="hasBirthday">Herzlichen Glückwunsch zum Geburtstag!<br /></span>
|
||||
<span v-if="birthday.length > 0">Heute <span v-if="birthday.length === 1">hat </span><span v-else>haben </span><span
|
||||
v-for="(user, index) in birthday"
|
||||
v-bind:key="index"
|
||||
>{{user.display_name}}<span v-if="index < (birthday.length-1)">, </span></span> Geburtstag.</span>
|
||||
<span v-if="birthday.length > 0"
|
||||
>Heute <span v-if="birthday.length === 1">hat </span><span v-else>haben </span
|
||||
><span v-for="(user, index) in birthday" v-bind:key="index"
|
||||
>{{ user.display_name }}<span v-if="index < birthday.length - 1">, </span></span
|
||||
>
|
||||
Geburtstag.</span
|
||||
>
|
||||
<span v-else>Heute stehen keine Geburtstage an</span>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
@ -22,12 +26,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onMounted,
|
||||
ref,
|
||||
} from '@vue/composition-api';
|
||||
import { computed, defineComponent, onMounted, ref } from '@vue/composition-api';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
|
||||
|
@ -57,10 +56,10 @@ export default defineComponent({
|
|||
const birthday = computed(() =>
|
||||
store.state.user.users
|
||||
.filter(userHasBirthday)
|
||||
.filter((user) => user.userid !== store.state.user.currentUser?.userid)
|
||||
.filter(user => user.userid !== store.state.user.currentUser?.userid)
|
||||
);
|
||||
|
||||
return { avatarLink, name, hasBirthday, birthday };
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
<div>
|
||||
<q-card class="col-12">
|
||||
<q-form @submit="save" @reset="reset">
|
||||
<q-card-section
|
||||
class="fit row justify-start content-center items-center"
|
||||
>
|
||||
<q-card-section class="fit row justify-start content-center items-center">
|
||||
<span class="col-xs-12 col-sm-6 text-center text-h6">
|
||||
Rollen und Berechtigungen
|
||||
</span>
|
||||
|
@ -26,17 +24,9 @@
|
|||
/>
|
||||
</q-card-section>
|
||||
<q-separator />
|
||||
<q-card-section
|
||||
v-if="role"
|
||||
class="fit row justify-start content-center items-center"
|
||||
>
|
||||
<q-card-section v-if="role" class="fit row justify-start content-center items-center">
|
||||
<q-scroll-area style="height: 20em; width: 100%">
|
||||
<q-input
|
||||
filled
|
||||
v-model="newRoleName"
|
||||
label="neuer Name"
|
||||
v-if="role.id != -1"
|
||||
/>
|
||||
<q-input filled v-model="newRoleName" label="neuer Name" v-if="role.id != -1" />
|
||||
<q-option-group
|
||||
:value="role.permissions"
|
||||
@input="updatePermissions"
|
||||
|
@ -57,12 +47,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
ref,
|
||||
onBeforeMount,
|
||||
} from '@vue/composition-api';
|
||||
import { computed, defineComponent, ref, onBeforeMount } from '@vue/composition-api';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
|
||||
|
@ -72,10 +57,10 @@ export default defineComponent({
|
|||
const store = <Store<StateInterface>>root.$store;
|
||||
|
||||
onBeforeMount(() => {
|
||||
store.dispatch('user/getRoles').catch((error) => {
|
||||
store.dispatch('user/getRoles').catch(error => {
|
||||
console.warn(error);
|
||||
});
|
||||
store.dispatch('user/getPermissions').catch((error) => {
|
||||
store.dispatch('user/getPermissions').catch(error => {
|
||||
console.warn(error);
|
||||
});
|
||||
});
|
||||
|
@ -83,20 +68,17 @@ export default defineComponent({
|
|||
const role = ref<FG.Role | null>(null);
|
||||
const roles = computed(() => store.state.user.roles);
|
||||
const permissions = computed(() =>
|
||||
store.state.user.permissions.map((perm) => {
|
||||
store.state.user.permissions.map(perm => {
|
||||
return {
|
||||
value: perm,
|
||||
label: perm,
|
||||
label: perm
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const newRoleName = ref<string>('');
|
||||
|
||||
function createRole(
|
||||
name: string,
|
||||
done: (arg0: string, arg1: string) => void
|
||||
): void {
|
||||
function createRole(name: string, done: (arg0: string, arg1: string) => void): void {
|
||||
role.value = { name: name, permissions: [], id: -1 };
|
||||
done(name, 'add-unique');
|
||||
}
|
||||
|
@ -116,16 +98,14 @@ export default defineComponent({
|
|||
role.value = {
|
||||
id: rl.id,
|
||||
name: rl.name,
|
||||
permissions: Array.from(rl.permissions),
|
||||
permissions: Array.from(rl.permissions)
|
||||
};
|
||||
}
|
||||
|
||||
function save() {
|
||||
if (role.value) {
|
||||
if (role.value.id === -1)
|
||||
void store
|
||||
.dispatch('user/newRole', role.value)
|
||||
.then((createdRole: FG.Role) => {
|
||||
void store.dispatch('user/newRole', role.value).then((createdRole: FG.Role) => {
|
||||
console.log(createdRole);
|
||||
role.value = createdRole;
|
||||
});
|
||||
|
@ -139,9 +119,7 @@ export default defineComponent({
|
|||
|
||||
function reset() {
|
||||
if (role.value && role.value.id !== -1) {
|
||||
const original = roles.value.find(
|
||||
(value) => value.name === role.value?.name
|
||||
);
|
||||
const original = roles.value.find(value => value.name === role.value?.name);
|
||||
if (original) updateRole(original);
|
||||
} else {
|
||||
role.value = null;
|
||||
|
@ -156,7 +134,7 @@ export default defineComponent({
|
|||
store
|
||||
.dispatch('user/deleteRole', role.value)
|
||||
.then(() => (role.value = null))
|
||||
.catch((error) => console.warn(error));
|
||||
.catch(error => console.warn(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,8 +150,8 @@ export default defineComponent({
|
|||
reset,
|
||||
removeRole,
|
||||
remove,
|
||||
newRoleName,
|
||||
newRoleName
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -16,30 +16,42 @@
|
|||
{{ session.platform }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row" v-if="!isEdit">
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
Lebenszeit:
|
||||
{{ session.lifetime }}
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
Läuft aus: {{ session.expires | dateTime(true) }}
|
||||
<div class="col-xs-12 col-sm-6">Läuft aus: {{ session.expires | dateTime(true) }}</div>
|
||||
</div>
|
||||
<div class="row q-my-sm" v-else>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-px-sm"
|
||||
v-model="computedLifetime"
|
||||
type="number"
|
||||
label="Zeit"
|
||||
filled
|
||||
/>
|
||||
<q-select
|
||||
class="col-xs-12 col-sm-6 q-px-sm"
|
||||
:options="options"
|
||||
v-model="option"
|
||||
filled
|
||||
/>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-actions align="right">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
icon="mdi-delete"
|
||||
@click="deleteSession(session.token)"
|
||||
/>
|
||||
<q-card-actions align="right" v-if="!isEdit">
|
||||
<q-btn flat round dense icon="mdi-pencil" @click="edit(true)" />
|
||||
<q-btn flat round dense icon="mdi-delete" @click="deleteSession(session.token)" />
|
||||
</q-card-actions>
|
||||
<q-card-actions align="right" v-else>
|
||||
<q-btn flat dense label="Abbrechen" @click="edit(false)" />
|
||||
<q-btn flat dense label="Speichern" @click="save" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from '@vue/composition-api';
|
||||
import { defineComponent, ref, computed } from '@vue/composition-api';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
|
||||
|
@ -47,12 +59,14 @@ export default defineComponent({
|
|||
name: 'Sessions',
|
||||
props: {
|
||||
session: {
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
setup(_, { root }) {
|
||||
},
|
||||
setup(props: {session: FG.Session}, { root }) {
|
||||
const store = <Store<StateInterface>>root.$store;
|
||||
|
||||
const options = ref(['Minuten', 'Stunden', 'Tage']);
|
||||
const option = ref<string>(options.value[0]);
|
||||
const lifetime = ref(0);
|
||||
function getBrowserIcon(browser: string) {
|
||||
return browser == 'firefox'
|
||||
? 'mdi-firefox'
|
||||
|
@ -78,7 +92,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
function deleteSession(token: string) {
|
||||
store.dispatch('session/deleteSession', token).catch(error => {
|
||||
store.dispatch('session/deleteSession', token).catch((error) => {
|
||||
console.warn(error);
|
||||
});
|
||||
}
|
||||
|
@ -86,12 +100,67 @@ export default defineComponent({
|
|||
return store.state.session.currentSession?.token === token;
|
||||
}
|
||||
|
||||
const isEdit = ref(false);
|
||||
|
||||
const computedLifetime = computed({
|
||||
get: () => {
|
||||
switch (option.value) {
|
||||
case options.value[0]:
|
||||
return (lifetime.value / 60).toFixed(2);
|
||||
case options.value[1]:
|
||||
return (lifetime.value / (60 * 60)).toFixed(2);
|
||||
case options.value[2]:
|
||||
return (lifetime.value / (60 * 60 * 24)).toFixed(2);
|
||||
}
|
||||
},
|
||||
set: (val) => {
|
||||
if (val) {
|
||||
switch (option.value) {
|
||||
case options.value[0]:
|
||||
lifetime.value = parseFloat(val) * 60;
|
||||
break;
|
||||
case options.value[1]:
|
||||
lifetime.value = parseFloat(val) * 60 * 60;
|
||||
break;
|
||||
case options.value[2]:
|
||||
lifetime.value = parseFloat(val) * 60 * 60 * 24;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function edit(value: boolean) {
|
||||
lifetime.value = props.session.lifetime;
|
||||
isEdit.value = value;
|
||||
}
|
||||
|
||||
function save() {
|
||||
console.log(lifetime.value);
|
||||
isEdit.value = false;
|
||||
void store
|
||||
.dispatch(
|
||||
'session/updateSession',
|
||||
{lifetime: lifetime.value, token: props.session.token}
|
||||
)
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
getBrowserIcon,
|
||||
getPlatformIcon,
|
||||
isThisSession,
|
||||
deleteSession
|
||||
deleteSession,
|
||||
isEdit,
|
||||
edit,
|
||||
options,
|
||||
option,
|
||||
lifetime,
|
||||
computedLifetime,
|
||||
save,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<q-tabs
|
||||
v-model="tab"
|
||||
v-if="$q.screen.gt.sm"
|
||||
>
|
||||
<q-tabs v-model="tab" v-if="$q.screen.gt.sm">
|
||||
<q-tab
|
||||
v-for="(tabindex, index) in tabs"
|
||||
:key="'tab' + index"
|
||||
|
@ -11,23 +8,10 @@
|
|||
:label="tabindex.label"
|
||||
/>
|
||||
</q-tabs>
|
||||
<div
|
||||
class="fit row justify-end"
|
||||
v-else
|
||||
>
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
icon="mdi-menu"
|
||||
@click="showDrawer = !showDrawer"
|
||||
/>
|
||||
<div class="fit row justify-end" v-else>
|
||||
<q-btn flat round icon="mdi-menu" @click="showDrawer = !showDrawer" />
|
||||
</div>
|
||||
<q-drawer
|
||||
side="right"
|
||||
v-model="showDrawer"
|
||||
@click="showDrawer = !showDrawer"
|
||||
behavior="mobile"
|
||||
>
|
||||
<q-drawer side="right" v-model="showDrawer" @click="showDrawer = !showDrawer" behavior="mobile">
|
||||
<q-list v-model="tab">
|
||||
<q-item
|
||||
v-for="(tabindex, index) in tabs"
|
||||
|
@ -40,10 +24,7 @@
|
|||
</q-item>
|
||||
</q-list>
|
||||
</q-drawer>
|
||||
<q-page
|
||||
padding
|
||||
class="fit row justify-center content-start items-start q-gutter-sm"
|
||||
>
|
||||
<q-page padding class="fit row justify-center content-start items-start q-gutter-sm">
|
||||
<q-tab-panels
|
||||
v-model="tab"
|
||||
style="background-color: transparent;"
|
||||
|
@ -81,9 +62,7 @@ export default defineComponent({
|
|||
setup(_, { root }) {
|
||||
const store = <Store<StateInterface>>root.$store;
|
||||
|
||||
const canEditRoles = computed(() =>
|
||||
hasPermission(PERMISSIONS.ROLES_EDIT, store)
|
||||
);
|
||||
const canEditRoles = computed(() => hasPermission(PERMISSIONS.ROLES_EDIT, store));
|
||||
|
||||
interface Tab {
|
||||
name: string;
|
||||
|
@ -93,7 +72,7 @@ export default defineComponent({
|
|||
const tabs: Tab[] = [
|
||||
{ name: 'user', label: 'Mitglieder' },
|
||||
{ name: 'newUser', label: 'Neues Mitglied' },
|
||||
{ name: 'roles', label: 'Rollen' },
|
||||
{ name: 'roles', label: 'Rollen' }
|
||||
];
|
||||
|
||||
const drawer = ref<boolean>(false);
|
||||
|
@ -104,7 +83,7 @@ export default defineComponent({
|
|||
},
|
||||
set: (val: boolean) => {
|
||||
drawer.value = val;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const tab = ref<string>('user');
|
||||
|
@ -113,8 +92,8 @@ export default defineComponent({
|
|||
canEditRoles,
|
||||
showDrawer,
|
||||
tab,
|
||||
tabs,
|
||||
tabs
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -3,10 +3,7 @@
|
|||
<q-page padding v-if="checkMain">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-list
|
||||
v-for="(mainRoute, index) in mainRoutes"
|
||||
:key="'mainRoute' + index"
|
||||
>
|
||||
<q-list v-for="(mainRoute, index) in mainRoutes" :key="'mainRoute' + index">
|
||||
<essential-link
|
||||
v-for="(route, index2) in mainRoute.children"
|
||||
:key="'route' + index2"
|
||||
|
|
|
@ -14,12 +14,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onBeforeMount,
|
||||
ref
|
||||
} from '@vue/composition-api';
|
||||
import { computed, defineComponent, onBeforeMount, ref } from '@vue/composition-api';
|
||||
import Sessions from '../components/settings/Sessions.vue';
|
||||
import MainUserSettings from '../components/settings/MainUserSettings.vue';
|
||||
import { Store } from 'vuex';
|
||||
|
@ -40,9 +35,7 @@ export default defineComponent({
|
|||
|
||||
const currentUser = ref(<FG.User>store.state.user.currentUser);
|
||||
const sessions = computed(() => store.state.session.sessions);
|
||||
const loading = computed(
|
||||
() => store.state.session.loading || store.state.user.loading > 0
|
||||
);
|
||||
const loading = computed(() => store.state.session.loading || store.state.user.loading > 0);
|
||||
function updateUser(value: FG.User) {
|
||||
store.dispatch('user/updateUser', value).catch(error => {
|
||||
console.warn(error);
|
||||
|
|
|
@ -25,7 +25,7 @@ function loadCurrentSession() {
|
|||
const state: SessionInterface = {
|
||||
sessions: [],
|
||||
currentSession: loadCurrentSession() || undefined,
|
||||
loading: false
|
||||
loading: false,
|
||||
};
|
||||
|
||||
const mutations: MutationTree<SessionInterface> = {
|
||||
|
@ -42,7 +42,13 @@ const mutations: MutationTree<SessionInterface> = {
|
|||
},
|
||||
setLoading(state, value: boolean) {
|
||||
state.loading = value;
|
||||
},
|
||||
updateSession(state, session: FG.Session) {
|
||||
const index = state.sessions.findIndex((x) => x.token == session.token);
|
||||
if (index > -1) {
|
||||
state.sessions[index] = session;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const actions: ActionTree<SessionInterface, StateInterface> = {
|
||||
|
@ -59,7 +65,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
|||
commit('setCurrentSession', response.data.session);
|
||||
commit('user/setCurrentUser', response.data.user, { root: true });
|
||||
commit('user/setCurrentPermissions', response.data.permissions, {
|
||||
root: true
|
||||
root: true,
|
||||
});
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
|
@ -72,7 +78,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
|||
*/
|
||||
logout({ dispatch, rootState }) {
|
||||
if (rootState.session.currentSession) {
|
||||
dispatch('deleteSession', rootState.session.currentSession.token).catch(error => {
|
||||
dispatch('deleteSession', rootState.session.currentSession.token).catch((error) => {
|
||||
console.log(error);
|
||||
void dispatch('clearCurrent', false);
|
||||
});
|
||||
|
@ -91,7 +97,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
|||
if (token === rootState.session.currentSession?.token) {
|
||||
void dispatch('clearCurrent', false);
|
||||
} else {
|
||||
dispatch('getSessions').catch(error => {
|
||||
dispatch('getSessions').catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
@ -110,7 +116,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
|||
void Router.push({
|
||||
name: 'login',
|
||||
query: redirect ? { redirect: Router.currentRoute.fullPath } : {},
|
||||
params: { logout: 'true' }
|
||||
params: { logout: 'true' },
|
||||
}).then(() => {
|
||||
commit('clearCurrentSession');
|
||||
commit('user/clearCurrentUser', null, { root: true });
|
||||
|
@ -126,7 +132,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
|||
axios
|
||||
.get('/auth')
|
||||
.then((response: AxiosResponse<FG.Session[]>) => {
|
||||
response.data.forEach(session => {
|
||||
response.data.forEach((session) => {
|
||||
session.expires = new Date(session.expires);
|
||||
});
|
||||
commit('setSessions', response.data);
|
||||
|
@ -137,13 +143,29 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
|||
commit('setCurrentSession', currentSession);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
})
|
||||
.finally(() => {
|
||||
commit('setLoading', false);
|
||||
});
|
||||
},
|
||||
updateSession({ commit, state }, data: { lifetime: number; token: string }) {
|
||||
commit('setLoading', true);
|
||||
axios
|
||||
.put(`auth/${data.token}`, { value: data.lifetime })
|
||||
.then((response: AxiosResponse<FG.Session>) => {
|
||||
response.data.expires = new Date(response.data.expires);
|
||||
if (state.currentSession?.token == response.data.token) {
|
||||
commit('setCurrentSession', response.data);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.log(err))
|
||||
.finally(() => {
|
||||
commit('setLoading', false);
|
||||
});
|
||||
console.log('updateSession', data);
|
||||
},
|
||||
requestPasswordReset({}, data) {
|
||||
return axios.post('/auth/reset', data);
|
||||
},
|
||||
|
@ -151,7 +173,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
|||
return axios.post('/auth/reset', data).catch((error: AxiosError) => {
|
||||
return Promise.reject(error.response);
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const getters: GetterTree<SessionInterface, StateInterface> = {
|
||||
|
@ -163,7 +185,7 @@ const getters: GetterTree<SessionInterface, StateInterface> = {
|
|||
},
|
||||
loading(state) {
|
||||
return state.loading;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const sessions: Module<SessionInterface, StateInterface> = {
|
||||
|
@ -171,7 +193,7 @@ const sessions: Module<SessionInterface, StateInterface> = {
|
|||
state,
|
||||
mutations,
|
||||
actions,
|
||||
getters
|
||||
getters,
|
||||
};
|
||||
|
||||
export default sessions;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
import { Module, MutationTree, ActionTree, GetterTree } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
import { axios } from 'boot/axios';
|
||||
|
@ -48,6 +49,11 @@ const mutations: MutationTree<UserStateInterface> = {
|
|||
setUsers(state, data: FG.User[]) {
|
||||
state.users = data;
|
||||
},
|
||||
setUser(state, data: FG.User) {
|
||||
const index = state.users.findIndex(x => x.userid === data.userid);
|
||||
if (index > -1) state.users[index] = data;
|
||||
else state.users.push(data);
|
||||
},
|
||||
setRoles(state, data: FG.Role[]) {
|
||||
state.roles = data;
|
||||
},
|
||||
|
@ -221,10 +227,26 @@ const actions: ActionTree<UserStateInterface, StateInterface> = {
|
|||
commit('setPermissions', response.data);
|
||||
})
|
||||
.finally(() => commit('setLoading', false));
|
||||
},
|
||||
getUser({ commit, getters }, data: { userid: string; force?: boolean }) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
const user = <FG.User | undefined>getters['getUser'](data.userid);
|
||||
if (user === undefined || data.force === true) {
|
||||
return axios.get(`/users/${data.userid}`).then((response: AxiosResponse<FG.User>) => {
|
||||
commit('setUser', response.data);
|
||||
return response.data;
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(user);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getters: GetterTree<UserStateInterface, StateInterface> = {
|
||||
getUser: state => (userid: string) => {
|
||||
const user = state.users.filter(usr => usr.userid === userid);
|
||||
return user.length > 0 ? user[0] : undefined;
|
||||
},
|
||||
currentUser({ currentUser }) {
|
||||
return currentUser;
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue