2025-07-24 22:17:38 +02:00
|
|
|
import { EventBus } from '../core/EventBus';
|
|
|
|
|
import { IEventBus, CalendarEvent } from '../types/CalendarTypes';
|
|
|
|
|
import { EventTypes } from '../constants/EventTypes';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* EventManager - Administrerer event lifecycle og CRUD operationer
|
|
|
|
|
* Håndterer mock data og event synchronization
|
|
|
|
|
*/
|
|
|
|
|
export class EventManager {
|
|
|
|
|
private eventBus: IEventBus;
|
|
|
|
|
private events: CalendarEvent[] = [];
|
|
|
|
|
|
|
|
|
|
constructor(eventBus: IEventBus) {
|
|
|
|
|
this.eventBus = eventBus;
|
|
|
|
|
this.setupEventListeners();
|
|
|
|
|
this.loadMockData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private setupEventListeners(): void {
|
|
|
|
|
this.eventBus.on(EventTypes.CALENDAR_INITIALIZED, () => {
|
|
|
|
|
this.syncEvents();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.eventBus.on(EventTypes.DATE_CHANGED, () => {
|
|
|
|
|
this.syncEvents();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.eventBus.on(EventTypes.VIEW_RENDERED, () => {
|
|
|
|
|
this.syncEvents();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private loadMockData(): void {
|
2025-08-02 23:59:52 +02:00
|
|
|
// Mock events for current week (July 27 - August 2, 2025)
|
2025-07-24 22:17:38 +02:00
|
|
|
this.events = [
|
2025-08-03 21:23:24 +02:00
|
|
|
// Sunday August 3, 2025
|
2025-07-24 22:17:38 +02:00
|
|
|
{
|
|
|
|
|
id: '1',
|
2025-08-02 23:59:52 +02:00
|
|
|
title: 'Weekend Planning',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-03T10:00:00',
|
|
|
|
|
end: '2025-08-03T11:00:00',
|
2025-08-02 23:59:52 +02:00
|
|
|
type: 'work',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 60, color: '#9c27b0' } // Purple
|
2025-08-02 23:59:52 +02:00
|
|
|
},
|
2025-08-03 21:23:24 +02:00
|
|
|
// Monday August 4, 2025
|
2025-08-02 23:59:52 +02:00
|
|
|
{
|
|
|
|
|
id: '2',
|
2025-07-24 22:17:38 +02:00
|
|
|
title: 'Team Standup',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-04T09:00:00',
|
|
|
|
|
end: '2025-08-04T09:30:00',
|
2025-07-24 22:17:38 +02:00
|
|
|
type: 'meeting',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 30, color: '#ff5722' } // Deep Orange
|
2025-07-24 22:17:38 +02:00
|
|
|
},
|
|
|
|
|
{
|
2025-08-02 23:59:52 +02:00
|
|
|
id: '3',
|
|
|
|
|
title: 'Project Kickoff',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-04T14:00:00',
|
|
|
|
|
end: '2025-08-04T15:30:00',
|
2025-07-24 22:17:38 +02:00
|
|
|
type: 'meeting',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 90, color: '#e91e63' } // Pink
|
2025-07-24 22:17:38 +02:00
|
|
|
},
|
2025-08-03 21:23:24 +02:00
|
|
|
// Tuesday August 5, 2025
|
2025-07-24 22:17:38 +02:00
|
|
|
{
|
|
|
|
|
id: '4',
|
|
|
|
|
title: 'Deep Work Session',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-05T10:00:00',
|
|
|
|
|
end: '2025-08-05T12:00:00',
|
2025-07-24 22:17:38 +02:00
|
|
|
type: 'work',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 120, color: '#3f51b5' } // Indigo
|
2025-07-24 22:17:38 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: '5',
|
2025-08-02 23:59:52 +02:00
|
|
|
title: 'Lunch Meeting',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-05T12:30:00',
|
|
|
|
|
end: '2025-08-05T13:30:00',
|
2025-07-24 22:17:38 +02:00
|
|
|
type: 'meal',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 60, color: '#ff9800' } // Orange
|
2025-07-24 22:17:38 +02:00
|
|
|
},
|
2025-08-03 21:23:24 +02:00
|
|
|
// Wednesday August 6, 2025
|
2025-07-24 22:17:38 +02:00
|
|
|
{
|
2025-08-02 23:59:52 +02:00
|
|
|
id: '6',
|
|
|
|
|
title: 'Client Review',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-06T15:00:00',
|
|
|
|
|
end: '2025-08-06T16:00:00',
|
2025-07-24 22:17:38 +02:00
|
|
|
type: 'meeting',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 60, color: '#795548' } // Brown
|
2025-07-24 22:17:38 +02:00
|
|
|
},
|
2025-08-03 21:23:24 +02:00
|
|
|
// Thursday August 7, 2025
|
2025-07-24 22:17:38 +02:00
|
|
|
{
|
2025-08-02 23:59:52 +02:00
|
|
|
id: '7',
|
2025-07-24 22:17:38 +02:00
|
|
|
title: 'Sprint Planning',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-07T09:00:00',
|
|
|
|
|
end: '2025-08-07T10:30:00',
|
2025-07-24 22:17:38 +02:00
|
|
|
type: 'meeting',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 90, color: '#607d8b' } // Blue Grey
|
2025-07-24 22:17:38 +02:00
|
|
|
},
|
|
|
|
|
{
|
2025-08-02 23:59:52 +02:00
|
|
|
id: '8',
|
|
|
|
|
title: 'Code Review',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-07T14:00:00',
|
|
|
|
|
end: '2025-08-07T15:00:00',
|
2025-07-24 22:17:38 +02:00
|
|
|
type: 'work',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 60, color: '#009688' } // Teal
|
2025-08-02 00:28:45 +02:00
|
|
|
},
|
2025-08-03 21:23:24 +02:00
|
|
|
// Friday August 8, 2025
|
2025-08-02 00:28:45 +02:00
|
|
|
{
|
2025-08-02 23:59:52 +02:00
|
|
|
id: '9',
|
|
|
|
|
title: 'Team Standup',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-08T09:00:00',
|
|
|
|
|
end: '2025-08-08T09:30:00',
|
2025-08-02 23:59:52 +02:00
|
|
|
type: 'meeting',
|
2025-08-02 00:28:45 +02:00
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 30, color: '#8bc34a' } // Light Green
|
2025-08-02 00:28:45 +02:00
|
|
|
},
|
|
|
|
|
{
|
2025-08-02 23:59:52 +02:00
|
|
|
id: '10',
|
|
|
|
|
title: 'Client Meeting',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-08T14:00:00',
|
|
|
|
|
end: '2025-08-08T15:30:00',
|
2025-08-02 00:28:45 +02:00
|
|
|
type: 'meeting',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 90, color: '#cddc39' } // Lime
|
2025-08-02 00:28:45 +02:00
|
|
|
},
|
2025-08-03 21:23:24 +02:00
|
|
|
// Saturday August 9, 2025
|
2025-08-02 00:28:45 +02:00
|
|
|
{
|
2025-08-02 23:59:52 +02:00
|
|
|
id: '11',
|
|
|
|
|
title: 'Weekend Project',
|
2025-08-03 21:23:24 +02:00
|
|
|
start: '2025-08-09T10:00:00',
|
|
|
|
|
end: '2025-08-09T12:00:00',
|
|
|
|
|
type: 'work',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 120, color: '#f44336' } // Red
|
2025-08-03 21:23:24 +02:00
|
|
|
},
|
|
|
|
|
// Test events for early/late hours
|
|
|
|
|
{
|
|
|
|
|
id: '12',
|
|
|
|
|
title: 'Early Morning Workout',
|
|
|
|
|
start: '2025-08-05T06:00:00',
|
|
|
|
|
end: '2025-08-05T07:00:00',
|
|
|
|
|
type: 'work',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 60, color: '#00bcd4' } // Cyan
|
2025-08-03 21:23:24 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: '13',
|
|
|
|
|
title: 'Late Evening Call',
|
|
|
|
|
start: '2025-08-06T21:00:00',
|
|
|
|
|
end: '2025-08-06T22:00:00',
|
|
|
|
|
type: 'meeting',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 60, color: '#673ab7' } // Deep Purple
|
2025-08-03 21:23:24 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: '14',
|
|
|
|
|
title: 'Midnight Deployment',
|
|
|
|
|
start: '2025-08-07T23:00:00',
|
|
|
|
|
end: '2025-08-08T01:00:00',
|
2025-08-02 00:28:45 +02:00
|
|
|
type: 'work',
|
|
|
|
|
allDay: false,
|
|
|
|
|
syncStatus: 'synced',
|
2025-08-05 00:49:00 +02:00
|
|
|
metadata: { duration: 120, color: '#ffc107' } // Amber
|
2025-08-04 23:55:04 +02:00
|
|
|
},
|
|
|
|
|
// All-day events for demo
|
|
|
|
|
{
|
|
|
|
|
id: '15',
|
|
|
|
|
title: 'Company Holiday',
|
|
|
|
|
start: '2025-08-04T00:00:00',
|
2025-08-05 00:49:00 +02:00
|
|
|
end: '2025-08-05T23:59:59',
|
2025-08-04 23:55:04 +02:00
|
|
|
type: 'milestone',
|
|
|
|
|
allDay: true,
|
|
|
|
|
syncStatus: 'synced',
|
|
|
|
|
metadata: {
|
|
|
|
|
duration: 1440, // Full day in minutes
|
|
|
|
|
color: '#4caf50' // Green color
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: '16',
|
|
|
|
|
title: 'Team Building Event',
|
|
|
|
|
start: '2025-08-06T00:00:00',
|
|
|
|
|
end: '2025-08-06T23:59:59',
|
|
|
|
|
type: 'meeting',
|
|
|
|
|
allDay: true,
|
|
|
|
|
syncStatus: 'synced',
|
|
|
|
|
metadata: {
|
|
|
|
|
duration: 1440, // Full day in minutes
|
|
|
|
|
color: '#2196f3' // Blue color
|
|
|
|
|
}
|
2025-07-24 22:17:38 +02:00
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
console.log(`EventManager: Loaded ${this.events.length} mock events`);
|
2025-08-02 00:28:45 +02:00
|
|
|
console.log('EventManager: First event:', this.events[0]);
|
|
|
|
|
console.log('EventManager: Last event:', this.events[this.events.length - 1]);
|
2025-07-24 22:17:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private syncEvents(): void {
|
|
|
|
|
// Emit events for rendering
|
|
|
|
|
this.eventBus.emit(EventTypes.EVENTS_LOADED, {
|
|
|
|
|
events: this.events
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log(`EventManager: Synced ${this.events.length} events`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getEvents(): CalendarEvent[] {
|
|
|
|
|
return [...this.events];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public getEventById(id: string): CalendarEvent | undefined {
|
|
|
|
|
return this.events.find(event => event.id === id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public addEvent(event: Omit<CalendarEvent, 'id'>): CalendarEvent {
|
|
|
|
|
const newEvent: CalendarEvent = {
|
|
|
|
|
...event,
|
|
|
|
|
id: Date.now().toString()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.events.push(newEvent);
|
|
|
|
|
this.syncEvents();
|
|
|
|
|
|
|
|
|
|
this.eventBus.emit(EventTypes.EVENT_CREATED, {
|
|
|
|
|
event: newEvent
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return newEvent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public updateEvent(id: string, updates: Partial<CalendarEvent>): CalendarEvent | null {
|
|
|
|
|
const eventIndex = this.events.findIndex(event => event.id === id);
|
|
|
|
|
if (eventIndex === -1) return null;
|
|
|
|
|
|
|
|
|
|
const updatedEvent = { ...this.events[eventIndex], ...updates };
|
|
|
|
|
this.events[eventIndex] = updatedEvent;
|
|
|
|
|
|
|
|
|
|
this.syncEvents();
|
|
|
|
|
|
|
|
|
|
this.eventBus.emit(EventTypes.EVENT_UPDATED, {
|
|
|
|
|
event: updatedEvent
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return updatedEvent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public deleteEvent(id: string): boolean {
|
|
|
|
|
const eventIndex = this.events.findIndex(event => event.id === id);
|
|
|
|
|
if (eventIndex === -1) return false;
|
|
|
|
|
|
|
|
|
|
const deletedEvent = this.events[eventIndex];
|
|
|
|
|
this.events.splice(eventIndex, 1);
|
|
|
|
|
|
|
|
|
|
this.syncEvents();
|
|
|
|
|
|
|
|
|
|
this.eventBus.emit(EventTypes.EVENT_DELETED, {
|
|
|
|
|
event: deletedEvent
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public refresh(): void {
|
|
|
|
|
this.syncEvents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public destroy(): void {
|
|
|
|
|
this.events = [];
|
|
|
|
|
}
|
|
|
|
|
}
|