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:
parent
c2f7564f8e
commit
dd647acab8
8 changed files with 331 additions and 41 deletions
|
|
@ -4,13 +4,16 @@ import { EventRenderer } from '../features/event/EventRenderer';
|
|||
import { ScheduleRenderer } from '../features/schedule/ScheduleRenderer';
|
||||
import { HeaderDrawerRenderer } from '../features/headerdrawer/HeaderDrawerRenderer';
|
||||
import { ViewConfig } from './ViewConfig';
|
||||
import { FilterTemplate } from './FilterTemplate';
|
||||
import { DateService } from './DateService';
|
||||
|
||||
export class CalendarOrchestrator {
|
||||
constructor(
|
||||
private allRenderers: IRenderer[],
|
||||
private eventRenderer: EventRenderer,
|
||||
private scheduleRenderer: ScheduleRenderer,
|
||||
private headerDrawerRenderer: HeaderDrawerRenderer
|
||||
private headerDrawerRenderer: HeaderDrawerRenderer,
|
||||
private dateService: DateService
|
||||
) {}
|
||||
|
||||
async render(viewConfig: ViewConfig, container: HTMLElement): Promise<void> {
|
||||
|
|
@ -26,6 +29,12 @@ export class CalendarOrchestrator {
|
|||
filter[grouping.type] = grouping.values;
|
||||
}
|
||||
|
||||
// Byg FilterTemplate fra viewConfig groupings
|
||||
const filterTemplate = new FilterTemplate(this.dateService);
|
||||
for (const grouping of viewConfig.groupings) {
|
||||
filterTemplate.addField(grouping.idProperty, grouping.derivedFrom);
|
||||
}
|
||||
|
||||
const context: IRenderContext = { headerContainer, columnContainer, filter };
|
||||
|
||||
// Clear
|
||||
|
|
@ -50,11 +59,11 @@ export class CalendarOrchestrator {
|
|||
// Render schedule unavailable zones (før events)
|
||||
await this.scheduleRenderer.render(container, filter);
|
||||
|
||||
// Render timed events in grid
|
||||
await this.eventRenderer.render(container, filter);
|
||||
// Render timed events in grid (med filterTemplate til matching)
|
||||
await this.eventRenderer.render(container, filter, filterTemplate);
|
||||
|
||||
// Render allDay events in header drawer
|
||||
await this.headerDrawerRenderer.render(container, filter);
|
||||
// Render allDay events in header drawer (med filterTemplate til matching)
|
||||
await this.headerDrawerRenderer.render(container, filter, filterTemplate);
|
||||
}
|
||||
|
||||
private selectRenderers(viewConfig: ViewConfig): IRenderer[] {
|
||||
|
|
|
|||
|
|
@ -11,9 +11,26 @@ dayjs.extend(isoWeek);
|
|||
|
||||
export class DateService {
|
||||
private timezone: string;
|
||||
private baseDate: dayjs.Dayjs;
|
||||
|
||||
constructor(private config: ITimeFormatConfig) {
|
||||
constructor(private config: ITimeFormatConfig, baseDate?: Date) {
|
||||
this.timezone = config.timezone;
|
||||
// Allow setting a fixed base date for demo/testing purposes
|
||||
this.baseDate = baseDate ? dayjs(baseDate) : dayjs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a fixed base date (useful for demos with static mock data)
|
||||
*/
|
||||
setBaseDate(date: Date): void {
|
||||
this.baseDate = dayjs(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current base date (either fixed or today)
|
||||
*/
|
||||
getBaseDate(): Date {
|
||||
return this.baseDate.toDate();
|
||||
}
|
||||
|
||||
parseISO(isoString: string): Date {
|
||||
|
|
@ -25,7 +42,7 @@ export class DateService {
|
|||
}
|
||||
|
||||
getWeekDates(offset = 0, days = 7): string[] {
|
||||
const monday = dayjs().startOf('week').add(1, 'day').add(offset, 'week');
|
||||
const monday = this.baseDate.startOf('week').add(1, 'day').add(offset, 'week');
|
||||
return Array.from({ length: days }, (_, i) =>
|
||||
monday.add(i, 'day').format('YYYY-MM-DD')
|
||||
);
|
||||
|
|
|
|||
75
src/v2/core/FilterTemplate.ts
Normal file
75
src/v2/core/FilterTemplate.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
import { DateService } from './DateService';
|
||||
|
||||
/**
|
||||
* Field definition for FilterTemplate
|
||||
*/
|
||||
interface IFilterField {
|
||||
idProperty: string;
|
||||
derivedFrom?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* FilterTemplate - Bygger nøgler til event-kolonne matching
|
||||
*
|
||||
* 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å.
|
||||
*
|
||||
* @see docs/filter-template.md
|
||||
*/
|
||||
export class FilterTemplate {
|
||||
private fields: IFilterField[] = [];
|
||||
|
||||
constructor(private dateService: DateService) {}
|
||||
|
||||
/**
|
||||
* Tilføj felt til template
|
||||
* @param idProperty - Property-navn (bruges på både event og column.dataset)
|
||||
* @param derivedFrom - Hvis feltet udledes fra anden property (f.eks. date fra start)
|
||||
*/
|
||||
addField(idProperty: string, derivedFrom?: string): this {
|
||||
this.fields.push({ idProperty, derivedFrom });
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Byg nøgle fra kolonne
|
||||
* Læser værdier fra column.dataset[idProperty]
|
||||
*/
|
||||
buildKeyFromColumn(column: HTMLElement): string {
|
||||
return this.fields
|
||||
.map(f => column.dataset[f.idProperty] || '')
|
||||
.join(':');
|
||||
}
|
||||
|
||||
/**
|
||||
* Byg nøgle fra event
|
||||
* Læser værdier fra event[idProperty] eller udleder fra derivedFrom
|
||||
*/
|
||||
buildKeyFromEvent(event: ICalendarEvent): string {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const eventRecord = event as any;
|
||||
return this.fields
|
||||
.map(f => {
|
||||
if (f.derivedFrom) {
|
||||
// Udled værdi (f.eks. date fra start)
|
||||
const sourceValue = eventRecord[f.derivedFrom];
|
||||
if (sourceValue instanceof Date) {
|
||||
return this.dateService.getDateKey(sourceValue);
|
||||
}
|
||||
return String(sourceValue || '');
|
||||
}
|
||||
return String(eventRecord[f.idProperty] || '');
|
||||
})
|
||||
.join(':');
|
||||
}
|
||||
|
||||
/**
|
||||
* Match event mod kolonne
|
||||
*/
|
||||
matches(event: ICalendarEvent, column: HTMLElement): boolean {
|
||||
return this.buildKeyFromEvent(event) === this.buildKeyFromColumn(column);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,5 +12,7 @@ export interface ViewConfig {
|
|||
export interface GroupingConfig {
|
||||
type: string;
|
||||
values: string[];
|
||||
idProperty: string; // Property-navn på event (f.eks. 'resourceId', 'teamId')
|
||||
derivedFrom?: string; // Hvis feltet udledes fra anden property (f.eks. 'date' fra 'start')
|
||||
parentKey?: string;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue