feature(Calendar Tagesansicht): Calender wird ausgegraut wenn keine LVs an diesen Tag vorhanden sind

This commit is contained in:
SimonGschnell
2024-11-13 11:00:08 +01:00
parent c244b3ad1b
commit 59f8f91af9
6 changed files with 110 additions and 50 deletions
+15
View File
@@ -68,6 +68,21 @@
box-shadow: 0 0 0 1px #dee2e6 !important;
}
.fhc-calendar-no-events-overlay{
position: relative;
}
.fhc-calendar-no-events-overlay::before {
content: "";
position: absolute;
top: 0;
left: 0;
margin:auto;
width: 100%;
height: 100%;
background-image: linear-gradient(120deg, white, gray );
opacity: .7;
}
.fhc-calendar-day-page {
/*aspect-ratio: 7/6;*/
min-height: 0;
+37 -18
View File
@@ -26,6 +26,7 @@ export default {
focusDate: this.focusDate,
size: Vue.computed({ get: () => this.size, set: v => this.size = v }),
events: Vue.computed(() => this.eventsPerDay),
filteredEvents: Vue.computed(() => this.filteredEvents),
minimized: Vue.computed({ get: () => this.minimized, set: v => this.$emit('update:minimized', v) }),
showWeeks: this.showWeeks,
noMonthView: this.noMonthView,
@@ -126,31 +127,46 @@ export default {
},
// returns the hour of the earliest event, used to scroll to the events in the calendar (week / day view)
scrollTime: function () {
if (this.events && Array.isArray(this.events) && this.events.length > 0) {
let filteredEvents = this.events.filter(event => {
// week view filters the elements only for the same week
let eventDate = new CalendarDate(new Date(event.datum));
if(this.mode == 'week'){
return this.focusDate.w == eventDate.w;
}
// day view filters the elements for the same day and the same week
else if(this.mode == 'day'){
return this.focusDate.d == eventDate.d && this.focusDate.w == eventDate.w;
}
})
// return the first beginning time of the filtered events
if(filteredEvents.length > 0)
if(this.filteredEvents && Array.isArray(this.filteredEvents) && this.filteredEvents.length > 0)
{
return parseInt(filteredEvents.sort((a, b) => parseInt(a.beginn) - parseInt(b.beginn))[0].beginn);
return parseInt(this.filteredEvents.sort((a, b) => parseInt(a.beginn) - parseInt(b.beginn))[0].beginn);
}
// if there is not filtered event that matches the current week
// there is no event that matches the current view mode constraints
else
{
return null;
}
},
// filters the events based on the current calendar view mode
// week view - filter events based on their week
// day view - filter events based on their day and week
// month view - does not filter the events
filteredEvents: function(){
if (this.events && Array.isArray(this.events) && this.events.length > 0) {
let filteredEvents = this.events.filter(event => {
let eventDate = new CalendarDate(new Date(event.datum));
if (this.mode == 'week')
{
// week view filters the elements only for the same week
return this.focusDate.w == eventDate.w;
}
else if (this.mode == 'day')
{
// day view filters the elements for the same day and the same week
return this.focusDate.d == eventDate.d && this.focusDate.w == eventDate.w;
}
else
{
// returns all the events, does not filter the events
return true;
}
})
return filteredEvents;
}
else {
else
{
return null;
}
},
@@ -190,7 +206,7 @@ export default {
},
template: /*html*/`
<div ref="container" class="fhc-calendar card" :class="sizeClass">
<div ref="container" class="fhc-calendar card h-100" :class="sizeClass">
<component :is="'calendar-' + mode" @updateMode="mode = $event" @change:range="$emit('change:range',$event)" @input="handleInput" >
<template #weekPage="{event,day}">
<slot name="weekPage" :event="event" :day="day"></slot>
@@ -198,6 +214,9 @@ export default {
<template #dayPage="{event,day}">
<slot name="dayPage" :event="event" :day="day"></slot>
</template>
<template #minimizedPage="{event,day}">
<slot name="minimizedPage" :event="event" :day="day"></slot>
</template>
</component>
</div>`
}
+22 -11
View File
@@ -16,7 +16,10 @@ export default {
'size',
'events',
'noMonthView',
'isSliding'
'filteredEvents',
'isSliding',
'calendarScrollTop',
'calendarClientHeight',
],
props: {
year: Number,
@@ -29,6 +32,9 @@ export default {
'input'
],
computed: {
noEventsTextPosition(){
return this.calendarScrollTop + 100;
},
hours() {
// returns an array with elements starting at 7 and ending at 24
return [...Array(24).keys()].filter(hour => hour >= 7 && hour <= 24);
@@ -79,7 +85,7 @@ export default {
},
smallestTimeFrame() {
return [30, 15, 10, 5][this.size];
}
},
},
methods: {
calcHourPosition(event) {
@@ -157,19 +163,24 @@ export default {
<div v-if="hourPosition" class="position-absolute border-top small" style="pointer-events: none; padding-left:3.5rem; margin-top:-1px;z-index:2;border-color:#00649C !important" :style="{top:hourPosition+'px',left:0,right:0}">
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
</div>
<div class="events">
<div class="hours">
<div v-for="hour in hours" style="min-height:100px" :key="hour" class="text-muted text-end small" :ref="'hour' + hour">{{hour}}:00</div>
</div>
<div v-for="day in eventsPerDayAndHour" :key="day" class=" day border-start" :style="{'grid-template-columns': '1 1fr', 'grid-template-rows': 'repeat(' + (hours.length * 60 / smallestTimeFrame) + ', 1fr)'}">
<div :style="{'background-color':event.orig.color}" class="mx-2 border border-dark border-2 small rounded overflow-hidden " @click.prevent="$emit('input', event.orig)" :style="{'z-index':1,'grid-column-start': 1+(event.lane-1)*day.lanes/event.maxLane, 'grid-column-end': 1+event.lane*day.lanes/event.maxLane, 'grid-row-start': dateToMinutesOfDay(event.start), 'grid-row-end': dateToMinutesOfDay(event.end) ,'--test': dateToMinutesOfDay(event.end)}" v-for="event in day.events" :key="event">
<slot name="dayPage" :event="event" :day="day">
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
</slot>
<div>
<h1 v-if="filteredEvents.length==0" class="m-0 text-secondary" ref="noEventsText" :style="{'top':noEventsTextPosition+'px'}" style="position:absolute; left:0; text-align:center; width: 100%; z-index:1">Keine Lehrveranstaltungen</h1>
<div class="events" :class="{'fhc-calendar-no-events-overlay':filteredEvents.length==0}">
<div class="hours">
<div v-for="hour in hours" style="min-height:100px" :key="hour" class="text-muted text-end small" :ref="'hour' + hour">{{hour}}:00</div>
</div>
<div v-for="day in eventsPerDayAndHour" :key="day" class=" day border-start" :style="{'grid-template-columns': '1 1fr', 'grid-template-rows': 'repeat(' + (hours.length * 60 / smallestTimeFrame) + ', 1fr)'}">
<div :style="{'background-color':event.orig.color}" class="mx-2 border border-dark border-2 small rounded overflow-hidden " @click.prevent="$emit('input', event.orig)" :style="{'z-index':1,'grid-column-start': 1+(event.lane-1)*day.lanes/event.maxLane, 'grid-column-end': 1+event.lane*day.lanes/event.maxLane, 'grid-row-start': dateToMinutesOfDay(event.start), 'grid-row-end': dateToMinutesOfDay(event.end) ,'--test': dateToMinutesOfDay(event.end)}" v-for="event in day.events" :key="event">
<slot name="dayPage" :event="event" :day="day">
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
</slot>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
+2 -1
View File
@@ -22,9 +22,10 @@ export default {
}
},
template: `
<div class="fhc-calendar-minimized">
<div class="fhc-calendar-minimized h-100 d-flex flex-column">
<div class="card-header d-grid" :class="classHeader">
<button class="btn btn-link link-secondary text-decoration-none" @click="maximize">{{ date.format({dateStyle: ['long','full','full','full'][this.size]}) }}</button>
</div>
<slot name="minimizedPage"></slot>
</div>`
}
+11 -1
View File
@@ -8,11 +8,15 @@ export default {
queue: 0,
offset: 0,
slideAnimation:false,
scrollTop:null,
clientHeight:null,
}
},
provide() {
return {
isSliding: Vue.computed(() => this.slideAnimation),
calendarScrollTop: Vue.computed(() =>this.scrollTop),
calendarClientHeight: Vue.computed(() => this.clientHeight),
}
},
computed: {
@@ -21,6 +25,10 @@ export default {
}
},
methods: {
scrollCalendar(event){
this.scrollTop = this.$refs.calendarContainer.scrollTop;
this.clientHeight = this.$refs.calendarContainer.clientHeight;
},
prev() {
if (!this.queue--)
this.carousel.prev();
@@ -53,11 +61,13 @@ export default {
interval: false
});
}
this.scrollTop = this.$refs.calendarContainer.scrollTop;
this.clientHeight = this.$refs.calendarContainer.clientHeight;
},
template: `
<div ref="carousel" class="calendar-pane carousel slide" @[\`slide.bs.carousel\`]="slide" @[\`slid.bs.carousel\`]="slid" :data-queue="queue">
<!--height calc function just for user testing purpose (has to be fixed)-->
<div class="carousel-inner " style="height:calc(100vh - 220px); overflow:scroll">
<div @scroll="scrollCalendar" ref="calendarContainer" class="carousel-inner " style="height:calc(100vh - 220px); overflow:scroll">
<div v-for="i in [...Array(3).keys()]" :key="i" class="carousel-item">
<slot :index="i" :offset="offsets[i]" />
</div>
@@ -160,25 +160,29 @@ export default {
<div class="dashboard-widget-stundenplan d-flex flex-column h-100">
<lv-modal v-if="selectedEvent" ref="lvmodal" :event="selectedEvent" />
<content-modal :contentID="roomInfoContentID" :ort_kurzbz="" dialogClass="modal-lg" ref="contentModal"/>
<fhc-calendar @change:range="updateRange" :initial-date="currentDay" class="border-0" class-header="p-0" @select:day="selectDay" v-model:minimized="minimized" :events="events" no-week-view :show-weeks="false" />
<div v-show="minimized" class="flex-grow-1 overflow-scroll">
<div v-if="events === null" class="d-flex h-100 justify-content-center align-items-center">
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
<div v-else-if="currentEvents.length" class="list-group list-group-flush">
<div role="button" @click="showLvUebersicht(evt)" class="" v-for="evt in currentEvents" :key="evt.id" class="list-group-item small" :style="{'background-color':evt.color}">
<b>{{evt.topic}}</b>
<br>
<small class="d-flex w-100 justify-content-between">
<!-- event modifier stop to prevent opening the modal for the lv Uebersicht when clicking on the ort_kurzbz -->
<span @click.stop="showRoomInfoModal(evt.ort_kurzbz)" style="text-decoration:underline" type="button">{{evt.ort_kurzbz}}</span>
<span>{{evt.start.toLocaleTimeString(undefined, {hour:'numeric',minute:'numeric'})}}-{{evt.end.toLocaleTimeString(undefined, {hour:'numeric',minute:'numeric'})}}</span>
</small>
<fhc-calendar @change:range="updateRange" :initial-date="currentDay" class="border-0" class-header="p-0" @select:day="selectDay" v-model:minimized="minimized" :events="events" no-week-view :show-weeks="false" >
<template #minimizedPage >
<div class="flex-grow-1 overflow-scroll">
<div v-if="events === null" class="d-flex h-100 justify-content-center align-items-center">
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
<div v-else-if="currentEvents.length" class="list-group list-group-flush">
<div role="button" @click="showLvUebersicht(evt)" class="" v-for="evt in currentEvents" :key="evt.id" class="list-group-item small" :style="{'background-color':evt.color}">
<b>{{evt.topic}}</b>
<br>
<small class="d-flex w-100 justify-content-between">
<!-- event modifier stop to prevent opening the modal for the lv Uebersicht when clicking on the ort_kurzbz -->
<span @click.stop="showRoomInfoModal(evt.ort_kurzbz)" style="text-decoration:underline" type="button">{{evt.ort_kurzbz}}</span>
<span>{{evt.start.toLocaleTimeString(undefined, {hour:'numeric',minute:'numeric'})}}-{{evt.end.toLocaleTimeString(undefined, {hour:'numeric',minute:'numeric'})}}</span>
</small>
</div>
</div>
<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>
<div v-else class="d-flex h-100 justify-content-center align-items-center fst-italic text-center">
{{ p.t('lehre/noLvFound') }}
</div>
</div>
</template>
</fhc-calendar>
</div>`
}