feat(CalendarView): enhance training slot merging and event normalization
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 43s

- Added a new method to normalize training time keys, improving consistency in event handling.
- Updated the logic for merging recurring training slots to include only relevant events, enhancing calendar management.
- Improved subtitle handling for merged training slots to reflect recurring status and additional details, providing clearer event information.
This commit is contained in:
Torsten Schulz (local)
2026-05-13 00:11:03 +02:00
parent 61b1f27e5e
commit ea46a6d4f9
4 changed files with 976 additions and 19 deletions

View File

@@ -284,21 +284,33 @@ export default {
this.loading = false;
},
normalizeTrainingTimeKey(time) {
return String(time || '')
.trim()
.replace(/[\u2013\u2012\u2212]/g, '-')
.replace(/\s*-\s*/g, '-')
.replace(/\s+/g, '');
},
/**
* Mehrere regelmäßige Trainingszeiten mit identischem Wochentag und gleichem Uhrzeit-Fenster
* (z. B. parallel genutzte Gruppen / Dubletten) zu einem Kalendereintrag zusammenführen.
* Alle Trainings-Einträge (Tagebuch + regelmäßige Zeiten) mit gleichem Kalendertag und gleichem
* Zeitfenster-Text zu einem Eintrag zusammenführen (z. B. Dublette „Training“ + Gruppen).
*/
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) {
if (e.type !== 'training' || !e.date) {
passthrough.push(e);
continue;
}
const timeKey = this.normalizeTrainingTimeKey(e.time);
if (!timeKey) {
passthrough.push(e);
continue;
}
const dk = this.toDateKey(e.date);
const slotKey = `${dk}|${e.time}`;
const slotKey = `${dk}|${timeKey}`;
if (!slotMap.has(slotKey)) {
slotMap.set(slotKey, []);
}
@@ -316,12 +328,25 @@ export default {
const specific = rawTitles.filter((t) => !genericTitle(t));
const titleJoined = specific.length ? specific.join(' · ') : (rawTitles.join(' · ') || base.title);
const safeIdKey = slotKey.replace(/\|/g, '-');
const hasRecurring = sorted.some((x) => x.isRecurringTraining);
const otherSubtitles = [...new Set(
sorted.map((x) => String(x.subtitle || '').trim()).filter((s) => s && s !== 'Regelmäßige Trainingszeit')
)];
let subtitleJoined = '';
if (hasRecurring) {
subtitleJoined = otherSubtitles.length
? `Regelmäßige Trainingszeit · ${otherSubtitles.join(' · ')}`
: 'Regelmäßige Trainingszeit';
} else {
subtitleJoined = otherSubtitles.join(' · ') || String(base.subtitle || '').trim() || '';
}
mergedSlots.push({
...base,
id: `training-merged-${safeIdKey}`,
title: titleJoined,
subtitle: 'Regelmäßige Trainingszeit',
startsAt: Math.min(...sorted.map((x) => x.startsAt))
subtitle: subtitleJoined,
startsAt: Math.min(...sorted.map((x) => x.startsAt)),
isRecurringTraining: hasRecurring
});
}
return [...passthrough, ...mergedSlots];