# FilterTemplate & Grouping System Specification

> **Version:** 1.0
> **Dato:** 2025-12-15
> **Status:** Godkendt

## Formål

Dette dokument specificerer hvordan kalenderen matcher events til kolonner i alle view-typer (Simple, Dag, Resource, Team, Department). Formålet er at sikre konsistent opførsel og undgå fremtidige hacks.

---

## Kerneprincipper

### 1. Én sandhedskilde for key-format

**FilterTemplate** er den ENESTE kilde til key-format for event-kolonne matching.

```
KORREKT:  filterTemplate.buildKeyFromColumn(column)
KORREKT:  filterTemplate.buildKeyFromEvent(event)
FORKERT:  column.dataset.columnKey (bruger DateService-format)
FORKERT:  Manuel key-konstruktion med string concatenation
```

### 2. Fields-rækkefølge bestemmer key-format

Keys bygges fra `fields` array i samme rækkefølge som defineret i ViewConfig groupings.

```typescript
// ViewConfig groupings: [resource, date]
// Resultat: "EMP001:2025-12-09"

// ViewConfig groupings: [date, resource]
// Resultat: "2025-12-09:EMP001"
```

### 3. Kolonner og events bruger SAMME template

```typescript
// Opret template fra ViewConfig
const filterTemplate = new FilterTemplate(dateService);
for (const grouping of viewConfig.groupings) {
  if (grouping.idProperty) {
    filterTemplate.addField(grouping.idProperty, grouping.derivedFrom);
  }
}

// Brug til BÅDE kolonner og events
const columnKey = filterTemplate.buildKeyFromColumn(column);
const eventKey = filterTemplate.buildKeyFromEvent(event);
const matches = columnKey === eventKey;
```

---

## API Kontrakt

### FilterTemplate

```typescript
class FilterTemplate {
  constructor(dateService: DateService, entityResolver?: IEntityResolver)

  // Tilføj felt til template
  addField(idProperty: string, derivedFrom?: string): this

  // Byg key fra kolonne (læser fra dataset)
  buildKeyFromColumn(column: HTMLElement): string

  // Byg key fra event (læser fra event properties)
  buildKeyFromEvent(event: ICalendarEvent): string

  // Convenience: matcher event mod kolonne
  matches(event: ICalendarEvent, column: HTMLElement): boolean
}
```

### Field Definition

| Parameter | Type | Beskrivelse |
|-----------|------|-------------|
| `idProperty` | `string` | Property-navn på event ELLER dot-notation |
| `derivedFrom` | `string?` | Kilde-property hvis værdi skal udledes |

### Dot-Notation

For hierarkiske relationer bruges dot-notation:

```typescript
idProperty: 'resource.teamId'
// Betyder: event.resourceId → opslag i resource → teamId
```

**Convention:** `{entityType}.{property}` → foreignKey er `{entityType}Id`

---

## Kolonne Dataset Krav

Kolonner (`swp-day-column`) SKAL have dataset-attributter for alle felter i template:

```html
<!-- Simple view (kun date) -->
<swp-day-column data-date="2025-12-09"></swp-day-column>

<!-- Resource view (resource + date) -->
<swp-day-column
  data-date="2025-12-09"
  data-resource-id="EMP001">
</swp-day-column>

<!-- Team view (team + resource + date) -->
<swp-day-column
  data-date="2025-12-09"
  data-resource-id="EMP001"
  data-team-id="team-1">
</swp-day-column>
```

### Dataset Key Mapping

| idProperty | Dataset Key | Eksempel |
|------------|-------------|----------|
| `date` | `data-date` | `"2025-12-09"` |
| `resourceId` | `data-resource-id` | `"EMP001"` |
| `resource.teamId` | `data-team-id` | `"team-1"` |

**Regel:** Dot-notation bruger sidste segment som dataset-key.

---

## Event Property Krav

Events SKAL have properties der matcher template fields:

```typescript
interface ICalendarEvent {
  id: string;
  start: Date;          // derivedFrom: 'start' → date key
  end: Date;
  resourceId?: string;  // Direkte match
  // ... andre properties
}
```

### Derived Values

| idProperty | derivedFrom | Transformation |
|------------|-------------|----------------|
| `date` | `start` | `Date → "YYYY-MM-DD"` |

---

## ViewConfig Groupings

### Struktur

```typescript
interface GroupingConfig {
  type: string;           // 'date', 'resource', 'team', 'department'
  values: string[];       // Synlige værdier
  idProperty?: string;    // Felt til key-matching
  derivedFrom?: string;   // Kilde hvis udledt
  belongsTo?: string;     // Parent-child relation
}
```

