[schedule][events] Improved plugin
* Allow creating recurring events * Fixed AgendaView
This commit is contained in:
parent
17460a8543
commit
77bb463e5e
|
@ -38,11 +38,11 @@ declare namespace FG {
|
||||||
id: number;
|
id: number;
|
||||||
start: Date;
|
start: Date;
|
||||||
end?: Date;
|
end?: Date;
|
||||||
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
type: EventType | number;
|
type: EventType | number;
|
||||||
|
is_template: boolean;
|
||||||
jobs: Array<Job>;
|
jobs: Array<Job>;
|
||||||
recurrence_rule?: RecurrenceRule;
|
|
||||||
template_id?: number;
|
|
||||||
}
|
}
|
||||||
interface EventType {
|
interface EventType {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -67,12 +67,6 @@ declare namespace FG {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
interface RecurrenceRule {
|
|
||||||
frequency: string;
|
|
||||||
until?: Date;
|
|
||||||
count?: number;
|
|
||||||
interval: number;
|
|
||||||
}
|
|
||||||
interface Service {
|
interface Service {
|
||||||
userid: string;
|
userid: string;
|
||||||
value: number;
|
value: number;
|
||||||
|
|
|
@ -1,11 +1,29 @@
|
||||||
<template>
|
<template>
|
||||||
<q-page padding>
|
<q-page padding>
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-form @submit="save" @reset="reset">
|
<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">
|
||||||
<q-card-section class="fit">
|
<div class="text-h6 col-xs-12 col-sm-6 q-pa-sm">Veranstaltung erstellen</div>
|
||||||
<div class="text-h6">Veranstaltung erstellen</div>
|
<q-select
|
||||||
</q-card-section>
|
:model-value="template"
|
||||||
|
filled
|
||||||
|
label="Vorlage"
|
||||||
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
|
:options="templates"
|
||||||
|
option-label="name"
|
||||||
|
map-options
|
||||||
|
clearable
|
||||||
|
:disable="templates.length == 0"
|
||||||
|
@update:modelValue="fromTemplate"
|
||||||
|
@clear="reset()"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
v-model="event.name"
|
||||||
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
|
label="Name"
|
||||||
|
type="text"
|
||||||
|
filled
|
||||||
|
/>
|
||||||
<q-select
|
<q-select
|
||||||
v-model="event.type"
|
v-model="event.type"
|
||||||
filled
|
filled
|
||||||
|
@ -15,7 +33,8 @@
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
:options="eventtypes"
|
:options="eventtypes"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
option-value="name"
|
option-value="id"
|
||||||
|
emit-value
|
||||||
map-options
|
map-options
|
||||||
clearable
|
clearable
|
||||||
:rules="[notEmpty]"
|
:rules="[notEmpty]"
|
||||||
|
@ -24,22 +43,37 @@
|
||||||
v-model="event.start"
|
v-model="event.start"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
label="Veranstaltungsbeginn"
|
label="Veranstaltungsbeginn"
|
||||||
:rules="[noValidDate, notEmpty]"
|
:rules="[notEmpty]"
|
||||||
|
/>
|
||||||
|
<IsoDateInput
|
||||||
|
v-model="event.end"
|
||||||
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
|
label="Veranstaltungsende"
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
|
||||||
<q-card-section class="fit justify-start content-center items-center">
|
|
||||||
<q-input
|
<q-input
|
||||||
v-model="event.description"
|
v-model="event.description"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-12 q-pa-sm"
|
||||||
label="Beschreibung"
|
label="Beschreibung"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
filled
|
filled
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
<q-card-section v-if="event.template_id === undefined">
|
||||||
|
<q-btn-toggle
|
||||||
|
v-model="recurrent"
|
||||||
|
spread
|
||||||
|
no-caps
|
||||||
|
:options="[
|
||||||
|
{ label: 'Einmalig', value: false },
|
||||||
|
{ label: 'Wiederkehrend', value: true },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
<RecurrenceRule v-if="!!recurrent" v-model="recurrenceRule" />
|
||||||
|
</q-card-section>
|
||||||
|
<q-separator />
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<q-btn color="primary" label="Schicht hinzufügen" @click="addJob()" />
|
<q-btn color="primary" label="Schicht hinzufügen" @click="addJob()" />
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<q-card-section v-for="(job, index) in event.jobs" :key="index">
|
<q-card-section v-for="(job, index) in event.jobs" :key="index">
|
||||||
<q-card class="q-my-auto">
|
<q-card class="q-my-auto">
|
||||||
<job
|
<job
|
||||||
|
@ -51,7 +85,8 @@
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-actions align="right">
|
<q-card-actions align="right">
|
||||||
<q-btn label="Reset" type="reset" />
|
<q-btn label="Reset" type="reset" />
|
||||||
<q-btn color="primary" type="submit" label="Speichern" />
|
<q-btn color="secondary" label="Neue Vorlage" @click="save(true)" />
|
||||||
|
<q-btn color="primary" type="submit" label="Erstellen" />
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-form>
|
</q-form>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
@ -62,50 +97,97 @@
|
||||||
import { defineComponent, ref, onBeforeMount, computed } from 'vue';
|
import { defineComponent, ref, onBeforeMount, computed } from 'vue';
|
||||||
import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
|
import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
|
||||||
import Job from './Job.vue';
|
import Job from './Job.vue';
|
||||||
import { date } from 'quasar';
|
import RecurrenceRule from './RecurrenceRule.vue';
|
||||||
|
import { date, ModifyDateOptions } from 'quasar';
|
||||||
import { useScheduleStore } from '../../store';
|
import { useScheduleStore } from '../../store';
|
||||||
|
import { notEmpty } from 'src/utils/validators';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'CreateEvent',
|
name: 'CreateEvent',
|
||||||
components: { IsoDateInput, Job },
|
components: { IsoDateInput, Job, RecurrenceRule },
|
||||||
setup() {
|
setup() {
|
||||||
const store = useScheduleStore();
|
const store = useScheduleStore();
|
||||||
const eventtypes = computed(() => store.eventTypes);
|
|
||||||
const jobDeleteDisabled = computed(() => event.value.jobs.length < 2);
|
|
||||||
|
|
||||||
const newJob = ref<FG.Job>({
|
const emptyJob = {
|
||||||
id: NaN,
|
id: NaN,
|
||||||
start: new Date(),
|
start: new Date(),
|
||||||
end: date.addToDate(new Date(), { hours: 1 }),
|
end: date.addToDate(new Date(), { hours: 1 }),
|
||||||
services: [],
|
services: [],
|
||||||
required_services: 2,
|
required_services: 2,
|
||||||
type: store.jobTypes[0],
|
type: store.jobTypes[0],
|
||||||
});
|
};
|
||||||
|
|
||||||
const event = ref<FG.Event>({
|
const emptyEvent = {
|
||||||
id: NaN,
|
id: NaN,
|
||||||
start: new Date(),
|
start: new Date(),
|
||||||
jobs: [Object.assign({}, newJob.value)],
|
jobs: [Object.assign({}, emptyJob)],
|
||||||
type: store.eventTypes[0],
|
type: store.eventTypes[0],
|
||||||
});
|
is_template: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const templates = computed(() => store.templates);
|
||||||
|
const template = ref<FG.Event | undefined>(undefined);
|
||||||
|
const event = ref<FG.Event>(Object.assign({}, emptyEvent));
|
||||||
|
const eventtypes = computed(() => store.eventTypes);
|
||||||
|
const jobDeleteDisabled = computed(() => event.value.jobs.length < 2);
|
||||||
|
const recurrent = ref(false);
|
||||||
|
const recurrenceRule = ref<FG.RecurrenceRule>({ frequency: 'daily', interval: 1 });
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
void store.getEventTypes();
|
void store.getEventTypes();
|
||||||
void store.getJobTypes();
|
void store.getJobTypes();
|
||||||
|
void store.getTemplates();
|
||||||
});
|
});
|
||||||
|
|
||||||
function addJob() {
|
function addJob() {
|
||||||
event.value.jobs.push(Object.assign({}, newJob.value));
|
event.value.jobs.push(Object.assign({}, emptyJob));
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeJob(index: number) {
|
function removeJob(index: number) {
|
||||||
event.value.jobs.splice(index, 1);
|
event.value.jobs.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save() {
|
function fromTemplate(tpl: FG.Event) {
|
||||||
console.log('Event:', event);
|
template.value = tpl;
|
||||||
|
event.value = Object.assign({}, tpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save(template = false) {
|
||||||
|
console.log(template);
|
||||||
|
event.value.is_template = template;
|
||||||
try {
|
try {
|
||||||
await store.addEvent(event.value);
|
await store.addEvent(event.value);
|
||||||
|
if (recurrent.value && !event.value.is_template) {
|
||||||
|
let count = 0;
|
||||||
|
const options: ModifyDateOptions = {};
|
||||||
|
switch (recurrenceRule.value.frequency) {
|
||||||
|
case 'daily':
|
||||||
|
options['days'] = 1 * recurrenceRule.value.interval;
|
||||||
|
break;
|
||||||
|
case 'weekly':
|
||||||
|
options['days'] = 7 * recurrenceRule.value.interval;
|
||||||
|
break;
|
||||||
|
case 'monthly':
|
||||||
|
options['months'] = 1 * recurrenceRule.value.interval;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
event.value.start = date.addToDate(event.value.start, options);
|
||||||
|
if (event.value.end) event.value.end = date.addToDate(event.value.end, options);
|
||||||
|
event.value.jobs.forEach((job) => {
|
||||||
|
job.start = date.addToDate(job.start, options);
|
||||||
|
if (job.end) job.end = date.addToDate(job.end, options);
|
||||||
|
});
|
||||||
|
count++;
|
||||||
|
if (
|
||||||
|
count <= 120 &&
|
||||||
|
(!recurrenceRule.value.count || count <= recurrenceRule.value.count) &&
|
||||||
|
(!recurrenceRule.value.until || event.value.start < recurrenceRule.value.until)
|
||||||
|
)
|
||||||
|
await store.addEvent(event.value);
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
}
|
||||||
reset();
|
reset();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -113,34 +195,24 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
event.value.id = NaN;
|
event.value = Object.assign({}, emptyEvent);
|
||||||
event.value.start = new Date();
|
template.value = undefined;
|
||||||
event.value.description = '';
|
|
||||||
event.value.type = { id: -1, name: '' };
|
|
||||||
event.value.jobs = [Object.assign({}, newJob.value)];
|
|
||||||
}
|
|
||||||
function notEmpty(val: string) {
|
|
||||||
return !!val || 'Feld darf nicht leer sein!';
|
|
||||||
}
|
|
||||||
function noValidDate(val: string) {
|
|
||||||
return !!date.isValid(val) || 'Datum/Zeit muss gesetzt sein!';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAfterDate(val: Date) {
|
|
||||||
return !!val;
|
|
||||||
// return event.value.jobsstart.getTime() > val.getTime() || 'Ende muss hinter dem Start liegen';
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
jobDeleteDisabled,
|
jobDeleteDisabled,
|
||||||
addJob,
|
addJob,
|
||||||
eventtypes,
|
eventtypes,
|
||||||
|
templates,
|
||||||
removeJob,
|
removeJob,
|
||||||
notEmpty,
|
notEmpty,
|
||||||
noValidDate,
|
|
||||||
save,
|
save,
|
||||||
reset,
|
reset,
|
||||||
|
recurrent,
|
||||||
|
fromTemplate,
|
||||||
|
template,
|
||||||
|
recurrenceRule,
|
||||||
event,
|
event,
|
||||||
isAfterDate,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,17 +6,15 @@
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
label="Beginn"
|
label="Beginn"
|
||||||
type="datetime"
|
type="datetime"
|
||||||
:rules="[notEmpty, noValidDate]"
|
:rules="[notEmpty]"
|
||||||
/>
|
/>
|
||||||
<IsoDateInput
|
<IsoDateInput
|
||||||
v-model="job.end"
|
v-model="job.end"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
label="Ende"
|
label="Ende"
|
||||||
type="datetime"
|
type="datetime"
|
||||||
:rules="[notEmpty, noValidDate, isAfterDate]"
|
:rules="[notEmpty, isAfterDate]"
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
|
||||||
<q-card-section class="row fit justify-start content-center items-center">
|
|
||||||
<q-select
|
<q-select
|
||||||
v-model="job.type"
|
v-model="job.type"
|
||||||
filled
|
filled
|
||||||
|
@ -39,11 +37,9 @@
|
||||||
type="number"
|
type="number"
|
||||||
:rules="[notEmpty]"
|
:rules="[notEmpty]"
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
|
||||||
<q-card-section class="fit row justify-start content-center items-center">
|
|
||||||
<q-input
|
<q-input
|
||||||
v-model="job.comment"
|
v-model="job.comment"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-12 q-pa-sm"
|
||||||
label="Beschreibung"
|
label="Beschreibung"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
filled
|
filled
|
||||||
|
@ -55,10 +51,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
|
|
||||||
import { defineComponent, computed, onBeforeMount, PropType } from 'vue';
|
import { defineComponent, computed, onBeforeMount, PropType } from 'vue';
|
||||||
|
import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
|
||||||
|
import { notEmpty } from 'src/utils/validators';
|
||||||
import { useScheduleStore } from '../../store';
|
import { useScheduleStore } from '../../store';
|
||||||
import { date } from 'quasar';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Job',
|
name: 'Job',
|
||||||
|
@ -88,7 +84,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
set(obj, prop, value) {
|
set(obj, prop, value) {
|
||||||
console.log('...', obj, prop, value);
|
|
||||||
if (typeof prop === 'string') {
|
if (typeof prop === 'string') {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
emit('update:modelValue', Object.assign({}, props.modelValue, { [prop]: value }));
|
emit('update:modelValue', Object.assign({}, props.modelValue, { [prop]: value }));
|
||||||
|
@ -101,13 +96,6 @@ export default defineComponent({
|
||||||
emit('remove-job');
|
emit('remove-job');
|
||||||
}
|
}
|
||||||
|
|
||||||
function notEmpty(val: string) {
|
|
||||||
return !!val || 'Feld darf nicht leer sein!';
|
|
||||||
}
|
|
||||||
function noValidDate(val: string) {
|
|
||||||
return !!date.isValid(val) || 'Datum/Zeit muss gesetzt sein!';
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAfterDate(val: string) {
|
function isAfterDate(val: string) {
|
||||||
return props.modelValue.start < new Date(val) || 'Ende muss hinter dem Start liegen';
|
return props.modelValue.start < new Date(val) || 'Ende muss hinter dem Start liegen';
|
||||||
}
|
}
|
||||||
|
@ -117,7 +105,6 @@ export default defineComponent({
|
||||||
jobtypes,
|
jobtypes,
|
||||||
removeJob,
|
removeJob,
|
||||||
notEmpty,
|
notEmpty,
|
||||||
noValidDate,
|
|
||||||
isAfterDate,
|
isAfterDate,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
<template>
|
||||||
|
<q-card class="fit row justify-start content-center items-center">
|
||||||
|
<q-input
|
||||||
|
v-model="rule.interval"
|
||||||
|
filled
|
||||||
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
|
label="Interval"
|
||||||
|
type="number"
|
||||||
|
:rules="[notEmpty]"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
v-model="rule.frequency"
|
||||||
|
filled
|
||||||
|
label="Wiederholung"
|
||||||
|
input-debounce="200"
|
||||||
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
|
:options="freqTypes"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
v-model="rule.count"
|
||||||
|
filled
|
||||||
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
|
label="Anzahl Wiederholungen"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
<IsoDateInput
|
||||||
|
v-model="rule.until"
|
||||||
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
|
label="Wiederholen bis"
|
||||||
|
type="date"
|
||||||
|
/>
|
||||||
|
</q-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
|
||||||
|
import { defineComponent, PropType } from 'vue';
|
||||||
|
import { notEmpty } from 'src/utils/validators';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'RecurrenceRule',
|
||||||
|
components: { IsoDateInput },
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
required: true,
|
||||||
|
type: Object as PropType<FG.RecurrenceRule>,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
'update:modelValue': (rule: FG.RecurrenceRule) => !!rule,
|
||||||
|
},
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const freqTypes = [
|
||||||
|
{ label: 'Täglich', value: 'daily' },
|
||||||
|
{ label: 'Wöchentlich', value: 'weekly' },
|
||||||
|
{ label: 'Monatlich', value: 'monthly' },
|
||||||
|
{ label: 'Jährlich', value: 'yearly' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const rule = new Proxy(props.modelValue, {
|
||||||
|
get(target, prop) {
|
||||||
|
if (typeof prop === 'string') {
|
||||||
|
return ((props.modelValue as unknown) as Record<string, unknown>)[prop];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set(target, prop, value) {
|
||||||
|
if (typeof prop === 'string') {
|
||||||
|
const obj = Object.assign({}, props.modelValue);
|
||||||
|
if (prop == 'frequency' && typeof value === 'string') obj.frequency = value;
|
||||||
|
else if (prop == 'interval') {
|
||||||
|
obj.interval = typeof value === 'string' ? parseInt(value) : <number>value;
|
||||||
|
} else if (prop == 'count') {
|
||||||
|
obj.until = undefined;
|
||||||
|
obj.count = typeof value === 'string' ? parseInt(value) : <number>value;
|
||||||
|
} else if (prop == 'until' && (value instanceof Date || value === undefined)) {
|
||||||
|
obj.count = undefined;
|
||||||
|
obj.until = <Date | undefined>value;
|
||||||
|
} else return false;
|
||||||
|
emit('update:modelValue', obj);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
rule,
|
||||||
|
notEmpty,
|
||||||
|
freqTypes,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
|
@ -55,8 +55,8 @@
|
||||||
>
|
>
|
||||||
<template #day="{ scope: { timestamp } }" style="min-height: 200px">
|
<template #day="{ scope: { timestamp } }" style="min-height: 200px">
|
||||||
<template v-if="!events[timestamp.weekday]" style="min-height: 200px"> </template>
|
<template v-if="!events[timestamp.weekday]" style="min-height: 200px"> </template>
|
||||||
<template v-for="agenda in events[timestamp.weekday]" :key="agenda.id">
|
<template v-for="(agenda, index) in events[timestamp.weekday]" :key="agenda.id">
|
||||||
<eventslot :event="agenda" />
|
<eventslot v-model="events[timestamp.weekday][index]" />
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</q-calendar-agenda>
|
</q-calendar-agenda>
|
||||||
|
@ -104,6 +104,7 @@ export default defineComponent({
|
||||||
|
|
||||||
async function loadAgendas() {
|
async function loadAgendas() {
|
||||||
const selected = new Date(selectedDate.value);
|
const selected = new Date(selectedDate.value);
|
||||||
|
console.log(selected);
|
||||||
const start = calendarRealView.value === 'day' ? selected : startOfWeek(selected);
|
const start = calendarRealView.value === 'day' ? selected : startOfWeek(selected);
|
||||||
const end = date.addToDate(start, { days: calendarDays.value });
|
const end = date.addToDate(start, { days: calendarDays.value });
|
||||||
|
|
||||||
|
@ -120,12 +121,18 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
function calendarNext() {
|
function calendarNext() {
|
||||||
calendar.value?.next();
|
selectedDate.value = date.formatDate(
|
||||||
|
date.addToDate(selectedDate.value, { days: calendarDays.value }),
|
||||||
|
'YYYY-MM-DD'
|
||||||
|
);
|
||||||
void loadAgendas();
|
void loadAgendas();
|
||||||
}
|
}
|
||||||
|
|
||||||
function calendarPrev() {
|
function calendarPrev() {
|
||||||
calendar.value?.prev();
|
selectedDate.value = date.formatDate(
|
||||||
|
date.subtractFromDate(selectedDate.value, { days: calendarDays.value }),
|
||||||
|
'YYYY-MM-DD'
|
||||||
|
);
|
||||||
void loadAgendas();
|
void loadAgendas();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,165 +7,39 @@
|
||||||
<div class="col text-weight-bolder ellipsis">
|
<div class="col text-weight-bolder ellipsis">
|
||||||
{{ event.type.name }}
|
{{ event.type.name }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="event.description" class="col text-weight-medium" style="font-size: 10px">
|
<div v-if="event.description" class="col text-weight-medium" style="font-size: 10px">
|
||||||
Info
|
Info
|
||||||
{{ event.description }}
|
{{ event.description }}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div v-for="(job, index) in event.jobs" :key="index">
|
<div v-for="(job, index) in event.jobs" :key="index">
|
||||||
<q-separator style="justify-start content-center" />
|
<q-separator style="justify-start content-center" />
|
||||||
<div class="text-weight-medium q-px-xs">
|
<JobSlot v-model="event.jobs[index]" :event-id="event.id" />
|
||||||
{{ asHour(job.start) }} <template v-if="job.end">- {{ asHour(job.end) }}</template>
|
|
||||||
</div>
|
|
||||||
<div class="q-px-xs">
|
|
||||||
{{ job.type.name }}
|
|
||||||
</div>
|
|
||||||
<div class="col-auto q-px-xs" style="font-size: 10px">
|
|
||||||
{{ job.comment }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<q-select
|
|
||||||
:key="refreshKey"
|
|
||||||
v-model="job.services"
|
|
||||||
filled
|
|
||||||
:option-label="(opt) => userDisplay(opt)"
|
|
||||||
multiple
|
|
||||||
disable
|
|
||||||
use-chips
|
|
||||||
stack-label
|
|
||||||
label="Dienste"
|
|
||||||
class="col-auto q-px-xs"
|
|
||||||
style="font-size: 6px"
|
|
||||||
counter
|
|
||||||
:max-values="job.required_services"
|
|
||||||
>
|
|
||||||
</q-select>
|
|
||||||
<div class="row col-12 justify-end">
|
|
||||||
<q-btn v-if="false" />
|
|
||||||
<q-btn
|
|
||||||
v-if="!isUserEnrolled(job) && !jobFull(job)"
|
|
||||||
:key="refreshKey"
|
|
||||||
flat
|
|
||||||
color="primary"
|
|
||||||
label="Eintragen"
|
|
||||||
@click="enrollForJob(job)"
|
|
||||||
/>
|
|
||||||
<q-btn
|
|
||||||
v-if="isUserEnrolled(job)"
|
|
||||||
:key="refreshKey"
|
|
||||||
flat
|
|
||||||
color="negative"
|
|
||||||
label="Austragen"
|
|
||||||
@click="signOutFromJob(job)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
import { defineComponent, computed, PropType } from 'vue';
|
||||||
import { defineComponent, ref, onBeforeMount, PropType } from 'vue';
|
import JobSlot from './JobSlot.vue';
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import { Notify } from 'quasar';
|
|
||||||
import { asHour } from 'src/utils/datetime';
|
|
||||||
import { useUserStore } from 'src/plugins/user/store';
|
|
||||||
import { useMainStore } from 'src/store';
|
|
||||||
import { useScheduleStore } from 'src/plugins/schedule/store';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Eventslot',
|
name: 'Eventslot',
|
||||||
components: {},
|
components: { JobSlot },
|
||||||
props: {
|
props: {
|
||||||
event: {
|
modelValue: {
|
||||||
required: true,
|
required: true,
|
||||||
type: Object as PropType<FG.Event>,
|
type: Object as PropType<FG.Event>,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
emits: { 'update:modelValue': (val: FG.Event) => !!val },
|
||||||
setup(props) {
|
setup(props, { emit }) {
|
||||||
const store = useScheduleStore();
|
const event = computed({
|
||||||
const mainStore = useMainStore();
|
get: () => props.modelValue,
|
||||||
const userStore = useUserStore();
|
set: (v) => emit('update:modelValue', v),
|
||||||
const router = useRouter();
|
|
||||||
const availableUsers = null;
|
|
||||||
const refreshKey = ref<number>(0);
|
|
||||||
|
|
||||||
onBeforeMount(async () => userStore.getUsers());
|
|
||||||
|
|
||||||
function refresh() {
|
|
||||||
router.go(0);
|
|
||||||
refreshKey.value += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUserEnrolled(job: FG.Job) {
|
|
||||||
return (
|
|
||||||
job.services.findIndex((service) => service.userid == mainStore.currentUser.userid) >= 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function jobFull(job: FG.Job) {
|
|
||||||
return job.services.length >= job.required_services;
|
|
||||||
}
|
|
||||||
|
|
||||||
function userDisplay(userid: string) {
|
|
||||||
return userStore.users.find((user) => (user.userid = userid))?.display_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function enrollForJob(job: FG.Job) {
|
|
||||||
const newService: FG.Service = {
|
|
||||||
userid: mainStore.currentUser.userid,
|
|
||||||
value: 1,
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
await store.updateEvent(props.event.id, job.id, { user: newService });
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error);
|
|
||||||
Notify.create({
|
|
||||||
group: false,
|
|
||||||
type: 'negative',
|
|
||||||
message: 'Fehler beim Eintragen als Dienst',
|
|
||||||
timeout: 10000,
|
|
||||||
progress: true,
|
|
||||||
actions: [{ icon: 'mdi-close', color: 'white' }],
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
async function signOutFromJob(job: FG.Job) {
|
|
||||||
const newService: FG.Service = {
|
|
||||||
userid: mainStore.currentUser.userid,
|
|
||||||
value: -1,
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
await store.updateEvent(props.event.id, job.id, { user: newService });
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error);
|
|
||||||
Notify.create({
|
|
||||||
group: false,
|
|
||||||
type: 'negative',
|
|
||||||
message: 'Fehler beim Austragen als Dienst',
|
|
||||||
timeout: 10000,
|
|
||||||
progress: true,
|
|
||||||
actions: [{ icon: 'mdi-close', color: 'white' }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
refreshKey,
|
event,
|
||||||
availableUsers,
|
|
||||||
enrollForJob,
|
|
||||||
isUserEnrolled,
|
|
||||||
signOutFromJob,
|
|
||||||
jobFull,
|
|
||||||
userDisplay,
|
|
||||||
refresh,
|
|
||||||
asHour,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
<template>
|
||||||
|
<q-card>
|
||||||
|
<div class="text-weight-medium q-px-xs">
|
||||||
|
{{ asHour(modelValue.start) }}
|
||||||
|
<template v-if="modelValue.end">- {{ asHour(modelValue.end) }}</template>
|
||||||
|
</div>
|
||||||
|
<div class="q-px-xs">
|
||||||
|
{{ modelValue.type.name }}
|
||||||
|
</div>
|
||||||
|
<div class="col-auto q-px-xs" style="font-size: 10px">
|
||||||
|
{{ modelValue.comment }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<q-select
|
||||||
|
:model-value="modelValue.services"
|
||||||
|
filled
|
||||||
|
:option-label="(opt) => userDisplay(opt)"
|
||||||
|
multiple
|
||||||
|
disable
|
||||||
|
use-chips
|
||||||
|
stack-label
|
||||||
|
label="Dienste"
|
||||||
|
class="col-auto q-px-xs"
|
||||||
|
style="font-size: 6px"
|
||||||
|
counter
|
||||||
|
:max-values="modelValue.required_services"
|
||||||
|
>
|
||||||
|
</q-select>
|
||||||
|
<div class="row col-12 justify-end">
|
||||||
|
<q-btn v-if="canEnroll" flat color="primary" label="Eintragen" @click="enrollForJob" />
|
||||||
|
<q-btn v-if="isEnrolled" flat color="negative" label="Austragen" @click="signOutFromJob" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, onBeforeMount, computed, PropType } from 'vue';
|
||||||
|
import { Notify } from 'quasar';
|
||||||
|
import { asHour } from 'src/utils/datetime';
|
||||||
|
import { useUserStore } from 'src/plugins/user/store';
|
||||||
|
import { useMainStore } from 'src/store';
|
||||||
|
import { useScheduleStore } from 'src/plugins/schedule/store';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'JobSlot',
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
required: true,
|
||||||
|
type: Object as PropType<FG.Job>,
|
||||||
|
},
|
||||||
|
eventId: {
|
||||||
|
required: true,
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: { 'update:modelValue': (v: FG.Job) => !!v },
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const store = useScheduleStore();
|
||||||
|
const mainStore = useMainStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const availableUsers = null;
|
||||||
|
|
||||||
|
onBeforeMount(async () => userStore.getUsers());
|
||||||
|
|
||||||
|
function userDisplay(service: FG.Service) {
|
||||||
|
return userStore.findUser(service.userid)?.display_name || service.userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isEnrolled = computed(
|
||||||
|
() =>
|
||||||
|
props.modelValue.services.findIndex(
|
||||||
|
(service) => service.userid == mainStore.currentUser.userid
|
||||||
|
) !== -1
|
||||||
|
);
|
||||||
|
|
||||||
|
const canEnroll = computed(() => {
|
||||||
|
const is = isEnrolled.value;
|
||||||
|
let sum = 0;
|
||||||
|
props.modelValue.services.forEach((s) => (sum += s.value));
|
||||||
|
return sum < props.modelValue.required_services && !is;
|
||||||
|
});
|
||||||
|
|
||||||
|
async function enrollForJob() {
|
||||||
|
const newService: FG.Service = {
|
||||||
|
userid: mainStore.currentUser.userid,
|
||||||
|
value: 1,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const job = await store.updateJob(props.eventId, props.modelValue.id, { user: newService });
|
||||||
|
emit('update:modelValue', job);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error);
|
||||||
|
Notify.create({
|
||||||
|
group: false,
|
||||||
|
type: 'negative',
|
||||||
|
message: 'Fehler beim Eintragen als Dienst',
|
||||||
|
timeout: 10000,
|
||||||
|
progress: true,
|
||||||
|
actions: [{ icon: 'mdi-close', color: 'white' }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function signOutFromJob() {
|
||||||
|
const newService: FG.Service = {
|
||||||
|
userid: mainStore.currentUser.userid,
|
||||||
|
value: -1,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const job = await store.updateJob(props.eventId, props.modelValue.id, {
|
||||||
|
user: newService,
|
||||||
|
});
|
||||||
|
emit('update:modelValue', job);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error);
|
||||||
|
Notify.create({
|
||||||
|
group: false,
|
||||||
|
type: 'negative',
|
||||||
|
message: 'Fehler beim Austragen als Dienst',
|
||||||
|
timeout: 10000,
|
||||||
|
progress: true,
|
||||||
|
actions: [{ icon: 'mdi-close', color: 'white' }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
availableUsers,
|
||||||
|
enrollForJob,
|
||||||
|
isEnrolled,
|
||||||
|
signOutFromJob,
|
||||||
|
canEnroll,
|
||||||
|
userDisplay,
|
||||||
|
asHour,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,9 @@
|
||||||
|
declare namespace FG {
|
||||||
|
export interface RecurrenceRule {
|
||||||
|
frequency: string;
|
||||||
|
interval: number;
|
||||||
|
count?: number;
|
||||||
|
until?: Date;
|
||||||
|
weekdays?: Array<number>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ const plugin: FG_Plugin.Plugin = {
|
||||||
name: 'Schedule',
|
name: 'Schedule',
|
||||||
mainRoutes,
|
mainRoutes,
|
||||||
requiredModules: ['User'],
|
requiredModules: ['User'],
|
||||||
requiredBackendModules: ['schedule'],
|
requiredBackendModules: ['events'],
|
||||||
version: '0.0.1',
|
version: '0.0.1',
|
||||||
widgets: [
|
widgets: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,14 +6,16 @@ interface UserService {
|
||||||
user: FG.Service;
|
user: FG.Service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fixJob(job: FG.Job) {
|
||||||
|
job.start = new Date(job.start);
|
||||||
|
if (job.end) job.end = new Date(job.end);
|
||||||
|
}
|
||||||
|
|
||||||
function fixEvent(event: FG.Event) {
|
function fixEvent(event: FG.Event) {
|
||||||
event.start = new Date(event.start);
|
event.start = new Date(event.start);
|
||||||
if (event.end) event.end = new Date(event.end);
|
if (event.end) event.end = new Date(event.end);
|
||||||
|
|
||||||
event.jobs.forEach((job) => {
|
event.jobs.forEach((job) => fixJob(job));
|
||||||
job.start = new Date(job.start);
|
|
||||||
if (job.end) job.end = new Date(job.end);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useScheduleStore = defineStore({
|
export const useScheduleStore = defineStore({
|
||||||
|
@ -22,6 +24,7 @@ export const useScheduleStore = defineStore({
|
||||||
state: () => ({
|
state: () => ({
|
||||||
jobTypes: [] as FG.JobType[],
|
jobTypes: [] as FG.JobType[],
|
||||||
eventTypes: [] as FG.EventType[],
|
eventTypes: [] as FG.EventType[],
|
||||||
|
templates: [] as FG.Event[],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getters: {},
|
getters: {},
|
||||||
|
@ -53,13 +56,15 @@ export const useScheduleStore = defineStore({
|
||||||
// TODO handle rename
|
// TODO handle rename
|
||||||
},
|
},
|
||||||
|
|
||||||
async getEventTypes() {
|
async getEventTypes(force = false) {
|
||||||
|
if (force || this.eventTypes.length == 0)
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get<FG.EventType[]>('/schedule/event-types');
|
const { data } = await api.get<FG.EventType[]>('/schedule/event-types');
|
||||||
this.eventTypes = data;
|
this.eventTypes = data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
return this.eventTypes;
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Add new EventType
|
/** Add new EventType
|
||||||
|
@ -100,6 +105,15 @@ export const useScheduleStore = defineStore({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async getTemplates(force = false) {
|
||||||
|
if (force || this.templates.length == 0) {
|
||||||
|
const { data } = await api.get<FG.Event[]>('/schedule/templates');
|
||||||
|
data.forEach((element) => fixEvent(element));
|
||||||
|
this.templates = data;
|
||||||
|
}
|
||||||
|
return this.templates;
|
||||||
|
},
|
||||||
|
|
||||||
async getEvents(filter: { from?: Date; to?: Date } | undefined = undefined) {
|
async getEvents(filter: { from?: Date; to?: Date } | undefined = undefined) {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get<FG.Event[]>('/schedule/events', { params: filter });
|
const { data } = await api.get<FG.Event[]>('/schedule/events', { params: filter });
|
||||||
|
@ -109,13 +123,13 @@ export const useScheduleStore = defineStore({
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async updateEvent(eventId: number, jobId: number, service: FG.Service | UserService) {
|
async updateJob(eventId: number, jobId: number, service: FG.Service | UserService) {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.put<FG.Event>(
|
const { data } = await api.put<FG.Job>(
|
||||||
`/schedule/events/${eventId}/jobs/${jobId}`,
|
`/schedule/events/${eventId}/jobs/${jobId}`,
|
||||||
service
|
service
|
||||||
);
|
);
|
||||||
fixEvent(data);
|
fixJob(data);
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -124,8 +138,8 @@ export const useScheduleStore = defineStore({
|
||||||
|
|
||||||
async addEvent(event: FG.Event) {
|
async addEvent(event: FG.Event) {
|
||||||
const { data } = await api.post<FG.Event>('/schedule/events', event);
|
const { data } = await api.post<FG.Event>('/schedule/events', event);
|
||||||
|
if (data.is_template) this.templates.push(data);
|
||||||
return data;
|
return data;
|
||||||
//TODO: Handle add event}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,20 +10,19 @@ export const useUserStore = defineStore({
|
||||||
roles: [] as FG.Role[],
|
roles: [] as FG.Role[],
|
||||||
users: [] as FG.User[],
|
users: [] as FG.User[],
|
||||||
permissions: [] as FG.Permission[],
|
permissions: [] as FG.Permission[],
|
||||||
_dirty_users: 0,
|
_dirty_users: true,
|
||||||
_dirty_roles: 0,
|
_dirty_roles: true,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getters: {
|
getters: {},
|
||||||
isDirty() {
|
|
||||||
return new Date().getTime() - this._dirty_users > 60000;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async getUser(userid: string, force = true) {
|
findUser(userid: string) {
|
||||||
|
return this.users.find((user) => user.userid === userid);
|
||||||
|
},
|
||||||
|
async getUser(userid: string, force = false) {
|
||||||
const idx = this.users.findIndex((user) => user.userid === userid);
|
const idx = this.users.findIndex((user) => user.userid === userid);
|
||||||
if (force || idx == -1 || this.isDirty) {
|
if (force || this._dirty_users || idx === -1) {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get<FG.User>(`/users/${userid}`);
|
const { data } = await api.get<FG.User>(`/users/${userid}`);
|
||||||
if (data.birthday) data.birthday = new Date(data.birthday);
|
if (data.birthday) data.birthday = new Date(data.birthday);
|
||||||
|
@ -39,17 +38,16 @@ export const useUserStore = defineStore({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async getUsers(force = true) {
|
async getUsers(force = false) {
|
||||||
if (force || this.isDirty) {
|
if (force || this._dirty_users) {
|
||||||
const { data } = await api.get<FG.User[]>('/users');
|
const { data } = await api.get<FG.User[]>('/users');
|
||||||
data.forEach((user) => {
|
data.forEach((user) => {
|
||||||
if (user.birthday) user.birthday = new Date(user.birthday);
|
if (user.birthday) user.birthday = new Date(user.birthday);
|
||||||
});
|
});
|
||||||
this.users = data;
|
this.users = data;
|
||||||
this._dirty_users = new Date().getTime();
|
this._dirty_users = false;
|
||||||
} else {
|
|
||||||
return this.users;
|
|
||||||
}
|
}
|
||||||
|
return this.users;
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateUser(user: FG.User) {
|
async updateUser(user: FG.User) {
|
||||||
|
@ -57,7 +55,7 @@ export const useUserStore = defineStore({
|
||||||
|
|
||||||
const mainStore = useMainStore();
|
const mainStore = useMainStore();
|
||||||
if (user.userid === mainStore.user?.userid) mainStore.user = user;
|
if (user.userid === mainStore.user?.userid) mainStore.user = user;
|
||||||
this._dirty_users = 0;
|
this._dirty_users = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
async createUser(user: FG.User) {
|
async createUser(user: FG.User) {
|
||||||
|
@ -85,10 +83,10 @@ export const useUserStore = defineStore({
|
||||||
},
|
},
|
||||||
|
|
||||||
async getRoles(force = false) {
|
async getRoles(force = false) {
|
||||||
if (force || new Date().getTime() - this._dirty_roles > 60000) {
|
if (force || this._dirty_roles) {
|
||||||
const { data } = await api.get<FG.Role[]>('/roles');
|
const { data } = await api.get<FG.Role[]>('/roles');
|
||||||
this.roles = data;
|
this.roles = data;
|
||||||
this._dirty_roles = new Date().getTime();
|
this._dirty_roles = false;
|
||||||
}
|
}
|
||||||
return this.roles;
|
return this.roles;
|
||||||
},
|
},
|
||||||
|
@ -97,20 +95,18 @@ export const useUserStore = defineStore({
|
||||||
await api.put(`/roles/${role.id}`, role);
|
await api.put(`/roles/${role.id}`, role);
|
||||||
const idx = this.roles.findIndex((r) => r.id === role.id);
|
const idx = this.roles.findIndex((r) => r.id === role.id);
|
||||||
if (idx != -1) this.roles[idx] = role;
|
if (idx != -1) this.roles[idx] = role;
|
||||||
this._dirty_roles = 0;
|
this._dirty_roles = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
async newRole(role: FG.Role) {
|
async newRole(role: FG.Role) {
|
||||||
const { data } = await api.post<FG.Role>('/roles', role);
|
const { data } = await api.post<FG.Role>('/roles', role);
|
||||||
this.roles.push(data);
|
this.roles.push(data);
|
||||||
this._dirty_roles = 0;
|
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteRole(role: FG.Role | number) {
|
async deleteRole(role: FG.Role | number) {
|
||||||
await api.delete(`/roles/${typeof role === 'number' ? role : role.id}`);
|
await api.delete(`/roles/${typeof role === 'number' ? role : role.id}`);
|
||||||
this.roles = this.roles.filter((r) => r.id !== (typeof role == 'number' ? role : role.id));
|
this.roles = this.roles.filter((r) => r.id !== (typeof role == 'number' ? role : role.id));
|
||||||
this._dirty_roles = 0;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue