feat(CalendarView): merge recurring training slots and enhance event filtering
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 42s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 42s
- Implemented a new method to merge recurring training slots with identical weekdays and time windows, improving calendar event management. - Updated event filtering logic to exclude cancelled training sessions, ensuring only relevant training events are displayed. - Enhanced the loading process to handle source errors more effectively, improving user experience in the CalendarView.
This commit is contained in:
@@ -269,9 +269,10 @@ export default {
|
||||
.filter(event => event.type === 'trainingCancellation')
|
||||
.flatMap(event => this.getDateKeysForRange(event.date, event.endDate || event.date))
|
||||
);
|
||||
this.events = loadedEvents.filter(event => (
|
||||
const afterCancellations = loadedEvents.filter(event => (
|
||||
!event.isRecurringTraining || !cancellationDates.has(this.toDateKey(event.date))
|
||||
));
|
||||
this.events = this.mergeRecurringTrainingSlots(afterCancellations);
|
||||
this.sourceErrors = sources
|
||||
.filter(result => result.status === 'rejected')
|
||||
.map(result => result.reason?.source)
|
||||
@@ -283,6 +284,48 @@ export default {
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
/**
|
||||
* Mehrere regelmäßige Trainingszeiten mit identischem Wochentag und gleichem Uhrzeit-Fenster
|
||||
* (z. B. parallel genutzte Gruppen / Dubletten) zu einem Kalendereintrag zusammenführen.
|
||||
*/
|
||||
mergeRecurringTrainingSlots(events) {
|
||||
const genericTitle = (t) => !t || /^training$/i.test(String(t).trim());
|
||||
const slotMap = new Map();
|
||||
const passthrough = [];
|
||||
for (const e of events) {
|
||||
if (e.type !== 'training' || !e.isRecurringTraining || !e.time || !e.date) {
|
||||
passthrough.push(e);
|
||||
continue;
|
||||
}
|
||||
const dk = this.toDateKey(e.date);
|
||||
const slotKey = `${dk}|${e.time}`;
|
||||
if (!slotMap.has(slotKey)) {
|
||||
slotMap.set(slotKey, []);
|
||||
}
|
||||
slotMap.get(slotKey).push(e);
|
||||
}
|
||||
const mergedSlots = [];
|
||||
for (const [slotKey, list] of slotMap) {
|
||||
if (list.length === 1) {
|
||||
mergedSlots.push(list[0]);
|
||||
continue;
|
||||
}
|
||||
const sorted = list.slice().sort((a, b) => a.startsAt - b.startsAt);
|
||||
const base = sorted[0];
|
||||
const rawTitles = [...new Set(sorted.map((x) => String(x.title || '').trim()).filter(Boolean))];
|
||||
const specific = rawTitles.filter((t) => !genericTitle(t));
|
||||
const titleJoined = specific.length ? specific.join(' · ') : (rawTitles.join(' · ') || base.title);
|
||||
const safeIdKey = slotKey.replace(/\|/g, '-');
|
||||
mergedSlots.push({
|
||||
...base,
|
||||
id: `training-merged-${safeIdKey}`,
|
||||
title: titleJoined,
|
||||
subtitle: 'Regelmäßige Trainingszeit',
|
||||
startsAt: Math.min(...sorted.map((x) => x.startsAt))
|
||||
});
|
||||
}
|
||||
return [...passthrough, ...mergedSlots];
|
||||
},
|
||||
async loadSource(source, loader) {
|
||||
try {
|
||||
const events = await loader();
|
||||
|
||||
Reference in New Issue
Block a user