181 lines
4.7 KiB
Markdown
181 lines
4.7 KiB
Markdown
|
|
# 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.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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:**
|
||
|
|
```typescript
|
||
|
|
{
|
||
|
|
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:**
|
||
|
|
```typescript
|
||
|
|
{
|
||
|
|
groupings: [
|
||
|
|
{ type: 'date', values: ['2025-12-09', '2025-12-10'], idProperty: 'date', derivedFrom: 'start' }
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## FilterTemplate Klasse
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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:**
|
||
|
|
```typescript
|
||
|
|
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:**
|
||
|
|
```typescript
|
||
|
|
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:**
|
||
|
|
```typescript
|
||
|
|
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)
|