refactor(Calendar Tagesansicht): changes how the logic for opening a modal or showing the lvMenu next to the calendar is handled

This commit is contained in:
SimonGschnell
2024-11-19 10:11:49 +01:00
parent 06dab52461
commit 4aed321b5e
7 changed files with 91 additions and 51 deletions
+1 -1
View File
@@ -175,7 +175,7 @@ html {
}
#cis-header .btn.dropdown-toggle:after,
#cis-nav .btn.dropdown-toggle:after {
transition: all 0.2s ease-in-out;
transition: transform 0.2s ease-in-out;
}
#cis-header .btn.dropdown-toggle.collapsed:after,
#cis-nav .btn.dropdown-toggle.collapsed:after {
+3 -18
View File
@@ -3,6 +3,7 @@ import Phrasen from "../../plugin/Phrasen.js";
import CalendarDate from "../../composables/CalendarDate.js";
import LvModal from "../../components/Cis/Mylv/LvModal.js";
const app = Vue.createApp({
name: 'StundenplanApp',
data() {
@@ -17,8 +18,7 @@ const app = Vue.createApp({
}
},
components: {
FhcCalendar,
LvModal
FhcCalendar, LvModal
},
computed:{
weekFirstDay: function () {
@@ -36,20 +36,12 @@ const app = Vue.createApp({
},
methods:{
setWindowWidth: function () {
this.windowWidth = window.innerWidth;
},
getLvID: function () {
this.lv_id = window.location.pathname
},
selectDay: function(day){
this.currentDay = day;
},
showDayModal: function (event) {
// only show the modal if the window width is smaller than 1200px
if(this.windowWidth >= 1200) return;
this.showModal(event);
},
showModal: function(event){
this.currentlySelectedEvent = event;
Vue.nextTick(() => {
@@ -116,13 +108,6 @@ const app = Vue.createApp({
this.loadEvents();
this.getLvID()
},
mounted(){
this.windowWidth = window.innerWidth;
window.addEventListener('resize', this.setWindowWidth);
},
beforeUnmount(){
window.removeEventListener('resize', this.setWindowWidth);
},
//TODO: Stundenplan phrase
template:/*html*/`
<h2>Stundenplan</h2>
@@ -142,7 +127,7 @@ const app = Vue.createApp({
</div>
</template>
<template #dayPage="{event,day}">
<div @click="showDayModal(event?.orig)" type="button" class="border border-secondary border row h-100 justify-content-center align-items-center text-center">
<div type="button" class="fhc-entry border border-secondary border row h-100 justify-content-center align-items-center text-center">
<div class="col ">
<p>Lehrveranstaltung:</p>
<p class="m-0">{{event?.orig.topic}}</p>
+1 -1
View File
@@ -42,7 +42,7 @@ export default {
<div class="fhc-calendar-day">
<calendar-header :title="title" @prev="prev" @next="next" @updateMode="$emit('updateMode', $event)" @click="$emit('updateMode', 'week')"/>
<calendar-pane ref="pane" v-slot="slot" @slid="paneChanged">
<calendar-day-page :year="focusDate.y" :week="focusDate.w+slot.offset" @updateMode="$emit('updateMode', $event)" @page:back="prev" @page:forward="next" @input="selectEvent" >
<calendar-day-page :active="slot.active" :year="focusDate.y" :week="focusDate.w+slot.offset" @updateMode="$emit('updateMode', $event)" @page:back="prev" @page:forward="next" @input="selectEvent" >
<template #dayPage="{event,day}">
<slot name="dayPage" :event="event" :day="day" ></slot>
</template>
+63 -21
View File
@@ -1,6 +1,7 @@
import CalendarDate from '../../../composables/CalendarDate.js';
import LvMenu from "../../../components/Cis/Mylv/LvMenu.js"
import LvInfo from "../../../components/Cis/Mylv/LvInfo.js"
import LvModal from "../../../components/Cis/Mylv/LvModal.js";
function ggt(m, n) { return n == 0 ? m : ggt(n, m % n); }
function kgv(m, n) { return (m * n) / ggt(m, n); }
@@ -9,6 +10,7 @@ export default {
components:{
LvMenu,
LvInfo,
LvModal,
},
data() {
return {
@@ -32,7 +34,8 @@ export default {
],
props: {
year: Number,
week: Number
week: Number,
active: Boolean,
},
emits: [
'updateMode',
@@ -41,6 +44,18 @@ export default {
'input'
],
watch:{
//TODO: on first render non of the day-page components are active and the watcher on selectedEvent does not fetch the lvMenu
//TODO: workaround is to watch the active state and refetch in case the lvMenu is empty
active:{
handler(value){
if(value){
if(!this.lvMenu){
this.fetchLvMenu(this.selectedEvent);
}
}
},
immediate:true,
},
eventsPerDayAndHour:{
handler(newEvents) {
// if no event is selected, select the first event of the day
@@ -55,27 +70,20 @@ export default {
},
selectedEvent:{
handler(event) {
this.lvMenu = null;
if (event && event.type == 'lehreinheit') {
this.$fhcApi.factory.stundenplan.getLehreinheitStudiensemester(event.lehreinheit_id[0]).then(
res => res.data
).then(
studiensemester_kurzbz => {
this.$fhcApi.factory.addons.getLvMenu(event.lehrveranstaltung_id, studiensemester_kurzbz).then(res => {
if (res.data) {
this.lvMenu = res.data;
}
});
}
)
// return early if the day-page component is not the active carousel item
if(!this.active)
{
return;
}
this.lvMenu = null;
this.fetchLvMenu(event);
},
immediate:true,
}
},
computed: {
noEventsCondition(){
return !this.isSliding && this.filteredEvents.length === 0;
return !this.isSliding && this.filteredEvents?.length === 0;
},
hours() {
// returns an array with elements starting at 7 and ending at 24
@@ -127,6 +135,28 @@ export default {
},
},
methods: {
fetchLvMenu(event){
if (event && event.type == 'lehreinheit') {
this.$fhcApi.factory.stundenplan.getLehreinheitStudiensemester(event.lehreinheit_id[0]).then(
res => res.data
).then(
studiensemester_kurzbz => {
this.$fhcApi.factory.addons.getLvMenu(event.lehrveranstaltung_id, studiensemester_kurzbz).then(res => {
if (res.data) {
this.lvMenu = res.data;
}
});
}
)
}
},
showModal: function (evt) {
let event = evt.orig;
this.setSelectedEvent(event);
Vue.nextTick(() => {
this.$refs.lvmodal.show();
});
},
eventClick(evt) {
let event = evt.orig;
this.setSelectedEvent(event);
@@ -190,6 +220,8 @@ export default {
},
template: /*html*/`
<div class="fhc-calendar-day-page ">
<!-- lvModal for mobile view -->
<lv-modal v-if="selectedEvent" :event="selectedEvent" ref="lvmodal" />
<div class="row m-0">
<div class="col-12 col-xl-6 p-0">
<div class="d-flex flex-column">
@@ -212,10 +244,20 @@ export default {
<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="{'selectedEvent':event.orig == selectedEvent}" class="mx-2 small rounded overflow-hidden " @click.prevent="eventClick(event)" :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 :style="{'background-color':event.orig.color}" :class="{'selectedEvent':event.orig == selectedEvent}" class="mx-2 small rounded overflow-hidden " :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">
<!-- desktop version opens the lvMenu next to the calendar -->
<div class="d-none d-xl-block h-100 " @click.prevent="eventClick(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>
<!-- mobile version opens the lvModal in a modal -->
<div class="d-block d-xl-none h-100" @click.prevent="showModal(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>
@@ -225,7 +267,7 @@ export default {
</div>
<div class="d-none d-xl-block col-xl-6 p-0">
<div class="p-5 sticky-top d-flex justify-content-center align-items-center flex-column">
<div style="max-height: calc(var(--fhc-calendar-pane-height) - 100px); overflow-y:auto;">
<div style="max-height: calc(var(--fhc-calendar-pane-height) - 100px); overflow-y:auto;" class="w-100">
<template v-if="selectedEvent && lvMenu">
<h3 >{{$p.t('lvinfo','lehrveranstaltungsinformationen')}}</h3>
<div class="w-100">
@@ -238,7 +280,7 @@ export default {
<h3>Keine Lehrveranstaltungen</h3>
</template>
<template v-else>
<div class="d-flex h-100 justify-content-center align-items-center">
<div style="height: calc(var(--fhc-calendar-pane-height) - 100px);" class="d-flex w-100 justify-content-center align-items-center">
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
</template>
+2 -5
View File
@@ -63,10 +63,6 @@ export default {
this.$emit('updateMode', 'week');
}
},
changeToDay(day) {
this.focusDate.set(day);
this.$emit('updateMode', 'day');
},
highlight(week, day){
this.highlightedWeek = week.no;
this.highlightedDay = day;
@@ -77,7 +73,8 @@ export default {
clickEvent(day,week) {
if(!this.noWeekView)
{
this.changeToDay(day);
this.focusDate.set(day);
this.$emit('updateMode', 'day');
}
this.selectDay(day);
}
+17 -2
View File
@@ -10,6 +10,7 @@ export default {
slideAnimation:false,
scrollTop:null,
clientHeight:null,
carouselItems:null,
}
},
provide() {
@@ -22,7 +23,19 @@ export default {
computed: {
offsets() {
return [...Array(3).keys()].map(i => (3+i-this.offset)%3-1);
},
activeCarouselItemIndex() {
if (Array.isArray(this.carouselItems) && this.carouselItems.length > 0) {
for(let index=0; index < this.carouselItems.length; index++){
if (this.carouselItems[index] == true){
return index;
}
}
}
return -1;
}
},
methods: {
scrollCalendar(event){
@@ -48,6 +61,7 @@ export default {
else
this.carousel.prev();
}
this.carouselItems = this.$refs.carouselItems.map((item) => { return item.classList.contains('active') });
this.slideAnimation = false;
},
slide(evt) {
@@ -61,6 +75,7 @@ export default {
interval: false
});
}
this.carouselItems = this.$refs.carouselItems.map((item)=>{return item.classList.contains('active')});
this.scrollTop = this.$refs.calendarContainer.scrollTop;
this.clientHeight = this.$refs.calendarContainer.clientHeight;
},
@@ -68,8 +83,8 @@ export default {
<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 @scroll="scrollCalendar" ref="calendarContainer" class="carousel-inner " style="height:var(--fhc-calendar-pane-height); overflow:scroll">
<div v-for="i in [...Array(3).keys()]" :key="i" class="carousel-item">
<slot :index="i" :offset="offsets[i]" />
<div ref="carouselItems" v-for="i in [...Array(3).keys()]" :key="i" class="carousel-item">
<slot :active="i == activeCarouselItemIndex" :index="i" :offset="offsets[i]" />
</div>
</div>
</div>`
+4 -3
View File
@@ -141,9 +141,10 @@ export default {
dateToMinutesOfDay(day) {
return Math.floor(((day.getHours()-7) * 60 + day.getMinutes()) / this.smallestTimeFrame) + 1;
},
weekPageClick(event) {
weekPageClick(event, day) {
this.setSelectedEvent(event);
$emit('input', event)
this.focusDate.set(new CalendarDate(new Date(event.datum)));
this.$emit('input', event)
}
},
mounted() {
@@ -170,7 +171,7 @@ export default {
<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': 'repeat(' + day.lanes + ', 1fr)', 'grid-template-rows': 'repeat(' + (hours.length * 60 / smallestTimeFrame) + ', 1fr)'}">
<div :style="{'background-color':event.orig.color}" class="mx-2 small rounded overflow-hidden " @click.prevent="weekPageClick(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">
<div :style="{'background-color':event.orig.color}" class="mx-2 small rounded overflow-hidden " @click.prevent="weekPageClick(event.orig, day)" :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="weekPage" :event="event" :day="day" :isSelected="event.orig == selectedEvent" >
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
</slot>