Calendar/docs/filter-template.md
Janus C. H. Knudsen dd647acab8 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
2025-12-15 00:33:27 +01:00

4.7 KiB

FilterTemplate System

Problem

En kolonne har en unik nøgle baseret på view-konfigurationen (f.eks. team + resource + date). Events skal matches mod denne nøgle - men kun på de felter viewet definerer.

Løsning: FilterTemplate

ViewConfig definerer hvilke felter (idProperties) der indgår i kolonnens nøgle. Samme template bruges til at bygge nøgle for både kolonne og event.

Princip: Kolonnens nøgle-template bestemmer hvad der matches på.


ViewConfig med idProperty

ViewConfig er kilden til sandhed - den definerer grupper OG deres relations-id property.

interface GroupingConfig {
  type: string;                 // 'team', 'resource', 'date'
  values: string[];             // ['EMP001', 'EMP002']
  idProperty: string;           // property-navn på event (eks. 'resourceId')
  derivedFrom?: string;         // for date: udledes fra 'start'
}

Eksempler

Team → Resource → Date view:

{
  groupings: [
    { type: 'team', values: ['team-a'], idProperty: 'teamId' },
    { type: 'resource', values: ['EMP001', 'EMP002'], idProperty: 'resourceId' },
    { type: 'date', values: ['2025-12-09', '2025-12-10'], idProperty: 'date', derivedFrom: 'start' }
  ]
}

Simple date-only view:

{
  groupings: [
    { type: 'date', values: ['2025-12-09', '2025-12-10'], idProperty: 'date', derivedFrom: 'start' }
  ]
}

FilterTemplate Klasse

class FilterTemplate {
  private fields: Array<{
    idProperty: string;
    derivedFrom?: string;
  }> = [];

  addField(idProperty: string, derivedFrom?: string): this {
    this.fields.push({ idProperty, derivedFrom });
    return this;
  }

  buildKeyFromColumn(column: HTMLElement): string {
    return this.fields
      .map(f => column.dataset[f.idProperty] || '')
      .join(':');
  }

  buildKeyFromEvent(event: ICalendarEvent, dateService: DateService): string {
    return this.fields
      .map(f => {
        if (f.derivedFrom) {
          return dateService.getDateKey((event as any)[f.derivedFrom]);
        }
        return (event as any)[f.idProperty] || '';
      })
      .join(':');
  }
}

Flow

Orchestrator
    │
    ├── Læs ViewConfig.groupings
    │
    ├── Byg FilterTemplate fra groupings:
    │     for (grouping of viewConfig.groupings) {
    │       template.addField(grouping.idProperty, grouping.derivedFrom);
    │     }
    │
    ├── Kør group-renderers (bygger headers + kolonner)
    │     └── DateRenderer sætter column.dataset[idProperty] for ALLE grupperinger
    │
    └── EventRenderer.render(ctx, template)
            │
            └── for each column:
                    columnKey = template.buildKeyFromColumn(column)
                    columnEvents = events.filter(e =>
                        template.buildKeyFromEvent(e) === columnKey
                    )

Eksempler

3-niveau view: Team → Resource → Date

ViewConfig:

groupings: [
  { type: 'team', values: ['team-a'], idProperty: 'teamId' },
  { type: 'resource', values: ['EMP001'], idProperty: 'resourceId' },
  { type: 'date', values: ['2025-12-09'], idProperty: 'date', derivedFrom: 'start' }
]

Template: ['teamId', 'resourceId', 'date']

Kolonne-nøgle: "team-a:EMP001:2025-12-09" Event-nøgle: "team-a:EMP001:2025-12-09"

Match!


2-niveau view: Resource → Date

ViewConfig:

groupings: [
  { type: 'resource', values: ['EMP001'], idProperty: 'resourceId' },
  { type: 'date', values: ['2025-12-09'], idProperty: 'date', derivedFrom: 'start' }
]

Template: ['resourceId', 'date']

Kolonne-nøgle: "EMP001:2025-12-09" Event-nøgle: "EMP001:2025-12-09" (teamId ignoreres - ikke i template)

Match!


1-niveau view: Kun Date

ViewConfig:

groupings: [
  { type: 'date', values: ['2025-12-09'], idProperty: 'date', derivedFrom: 'start' }
]

Template: ['date']

Kolonne-nøgle: "2025-12-09" Event-nøgle: "2025-12-09" (alle andre felter ignoreres)

Match! Samme event vises i alle views - kun de relevante felter indgår i matching.


Kerneprincipper

  1. ViewConfig definerer nøgle-template - hvilke idProperties der indgår
  2. Samme template til kolonne og event - sikrer konsistent matching
  3. Felter udenfor template ignoreres - event med ekstra felter matcher stadig
  4. idProperty - eksplicit mapping mellem gruppering og event-felt
  5. derivedFrom - håndterer felter der udledes (f.eks. date fra start)