### Eksempler

```typescript
// Simple view
groupings: [
  { type: 'date', values: ['2025-12-09', ...], idProperty: 'date', derivedFrom: 'start' }
]

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

// Team view
groupings: [
  { type: 'team', values: ['team-1', 'team-2'], idProperty: 'resource.teamId' },
  { type: 'resource', values: ['EMP001', ...], idProperty: 'resourceId', belongsTo: 'team.resourceIds' },
  { type: 'date', values: ['2025-12-09', ...], idProperty: 'date', derivedFrom: 'start' }
]
```

---

## BelongsTo Resolution

### Formål

`belongsTo` definerer parent-child relationer for nested groupings.

### Syntax

```
belongsTo: '{parentEntityType}.{childArrayProperty}'
```

### Eksempel

```typescript
// Team har resourceIds array
{ type: 'resource', belongsTo: 'team.resourceIds' }

// Resolver:
// 1. Hent team entities fra filter['team']
// 2. For hver team, læs team.resourceIds
// 3. Byg map: { 'team-1': ['EMP001', 'EMP002'], 'team-2': ['EMP003'] }
```

### Implementering

```typescript
// CalendarOrchestrator.resolveBelongsTo()
const [entityType, property] = belongsTo.split('.');
const service = entityServices.find(s => s.entityType === entityType);
const entities = await service.getAll();
// Byg parent-child map
```

---

## HeaderDrawerRenderer Regler

### Key Matching for AllDay Events

```typescript
// KORREKT: Brug FilterTemplate
private getVisibleColumnKeysFromDOM(): string[] {
  const columns = document.querySelectorAll('swp-day-column');
  return Array.from(columns).map(col =>
    this.filterTemplate.buildKeyFromColumn(col as HTMLElement)
  );
}

// FORKERT: Læs dataset.columnKey direkte
// (bruger DateService-format som ikke matcher FilterTemplate)
```

### Layout Beregning

1. Hent synlige columnKeys via `getVisibleColumnKeysFromDOM()`
2. For hver event, byg key via `filterTemplate.buildKeyFromEvent(event)`
3. Find kolonne-index via `columnKeys.indexOf(eventKey)`
4. Beregn row via track-algoritme

---

## Anti-Patterns (UNDGÅ)

### 1. Manuel Key Konstruktion

```typescript
// FORKERT
const key = `${resourceId}:${dateStr}`;

// KORREKT
const key = filterTemplate.buildKeyFromEvent(event);
```

### 2. Direkte Dataset Læsning for Matching

```typescript
// FORKERT
const columnKey = column.dataset.columnKey;

// KORREKT
const columnKey = filterTemplate.buildKeyFromColumn(column);
```

### 3. Hardcoded Field Order

```typescript
// FORKERT
const key = [event.resourceId, dateStr].join(':');

// KORREKT
// Lad FilterTemplate håndtere rækkefølge fra ViewConfig
```

### 4. Separate Key-Formater

```typescript
// FORKERT: DateService til kolonner, FilterTemplate til events
DateService.buildColumnKey(segments)  // "2025-12-09:EMP001"
FilterTemplate.buildKeyFromEvent(e)   // "EMP001:2025-12-09"

// KORREKT: FilterTemplate til begge
FilterTemplate.buildKeyFromColumn(col)
FilterTemplate.buildKeyFromEvent(event)
```

---

## Testcases

### TC1: Simple View Matching

```
Given: ViewConfig med [date] grouping
When:  Event har start=2025-12-09
Then:  Event matcher kolonne med data-date="2025-12-09"
```

### TC2: Resource View Matching

```
Given: ViewConfig med [resource, date] groupings
When:  Event har resourceId=EMP001, start=2025-12-09
Then:  Event matcher kolonne med data-resource-id="EMP001" OG data-date="2025-12-09"
```

### TC3: Team View Matching

```
Given: ViewConfig med [team, resource, date] groupings
       Resource EMP001 tilhører team-1
When:  Event har resourceId=EMP001, start=2025-12-09
Then:  Event matcher kolonne med data-team-id="team-1" OG data-resource-id="EMP001" OG data-date="2025-12-09"
```

### TC4: Multi-Day Event

```
Given: Event spænder 2025-12-09 til 2025-12-11
When:  HeaderDrawerRenderer beregner layout
Then:  Event vises fra kolonne 09 til kolonne 11 (inclusive)
```

---

## Ændringslog

| Version | Dato | Ændring |
|---------|------|---------|
| 1.0 | 2025-12-15 | Initial specifikation |
