Implementation CIS => LvPlanWidget

This commit is contained in:
chfhtw
2025-07-23 08:56:23 +02:00
parent ad080e61b2
commit 72df2cb79c
11 changed files with 190 additions and 277 deletions
+7 -3
View File
@@ -17,6 +17,9 @@ class Cis4 extends Auth_Controller
'index' => 'basis/cis:r' 'index' => 'basis/cis:r'
) )
); );
// Load Config
$this->load->config('calendar');
} }
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
@@ -27,15 +30,16 @@ class Cis4 extends Auth_Controller
*/ */
public function index() public function index()
{ {
$this->load->model('person/Person_model','PersonModel'); $this->load->model('person/Person_model', 'PersonModel');
$personData = getData($this->PersonModel->getByUid(getAuthUID()))[0]; $personData = getData($this->PersonModel->getByUid(getAuthUID()))[0];
$viewData = array( $viewData = array(
'uid' => getAuthUID(), 'uid' => getAuthUID(),
'name' => $personData->vorname, 'name' => $personData->vorname,
'person_id' => $personData->person_id 'person_id' => $personData->person_id,
'timezone' => $this->config->item('timezone')
); );
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'FhcDashboard']); $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'FhcDashboard']);
} }
} }
+38 -10
View File
@@ -124,27 +124,27 @@
.dayPageContainer .feiertagEventContent #ferienEventTitle {} .dayPageContainer .feiertagEventContent #ferienEventTitle {}
/* Minimized Container Styles */ /* List Page Container Styles */
/* Ferientage Events for Minimized */ /* Ferientage Events for List Page */
.minimizedContainer .feiertagEventContent{ .listPageContainer .feiertagEventContent{
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
color: var(--fhc-light); color: var(--fhc-light);
} }
.minimizedContainer .feiertagEventContent #ferienEventIcon{ .listPageContainer .feiertagEventContent #ferienEventIcon{
margin: 0.5rem; margin: 0.5rem;
} }
.minimizedContainer .feiertagEventContent #ferienEventTitle{ .listPageContainer .feiertagEventContent #ferienEventTitle{
flex-grow: 1; flex-grow: 1;
} }
/* Lehreinheits Events in Minimized */ /* Lehreinheits Events in List Page */
.dayPageContainer .lehreinheitEventContent { .listPageContainer .lehreinheitEventContent {
display: flex; display: flex;
padding-left: 4rem; padding-left: 4rem;
justify-content: space-evenly; justify-content: space-evenly;
@@ -152,15 +152,43 @@
} }
.dayPageContainer .lehreinheitEventContent #lehreinheitEventHeader { .listPageContainer .lehreinheitEventContent #lehreinheitEventHeader {
display: grid; display: grid;
align-content: center; align-content: center;
} }
.dayPageContainer .lehreinheitEventContent #lehreinheitEventText { .listPageContainer .lehreinheitEventContent #lehreinheitEventText {
display: contents; display: contents;
} }
.dayPageContainer .lehreinheitEventContent #lehreinheitEventText * { .listPageContainer .lehreinheitEventContent #lehreinheitEventText * {
font-weight: 500; font-weight: 500;
} }
/* Widget Styles */
.dashboard-widget-lvplan .fhc-calendar-base .card {
--bs-card-cap-padding-x: 0;
--bs-card-cap-padding-y: 0;
--bs-card-border-radius: 0;
--bs-card-border-width: 0;
--dp-input-padding: .375rem .75rem;
--dp-input-icon-padding: calc(1rem + 24px);
}
.dashboard-widget-lvplan .fhc-calendar-base .card-header {
border-bottom-width: var(--bs-border-width);
}
.dashboard-widget-lvplan .fhc-calendar-base .card-header > .row > .col-auto,
.dashboard-widget-lvplan .fhc-calendar-base .card-header .btn-group {
width: 100%;
}
.dashboard-widget-lvplan .fhc-calendar-base .card-header input {
--dp-font-size: .7em;
}
.dashboard-widget-lvplan .fhc-calendar-base .card-header .btn {
--bs-btn-font-size: .7em;
}
.dashboard-widget-lvplan .fhc-calendar-base-label-day {
cursor: initial;
}
+3 -3
View File
@@ -1,4 +1,4 @@
.fhc-calendar-base-grid-line-event { .event {
cursor: pointer; cursor: pointer;
} }
.fhc-calendar-mode-week .all-day-events .fhc-calendar-base-grid-line-event .weekPageContainer { .fhc-calendar-mode-week .all-day-events .fhc-calendar-base-grid-line-event .weekPageContainer {
@@ -59,9 +59,9 @@
.fhc-calendar-base-grid-line-event .event-type-reservierung { .fhc-calendar-base-grid-line-event .event-type-reservierung {
--event-default-bg: var(--fhc-calendar-bg-reservierung, #fff); --event-default-bg: var(--fhc-calendar-bg-reservierung, #fff);
} }
.fhc-calendar-base-grid-line-event > * { .fhc-calendar-base .event > * {
background-color: var(--event-bg, var(--event-default-bg)); background-color: var(--event-bg, var(--event-default-bg));
} }
.fhc-calendar-base-grid-line-event > *:hover { .fhc-calendar-base .event > *:hover {
filter: brightness(120%); filter: brightness(120%);
} }
+1
View File
@@ -264,6 +264,7 @@ export default {
:btn-week="!!modes['week'] && (btnWeek || (showBtns && btnWeek !== false))" :btn-week="!!modes['week'] && (btnWeek || (showBtns && btnWeek !== false))"
:btn-month="!!modes['month'] && (btnMonth || (showBtns && btnMonth !== false))" :btn-month="!!modes['month'] && (btnMonth || (showBtns && btnMonth !== false))"
:btn-list="!!modes['list'] && (btnList || (showBtns && btnList !== false))" :btn-list="!!modes['list'] && (btnList || (showBtns && btnList !== false))"
:mode-options="modeOptions ? modeOptions[cMode] : undefined"
> >
<slot name="actions" /> <slot name="actions" />
</base-header> </base-header>
@@ -42,7 +42,7 @@ export default {
}, },
template: /* html */` template: /* html */`
<div <div
class="fhc-calendar-base-grid-line-event" class="fhc-calendar-base-grid-line-event event"
:class="classes" :class="classes"
style="z-index: 1" style="z-index: 1"
:draggable="draggable" :draggable="draggable"
@@ -17,6 +17,10 @@ export default {
type: String, type: String,
required: true required: true
}, },
modeOptions: {
type: Object,
default: {}
},
btnMonth: Boolean, btnMonth: Boolean,
btnWeek: Boolean, btnWeek: Boolean,
btnDay: Boolean, btnDay: Boolean,
@@ -62,6 +66,7 @@ export default {
@update:date="$emit('update:date', $event)" @update:date="$emit('update:date', $event)"
@open="open = true" @open="open = true"
@closed="open = false" @closed="open = false"
:list-length="modeOptions.length"
/> />
<button <button
class="btn btn-outline-secondary border-0" class="btn btn-outline-secondary border-0"
@@ -51,20 +51,17 @@ export default {
case "month": case "month":
return this.date.toLocaleString({ month: 'long', year: 'numeric' }); return this.date.toLocaleString({ month: 'long', year: 'numeric' });
case "week": case "week":
const year = this.date.localWeekYear; var year = this.date.localWeekYear;
const week = this.date.toFormat('nn'); var week = this.date.toFormat('nn');
return this.$p.t('calendar/year_kw', { year, week }); return this.$p.t('calendar/year_kw', { year, week });
case "list": case "list":
return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.listLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL);
case "day": case "day":
return this.date.toLocaleString(luxon.DateTime.DATE_FULL); return this.date.toLocaleString(luxon.DateTime.DATE_FULL);
default: default:
return 'View not Supported'; return 'View not Supported';
} }
}, },
format() {
const title = this.title;
return `'${title}'`;
},
weekStart() { weekStart() {
return luxon.Info.getStartOfWeek(this.date)%7; return luxon.Info.getStartOfWeek(this.date)%7;
} }
@@ -97,10 +94,10 @@ export default {
<vue-date-picker <vue-date-picker
:model-value="current" :model-value="current"
@update:model-value="update" @update:model-value="update"
:format="format" :format="() => title"
:month-picker="mode == 'month'" :month-picker="mode == 'month'"
:week-picker="mode == 'week'" :week-picker="mode == 'week'"
:range="mode == 'list' ? { autoRange: listLength } : false" :range="mode == 'list' ? { autoRange: listLength - 1 } : false"
:text-input="mode == 'day'" :text-input="mode == 'day'"
:week-start="weekStart" :week-start="weekStart"
:week-numbers="{ type: weekNumbers }" :week-numbers="{ type: weekNumbers }"
+16 -6
View File
@@ -20,7 +20,8 @@ export default {
emits: [ emits: [
"update:currentDate", "update:currentDate",
"update:range", "update:range",
"click" "click",
"requestModalOpen"
], ],
data() { data() {
return { return {
@@ -35,9 +36,9 @@ export default {
if (this.rangeOffset != 0) { if (this.rangeOffset != 0) {
if (this.rangeOffset < 0) { if (this.rangeOffset < 0) {
first = first.plus({ days: this.rangeOffset * this.length }); first = first.plus({ days: this.rangeOffset });
} else { } else {
last = last.plus({ days: this.rangeOffset * this.length }); last = first.plus({ days: this.rangeOffset + this.length });
} }
} }
@@ -46,7 +47,7 @@ export default {
}, },
watch: { watch: {
currentDate() { currentDate() {
this.rangeOffset = Math.floor(this.currentDate.startOf('day').diff(this.focusDate.startOf('day'), 'days').days / this.length); this.rangeOffset = this.currentDate.startOf('day').diff(this.focusDate.startOf('day'), 'days').days;
if (this.rangeOffset) { if (this.rangeOffset) {
this.$emit('update:range', this.range); this.$emit('update:range', this.range);
this.$refs.slider.slidePages(this.rangeOffset).then(this.updatePage); this.$refs.slider.slidePages(this.rangeOffset).then(this.updatePage);
@@ -65,15 +66,23 @@ export default {
this.$refs.slider.nextPage().then(this.updatePage); this.$refs.slider.nextPage().then(this.updatePage);
}, },
updatePage(offset) { updatePage(offset) {
const newFocusDate = this.focusDate.plus({ days: offset * this.length }); const newFocusDate = this.focusDate.plus({ days: offset });
this.focusDate = newFocusDate; this.focusDate = newFocusDate;
this.rangeOffset = 0; this.rangeOffset = 0;
this.$emit('update:currentDate', this.focusDate); this.$emit('update:currentDate', this.focusDate);
this.$emit('update:range', this.range); this.$emit('update:range', this.range);
}, },
viewAttrs(offset) { viewAttrs(offset) {
const day = this.focusDate.plus({ days: offset * this.length }); const day = this.focusDate.plus({ days: offset });
return { day, length: this.length }; return { day, length: this.length };
},
handleClickDefaults(evt) {
switch (evt.detail.source) {
case 'event':
// default: Request Modal
this.$emit('requestModalOpen', { event: evt.detail.value });
break;
}
} }
}, },
mounted() { mounted() {
@@ -82,6 +91,7 @@ export default {
template: ` template: `
<div <div
class="fhc-calendar-mode-list flex-grow-1 position-relative" class="fhc-calendar-mode-list flex-grow-1 position-relative"
@cal-click-default.capture="handleClickDefaults"
> >
<base-slider ref="slider" v-slot="slot"> <base-slider ref="slider" v-slot="slot">
<list-view v-bind="viewAttrs(slot.offset)"> <list-view v-bind="viewAttrs(slot.offset)">
@@ -1,6 +1,9 @@
import LabelDay from '../../Base/Label/Day.js'; import LabelDay from '../../Base/Label/Day.js';
import LabelDow from '../../Base/Label/Dow.js'; import LabelDow from '../../Base/Label/Dow.js';
import CalDnd from '../../../../directives/Calendar/DragAndDrop.js';
import CalClick from '../../../../directives/Calendar/Click.js';
// TODO(chris): drag and drop // TODO(chris): drag and drop
export default { export default {
@@ -9,7 +12,12 @@ export default {
LabelDay, LabelDay,
LabelDow LabelDow
}, },
directives: {
CalDnd,
CalClick
},
inject: { inject: {
draggableEvents: "draggableEvents",
events: "events" events: "events"
}, },
props: { props: {
@@ -43,20 +51,31 @@ export default {
return eventsPerDay.filter(day => day.events.length); return eventsPerDay.filter(day => day.events.length);
} }
}, },
methods: {
draggable(event) {
return this.draggableEvents(event.orig, 'list');
},
},
template: /* html */` template: /* html */`
<div <div class="fhc-calendar-mode-list-view h-100 overflow-auto">
class="fhc-calendar-mode-list-view h-100" <div v-if="!eventsPerDay.length" class="h-100">
>
<div v-if="!eventsPerDay.length">
<slot :event="undefined" mode="list" /> <slot :event="undefined" mode="list" />
</div> </div>
<div v-for="{ day, events } in eventsPerDay" class="text-center"> <div v-for="{ day, events } in eventsPerDay" class="text-center">
<label-dow :date="day" class="d-inline" />, <label-day :date="day" class="d-inline" /> <label-dow :date="day" class="d-inline" />, <label-day :date="day" class="d-inline" />
<div v-for="event in events"> <div v-for="event in events">
<div v-if="slot.event.type == 'loading'" class="placeholder-glow opacity-50"> <div v-if="event.type == 'loading'" class="placeholder-glow opacity-50">
<span class="placeholder w-100" /> <span class="placeholder w-100" />
</div> </div>
<slot v-else :event="event.orig" mode="list" /> <div
v-else
class="event"
:draggable="draggable(event)"
v-cal-dnd:draggable="event"
v-cal-click:event="event.orig"
>
<slot :event="event.orig" mode="list" />
</div>
</div> </div>
</div> </div>
</div> </div>
+2 -2
View File
@@ -19,9 +19,8 @@ export default {
viewData: { viewData: {
type: Object, type: Object,
required: true, required: true,
default: () => ({name: '', uid: ''}),
validator(value) { validator(value) {
return value && value.name && value.uid return value && value.name && value.uid && value.timezone
} }
} }
}, },
@@ -37,6 +36,7 @@ export default {
return { return {
editMode: Vue.computed(()=>this.editMode), editMode: Vue.computed(()=>this.editMode),
widgetsSetup: Vue.computed(() => this.widgets), widgetsSetup: Vue.computed(() => this.widgets),
timezone: Vue.computed(() => this.viewData.timezone)
} }
}, },
computed: { computed: {
+83 -234
View File
@@ -1,266 +1,115 @@
import Phrasen from '../../mixins/Phrasen.js';
import AbstractWidget from './Abstract.js'; import AbstractWidget from './Abstract.js';
import FhcCalendar from '../Calendar/Calendar.js'; import FhcCalendar from '../Calendar/Base.js';
import LvModal from '../Cis/Mylv/LvModal.js';
import ContentModal from '../Cis/Cms/ContentModal.js'
import CalendarDate from '../../composables/CalendarDate.js';
import ApiLvPlan from '../../api/factory/lvPlan.js'; import ApiLvPlan from '../../api/factory/lvPlan.js';
import ApiOrt from '../../api/factory/ort.js';
import { useEventLoader } from '../../composables/EventLoader.js';
import ModeList from '../Calendar/Mode/List.js';
export default { export default {
name: "LvPlanWidget", name: "LvPlanWidget",
components: {
FhcCalendar
},
mixins: [ mixins: [
Phrasen,
AbstractWidget AbstractWidget
], ],
components: { inject: [
FhcCalendar, "renderers",
LvModal, "timezone"
ContentModal ],
},
data() { data() {
return { return {
stunden: [], now: luxon.DateTime.now().setZone(this.timezone),
minimized: true, modes: {
events: null, list: Vue.markRaw(ModeList)
currentDay: this.getCurrentDate(), },
calendarDate: new CalendarDate(new Date()), modeOptions: {
roomInfoContentID: null, list: {
ort_kurzbz: null, length: 7
selectedEvent: null,
isModalContentResolved: false,
isModalTitleResolved: false,
isShowModal: false,
}
},
inject: ["renderers"],
watch:{
modalLoaded: {
handler: function (newValue) {
if (this.isShowModal && newValue.isModalContentResolved && newValue.isModalTitleResolved) {
this.$nextTick(() => {
if (this.$refs.lvmodal) this.$refs.lvmodal.show();
this.isShowModal = false;
});
} }
}, },
immediate: true currentDay: luxon.DateTime.now().setZone(this.timezone).startOf('day')
}, }
},
computed: {
modalLoaded: function () {
return { isModalContentResolved: this.isModalContentResolved, isModalTitleResolved: this.isModalTitleResolved };
},
allEventsGrouped() {
// groups all events of the next 7 days together
const currentCalendarDate = new CalendarDate(this.currentDay)
const mapArr = currentCalendarDate.nextSevenDays.map((d) => [new CalendarDate(d), []])
// return map of key => calendar date of next 7 days & values the respective events on that date
return new Map((this.events || []).filter(evt => evt.start >= this.currentDay).reduce((acc, cur) => {
const date = new CalendarDate(new Date(cur.datum))
const arr = acc.find(el => el[0].compare(date))
if(!arr) return acc
arr[1].push(cur)
return acc
}, mapArr));
},
currentEvents() {
return (this.events || []).filter(evt => evt.end < this.dayAfterCurrentDay && evt.start >= this.currentDay);
},
dayAfterCurrentDay() {
let currentDay = new Date(this.currentDay);
currentDay.setDate(currentDay.getDate() + 1);
return currentDay;
},
monthFirstDay: function () {
return this.calendarDateToString(this.calendarDate.cdFirstDayOfCalendarMonth);
},
monthLastDay: function () {
return this.calendarDateToString(this.calendarDate.cdLastDayOfNextCalendarMonth);
},
}, },
methods: { methods: {
showModal: function (event) { eventStyle(event) {
this.currentlySelectedEvent = event; const styles = {};
Vue.nextTick(() => { if (event.farbe)
if (this.isModalContentResolved && this.isModalTitleResolved) { styles['--event-bg'] = '#' + event.farbe;
if (this.$refs.lvmodal) this.$refs.lvmodal.show(); else if (event.type == 'reservierung')
} styles['--event-bg'] = '#ffffff';
else { else
this.isShowModal = true; styles['--event-bg'] = '#cccccc';
}
})
},
modalTitleResolved: function () {
this.isModalTitleResolved = true;
}, const eventEnd = luxon.DateTime.fromISO(event.isoend, { zone: this.timezone });
modalContentResolved: function () { if (eventEnd < this.now)
this.isModalContentResolved = true; styles['opacity'] = .5;
},
modalTitleComponent(type) {
return this.renderers[type]?.modalTitle;
},
modalContentComponent(type) {
return this.renderers[type]?.modalContent;
},
calendarEventComponent(type) {
return this.renderers[type]?.calendarEvent;
},
getEventStyle: function(evt) {
const styles = {'background-color': evt.color};
if(evt.start.getTime() < Date.now()) styles.opacity = 0.5;
return styles; return styles;
}, },
getCurrentDate: function() { updateRange(rangeInterval) {
const today = new Date() this.rangeInterval = rangeInterval;
today.setHours(0,0,0)
return today
},
calendarDateToString: function (calendarDate) {
return calendarDate instanceof CalendarDate ?
[calendarDate.y, calendarDate.m + 1, calendarDate.d].join('-') :
null;
},
showRoomInfoModal: function(ort_kurzbz){
// getting the content_id of the ort_kurzbz
this.$api
.call(ApiOrt.getContentID(ort_kurzbz))
.then(res => {
this.roomInfoContentID = res.data;
this.ort_kurzbz = ort_kurzbz;
// only showing the modal after vue was able to set the reactive data
Vue.nextTick(() => { this.$refs.contentModal.show(); });
})
.catch(err => {
console.err(err);
this.ort_kurzbz = null;
this.roomInfoContentID = null;
});
},
showLvUebersicht: function (event){
this.selectedEvent= event;
Vue.nextTick(()=>{
this.$refs.lvmodal.show();
});
},
selectDay(day) {
this.currentDay = day;
this.minimized = true;
},
updateRange: function (data) {
let tmp_date = new CalendarDate(data.start);
// only load month data if the month or year has changed
if (tmp_date.m != this.calendarDate.m || tmp_date.y != this.calendarDate.y) {
this.calendarDate = tmp_date;
Vue.nextTick(() => {
this.loadEvents();
});
}
},
loadEvents: function () {
Promise.allSettled([
this.$api.call(ApiLvPlan.LvPlanEvents(this.monthFirstDay, this.monthLastDay)),
this.$api.call(ApiLvPlan.getLvPlanReservierungen(this.monthFirstDay, this.monthLastDay))
]).then((result) => {
let promise_events = [];
result.forEach((promise_result) => {
if (promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success") {
let data = promise_result.value.data;
// adding additional information to the events
if (data && data.forEach) {
data.forEach((el, i) => {
el.id = i;
if (el.type === 'reservierung') {
el.color = '#' + (el.farbe || 'FFFFFF');
} else {
el.color = '#' + (el.farbe || 'CCCCCC');
}
el.start = new Date(el.datum + ' ' + el.beginn);
el.end = new Date(el.datum + ' ' + el.ende);
});
}
promise_events = promise_events.concat(data);
}
})
this.events = promise_events;
});
},
setCalendarMaximized() {
this.minimized = false
} }
},
setup() {
const $api = Vue.inject('$api');
const rangeInterval = Vue.ref(null);
const { events } = useEventLoader(rangeInterval, (start, end) => {
return [
$api.call(ApiLvPlan.LvPlanEvents(start.toISODate(), end.toISODate())),
$api.call(ApiLvPlan.getLvPlanReservierungen(start.toISODate(), end.toISODate()))
];
});
return {
rangeInterval,
events
};
}, },
created() { created() {
this.$emit('setConfig', false); this.$emit('setConfig', false);
this.loadEvents();
}, },
template: /*html*/` template: /*html*/`
<div class="dashboard-widget-lvplan d-flex flex-column h-100"> <div class="dashboard-widget-lvplan d-flex flex-column h-100">
<lv-modal v-if="selectedEvent" ref="lvmodal" :event="selectedEvent" > <fhc-calendar
<template #modalTitle> v-model:date="currentDay"
<Suspense @pending="isModalTitleResolved=false" @resolve="modalTitleResolved"> :modes="modes"
<component :is="modalTitleComponent(selectedEvent.type)" v-if="selectedEvent" :event="selectedEvent" ></component> :mode-options="modeOptions"
</Suspense> @update:range="updateRange"
</template> :timezone="timezone"
<template #modalContent> :locale="$p.user_locale.value"
<Suspense @pending="isModalContentResolved=false" @resolve="modalContentResolved"> :events="events"
<component :is="modalContentComponent(selectedEvent.type)" v-if="selectedEvent" :event="selectedEvent" ></component> >
</Suspense> <template v-slot="{ event, mode }">
</template> <div
</lv-modal> v-if="!event"
<content-modal :content_id="roomInfoContentID" dialogClass="modal-lg" ref="contentModal"/> class="h-100 d-flex justify-content-center align-items-center"
<fhc-calendar @change:range="updateRange" :initial-date="currentDay" class="border-0" class-header="p-0" @select:day="selectDay" :widget="true" v-model:minimized="minimized" :events="events" no-week-view :show-weeks="false" > >
<template #monthPage="{event,day}"> {{ $p.t('lehre/noLvFound') }}
<div v-if="event.type=='moodle'">
<div class="d-flex small w-100" >
<moodle-svg></moodle-svg>
<span v-contrast class="flex-grow-1 text-center "><strong v-html="event.titel"></strong> - {{event.topic}}</span>
</div>
</div> </div>
<span v-else class="small" > <component
{{event.topic}} v-else-if="mode == 'eventheader'"
</span> :is="renderers[event.type]?.modalTitle"
</template> :event="event"
<template #minimizedPage > ></component>
<div class="minimizedContainer flex-grow-1" style="overflow-y: auto; overflow-x: hidden"> <component
<div v-if="events === null" class="d-flex h-100 justify-content-center align-items-center"> v-else-if="mode == 'event'"
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i> :is="renderers[event.type]?.modalContent"
</div> :event="event"
<template v-else-if="allEventsGrouped.size" v-for="([key, value], index) in allEventsGrouped" :key="index" style="margin-top: 8px;"> ></component>
<div class=" card-header d-grid p-0"> <div
<button class="btn fhc-tertiary text-decoration-none" @click="setCalendarMaximized">{{ key.format({dateStyle: "full"}, $p.user_locale.value)}}</button> v-else
</div> :class="'event-type-' + event.type + ' ' + mode + 'PageContainer'"
<div role="button" @click="showLvUebersicht(evt)" v-for="evt in value" :key="evt.id" class="list-group-item small" :style="getEventStyle(evt)"> :style="eventStyle(event)"
<component :is="calendarEventComponent(evt.type)" :event="evt" ></component> >
</div> <component
<div v-if="!value.length" class="list-group-item small text-center"> :is="renderers[event.type]?.calendarEvent"
{{ $p.t('lehre/noLvFound') }} :event="event"
</div> ></component>
</template>
<div v-else class="d-flex h-100 justify-content-center align-items-center fst-italic text-center">
{{ $p.t('lehre/noLvFound') }}
</div>
</div> </div>
</template> </template>
</fhc-calendar> </fhc-calendar>