release v2.0.0 #4
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"@quasar/qcalendar": {}
|
||||||
|
}
|
|
@ -71,7 +71,7 @@ import { Store } from 'vuex';
|
||||||
import { StateInterface } from 'src/store';
|
import { StateInterface } from 'src/store';
|
||||||
import { ScheduleInterface } from '../../store/schedule';
|
import { ScheduleInterface } from '../../store/schedule';
|
||||||
import { date } from 'quasar';
|
import { date } from 'quasar';
|
||||||
// import { emit } from 'process';
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'CreateEvent',
|
name: 'CreateEvent',
|
||||||
components: { IsoDateInput, Job },
|
components: { IsoDateInput, Job },
|
||||||
|
@ -140,16 +140,21 @@ export default defineComponent({
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
console.log('Event:', event);
|
console.log('Event:', event);
|
||||||
store.dispatch('schedule/addEvent', event.value).catch(error => {
|
void store
|
||||||
console.warn(error);
|
.dispatch('schedule/addEvent', event.value)
|
||||||
});
|
.catch(error => {
|
||||||
|
console.warn(error);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
reset();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
event.value.id = NaN;
|
event.value.id = NaN;
|
||||||
event.value.start = new Date();
|
event.value.start = new Date();
|
||||||
event.value.description = '';
|
event.value.description = '';
|
||||||
event.value.type = {id: -1, name: ''};
|
event.value.type = { id: -1, name: '' };
|
||||||
event.value.jobs = [Object.assign({}, newJob.value)];
|
event.value.jobs = [Object.assign({}, newJob.value)];
|
||||||
}
|
}
|
||||||
function notEmpty(val: string) {
|
function notEmpty(val: string) {
|
||||||
|
@ -183,4 +188,4 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style></style>
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class=" row fit justify-start content-center items-center">
|
<q-card-section class=" row fit justify-start content-center items-center">
|
||||||
<q-select
|
<q-select
|
||||||
|
:key="refreshKey"
|
||||||
filled
|
filled
|
||||||
use-input
|
use-input
|
||||||
label="Dienstart"
|
label="Dienstart"
|
||||||
|
@ -61,7 +62,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed } from '@vue/composition-api';
|
import { defineComponent, computed, ref } from '@vue/composition-api';
|
||||||
import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
|
import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
|
||||||
import { Store } from 'vuex';
|
import { Store } from 'vuex';
|
||||||
import { StateInterface } from 'src/store';
|
import { StateInterface } from 'src/store';
|
||||||
|
@ -82,11 +83,18 @@ export default defineComponent({
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
job: function() {
|
||||||
|
// this.type.name = this.type.name;
|
||||||
|
this.$props.job.type = this.$props.job.type;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setup(props: Props, { root, emit }) {
|
setup(props: Props, { root, emit }) {
|
||||||
const store = <Store<StateInterface>>root.$store;
|
const store = <Store<StateInterface>>root.$store;
|
||||||
const state = <ScheduleInterface>store.state.schedule;
|
const state = <ScheduleInterface>store.state.schedule;
|
||||||
const jobtypes = computed(() => state.jobTypes);
|
const jobtypes = computed(() => state.jobTypes);
|
||||||
|
// job = computed(() => job);
|
||||||
function setStart(value: Date) {
|
function setStart(value: Date) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
emit('set-start', { job: props.job, value });
|
emit('set-start', { job: props.job, value });
|
||||||
|
@ -106,6 +114,7 @@ export default defineComponent({
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
console.log('setJobType', value);
|
console.log('setJobType', value);
|
||||||
emit('set-job-type', { job: props.job, value });
|
emit('set-job-type', { job: props.job, value });
|
||||||
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRequired(value: number) {
|
function setRequired(value: number) {
|
||||||
|
@ -131,6 +140,12 @@ export default defineComponent({
|
||||||
return props.job.start < new Date(val) || 'Ende muss hinter dem Start liegen';
|
return props.job.start < new Date(val) || 'Ende muss hinter dem Start liegen';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const refreshKey = ref(0);
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
refreshKey.value += 1;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
jobtypes,
|
jobtypes,
|
||||||
setStart,
|
setStart,
|
||||||
|
@ -141,7 +156,8 @@ export default defineComponent({
|
||||||
removeJob,
|
removeJob,
|
||||||
notEmpty,
|
notEmpty,
|
||||||
noValidDate,
|
noValidDate,
|
||||||
isAfterDate
|
isAfterDate,
|
||||||
|
refreshKey
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
<template>
|
||||||
|
<q-page padding>
|
||||||
|
<q-card>
|
||||||
|
<template>
|
||||||
|
<div style="max-width: 1800px; width: 100%;">
|
||||||
|
<q-toolbar class="bg-primary text-white q-my-md shadow-2 items-center row justify-center">
|
||||||
|
<div class="row justify-center items-center">
|
||||||
|
<q-btn flat dense label="Prev" @click="calendarPrev" />
|
||||||
|
<q-separator vertical />
|
||||||
|
<q-btn flat dense
|
||||||
|
>{{ selectedDate | toMonth }} {{ selectedDate | toYear }}
|
||||||
|
<q-popup-proxy
|
||||||
|
@before-show="updateProxy"
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<q-date v-model="proxyDate">
|
||||||
|
<div class="row items-center justify-end q-gutter-sm">
|
||||||
|
<q-btn label="Cancel" color="primary" flat v-close-popup />
|
||||||
|
<q-btn
|
||||||
|
label="OK"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
@click="saveNewSelectedDate(proxyDate)"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-date>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-btn>
|
||||||
|
<q-separator vertical />
|
||||||
|
<q-btn flat dense label="Next" @click="calendarNext" />
|
||||||
|
</div>
|
||||||
|
</q-toolbar>
|
||||||
|
<q-calendar
|
||||||
|
v-model="selectedDate"
|
||||||
|
view="week-agenda"
|
||||||
|
:weekdays="[1, 2, 3, 4, 5, 6, 0]"
|
||||||
|
locale="de-de"
|
||||||
|
style="height: 100%; min-height: 400px;"
|
||||||
|
ref="calendar"
|
||||||
|
>
|
||||||
|
<template #day-body="{ timestamp }" style="min-height: 200px;">
|
||||||
|
<template !v-if="getAgenda(timestamp)" style="min-height: 200px;"> </template>
|
||||||
|
<template v-for="agenda in getAgenda(timestamp)">
|
||||||
|
<eventslot :event="agenda" :key="agenda.id" />
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</q-calendar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</q-card>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
|
import { computed, defineComponent, onBeforeMount, ref } from '@vue/composition-api';
|
||||||
|
import { Store } from 'vuex';
|
||||||
|
import { StateInterface } from 'src/store';
|
||||||
|
import { ScheduleInterface } from '../../store/schedule';
|
||||||
|
import Eventslot from './slots/EventSlot.vue';
|
||||||
|
import { date } from 'quasar';
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'AgendaView',
|
||||||
|
components: { Eventslot },
|
||||||
|
filters: {
|
||||||
|
toMonth: function(value: string) {
|
||||||
|
if (value) {
|
||||||
|
return date.formatDate(new Date(value), 'MMMM', {
|
||||||
|
months: [
|
||||||
|
'Januar',
|
||||||
|
'Februar',
|
||||||
|
'März',
|
||||||
|
'April',
|
||||||
|
'Mai',
|
||||||
|
'Juni',
|
||||||
|
'Juli',
|
||||||
|
'August',
|
||||||
|
'September',
|
||||||
|
'Oktober',
|
||||||
|
'November',
|
||||||
|
'Dezember'
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toYear: function(value: string) {
|
||||||
|
if (value) {
|
||||||
|
return date.formatDate(new Date(value), 'YYYY');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(_, { root }) {
|
||||||
|
const store = <Store<StateInterface>>root.$store;
|
||||||
|
const state = <ScheduleInterface>store.state.schedule;
|
||||||
|
const selectedDate = ref(date.formatDate(new Date(), 'YYYY-MM-DD'));
|
||||||
|
const proxyDate = ref('');
|
||||||
|
const events2 = computed(() => {
|
||||||
|
console.log(state.events);
|
||||||
|
let agenda: Agendas = {};
|
||||||
|
state.events
|
||||||
|
.filter(event => {
|
||||||
|
const thisWeek = date.formatDate(new Date(selectedDate.value), 'w');
|
||||||
|
console.log(thisWeek, date.formatDate(event.start, 'w'));
|
||||||
|
return date.formatDate(event.start, 'w') == thisWeek;
|
||||||
|
})
|
||||||
|
.forEach(event => {
|
||||||
|
let day = event.start.getDay();
|
||||||
|
console.log('event', event, day, !agenda[day]);
|
||||||
|
if (!agenda[day]) {
|
||||||
|
agenda[day] = [];
|
||||||
|
}
|
||||||
|
agenda[day].push(event);
|
||||||
|
});
|
||||||
|
console.log('finish agenda:', agenda);
|
||||||
|
return agenda;
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Agendas {
|
||||||
|
[index: number]: FG.Event[];
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
void store.dispatch('schedule/getEvents');
|
||||||
|
// void store.dispatch('schedule/getJobTypes');
|
||||||
|
});
|
||||||
|
function getAgenda(this: any, day: { weekday: string }) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
|
return this.events2[parseInt(day.weekday, 10)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function calendarNext(this: any) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
return this.$refs.calendar.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
function calendarPrev(this: any) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
return this.$refs.calendar.prev();
|
||||||
|
}
|
||||||
|
function updateProxy(this: any) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
proxyDate.value = selectedDate.value;
|
||||||
|
}
|
||||||
|
function saveNewSelectedDate(this: any) {
|
||||||
|
proxyDate.value = date.formatDate(proxyDate.value, 'YYYY-MM-DD');
|
||||||
|
selectedDate.value = proxyDate.value;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
selectedDate,
|
||||||
|
events2,
|
||||||
|
getAgenda,
|
||||||
|
calendarNext,
|
||||||
|
calendarPrev,
|
||||||
|
updateProxy,
|
||||||
|
saveNewSelectedDate,
|
||||||
|
proxyDate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<q-card
|
||||||
|
class="background: radial-gradient(circle, #35a2ff 0%, #014a88 100%) justify-start content-center items-center "
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
{{ event.type.name }}
|
||||||
|
|
||||||
|
<div v-if="event.description" class=" col-12 q-px-sm" style="font-size: 10px">
|
||||||
|
Info
|
||||||
|
{{ event.description }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-for="(job, index) in event.jobs" v-bind:key="index">
|
||||||
|
<q-separator style="justify-start content-center" />
|
||||||
|
<div>{{ job.start | formatToHour }} - {{ job.end | formatToHour }}</div>
|
||||||
|
<div>
|
||||||
|
{{ job.type.name }}
|
||||||
|
</div>
|
||||||
|
<div class="col-12 q-px-sm" style="font-size: 10px">
|
||||||
|
{{ job.comment }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
v-model="availableUsers"
|
||||||
|
multiple
|
||||||
|
:options="availableUsers"
|
||||||
|
use-chips
|
||||||
|
stack-label
|
||||||
|
label="Dienste"
|
||||||
|
class="col-12 q-px-sm"
|
||||||
|
style="font-size: 10px "
|
||||||
|
counter
|
||||||
|
:max-values="job.required_services"
|
||||||
|
>
|
||||||
|
</q-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref } from '@vue/composition-api';
|
||||||
|
import { Store } from 'vuex';
|
||||||
|
import { StateInterface } from 'src/store';
|
||||||
|
import { ScheduleInterface } from '../../../store/schedule';
|
||||||
|
import { date } from 'quasar';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
event: FG.Event;
|
||||||
|
}
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Eventslot',
|
||||||
|
components: {},
|
||||||
|
props: {
|
||||||
|
event: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
formatToHour: function(value: Date) {
|
||||||
|
if (value) {
|
||||||
|
return date.formatDate(value, 'HH:mm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props: Props, { root }) {
|
||||||
|
const store = <Store<StateInterface>>root.$store;
|
||||||
|
const state = <ScheduleInterface>store.state.schedule;
|
||||||
|
const availableUsers = null;
|
||||||
|
const refreshKey = ref(0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
refreshKey,
|
||||||
|
availableUsers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
|
@ -1,8 +1,100 @@
|
||||||
<template>
|
<template>
|
||||||
<q-page padding>
|
<div>
|
||||||
<q-card>
|
<q-tabs v-model="tab" v-if="$q.screen.gt.sm">
|
||||||
<q-card-section class="row"> </q-card-section>
|
<q-tab
|
||||||
<q-card-section> </q-card-section>
|
v-for="(tabindex, index) in tabs"
|
||||||
</q-card>
|
:key="'tab' + index"
|
||||||
</q-page>
|
:name="tabindex.name"
|
||||||
|
:label="tabindex.label"
|
||||||
|
/>
|
||||||
|
</q-tabs>
|
||||||
|
<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-list v-model="tab">
|
||||||
|
<q-item
|
||||||
|
v-for="(tabindex, index) in tabs"
|
||||||
|
:key="'tab' + index"
|
||||||
|
:active="tab == tabindex.name"
|
||||||
|
clickable
|
||||||
|
@click="tab = tabindex.name"
|
||||||
|
>
|
||||||
|
<q-item-label>{{ tabindex.label }}</q-item-label>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-drawer>
|
||||||
|
<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;"
|
||||||
|
class="q-ma-none q-pa-none fit row justify-center content-start items-start"
|
||||||
|
animated
|
||||||
|
>
|
||||||
|
<q-tab-panel name="agendaView">
|
||||||
|
<AgendaView />
|
||||||
|
</q-tab-panel>
|
||||||
|
<q-tab-panel name="eventtypes">
|
||||||
|
<Eventtypes />
|
||||||
|
</q-tab-panel>
|
||||||
|
<q-tab-panel name="jobtypes">
|
||||||
|
<JobTypes v-if="canEditRoles" />
|
||||||
|
</q-tab-panel>
|
||||||
|
</q-tab-panels>
|
||||||
|
</q-page>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, ref } from '@vue/composition-api';
|
||||||
|
import Eventtypes from '../components/management/Eventtypes.vue';
|
||||||
|
import JobTypes from '../components/management/JobTypes.vue';
|
||||||
|
import CreateEvent from '../components/management/CreateEvent.vue';
|
||||||
|
import AgendaView from '../components/overview/AgendaView.vue';
|
||||||
|
import { Store } from 'vuex';
|
||||||
|
import { StateInterface } from 'src/store';
|
||||||
|
import { hasPermission } from 'src/utils/permission';
|
||||||
|
import { PERMISSIONS } from '../permissions';
|
||||||
|
import { Screen } from 'quasar';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'EventOverview',
|
||||||
|
components: { AgendaView, Eventtypes, JobTypes },
|
||||||
|
setup(_, { root }) {
|
||||||
|
const store = <Store<StateInterface>>root.$store;
|
||||||
|
|
||||||
|
const canEditRoles = computed(() => hasPermission(PERMISSIONS.ROLES_EDIT, store));
|
||||||
|
|
||||||
|
interface Tab {
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabs: Tab[] = [
|
||||||
|
{ name: 'agendaView', label: 'Wochenkalendar' }
|
||||||
|
// { name: 'eventtypes', label: 'Veranstaltungsarten' },
|
||||||
|
// { name: 'jobtypes', label: 'Dienstarten' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const drawer = ref<boolean>(false);
|
||||||
|
|
||||||
|
const showDrawer = computed({
|
||||||
|
get: () => {
|
||||||
|
return !Screen.gt.sm && drawer.value;
|
||||||
|
},
|
||||||
|
set: (val: boolean) => {
|
||||||
|
drawer.value = val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tab = ref<string>('agendaView');
|
||||||
|
|
||||||
|
return {
|
||||||
|
canEditRoles,
|
||||||
|
showDrawer,
|
||||||
|
tab,
|
||||||
|
tabs
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
|
@ -78,6 +78,9 @@ const mutations: MutationTree<ScheduleInterface> = {
|
||||||
},
|
},
|
||||||
addEvent(state, event: FG.Event) {
|
addEvent(state, event: FG.Event) {
|
||||||
state.events.unshift(event);
|
state.events.unshift(event);
|
||||||
|
},
|
||||||
|
setEvents(state, events: FG.Event[]) {
|
||||||
|
state.events = events;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -174,13 +177,27 @@ const actions: ActionTree<ScheduleInterface, StateInterface> = {
|
||||||
addEvent({ commit }, event: Event) {
|
addEvent({ commit }, event: Event) {
|
||||||
axios
|
axios
|
||||||
.post('/schedule/events', event)
|
.post('/schedule/events', event)
|
||||||
.then((response: AxiosResponse<EventType>) => {
|
.then((response: AxiosResponse<Event>) => {
|
||||||
commit('addEvent', response.data);
|
commit('addEvent', response.data);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
});
|
});
|
||||||
console.log('Events: ', state.events);
|
console.log('Events: ', state.events);
|
||||||
|
},
|
||||||
|
getEvents({ commit }) {
|
||||||
|
axios
|
||||||
|
.get('/schedule/events')
|
||||||
|
.then((response: AxiosResponse<Event[]>) => {
|
||||||
|
console.log('action:', response.data);
|
||||||
|
response.data.forEach(event => {
|
||||||
|
event.start = new Date(event.start);
|
||||||
|
});
|
||||||
|
commit('setEvents', response.data);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.warn(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const getters: GetterTree<ScheduleInterface, StateInterface> = {
|
const getters: GetterTree<ScheduleInterface, StateInterface> = {
|
||||||
|
|
Loading…
Reference in New Issue