Compare commits

...

6 Commits

8 changed files with 93 additions and 45 deletions

View File

@ -1,25 +1,53 @@
<template>
<q-card class="row justify-center content-center" style="text-align: center">
<q-card-section>
<div class="text-h6 col-12">Dienste diesen Monat: {{ jobs }}</div>
<!--TODO: Filters are deprecated! -->
<!--<div class="text-h6 col-12">Nächster Dienst: {{ nextJob | date }}</div>-->
<q-card style="text-align: center">
<q-card-section class="row justify-center items-center content-center">
<div class="col-5">
<q-icon :name="jobs == 0 ? 'mdi-calendar-blank' : 'mdi-calendar-alert'" :size="divHeight" />
</div>
<div v-if="(jobs || 0) > 0" ref="div" class="col-7">
<div class="text-h6">Anstehende Dienste</div>
<div class="text-body1">{{ jobs }}</div>
<div class="text-h6">Nächster Dienst</div>
<div class="text-body1">{{ formatDate(nextJob) }}</div>
</div>
<div v-else ref="div" class="col-7">
<div class="text-subtitle1">Keine anstehenden Dienste</div>
</div>
</q-card-section>
</q-card>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { date } from 'quasar';
import { computed, defineComponent, onBeforeMount, ref } from 'vue';
import { asHour, formatDateTime } from '@flaschengeist/api';
import { useEventStore } from '../store';
export default defineComponent({
name: 'EventsWidget',
setup() {
function randomNumber(start: number, end: number) {
return start + Math.floor(Math.random() * Math.floor(end));
const store = useEventStore();
const jobs = ref<number>();
const nextJob = ref<Date>();
const div = ref<HTMLElement>();
const divHeight = computed(() => `${div.value?.scrollHeight || '100'}px`);
onBeforeMount(() => {
void store.getJobs({ limit: 1, from: new Date() }).then(({ count, result }) => {
jobs.value = count;
nextJob.value = count > 0 ? result[0].start : undefined;
});
});
function formatDate(d?: Date) {
if (d === undefined) return '-';
if (date.isSameDate(d, new Date(), 'day')) return `Heute ${asHour(d)} Uhr`;
return formatDateTime(d, true, true, false, true) + ' Uhr';
}
const jobs = randomNumber(0, 5);
const nextJob = new Date(2021, randomNumber(1, 12), randomNumber(1, 31));
return { jobs, nextJob };
return { div, divHeight, formatDate, jobs, nextJob };
},
});
</script>

View File

@ -64,7 +64,7 @@
{{ timestamp.day }}
<q-menu>
<q-list style="min-width: 100px">
<q-item exact :to="{ name: 'new-event', query: { date: timestamp.date } }">
<q-item exact clickable @click="create(timestamp.date)">
<q-item-section>Neue Veranstaltung</q-item-section>
</q-item>
</q-list>
@ -95,6 +95,7 @@ import { date, QDate, QPopupProxy, useQuasar } from 'quasar';
import { startOfWeek } from '@flaschengeist/api';
import EditEvent from '../management/EditEvent.vue';
import { QCalendarAgenda } from '@quasar/quasar-ui-qcalendar';
import { EditableEvent, emptyEvent } from '../../store/models';
export default defineComponent({
name: 'AgendaView',
@ -115,7 +116,7 @@ export default defineComponent({
calendarView.value == 'day' || quasar.screen.xs ? 1 : quasar.screen.sm ? 3 : 7
);
const events = ref<Agendas>({});
const editor = ref<FG.Event | undefined>(undefined);
const editor = ref<EditableEvent>();
interface Agendas {
[index: number]: FG.Event[];
@ -125,6 +126,9 @@ export default defineComponent({
await loadAgendas();
});
function create(ds: string) {
editor.value = emptyEvent(date.extractDate(ds, 'YYYY-MM-DD'));
}
async function edit(id: number) {
editor.value = await store.getEvent(id);
}
@ -156,12 +160,12 @@ export default defineComponent({
minutes: 0,
hours: 0,
});
const start = calendarRealView.value === 'day' ? selected : startOfWeek(selected);
const start = calendarView.value === 'day' ? selected : startOfWeek(selected);
const end = date.addToDate(start, { days: calendarDays.value });
events.value = {};
const list = await store.getEvents({ from: start, to: end });
list.forEach((event) => {
const { result } = await store.getEvents({ from: start, to: end });
result.forEach((event) => {
const day = event.start.getDay();
if (!events.value[day]) {
@ -221,6 +225,7 @@ export default defineComponent({
calendarPrev,
calendarRealView,
calendarView,
create,
edit,
editor,
editDone,

View File

@ -26,7 +26,7 @@
</q-item>
<template v-for="(events, index) in agendas" :key="index">
<q-separator />
<q-item-label overline>{{index}}</q-item-label>
<q-item-label overline>{{ index }}</q-item-label>
<q-item v-for="(event, idx) in events" :key="idx"
><event-slot :model-value="event" />{{ idx }}</q-item
>
@ -63,7 +63,7 @@ export default defineComponent({
const editor = ref<FG.Event | undefined>(undefined);
const events = ref<FG.Event[]>([]);
const scrollDiv = ref<Element>()
const scrollDiv = ref<Element>();
const agendas = computed<Agendas>(() => {
const ag = {} as Agendas;
@ -94,21 +94,24 @@ export default defineComponent({
async function load(index: number, done?: (stop: boolean) => void) {
const start = new Date();
if (index < 0) {
events.value.unshift(...(await store.getEvents({ to: start, limit: 5, descending: true })));
const { result } = await store.getEvents({ to: start, limit: 5, descending: true });
events.value.unshift(...result);
if (done) done(false);
} else {
const len = events.value.length;
if (
len ==
events.value.push(
...(await store.getEvents({ from: start, offset: (index - 1) * 10, limit: 10 }))
)
) {
const { result } = await store.getEvents({
from: start,
offset: (index - 1) * 10,
limit: 10,
});
if (len == events.value.push(...result)) {
if (done) return done(true);
} else if (done) done(false);
} else if (done) done(false);
}
if (index <= 1) {
window.setTimeout(() => {(<Element>scrollDiv.value).scrollTop = document.getElementById("bbb")?.scrollHeight || 0}, 150);
window.setTimeout(() => {
(<Element>scrollDiv.value).scrollTop = document.getElementById('bbb')?.scrollHeight || 0;
}, 150);
}
}
@ -159,7 +162,8 @@ export default defineComponent({
asYear,
asMonth,
edit,
editor,scrollDiv,
editor,
scrollDiv,
editDone,
load,
remove,

10
src/events.d.ts vendored
View File

@ -1,4 +1,4 @@
import { FG_Plugin } from "@flaschengeist/types";
import { FG_Plugin } from '@flaschengeist/types';
export interface RecurrenceRule {
frequency: string;
@ -13,13 +13,13 @@ interface InvitationData {
}
interface InvitationResponseData {
event: number,
job: number,
invitee: string
event: number;
job: number;
invitee: string;
}
export interface EventNotification extends FG_Plugin.Notification {
data: {
type: number
type: number;
} & (InvitationData | InvitationResponseData);
}

View File

@ -30,7 +30,10 @@ function transpile(msg: FG_Plugin.Notification) {
message.link = { name: 'events-requests' };
} else if ((message.data.type & EventTypes._mask_) === EventTypes.invitation_response) {
message.link = {name: 'events-single-view', params: {id: (<InvitationResponseData>message.data).event}}
message.link = {
name: 'events-single-view',
params: { id: (<InvitationResponseData>message.data).event },
};
}
return message;
}

View File

@ -53,7 +53,7 @@ export const privateRoutes: FG_Plugin.NamedRouteRecordRaw[] = [
name: 'events-single-view',
path: 'events/:id',
component: () => import('../pages/EventPage.vue'),
props: true
props: true,
},
{
name: 'events-edit',

View File

@ -107,13 +107,15 @@ export const useEventStore = defineStore({
},
async getEvents(
filter:
| { from?: Date; to?: Date; limit?: number; offset?: number; descending?: boolean }
| undefined = undefined
filter?: FG.PaginationFilter & {
user?: string;
}
) {
try {
const { data } = await api.get<FG.Event[]>('/events', { params: filter });
data.forEach((element) => fixEvent(element));
const { data } = await api.get<FG.PaginationResponse<FG.Event>>('/events', {
params: <unknown>filter,
});
data.result.forEach((element) => fixEvent(element));
return data;
} catch (error) {
throw error;
@ -166,6 +168,15 @@ export const useEventStore = defineStore({
.then(({ data }) => fixJob(data));
},
async getJobs(filter?: FG.PaginationFilter) {
return api
.get<FG.PaginationResponse<FG.Job>>('/events/jobs', { params: <unknown>filter })
.then(({ data }) => {
data.result.forEach((j) => fixJob(j));
return data;
});
},
/**
* Send invite to job or transfer to other user
* @param job Job to invite to

View File

@ -16,9 +16,6 @@ export type EditableJob = Omit<Omit<FG.Job, 'type'>, 'id'> & {
export function emptyJob(startDate = new Date()): EditableJob {
const start = date.adjustDate(startDate, {
hours: new Date().getHours(),
minutes: 0,
seconds: 0,
milliseconds: 0,
});
return {
start: start,
@ -29,9 +26,9 @@ export function emptyJob(startDate = new Date()): EditableJob {
};
}
export function emptyEvent(startDate?: Date): EditableEvent {
export function emptyEvent(startDate: Date = new Date()): EditableEvent {
return {
start: startDate === undefined ? new Date() : new Date(startDate),
start: date.adjustDate(startDate, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }),
jobs: [emptyJob(startDate)],
is_template: false,
};