Implements FilterTemplate system for event matching

Introduces flexible key-based filtering for calendar events across different view configurations

Adds new FilterTemplate class to:
- Define event matching rules based on view configuration
- Support multi-level grouping (team/resource/date)
- Handle dynamic key generation for columns and events

Enhances view configuration with explicit id properties and derived fields
This commit is contained in:
Janus C. H. Knudsen 2025-12-15 00:33:27 +01:00
parent c2f7564f8e
commit dd647acab8
8 changed files with 331 additions and 41 deletions

View file

@ -7,6 +7,7 @@ import { CoreEvents } from '../../constants/CoreEvents';
import { IDragColumnChangePayload, IDragMovePayload, IDragEndPayload, IDragLeaveHeaderPayload } from '../../types/DragTypes';
import { calculateColumnLayout } from './EventLayoutEngine';
import { IGridGroupLayout } from './EventLayoutTypes';
import { FilterTemplate } from '../../core/FilterTemplate';
/**
* EventRenderer - Renders calendar events to the DOM
@ -238,8 +239,9 @@ export class EventRenderer {
* Render events for visible dates into day columns
* @param container - Calendar container element
* @param filter - Filter with 'date' and optionally 'resource' arrays
* @param filterTemplate - Template for matching events to columns
*/
async render(container: HTMLElement, filter: Record<string, string[]>): Promise<void> {
async render(container: HTMLElement, filter: Record<string, string[]>, filterTemplate: FilterTemplate): Promise<void> {
// Store container reference for later re-renders
this.container = container;
@ -261,26 +263,12 @@ export class EventRenderer {
const columns = dayColumns.querySelectorAll('swp-day-column');
// Render events into each column based on data attributes
// Render events into each column based on FilterTemplate matching
columns.forEach(column => {
const date = (column as HTMLElement).dataset.date;
const columnResourceId = (column as HTMLElement).dataset.resourceId;
const columnEl = column as HTMLElement;
if (!date) return;
// Filter events for this column
const columnEvents = events.filter(event => {
// Must match date
if (this.dateService.getDateKey(event.start) !== date) return false;
// If column has resourceId, event must match
if (columnResourceId && event.resourceId !== columnResourceId) return false;
// If no resourceId on column but resources in filter, show all
// (this handles 'simple' view without resources)
return true;
});
// Use FilterTemplate for matching - only fields in template are checked
const columnEvents = events.filter(event => filterTemplate.matches(event, columnEl));
// Get or create events layer
let eventsLayer = column.querySelector('swp-events-layer');

View file

@ -4,6 +4,7 @@ import { CoreEvents } from '../../constants/CoreEvents';
import { HeaderDrawerManager } from '../../core/HeaderDrawerManager';
import { EventService } from '../../storage/events/EventService';
import { DateService } from '../../core/DateService';
import { FilterTemplate } from '../../core/FilterTemplate';
import {
IDragEnterHeaderPayload,
IDragMoveHeaderPayload,
@ -36,6 +37,7 @@ export class HeaderDrawerRenderer {
private container: HTMLElement | null = null;
private sourceElement: HTMLElement | null = null;
private wasExpandedBeforeDrag = false;
private filterTemplate: FilterTemplate | null = null;
constructor(
private eventBus: IEventBus,
@ -49,8 +51,12 @@ export class HeaderDrawerRenderer {
/**
* Render allDay events into the header drawer with row stacking
* @param filterTemplate - Template for matching events to columns
*/
async render(container: HTMLElement, filter: Record<string, string[]>): Promise<void> {
async render(container: HTMLElement, filter: Record<string, string[]>, filterTemplate: FilterTemplate): Promise<void> {
// Store filterTemplate for buildColumnKeyFromEvent
this.filterTemplate = filterTemplate;
const drawer = container.querySelector('swp-header-drawer');
if (!drawer) return;
@ -150,14 +156,24 @@ export class HeaderDrawerRenderer {
}
/**
* Build columnKey from event fields
* This is the only place we construct columnKey from event data
* Build columnKey from event using FilterTemplate
* Uses the same template that columns use for matching
*/
private buildColumnKeyFromEvent(event: ICalendarEvent, date?: Date): string {
const dateStr = this.dateService.getDateKey(date || event.start);
const segments: Record<string, string> = { date: dateStr };
if (event.resourceId) segments.resource = event.resourceId;
return this.dateService.buildColumnKey(segments);
if (!this.filterTemplate) {
// Fallback if no template - shouldn't happen in normal flow
const dateStr = this.dateService.getDateKey(date || event.start);
return dateStr;
}
// For multi-day events, we need to override the date in the event
if (date && date.getTime() !== event.start.getTime()) {
// Create temporary event with overridden start for key generation
const tempEvent = { ...event, start: date };
return this.filterTemplate.buildKeyFromEvent(tempEvent);
}
return this.filterTemplate.buildKeyFromEvent(event);
}
/**