From 7a2085b961ff5ece2ab0e515ba1d59dfdde572d3 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Mon, 7 Sep 2026 13:01:36 +0200 Subject: [PATCH] feat(mini-cal): ein Punkt je Kalender in dessen Farbe pro Tag Statt eines einzelnen Punkts in --primary zeigt der Mini-Kalender jetzt fuer jeden Kalender, der an dem Tag mindestens einen Termin hat, einen Punkt in dessen Farbe (max. 4, stabil nach erstem Auftreten sortiert). Die Farbquelle ist dieselbe wie in Monats-/Wochenansicht: ev.color || ev.calendarColor. Ausserdem laeuft der Mini-Kalender jetzt ueber filterEvents(), damit ausgeblendete Gruppenmitglieder keinen Punkt mehr erzeugen. Auf dem "heute"-Kreis bekommen die Punkte einen duennen weissen Ring, damit sie auf der farbigen Flaeche lesbar bleiben. Co-Authored-By: Claude Opus 5 --- frontend/css/app.css | 14 ++++++++++---- frontend/js/calendar.js | 31 ++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/frontend/css/app.css b/frontend/css/app.css index c51fda2..8f16b98 100644 --- a/frontend/css/app.css +++ b/frontend/css/app.css @@ -551,11 +551,17 @@ a { color: var(--primary); text-decoration: none; } color: #fff; font-weight: 700; } .mini-day.selected:not(.today) { background: var(--primary-dim); color: var(--primary); font-weight: 600; } -.mini-day.has-events::after { - content: ''; position: absolute; bottom: 2px; left: 50%; transform: translateX(-50%); - width: 4px; height: 4px; border-radius: 50%; background: var(--primary); +/* One dot per calendar with an event that day, in that calendar's colour. */ +.mini-dots { + position: absolute; bottom: 1px; left: 0; right: 0; + display: flex; justify-content: center; align-items: center; gap: 2px; + line-height: 0; pointer-events: none; } -.mini-day.today.has-events::after { background: #fff; } +.mini-dots i { + width: 4px; height: 4px; border-radius: 50%; flex: none; +} +/* Today's cell is a filled circle — ring the dots so they stay readable on it. */ +.mini-day.today .mini-dots i { box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.9); } /* ── Calendar List ──────────────────────────────────────── */ .cal-list { padding: 8px 0; } diff --git a/frontend/js/calendar.js b/frontend/js/calendar.js index 711774b..e0eaa9d 100644 --- a/frontend/js/calendar.js +++ b/frontend/js/calendar.js @@ -559,6 +559,8 @@ function updateViewButtons() { } // ── Mini Calendar ───────────────────────────────────────── +const MINI_MAX_DOTS = 4; + function renderMiniCal() { const d = state.currentDate; const miniD = new Date(d.getFullYear(), d.getMonth(), 1); @@ -576,10 +578,14 @@ function renderMiniCal() { const DOW_LABELS = weekStartDay === 'sunday' ? DOW_SUNDAY : DOW_MONDAY; miniDowEls.forEach((el, i) => { el.textContent = DOW_LABELS[i]; }); - // Build event date set — mark every day an event spans, not just its start - // day, so multi-day events (Urlaub/Ferien) show a dot across the whole range. - const eventDates = new Set(); - state.events.forEach(ev => { + // Build per-day colour sets — one dot per calendar that has at least one event + // that day, in that calendar's colour. Every day an event spans is marked, not + // just its start day, so multi-day events (Urlaub/Ferien) mark the whole range. + const dayColors = new Map(); // 'y-m-d' → Set of colours + const colorRank = new Map(); // colour → first-seen index, for a stable dot order + filterEvents(state.events).forEach(ev => { + const color = ev.color || ev.calendarColor || '#4285f4'; + if (!colorRank.has(color)) colorRank.set(color, colorRank.size); const s = new Date(ev.start); let end = new Date(ev.end || ev.start); // All-day events store an exclusive end → step back to the last real day. @@ -588,7 +594,10 @@ function renderMiniCal() { const last = new Date(end.getFullYear(), end.getMonth(), end.getDate()); let guard = 0; while (cur <= last && guard++ < 400) { - eventDates.add(`${cur.getFullYear()}-${cur.getMonth()}-${cur.getDate()}`); + const key = `${cur.getFullYear()}-${cur.getMonth()}-${cur.getDate()}`; + let set = dayColors.get(key); + if (!set) { set = new Set(); dayColors.set(key, set); } + set.add(color); cur.setDate(cur.getDate() + 1); } }); @@ -604,7 +613,8 @@ function renderMiniCal() { const isOther = day.getMonth() !== miniD.getMonth(); const isToday_ = isToday(day); const isSelected = isSameDay(day, state.currentDate); - const hasEvs = eventDates.has(`${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`); + const colors = dayColors.get(`${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`); + const hasEvs = !!colors; const cls = [ 'mini-day', isOther ? 'other-month' : '', @@ -612,7 +622,14 @@ function renderMiniCal() { isSelected && !isToday_ ? 'selected' : '', hasEvs ? 'has-events' : '', ].filter(Boolean).join(' '); - return `
${day.getDate()}
`; + // At most MINI_MAX_DOTS dots — more than that no longer fits under the digit. + const dots = hasEvs + ? '' + [...colors] + .sort((a, b) => colorRank.get(a) - colorRank.get(b)) + .slice(0, MINI_MAX_DOTS) + .map(c => ``).join('') + '' + : ''; + return `
${day.getDate()}${dots}
`; }).join(''); document.getElementById('mini-days').innerHTML = html;