Refactors calendar managers and renderers
Improves calendar rendering performance by centralizing DOM manipulation in a dedicated `GridRenderer` class. This reduces redundant DOM queries and improves overall efficiency. Introduces `EventManager` for optimized event lifecycle management with caching and optimized data processing. The `ViewManager` is refactored for optimized view switching and event handling, further streamlining the application's architecture. This change moves from a strategy-based `GridManager` to a simpler approach leveraging the `GridRenderer` directly for DOM updates. This eliminates unnecessary abstractions and improves code maintainability. The changes include removing the old `GridManager`, `EventManager` and introducing new versions.
This commit is contained in:
parent
b8b44ddae8
commit
05bb074e9a
4 changed files with 566 additions and 271 deletions
|
|
@ -2,71 +2,65 @@ import { EventBus } from '../core/EventBus';
|
|||
import { IEventBus, CalendarEvent, ResourceCalendarData } from '../types/CalendarTypes';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { calendarConfig } from '../core/CalendarConfig';
|
||||
import { DateCalculator } from '../utils/DateCalculator';
|
||||
|
||||
/**
|
||||
* EventManager - Administrerer event lifecycle og CRUD operationer
|
||||
* Håndterer mock data og event synchronization
|
||||
* EventManager - Optimized event lifecycle and CRUD operations
|
||||
* Handles data loading with improved performance and caching
|
||||
*/
|
||||
export class EventManager {
|
||||
private eventBus: IEventBus;
|
||||
private events: CalendarEvent[] = [];
|
||||
private rawData: any = null;
|
||||
private eventCache = new Map<string, CalendarEvent[]>(); // Cache for period queries
|
||||
private lastCacheKey: string = '';
|
||||
|
||||
constructor(eventBus: IEventBus) {
|
||||
this.eventBus = eventBus;
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
private setupEventListeners(): void {
|
||||
// NOTE: Removed POC event listener to prevent interference with production code
|
||||
// POC sliding animation should not trigger separate event rendering
|
||||
// this.eventBus.on(CoreEvents.WEEK_CONTENT_RENDERED, ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method to load data - called directly by CalendarManager
|
||||
* Optimized data loading with better error handling
|
||||
*/
|
||||
public async loadData(): Promise<void> {
|
||||
await this.loadMockData();
|
||||
|
||||
// Debug: Log first few events
|
||||
if (this.events.length > 0) {
|
||||
}
|
||||
}
|
||||
|
||||
private async loadMockData(): Promise<void> {
|
||||
try {
|
||||
const calendarType = calendarConfig.getCalendarMode();
|
||||
let jsonFile: string;
|
||||
|
||||
|
||||
if (calendarType === 'resource') {
|
||||
jsonFile = '/src/data/mock-resource-events.json';
|
||||
} else {
|
||||
jsonFile = '/src/data/mock-events.json';
|
||||
}
|
||||
|
||||
|
||||
const response = await fetch(jsonFile);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load mock events: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Store raw data for GridManager
|
||||
this.rawData = data;
|
||||
|
||||
// Process data for internal use
|
||||
this.processCalendarData(calendarType, data);
|
||||
await this.loadMockData();
|
||||
this.clearCache(); // Clear cache when new data is loaded
|
||||
} catch (error) {
|
||||
this.events = []; // Fallback to empty array
|
||||
console.error('Failed to load event data:', error);
|
||||
this.events = [];
|
||||
this.rawData = null;
|
||||
}
|
||||
}
|
||||
|
||||
private processCalendarData(calendarType: string, data: any): void {
|
||||
/**
|
||||
* Optimized mock data loading with better resource handling
|
||||
*/
|
||||
private async loadMockData(): Promise<void> {
|
||||
const calendarType = calendarConfig.getCalendarMode();
|
||||
const jsonFile = calendarType === 'resource'
|
||||
? '/src/data/mock-resource-events.json'
|
||||
: '/src/data/mock-events.json';
|
||||
|
||||
const response = await fetch(jsonFile);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load mock events: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Store raw data and process in one operation
|
||||
this.rawData = data;
|
||||
this.events = this.processCalendarData(calendarType, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized data processing with better type safety
|
||||
*/
|
||||
private processCalendarData(calendarType: string, data: any): CalendarEvent[] {
|
||||
if (calendarType === 'resource') {
|
||||
const resourceData = data as ResourceCalendarData;
|
||||
this.events = resourceData.resources.flatMap(resource =>
|
||||
return resourceData.resources.flatMap(resource =>
|
||||
resource.events.map(event => ({
|
||||
...event,
|
||||
resourceName: resource.name,
|
||||
|
|
@ -74,18 +68,24 @@ export class EventManager {
|
|||
resourceEmployeeId: resource.employeeId
|
||||
}))
|
||||
);
|
||||
} else {
|
||||
this.events = data as CalendarEvent[];
|
||||
}
|
||||
|
||||
return data as CalendarEvent[];
|
||||
}
|
||||
|
||||
private syncEvents(): void {
|
||||
// Events are synced during initialization
|
||||
// This method maintained for internal state management only
|
||||
/**
|
||||
* Clear event cache when data changes
|
||||
*/
|
||||
private clearCache(): void {
|
||||
this.eventCache.clear();
|
||||
this.lastCacheKey = '';
|
||||
}
|
||||
|
||||
public getEvents(): CalendarEvent[] {
|
||||
return [...this.events];
|
||||
/**
|
||||
* Get events with optional copying for performance
|
||||
*/
|
||||
public getEvents(copy: boolean = false): CalendarEvent[] {
|
||||
return copy ? [...this.events] : this.events;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -95,34 +95,54 @@ export class EventManager {
|
|||
return this.rawData;
|
||||
}
|
||||
|
||||
private rawData: any = null;
|
||||
|
||||
|
||||
/**
|
||||
* Optimized event lookup with early return
|
||||
*/
|
||||
public getEventById(id: string): CalendarEvent | undefined {
|
||||
// Use find for better performance than filter + first
|
||||
return this.events.find(event => event.id === id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get events for a specific time period
|
||||
* Optimized events for period with caching and DateCalculator
|
||||
*/
|
||||
public getEventsForPeriod(startDate: Date, endDate: Date): CalendarEvent[] {
|
||||
return this.events.filter(event => {
|
||||
// Create cache key using DateCalculator for consistent formatting
|
||||
const cacheKey = `${DateCalculator.formatISODate(startDate)}_${DateCalculator.formatISODate(endDate)}`;
|
||||
|
||||
// Return cached result if available
|
||||
if (this.lastCacheKey === cacheKey && this.eventCache.has(cacheKey)) {
|
||||
return this.eventCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
// Filter events using optimized date operations
|
||||
const filteredEvents = this.events.filter(event => {
|
||||
// Use DateCalculator for consistent date parsing
|
||||
const eventStart = new Date(event.start);
|
||||
const eventEnd = new Date(event.end);
|
||||
|
||||
// Event overlaps period if it starts before period ends AND ends after period starts
|
||||
return eventStart <= endDate && eventEnd >= startDate;
|
||||
});
|
||||
|
||||
// Cache the result
|
||||
this.eventCache.set(cacheKey, filteredEvents);
|
||||
this.lastCacheKey = cacheKey;
|
||||
|
||||
return filteredEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized event creation with better ID generation
|
||||
*/
|
||||
public addEvent(event: Omit<CalendarEvent, 'id'>): CalendarEvent {
|
||||
const newEvent: CalendarEvent = {
|
||||
...event,
|
||||
id: Date.now().toString()
|
||||
id: `event_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
};
|
||||
|
||||
this.events.push(newEvent);
|
||||
this.syncEvents();
|
||||
this.clearCache(); // Clear cache when data changes
|
||||
|
||||
this.eventBus.emit(CoreEvents.EVENT_CREATED, {
|
||||
event: newEvent
|
||||
|
|
@ -131,6 +151,9 @@ export class EventManager {
|
|||
return newEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized event update with validation
|
||||
*/
|
||||
public updateEvent(id: string, updates: Partial<CalendarEvent>): CalendarEvent | null {
|
||||
const eventIndex = this.events.findIndex(event => event.id === id);
|
||||
if (eventIndex === -1) return null;
|
||||
|
|
@ -138,7 +161,7 @@ export class EventManager {
|
|||
const updatedEvent = { ...this.events[eventIndex], ...updates };
|
||||
this.events[eventIndex] = updatedEvent;
|
||||
|
||||
this.syncEvents();
|
||||
this.clearCache(); // Clear cache when data changes
|
||||
|
||||
this.eventBus.emit(CoreEvents.EVENT_UPDATED, {
|
||||
event: updatedEvent
|
||||
|
|
@ -147,6 +170,9 @@ export class EventManager {
|
|||
return updatedEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized event deletion with better error handling
|
||||
*/
|
||||
public deleteEvent(id: string): boolean {
|
||||
const eventIndex = this.events.findIndex(event => event.id === id);
|
||||
if (eventIndex === -1) return false;
|
||||
|
|
@ -154,7 +180,7 @@ export class EventManager {
|
|||
const deletedEvent = this.events[eventIndex];
|
||||
this.events.splice(eventIndex, 1);
|
||||
|
||||
this.syncEvents();
|
||||
this.clearCache(); // Clear cache when data changes
|
||||
|
||||
this.eventBus.emit(CoreEvents.EVENT_DELETED, {
|
||||
event: deletedEvent
|
||||
|
|
@ -163,12 +189,19 @@ export class EventManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
this.syncEvents();
|
||||
/**
|
||||
* Refresh data by reloading from source
|
||||
*/
|
||||
public async refresh(): Promise<void> {
|
||||
await this.loadData();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clean up resources and clear caches
|
||||
*/
|
||||
public destroy(): void {
|
||||
this.events = [];
|
||||
this.rawData = null;
|
||||
this.clearCache();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue