From e317b799d00b6c91a85e9f6d97565598d89678f8 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Wed, 8 Apr 2026 14:47:11 +0200 Subject: [PATCH] =?UTF-8?q?Feature:=20Mehrt=C3=A4gige=20Termine=20in=20Woc?= =?UTF-8?q?hen-/Tagesansicht=20vollst=C3=A4ndig=20anzeigen=20Timed-Events?= =?UTF-8?q?=20die=20mehrere=20Tage=20=C3=BCberspannen=20werden=20neu=20in?= =?UTF-8?q?=20der=20Ganztags-Zeile=20f=C3=BCr=20jeden=20betroffenen=20Tag?= =?UTF-8?q?=20als=20Bar=20angezeigt=20(am=20Starttag=20mit=20Uhrzeit).=20D?= =?UTF-8?q?ie=20Tagesspalten=20erhalten=20einen=2015%-Farbhintergrund=20(c?= =?UTF-8?q?ol-span-tint)=20um=20die=20Abdeckung=20zu=20visualisieren.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/css/app.css | 7 +++++++ frontend/js/views/week.js | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/frontend/css/app.css b/frontend/css/app.css index 939f50b..d1e7920 100644 --- a/frontend/css/app.css +++ b/frontend/css/app.css @@ -554,11 +554,18 @@ a { color: var(--primary); text-decoration: none; } .allday-gutter { width: 56px; flex-shrink: 0; display: flex; align-items: center; justify-content: flex-end; padding-right: 6px; font-size: 10px; color: var(--text-3); } .allday-cols { display: flex; flex: 1; } .allday-col { flex: 1; border-left: 1px solid var(--border); padding: 2px; } +.col-span-tint { + position: absolute; inset: 0; pointer-events: none; z-index: 0; +} .allday-event { font-size: 11px; font-weight: 500; padding: 2px 6px; border-radius: 3px; margin-bottom: 2px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.allday-event.multiday-timed { + opacity: .88; + border-left: 3px solid rgba(255,255,255,.45); +} /* Time grid */ .week-body { display: flex; flex: 1; overflow-y: auto; position: relative; } diff --git a/frontend/js/views/week.js b/frontend/js/views/week.js index eed4c3f..b62a68e 100644 --- a/frontend/js/views/week.js +++ b/frontend/js/views/week.js @@ -16,8 +16,19 @@ export function renderWeek(container, currentDate, events, onSlotClick, onEventC } // Separate all-day and timed events - const allDayEvs = events.filter(ev => ev.allDay); - const timedEvs = events.filter(ev => !ev.allDay); + const allDayEvs = events.filter(ev => ev.allDay); + const timedEvs = events.filter(ev => !ev.allDay); + // Multi-day timed events: timed but spanning more than one calendar day + const multiDayTimedEvs = timedEvs.filter(ev => !isSameDay(new Date(ev.start), new Date(ev.end))); + + // Returns true if event overlaps any part of the given day + function spansDay(ev, day) { + const evStart = new Date(ev.start); + const evEnd = new Date(ev.end); + const dayStart = new Date(day); dayStart.setHours(0, 0, 0, 0); + const dayEnd = new Date(day); dayEnd.setHours(24, 0, 0, 0); + return evStart < dayEnd && evEnd > dayStart; + } // ── KW Badge ────────────────────────────────────────── const kwNum = getISOWeekNumber(days[0]); @@ -43,12 +54,22 @@ export function renderWeek(container, currentDate, events, onSlotClick, onEventC const d = new Date(day); d.setHours(0,0,0,0); return d >= s && d < e || isSameDay(d, s); }); - const inner = dayEvs.map(ev => { + const allDayHtml = dayEvs.map(ev => { const color = ev.color || ev.calendarColor || '#4285f4'; return `
${escHtml(ev.title)}
`; }).join(''); - return `
${inner}
`; + + // Multi-day timed events: show in all-day row for every day they span + const spanHtml = multiDayTimedEvs.filter(ev => spansDay(ev, day)).map(ev => { + const color = ev.color || ev.calendarColor || '#4285f4'; + const isStart = isSameDay(new Date(ev.start), day); + const label = isStart ? `${fmtTime(new Date(ev.start))} ${ev.title}` : ev.title; + return `
${escHtml(label)}
`; + }).join(''); + + return `
${allDayHtml}${spanHtml}
`; }).join(''); // ── Time column labels ──────────────────────────────── @@ -92,7 +113,14 @@ export function renderWeek(container, currentDate, events, onSlotClick, onEventC `; }).join(''); + // Background tint for days covered by multi-day timed events + const tintHtml = multiDayTimedEvs.filter(ev => spansDay(ev, day)).map(ev => { + const color = ev.color || ev.calendarColor || '#4285f4'; + return `
`; + }).join(''); + return `
+ ${tintHtml} ${hourLines} ${evHtml}
`;