diff --git a/application/libraries/KalenderLib.php b/application/libraries/KalenderLib.php
index 8d5aa1a9a..02f5cf4a9 100644
--- a/application/libraries/KalenderLib.php
+++ b/application/libraries/KalenderLib.php
@@ -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' => [],
diff --git a/public/css/Tempus.css b/public/css/Tempus.css
index 3e32789e1..72384c9c7 100644
--- a/public/css/Tempus.css
+++ b/public/css/Tempus.css
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/public/js/components/Calendar/Base/Grid/Line.js b/public/js/components/Calendar/Base/Grid/Line.js
index 4b0f6525e..7f70fd096 100644
--- a/public/js/components/Calendar/Base/Grid/Line.js
+++ b/public/js/components/Calendar/Base/Grid/Line.js
@@ -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,
});
diff --git a/public/js/components/Calendar/Base/Grid/Line/Event.js b/public/js/components/Calendar/Base/Grid/Line/Event.js
index 04d733583..4a2ff4f7c 100644
--- a/public/js/components/Calendar/Base/Grid/Line/Event.js
+++ b/public/js/components/Calendar/Base/Grid/Line/Event.js
@@ -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: `
- `
-}
\ No newline at end of file
+ `,
+};
diff --git a/public/js/components/Tempus/Tempus.js b/public/js/components/Tempus/Tempus.js
index cfbdf22f7..9495dfda0 100644
--- a/public/js/components/Tempus/Tempus.js
+++ b/public/js/components/Tempus/Tempus.js
@@ -14,43 +14,43 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-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;
diff --git a/public/js/composables/EventLoader.js b/public/js/composables/EventLoader.js
index ed1c01ee0..8de2218ce 100644
--- a/public/js/composables/EventLoader.js
+++ b/public/js/composables/EventLoader.js
@@ -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);