First cherry pick of modifing event loading

This commit is contained in:
Ivymaster
2026-09-01 17:19:20 +02:00
parent fb36b2a548
commit 1bbe4368bb
6 changed files with 343 additions and 186 deletions
+1 -1
View File
@@ -159,6 +159,7 @@ class KalenderLib
$bis = new DateTime($row->bis);
$events[$id] = (object) [
'kalender_id' => $id,
'eindeutige_kalender_gruppen_id' => $row->eindeutige_kalender_gruppen_id,
'type' => $row->typ,
'beginn' => $von->format('H:i:s'),
@@ -175,7 +176,6 @@ class KalenderLib
'farbe' => isset($row->farbe) ? $row->farbe : '',
'lehrveranstaltung_id' => $row->lehrveranstaltung_id,
'organisationseinheit' => isset($row->oe_kurzbz) ? $row->oe_kurzbz : '',
'kalender_id' => $id,
'lehreinheit_id' => [],
'lektor' => [],
'teilnehmer_gruppe' => [],
+66
View File
@@ -185,3 +185,69 @@ body {
}
.updated-event {
position: relative;
z-index: 10;
animation: modernFocus 1s ease-out;
}
.updated-event-long {
position: relative;
z-index: 10;
animation: modernFocus 1.8s ease-out;
}
@keyframes modernFocus {
0% {
background-color: #cfd4d8;
box-shadow:
0 0 0 0 rgba(120, 120, 120, 0.8),
0 0 0 0 rgba(255, 255, 255, 0.6);
}
30% {
background-color: #eef1f3;
box-shadow:
0 0 0 8px rgba(120, 120, 120, 0.25),
0 0 30px 8px rgba(255, 255, 255, 0.7);
}
60% {
background-color: #f7f8f9;
box-shadow:
0 0 0 14px rgba(120, 120, 120, 0.15),
0 0 45px 12px rgba(255, 255, 255, 0.5);
}
100% {
background-color: inherit;
box-shadow:
0 0 0 24px rgba(120, 120, 120, 0),
0 0 60px 20px rgba(255, 255, 255, 0);
}
}
.deemphasized-event {
animation: deEmphasize 1s ease-out;
}
.deemphasized-event-long {
animation: deEmphasize 1.8s ease-out;
}
@keyframes deEmphasize {
0% {
opacity: 1;
filter: saturate(1) brightness(1);
}
30% {
opacity: 0.55;
filter: saturate(0.4) brightness(0.4);
}
100% {
opacity: 1;
filter: saturate(1) brightness(1);
}
}
+17 -19
View File
@@ -1,5 +1,5 @@
import LineEvent from "./Line/Event.js";
import LineBackground from "./Line/Background.js";
import LineEvent from './Line/Event.js';
import LineBackground from './Line/Background.js';
/**
* TODO(chris):
@@ -7,19 +7,19 @@ import LineBackground from "./Line/Background.js";
*/
export default {
name: "GridLine",
name: 'GridLine',
components: {
LineEvent,
LineBackground,
},
inject: {
axisRow: "axisRow",
axisRow: 'axisRow',
shouldCompactEvents: {
from: "shouldCompactEvents",
from: 'shouldCompactEvents',
default: undefined,
},
compactibleEventTypes: {
from: "compactibleEventTypes",
from: 'compactibleEventTypes',
default: undefined,
},
},
@@ -50,19 +50,20 @@ export default {
let formattedEvents = this.events.map((event) => {
event.rows = [1, -1];
if (event.startsHere) {
event.rows[0] =
"t_" + event.start.diff(this.date).toMillis();
event.rows[0] = 't_' + event.start.diff(this.date).toMillis();
}
if (event.endsHere) {
event.rows[1] = "t_" + event.end.diff(this.date).toMillis();
event.rows[1] = 't_' + event.end.diff(this.date).toMillis();
}
return event;
});
if (this.shouldCompactEvents && this.compactibleEventTypes?.length) {
formattedEvents =
this.compactEvents(formattedEvents, this.compactibleEventTypes);
formattedEvents = this.compactEvents(
formattedEvents,
this.compactibleEventTypes,
);
}
return formattedEvents;
@@ -71,12 +72,9 @@ export default {
methods: {
compactEvents(events, compactibleEventTypes) {
let formattedEvents = events
.filter(
(event) =>
!compactibleEventTypes.includes(event.type),
)
.filter((event) => !compactibleEventTypes.includes(event.type))
.map((event) => {
event.display = "default";
event.display = 'default';
return event;
});
let eventsToBeCompacted = events.filter((event) =>
@@ -110,17 +108,17 @@ export default {
compactedEvents.forEach((compactedEvent) => {
if (compactedEvent.events.length < 4) {
formattedEvents.push({
display: "compacted",
display: 'compacted',
...compactedEvent,
});
} else {
formattedEvents.push({
display: "compacted",
display: 'compacted',
events: compactedEvent.events.slice(0, 3),
rows: compactedEvent.rows,
});
formattedEvents.push({
display: "compactedExtra",
display: 'compactedExtra',
events: compactedEvent.events.slice(3),
rows: compactedEvent.rows,
});
@@ -3,72 +3,74 @@ import drop from '../../../../../directives/drop.js';
import CalClick from '../../../../../directives/Calendar/Click.js';
export default {
name: "GridLineEvent",
name: 'GridLineEvent',
directives: {
draggable,
drop,
CalClick
CalClick,
},
emits: [
'resize-start'
],
emits: ['resize-start'],
data() {
return {
contextMenu: {
show: false,
x: 0,
y: 0
}
y: 0,
},
};
},
inject: {
draggableEvents: "draggableEvents",
draggableEvents: 'draggableEvents',
resizableEvents: {
from: "resizableEvents",
default: () => () => false
from: 'resizableEvents',
default: () => () => false,
},
mode: "mode",
mode: 'mode',
contextMenuActions: {
from: "contextMenuActions",
default: () => ({})
from: 'contextMenuActions',
default: () => ({}),
},
onDrop: {
from: "onDrop",
default: () => null
from: 'onDrop',
default: () => null,
},
onDropEvent: {
from: "onDropEvent",
default: () => () => {}
}
from: 'onDropEvent',
default: () => () => {},
},
},
props: {
event: {
type: Object,
required: true,
validator(value) {
return (value.start && value.end && value.orig);
}
}
return value.start && value.end && value.orig;
},
},
},
computed: {
isHeaderOrFooter() {
return ['header', 'footer'].includes(this.event.orig);
},
draggable() {
return !this.isHeaderOrFooter && this.draggableEvents(this.event.orig, this.mode);
return (
!this.isHeaderOrFooter &&
this.draggableEvents(this.event.orig, this.mode)
);
},
resizable() {
return !this.isHeaderOrFooter && this.resizableEvents(this.event.orig, this.mode);
return (
!this.isHeaderOrFooter &&
this.resizableEvents(this.event.orig, this.mode)
);
},
classes() {
const classes = [];
if (this.isHeaderOrFooter) {
classes.push('event-' + this.event.orig);
} else {
if (this.event.startsHere)
classes.push('event-begin');
if (this.event.endsHere)
classes.push('event-end');
if (this.event.startsHere) classes.push('event-begin');
if (this.event.endsHere) classes.push('event-end');
}
classes.push(`calender_id-${this.event.orig.kalender_id}`);
@@ -85,9 +87,14 @@ export default {
activeContextActions() {
if (this.isHeaderOrFooter) return [];
const type = this.event.orig?.type ?? 'lehreinheit';
const actions = this.contextMenuActions[type] ?? this.contextMenuActions['default'] ?? [];
return actions.filter(action => !action.visible || action.visible(this.event.orig));
}
const actions =
this.contextMenuActions[type] ??
this.contextMenuActions['default'] ??
[];
return actions.filter(
(action) => !action.visible || action.visible(this.event.orig),
);
},
},
methods: {
onResizeStart(edge, evt) {
@@ -95,7 +102,7 @@ export default {
edge,
evt,
el: this.$refs.eventEl,
event: this.event
event: this.event,
});
},
onRightClick(evt) {
@@ -116,27 +123,24 @@ export default {
evt.dataTransfer.setData('fhc-grab-offset-x', evt.clientX - rect.left);
},
onDropOnCard(evt, items) {
if (this.isHeaderOrFooter || !this.onDrop)
return;
if (this.isHeaderOrFooter || !this.onDrop) return;
const list = Array.isArray(items) ? items : [items];
const obj = list[0];
if (!obj)
return;
if (!obj) return;
if ((evt.ctrlKey || evt.metaKey) && obj.type === 'lehreinheit')
{
if ((evt.ctrlKey || evt.metaKey) && obj.type === 'lehreinheit') {
return this.onDrop({
item: [obj],
ctrlKey: true,
targetKalenderId: this.event.orig?.kalender_id ?? null
targetKalenderId: this.event.orig?.kalender_id ?? null,
});
}
return this.onDropEvent(evt, items, this.event.start.startOf('day'));
},
},
template:`
template: `
<div
class="fhc-calendar-base-grid-line-event event"
:class="classes"
@@ -195,5 +199,5 @@ export default {
</ul>
</teleport>
</div>
`
}
`,
};
+185 -115
View File
@@ -14,43 +14,43 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import CoreSearchbar from "../searchbar/searchbar.js";
import NavLanguage from "../navigation/Language.js";
import VerticalSplit from "../verticalsplit/verticalsplit.js";
import FhcCalendar from "../Calendar/Tempus.js";
import FhcCoursepicker from "./Coursepicker.js";
import LectureSelection from "./Filters/LectureSelection.js";
import VerbandSelection from "./Filters/VerbandSelection.js";
import RoomSelection from "./Filters/RoomSelection.js";
import ParkingSlot from "./ParkingSlot.js";
import ApiKalender from "../../api/factory/tempus/kalender.js";
import ApiSearchbar from "../../api/factory/searchbar.js";
import ApiRenderers from "../../api/factory/renderers.js";
import ApiTempusConfig from "../../api/factory/tempus/config.js";
import ApiOperationalResourceToCalender from "../../api/factory/operationalResourceToCalender.js";
import AppMenu from "../AppMenu.js";
import drop from "../../directives/drop.js";
import AppConfig from "../AppConfig.js";
import CoreSearchbar from '../searchbar/searchbar.js';
import NavLanguage from '../navigation/Language.js';
import VerticalSplit from '../verticalsplit/verticalsplit.js';
import FhcCalendar from '../Calendar/Tempus.js';
import FhcCoursepicker from './Coursepicker.js';
import LectureSelection from './Filters/LectureSelection.js';
import VerbandSelection from './Filters/VerbandSelection.js';
import RoomSelection from './Filters/RoomSelection.js';
import ParkingSlot from './ParkingSlot.js';
import ApiKalender from '../../api/factory/tempus/kalender.js';
import ApiSearchbar from '../../api/factory/searchbar.js';
import ApiRenderers from '../../api/factory/renderers.js';
import ApiTempusConfig from '../../api/factory/tempus/config.js';
import ApiOperationalResourceToCalender from '../../api/factory/operationalResourceToCalender.js';
import AppMenu from '../AppMenu.js';
import drop from '../../directives/drop.js';
import AppConfig from '../AppConfig.js';
import BsModal from "../Bootstrap/Modal.js";
import BsModal from '../Bootstrap/Modal.js';
import BaseTreemenu from '../Base/Treemenu.js';
import ApiStudiengangTree from "../../api/factory/tempus/studiengangtree.js";
import ApiInfo from "../../api/factory/tempus/info.js";
import StvStudiensemester from "../Stv/Studentenverwaltung/Studiensemester.js";
import FormInput from "../../../js/components/Form/Input.js";
import Reservierung from "./Reservierung.js";
import { getTempusShortcuts } from "./shortcuts.js";
import KeyboardShortcuts from "./KeyboardShortcuts.js";
import { useContextMenuActions } from "../../composables/Tempus/ContextMenuActions.js";
import MultiWeekPlanModal from "./MultiWeekPlanModal.js";
import { getTempusSearchbarOptions } from "./Filters/searchbarOptions.js";
import RaumauswahlModal from "./RaumauswahlModal.js";
import LehreinheitModal from "./LehreinheitModal.js";
import ApiStudiengangTree from '../../api/factory/tempus/studiengangtree.js';
import ApiInfo from '../../api/factory/tempus/info.js';
import StvStudiensemester from '../Stv/Studentenverwaltung/Studiensemester.js';
import FormInput from '../../../js/components/Form/Input.js';
import Reservierung from './Reservierung.js';
import { getTempusShortcuts } from './shortcuts.js';
import KeyboardShortcuts from './KeyboardShortcuts.js';
import { useContextMenuActions } from '../../composables/Tempus/ContextMenuActions.js';
import MultiWeekPlanModal from './MultiWeekPlanModal.js';
import { getTempusSearchbarOptions } from './Filters/searchbarOptions.js';
import RaumauswahlModal from './RaumauswahlModal.js';
import LehreinheitModal from './LehreinheitModal.js';
export default {
name: "Tempus",
name: 'Tempus',
components: {
CoreSearchbar,
VerticalSplit,
@@ -72,7 +72,7 @@ export default {
KeyboardShortcuts,
MultiWeekPlanModal,
RaumauswahlModal,
LehreinheitModal
LehreinheitModal,
},
props: {
defaultSemester: String,
@@ -98,7 +98,8 @@ export default {
contextMenuActions: useContextMenuActions({
openRaumauswahl: (orig) => this.$refs.raumModal.show(orig),
openLehreinheit: (orig) => this.$refs.lehreinheitModal.show(orig),
openResourcesAssignmentModal: (orig) => this.openResourcesAssignmentModal(orig),
openResourcesAssignmentModal: (orig) =>
this.openResourcesAssignmentModal(orig),
openHistory: (orig) => this.openHistory(orig),
deleteEntry: (orig) => this.deleteEntry(orig),
syncToLecturer: (orig) => this.syncToLecturer(orig),
@@ -113,7 +114,7 @@ export default {
data() {
return {
appconfig: {},
currentMode: "week",
currentMode: 'week',
configEndpoints: ApiTempusConfig,
endpoint: ApiStudiengangTree,
raumVorschlaege: [],
@@ -133,7 +134,7 @@ export default {
},
renderers: null,
ort_kurzbz: null,
view: "room",
view: 'room',
parkedKeys: new Set(),
lecturers: [],
studiengaenge: [],
@@ -146,14 +147,14 @@ export default {
studiensemester_kurzbz: null,
visibleStatusArray: {},
visibleStatus: ["all"],
visibleStatus: ['all'],
selectedStudiensemester:
this.studiensemester_kurzbz ?? this.defaultSemester,
calendarDate: luxon.DateTime.now()
.setZone(this.config.timezone)
.toISODate(),
historyEntries: [],
previewRole: "planer",
previewRole: 'planer',
multiWeekModal: {
show: false,
lehreinheitId: null,
@@ -170,6 +171,7 @@ export default {
assignedResources: [],
areFormButtonsDisplayed: false,
},
currentlyUpdatedEvent: null,
};
},
computed: {
@@ -189,9 +191,9 @@ export default {
}));
},
visibleStatusValue() {
if (this.visibleStatus.includes("all"))
if (this.visibleStatus.includes('all'))
return this.visibleStatusOptions.filter(
(visibleStatus) => visibleStatus.key === "all",
(visibleStatus) => visibleStatus.key === 'all',
);
return this.visibleStatus.map((status) => ({
key: status,
@@ -287,7 +289,6 @@ export default {
},
onSelectVerband({ path, stg_kz, semester, orgform_kurzbz, name }) {
let exists = this.studiengaenge.some(
(stg) =>
stg.stg_kz == stg_kz &&
@@ -321,12 +322,12 @@ export default {
if (this.lastRange) this.handleRange(this.lastRange);
},
addToFilter: function (filter, type) {
if (type === "ort") {
if (type === 'ort') {
const ort_kurzbz = filter.ort_kurzbz;
if (!this.rooms.some((room) => room.ort_kurzbz === ort_kurzbz)) {
this.rooms.push({ ort_kurzbz });
}
} else if (type === "mitarbeiter") {
} else if (type === 'mitarbeiter') {
const uid = filter.uid;
const label = filter.name;
if (!this.lecturers.some((l) => l.uid === uid)) {
@@ -371,83 +372,148 @@ export default {
.call(ApiTempusConfig.updateCollision())
.then(() => {
this.$refs.config.reload();
this.$fhcAlert.alertSuccess(this.$p.t("ui/settings_saved"));
this.$fhcAlert.alertSuccess(this.$p.t('ui/settings_saved'));
})
.catch(this.$fhcAlert.handleSystemErrors);
},
toggleStatus(selected) {
if (!selected || selected.length === 0) {
this.visibleStatus = ["all"];
this.visibleStatus = ['all'];
return;
}
const hasAll = selected.includes("all");
const hadAll = this.visibleStatus.includes("all");
const hasAll = selected.includes('all');
const hadAll = this.visibleStatus.includes('all');
if (hasAll && !hadAll) {
this.visibleStatus = ["all"];
this.visibleStatus = ['all'];
return;
}
this.visibleStatus = selected.filter((k) => k !== "all");
if (this.visibleStatus.length === 0) this.visibleStatus = ["all"];
this.visibleStatus = selected.filter((k) => k !== 'all');
if (this.visibleStatus.length === 0) this.visibleStatus = ['all'];
},
searchfunction(params) {
return this.$api.call(ApiSearchbar.search(params));
},
getPromiseFunc(start, end) {
const hasRooms = this.rooms.length > 0;
const hasRoom = !!this.ort_kurzbz;
const hasLektoren = this.lecturers.length > 0;
const hasStg = this.studiengaenge.length > 0;
const hasStg = !!this.stg;
const filter = {};
if (hasRooms) filter.ort = this.rooms.map((room) => room.ort_kurzbz);
if (hasStg) {
filter.stg = this.studiengaenge.map(
({ stg_kz, semester, orgform_kurzbz }) => ({
stg_kz,
semester,
orgform_kurzbz,
}),
);
}
if (hasLektoren)
filter.uid = this.lecturers.map((lecture) => lecture.uid);
if (hasRoom) filter.ort = this.ort_kurzbz;
if (hasStg) filter.stg = this.stg;
if (hasLektoren) filter.uid = this.lecturers.map((l) => l.uid);
if (this.previewRole === "lektor")
return [
let response = null;
if (this.previewRole === 'lektor')
response = [
this.$api.call(
ApiKalender.getPlanLecturer(start.toISODate(), end.toISODate()),
),
];
if (this.previewRole === "student")
return [
if (this.previewRole === 'student')
response = [
this.$api.call(
ApiKalender.getPlanStudent(start.toISODate(), end.toISODate()),
),
];
return [
response = [
this.$api.call(
ApiKalender.getPlan(filter, start.toISODate(), end.toISODate()),
),
];
if (response) {
response[0].then((result) => {
if (!this.currentlyUpdatedEvent) return;
document.querySelectorAll('.updated-event').forEach((el) => {
el.classList.remove('updated-event');
});
document.querySelectorAll('.updated-event-long').forEach((el) => {
el.classList.remove('updated-event-long');
});
document.querySelectorAll('.deemphasized-event').forEach((el) => {
el.classList.remove('deemphasized-event');
});
document
.querySelectorAll('.deemphasized-event-long')
.forEach((el) => {
el.classList.remove('deemphasized-event-long');
});
setTimeout(() => {
const eventEl = document.querySelector(
`[data-group-id="event-group-${this.currentlyUpdatedEvent.eindeutige_gruppen_id}"]`,
);
if (!eventEl) return;
const calendar = document.querySelector('.fhc-calendar-base-grid');
const eventRect = eventEl.getBoundingClientRect();
const offset = 300;
const isInsideScrolledView =
eventEl.offsetLeft < calendar.scrollLeft + calendar.clientWidth &&
eventEl.offsetLeft + eventEl.offsetWidth > calendar.scrollLeft &&
eventEl.offsetTop < calendar.scrollTop + calendar.clientHeight &&
eventEl.offsetTop + eventEl.offsetHeight > calendar.scrollTop;
const rect = eventEl.getBoundingClientRect();
if (!isInsideScrolledView) {
eventEl.scrollIntoView({
behavior: 'smooth',
inline: 'center',
block: 'nearest',
});
}
let timeout = 0;
let emphasizeUpdateClassName = isInsideScrolledView
? 'updated-event'
: 'updated-event-long';
let deemphasizedUpdateClassName = isInsideScrolledView
? 'deemphasized-event'
: 'deemphasized-event-long';
if (!isInsideScrolledView) timeout = 500;
setTimeout(() => {
eventEl.classList.add(emphasizeUpdateClassName);
document
.querySelectorAll('.fhc-calendar-base-grid-line-event')
.forEach((el) => {
if (el !== eventEl) {
el.classList.add(deemphasizedUpdateClassName);
}
});
}, timeout);
this.currentlyUpdatedEvent = null;
}, 100);
});
}
return response;
},
toDateTime(value, timezone) {
if (luxon.DateTime.isDateTime(value)) return value;
if (value?.date?.isValid) return value.date;
if (typeof value === "number")
if (typeof value === 'number')
return luxon.DateTime.fromMillis(value, { zone: timezone });
if (value instanceof Date)
return luxon.DateTime.fromJSDate(value, { zone: timezone });
if (typeof value === "string")
if (typeof value === 'string')
return luxon.DateTime.fromISO(value, { zone: timezone });
return luxon.DateTime.invalid("invalid datetime");
return luxon.DateTime.invalid('invalid datetime');
},
getLastEndOfSameDay(startDT, ends) {
if (!ends?.length) return null;
@@ -483,15 +549,15 @@ export default {
const endDT = luxon.DateTime.fromISO(end);
if (!startDT.isValid || !endDT.isValid) {
alert("Ungültiges Datum");
alert('Ungültiges Datum');
return null;
}
return {
startDT,
endDT,
start_time: startDT.toFormat("yyyy-MM-dd HH:mm"),
end_time: endDT.toFormat("yyyy-MM-dd HH:mm"),
start_time: startDT.toFormat('yyyy-MM-dd HH:mm'),
end_time: endDT.toFormat('yyyy-MM-dd HH:mm'),
};
},
@@ -508,7 +574,7 @@ export default {
const updatedInfos = {
ort_kurzbz: this.rooms.length
? this.rooms.map((room) => room.ort_kurzbz)
: obj.orig.ort_kurzbz ?? [],
: (obj.orig.ort_kurzbz ?? []),
start_time,
end_time,
};
@@ -518,18 +584,22 @@ export default {
ApiKalender.updateKalenderEvent(obj.orig.kalender_id, updatedInfos),
)
.then(() => {
if (onSuccess) onSuccess();
if (onSuccess) {
onSuccess();
this.$refs.calendar.$refs.calendar.$refs.mode.$refs.view.$refs.grid.disableAutoScroll();
this.currentlyUpdatedEvent = obj.orig;
}
});
},
resizeHandler(payload) {
if (this.previewRole !== "planer")
if (this.previewRole !== 'planer')
//TODO (david) testzweck
return;
const { item, start, end } = payload;
const obj = item[0];
if (!obj?.orig?.kalender_id)
return alert("Kein gültiges Kalender-Event zum Resizen");
return alert('Kein gültiges Kalender-Event zum Resizen');
const dates = this._parseDates(start, end);
@@ -549,19 +619,19 @@ export default {
},
dropHandler(payload) {
if (this.previewRole !== "planer")
if (this.previewRole !== 'planer')
//TODO (david) testzweck
return;
const { item, start, end } = payload;
if (!item?.length) return alert("Keine Daten gedroppt");
if (!item?.length) return alert('Keine Daten gedroppt');
const obj = item[0];
if (!obj?.type) return alert("Unbekannter Drop-Typ");
if (!obj?.type) return alert('Unbekannter Drop-Typ');
if (payload.targetKalenderId) {
if (obj.type !== "lehreinheit" || !obj.orig?.lehreinheit_id)
if (obj.type !== 'lehreinheit' || !obj.orig?.lehreinheit_id)
return alert(
"Nur Lehreinheiten können einem Termin hinzugefügt werden",
'Nur Lehreinheiten können einem Termin hinzugefügt werden',
);
return this.$api
@@ -573,7 +643,7 @@ export default {
)
.then(() => {
this.$refs.calendar.resetEventLoader();
this.bcc.postMessage("dropped");
this.bcc.postMessage('dropped');
});
}
@@ -582,10 +652,10 @@ export default {
const { startDT, endDT, start_time, end_time } = dates;
if (obj.type === "reservierung") {
if (obj.type === 'reservierung') {
this.reservierungPending = true;
this.$refs.reservierung.show(start_time, end_time);
} else if (obj.type === "lehreinheit") {
} else if (obj.type === 'lehreinheit') {
if (obj.multiweek) {
this.openMultiWeekPreview(
obj.orig.lehreinheit_id,
@@ -602,7 +672,7 @@ export default {
obj.orig.lehreinheit_id,
this.rooms.length
? this.rooms.map((r) => r.ort_kurzbz)
: obj.orig.ort_kurzbz ?? [],
: (obj.orig.ort_kurzbz ?? []),
start_time,
end_time,
),
@@ -611,9 +681,9 @@ export default {
.then((result) => {
this.$refs.calendar.resetEventLoader();
this.$refs.coursepicker.reload();
this.bcc.postMessage("dropped");
this.bcc.postMessage('dropped');
});
} else if (obj.type === "kalender") {
} else if (obj.type === 'kalender') {
return this._updateKalenderEvent(
obj,
startDT,
@@ -626,11 +696,11 @@ export default {
id: obj.orig.kalender_id,
});
this.$refs.calendar.resetEventLoader();
this.bcc.postMessage("dropped");
this.bcc.postMessage('dropped');
},
);
} else {
alert("Unbekannter Drop-Typ: " + obj.type);
alert('Unbekannter Drop-Typ: ' + obj.type);
}
},
openMultiWeekPreview(lehreinheit_id, ort_kurzbz, start_time, end_time) {
@@ -646,17 +716,17 @@ export default {
onMultiWeekConfirmed() {
this.$refs.calendar.resetEventLoader();
this.$refs.coursepicker.reload();
this.bcc.postMessage("dropped");
this.bcc.postMessage('dropped');
},
handleRange(range) {
if (!range?.start || !range?.end) return;
if (this.currentMode === "week") {
if (this.currentMode === 'week') {
//Workaround because, updateRange is emitting 2 times
const startDay = range.start.startOf("day");
const endDay = range.end.startOf("day");
const startDay = range.start.startOf('day');
const endDay = range.end.startOf('day');
const days = Math.round(endDay.diff(startDay, "days").days) + 1;
const days = Math.round(endDay.diff(startDay, 'days').days) + 1;
if (days > 8) return;
}
@@ -764,20 +834,20 @@ export default {
calenderId,
),
);
if (getAssignedResources.meta.status === "success") {
if (getAssignedResources.meta.status === 'success') {
return getAssignedResources.data
.filter((unit) => !!unit)
.map((unit) => {
return {
isNoteTextareaShown:
unit.anmerkung && unit.anmerkung.trim() !== "",
unit.anmerkung && unit.anmerkung.trim() !== '',
...unit,
};
})
.filter((unit) => !!unit);
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "failed_assigned_resources_fetch_error_message"),
this.$p.t('ui', 'failed_assigned_resources_fetch_error_message'),
);
}
@@ -789,11 +859,11 @@ export default {
calendarID,
),
);
if (getSchedulableResourcesByCalendar.meta.status === "success") {
if (getSchedulableResourcesByCalendar.meta.status === 'success') {
return getSchedulableResourcesByCalendar.data;
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "failed_schedulable_resources_fetch_error_message"),
this.$p.t('ui', 'failed_schedulable_resources_fetch_error_message'),
);
}
@@ -830,7 +900,7 @@ export default {
if (index !== -1) {
this.resourcesAssignmentModal.assignedResources[
index
].isNoteTextareaShown =
].isNoteTextareaShown =
!this.resourcesAssignmentModal.assignedResources[index]
.isNoteTextareaShown;
}
@@ -867,14 +937,14 @@ export default {
assignedResources,
),
);
if (getSchedulableResourcesByCalendar.meta.status === "success") {
if (getSchedulableResourcesByCalendar.meta.status === 'success') {
this.$fhcAlert.alertSuccess(
this.$p.t("ui", "assigned_resources_save_success_message"),
this.$p.t('ui', 'assigned_resources_save_success_message'),
);
await this.refreshResourcesAssignmentModalData(calenderItem);
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "failed_assigned_resources_save_error_message"),
this.$p.t('ui', 'failed_assigned_resources_save_error_message'),
);
}
@@ -925,7 +995,7 @@ export default {
this.$refs.parking.unpark({ type: event.type, id: event.kalender_id });
} else {
this.$refs.parking.park(null, {
type: "kalender",
type: 'kalender',
id: event.kalender_id,
orig: event,
});
@@ -960,14 +1030,14 @@ export default {
this.$refs.calendar.resetEventLoader();
},
},
"resourcesAssignmentModal.selectedAvailableResource": function (newVal) {
'resourcesAssignmentModal.selectedAvailableResource': function (newVal) {
if (!newVal) return;
this.resourcesAssignmentModal.assignedResources.push({
betriebsmittel_kalender_id: null,
betriebsmittel_id: newVal.data.betriebsmittel_id,
beschreibung: newVal.data.beschreibung,
anmerkung: "",
anmerkung: '',
isNoteTextareaShown: false,
});
@@ -976,9 +1046,9 @@ export default {
},
mounted() {
this.reservierungPending = false;
this.bcc = new BroadcastChannel("fhc-dnd");
this.bcc.addEventListener("message", (e) => {
if (e.data === "dropped" && !this.reservierungPending)
this.bcc = new BroadcastChannel('fhc-dnd');
this.bcc.addEventListener('message', (e) => {
if (e.data === 'dropped' && !this.reservierungPending)
this.$refs.calendar.resetEventLoader();
});
},
@@ -1020,9 +1090,9 @@ export default {
`link[href="${data[rendertype].calendarEventStyles}"]`,
)
) {
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
var link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = data[rendertype].calendarEventStyles;
head.appendChild(link);
}
@@ -1042,7 +1112,7 @@ export default {
this.$api.call(ApiTempusConfig.getHeader()).then((res) => {
this.visibleStatusArray = res.data.visible_status;
this.visibleStatus = ["all"];
this.visibleStatus = ['all'];
});
this.$api.call(ApiInfo.getStudiengaenge()).then((res) => {
this.studiengaenge_all = res.data;
+29 -10
View File
@@ -1,7 +1,12 @@
// TODO(chris): load events that are longer than the interval without doubling it
export function useEventLoader(rangeInterval, getPromiseFunc) {
export function useEventLoader(rangeInterval, getPromiseFunc, isLoaderVisible = true, isLoaderInitiallyVisible = true) {
let hasFirstLoadOccurred = false;
let loading_id = 0;
let tempEventsHolder = [];
let rangeIntervalHolder = null;
const events = Vue.ref([]);
const loadingEvents = Vue.ref([]);
const allEvents = Vue.computed(() => events.value.concat(loadingEvents.value));
@@ -100,13 +105,15 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
if (start.ts >= end.ts)
return result;
loadingEvents.value.push({
loading_id: loading_id++,
type: "loading",
isostart: start.toISODate() + 'T' + start.toISOTime(),
isoend: end.toISODate() + 'T' + end.toISOTime()
});
if (isLoaderVisible && (!hasFirstLoadOccurred && isLoaderInitiallyVisible)) {
loadingEvents.value.push({
loading_id: loading_id++,
type: "loading",
isostart: start.toISODate() + 'T' + start.toISOTime(),
isoend: end.toISODate() + 'T' + end.toISOTime()
});
}
return mergePromiseArr(getPromiseFunc(start, end), result);
};
@@ -115,7 +122,14 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
const range = Vue.toValue(rangeInterval);
if (!(range instanceof luxon.Interval))
return;
if (!rangeIntervalHolder || !range.equals(rangeIntervalHolder)) {
hasFirstLoadOccurred = false;
rangeIntervalHolder = range;
}
const promises = markEventsLoaded(range.start, range.end);
Promise
.allSettled(promises)
.then(results => {
@@ -127,18 +141,23 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
if (res.value.meta.lv)
lv.value = res.value.meta.lv;
events.value = events.value.concat(res.value.data);
tempEventsHolder = tempEventsHolder.concat(res.value.data);
loadingEvents.value = [];
}
})
events.value = tempEventsHolder;
});
hasFirstLoadOccurred = true;
};
Vue.watchEffect(reload);
const reset = () => {
loading_id = 0;
events.value = [];
tempEventsHolder = [];
loadingEvents.value = [];
eventsLoaded.splice(0, eventsLoaded.length);