feat(web): Kalender-Tag nach Kalender gruppieren, Kreise je Kalender berechnen (calendar-month)
- groupDayBySource gruppiert Tagestermine nach sourceId (nicht Farbe), Reihenfolge = erstes Auftreten, Farbe = Farbe des ersten Termins der Gruppe - buildDayBadges baut daraus bis zu drei Plaketten-Kreise, ab dem vierten Kalender einen grauen Restkreis mit Summe - Tests 8/9 in calendar-month.test.ts (TDD: RED bestaetigt vor Implementierung) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,12 @@
|
||||
* ist nicht Teil dieses Auftrags. Jede Tagesgruppe ist zusaetzlich nach Start
|
||||
* aufsteigend sortiert, damit die Plakettenfarbe (Farbe des ERSTEN Termins)
|
||||
* und die Tooltip-Reihenfolge deterministisch uebereinstimmen (quick-260916-jvj).
|
||||
*
|
||||
* Liegen an einem Tag Termine aus mehreren Kalendern, gruppiert
|
||||
* `groupDayBySource` sie nach `sourceId` (nicht nach Farbe) in der Reihenfolge
|
||||
* ihres ersten Auftretens in der bereits start-sortierten Tagesgruppe;
|
||||
* `buildDayBadges` macht daraus bis zu drei Kreise, ab dem vierten Kalender
|
||||
* einen grauen Restkreis mit der Summe der uebrigen (quick-260916-k2z).
|
||||
*/
|
||||
|
||||
import type { CalendarEvent } from '@/lib/calendar-api';
|
||||
@@ -120,6 +126,71 @@ export function groupEventsByDate(events: CalendarEvent[]): Map<string, Calendar
|
||||
return groupedEvents;
|
||||
}
|
||||
|
||||
export interface DaySourceGroup {
|
||||
sourceId: string;
|
||||
color?: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gruppiert die Termine EINES Tages nach `sourceId` (nicht nach Farbe).
|
||||
* Reihenfolge = erstes Auftreten in `events` — die Funktion sortiert selbst
|
||||
* NICHT, sie uebernimmt die Reihenfolge von `groupEventsByDate` (dort bereits
|
||||
* nach Start sortiert), damit Kreis- und Tooltip-Reihenfolge uebereinstimmen.
|
||||
* Die Farbe einer Gruppe ist die `color` ihres ERSTEN Termins.
|
||||
*/
|
||||
export function groupDayBySource(events: CalendarEvent[]): DaySourceGroup[] {
|
||||
const groups = new Map<string, DaySourceGroup>();
|
||||
for (const event of events) {
|
||||
const existing = groups.get(event.sourceId);
|
||||
if (existing) {
|
||||
existing.count += 1;
|
||||
} else {
|
||||
groups.set(event.sourceId, { sourceId: event.sourceId, color: event.color, count: 1 });
|
||||
}
|
||||
}
|
||||
return Array.from(groups.values());
|
||||
}
|
||||
|
||||
export const CALENDAR_DAY_BADGE_MAX = 3;
|
||||
|
||||
export interface DayBadge {
|
||||
key: string;
|
||||
color?: string;
|
||||
count: number;
|
||||
rest: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Baut die Plaketten-Kreise fuer einen Tag: bis `max` Kalender bekommen je
|
||||
* einen Kreis, ab `max` Kalendern fassen die ersten `max - 1` Kreise je einen
|
||||
* Kalender, der letzte Eintrag (`rest: true`) summiert die uebrigen Kalender
|
||||
* grau (kein Farbwert).
|
||||
*/
|
||||
export function buildDayBadges(
|
||||
events: CalendarEvent[],
|
||||
max: number = CALENDAR_DAY_BADGE_MAX,
|
||||
): DayBadge[] {
|
||||
const groups = groupDayBySource(events);
|
||||
if (groups.length <= max) {
|
||||
return groups.map((group) => ({
|
||||
key: group.sourceId,
|
||||
color: group.color,
|
||||
count: group.count,
|
||||
rest: false,
|
||||
}));
|
||||
}
|
||||
|
||||
const visible = groups.slice(0, max - 1).map((group) => ({
|
||||
key: group.sourceId,
|
||||
color: group.color,
|
||||
count: group.count,
|
||||
rest: false,
|
||||
}));
|
||||
const restCount = groups.slice(max - 1).reduce((sum, group) => sum + group.count, 0);
|
||||
return [...visible, { key: '__rest__', color: undefined, count: restCount, rest: true }];
|
||||
}
|
||||
|
||||
/** 42 Rasterzellen ab `gridStartFor(monthDate)`, Montag-basiert. */
|
||||
export function buildCalendarDays(
|
||||
monthDate: Date,
|
||||
|
||||
Reference in New Issue
Block a user