Refactor CalendarConfig to static configuration class

Converts CalendarConfig to a pure static configuration management class with improved initialization and dependency handling

Removes event bus dependencies and simplifies configuration loading
Adds static methods for config management and initialization
Improves flexibility and reduces class complexity
This commit is contained in:
Janus C. H. Knudsen 2025-10-31 15:26:07 +01:00
parent 8bbb2f05d3
commit 349e1e8293
7 changed files with 3299 additions and 318 deletions

View file

@ -1,8 +1,7 @@
// Calendar configuration management
// Pure static configuration class - no dependencies, no events
import { eventBus } from './EventBus';
import { CoreEvents } from '../constants/CoreEvents';
import { CalendarConfig as ICalendarConfig, ViewPeriod, CalendarMode } from '../types/CalendarTypes';
import { ICalendarConfig, ViewPeriod, CalendarMode } from '../types/CalendarTypes';
import { TimeFormatter, TimeFormatSettings } from '../utils/TimeFormatter';
/**
@ -86,20 +85,10 @@ interface TimeFormatConfig {
}
/**
* Calendar configuration management
* Calendar configuration management - Pure static config
*/
export class CalendarConfig {
private config: ICalendarConfig;
private calendarMode: CalendarMode = 'date';
private selectedDate: Date | null = null;
private gridSettings: GridSettings;
private dateViewSettings: DateViewSettings;
private resourceViewSettings: ResourceViewSettings;
private currentWorkWeek: string = 'standard';
private timeFormatConfig: TimeFormatConfig;
constructor() {
this.config = {
private static config: ICalendarConfig = {
// Scrollbar styling
scrollbarWidth: 16, // Width of scrollbar in pixels
scrollbarColor: '#666', // Scrollbar thumb color
@ -127,8 +116,12 @@ export class CalendarConfig {
maxEventDuration: 480 // 8 hours
};
private static calendarMode: CalendarMode = 'date';
private static selectedDate: Date | null = new Date();
private static currentWorkWeek: string = 'standard';
// Grid display settings
this.gridSettings = {
private static gridSettings: GridSettings = {
hourHeight: 60,
dayStartHour: 0,
dayEndHour: 24,
@ -143,7 +136,7 @@ export class CalendarConfig {
};
// Date view settings
this.dateViewSettings = {
private static dateViewSettings: DateViewSettings = {
period: 'week',
weekDays: 7,
firstDayOfWeek: 1,
@ -151,7 +144,7 @@ export class CalendarConfig {
};
// Resource view settings
this.resourceViewSettings = {
private static resourceViewSettings: ResourceViewSettings = {
maxResources: 10,
showAvatars: true,
avatarSize: 32,
@ -161,7 +154,7 @@ export class CalendarConfig {
};
// Time format settings - default to Denmark with technical format
this.timeFormatConfig = {
private static timeFormatConfig: TimeFormatConfig = {
timezone: 'Europe/Copenhagen',
use24HourFormat: true,
locale: 'da-DK',
@ -169,51 +162,25 @@ export class CalendarConfig {
showSeconds: false
};
/**
* Initialize configuration - called once at startup
*/
static initialize(): void {
// Set computed values
this.config.minEventDuration = this.gridSettings.snapInterval;
CalendarConfig.config.minEventDuration = CalendarConfig.gridSettings.snapInterval;
// Initialize TimeFormatter with default settings
TimeFormatter.configure(this.timeFormatConfig);
// Load calendar type from URL parameter
this.loadCalendarType();
TimeFormatter.configure(CalendarConfig.timeFormatConfig);
// Load from data attributes
this.loadFromDOM();
CalendarConfig.loadFromDOM();
}
/**
* Load calendar type and date from URL parameters
*/
private loadCalendarType(): void {
const urlParams = new URLSearchParams(window.location.search);
const typeParam = urlParams.get('type');
const dateParam = urlParams.get('date');
// Set calendar mode
if (typeParam === 'resource' || typeParam === 'date') {
this.calendarMode = typeParam;
} else {
this.calendarMode = 'date'; // Default
}
// Set selected date
if (dateParam) {
const parsedDate = new Date(dateParam);
if (!isNaN(parsedDate.getTime())) {
this.selectedDate = parsedDate;
} else {
this.selectedDate = new Date();
}
} else {
this.selectedDate = new Date(); // Default to today
}
}
/**
* Load configuration from DOM data attributes
*/
private loadFromDOM(): void {
private static loadFromDOM(): void {
const calendar = document.querySelector('swp-calendar') as HTMLElement;
if (!calendar) return;
@ -221,215 +188,152 @@ export class CalendarConfig {
const attrs = calendar.dataset;
// Update date view settings
if (attrs.view) this.dateViewSettings.period = attrs.view as ViewPeriod;
if (attrs.weekDays) this.dateViewSettings.weekDays = parseInt(attrs.weekDays);
if (attrs.view) CalendarConfig.dateViewSettings.period = attrs.view as ViewPeriod;
if (attrs.weekDays) CalendarConfig.dateViewSettings.weekDays = parseInt(attrs.weekDays);
// Update grid settings
if (attrs.snapInterval) this.gridSettings.snapInterval = parseInt(attrs.snapInterval);
if (attrs.dayStartHour) this.gridSettings.dayStartHour = parseInt(attrs.dayStartHour);
if (attrs.dayEndHour) this.gridSettings.dayEndHour = parseInt(attrs.dayEndHour);
if (attrs.hourHeight) this.gridSettings.hourHeight = parseInt(attrs.hourHeight);
if (attrs.fitToWidth !== undefined) this.gridSettings.fitToWidth = attrs.fitToWidth === 'true';
if (attrs.snapInterval) CalendarConfig.gridSettings.snapInterval = parseInt(attrs.snapInterval);
if (attrs.dayStartHour) CalendarConfig.gridSettings.dayStartHour = parseInt(attrs.dayStartHour);
if (attrs.dayEndHour) CalendarConfig.gridSettings.dayEndHour = parseInt(attrs.dayEndHour);
if (attrs.hourHeight) CalendarConfig.gridSettings.hourHeight = parseInt(attrs.hourHeight);
if (attrs.fitToWidth !== undefined) CalendarConfig.gridSettings.fitToWidth = attrs.fitToWidth === 'true';
// Update computed values
this.config.minEventDuration = this.gridSettings.snapInterval;
CalendarConfig.config.minEventDuration = CalendarConfig.gridSettings.snapInterval;
}
/**
* Get a config value
*/
get<K extends keyof ICalendarConfig>(key: K): ICalendarConfig[K] {
return this.config[key];
static get<K extends keyof ICalendarConfig>(key: K): ICalendarConfig[K] {
return CalendarConfig.config[key];
}
/**
* Set a config value
* Set a config value (no events - use ConfigManager for updates with events)
*/
set<K extends keyof ICalendarConfig>(key: K, value: ICalendarConfig[K]): void {
const oldValue = this.config[key];
this.config[key] = value;
// Update computed values handled in specific update methods
// Emit config update event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key,
value,
oldValue
});
static set<K extends keyof ICalendarConfig>(key: K, value: ICalendarConfig[K]): void {
CalendarConfig.config[key] = value;
}
/**
* Update multiple config values
* Update multiple config values (no events - use ConfigManager for updates with events)
*/
update(updates: Partial<ICalendarConfig>): void {
static update(updates: Partial<ICalendarConfig>): void {
Object.entries(updates).forEach(([key, value]) => {
this.set(key as keyof ICalendarConfig, value);
CalendarConfig.set(key as keyof ICalendarConfig, value);
});
}
/**
* Get all config
*/
getAll(): ICalendarConfig {
return { ...this.config };
static getAll(): ICalendarConfig {
return { ...CalendarConfig.config };
}
/**
* Calculate derived values
*/
get minuteHeight(): number {
return this.gridSettings.hourHeight / 60;
static get minuteHeight(): number {
return CalendarConfig.gridSettings.hourHeight / 60;
}
get totalHours(): number {
return this.gridSettings.dayEndHour - this.gridSettings.dayStartHour;
static get totalHours(): number {
return CalendarConfig.gridSettings.dayEndHour - CalendarConfig.gridSettings.dayStartHour;
}
get totalMinutes(): number {
return this.totalHours * 60;
static get totalMinutes(): number {
return CalendarConfig.totalHours * 60;
}
get slotsPerHour(): number {
return 60 / this.gridSettings.snapInterval;
static get slotsPerHour(): number {
return 60 / CalendarConfig.gridSettings.snapInterval;
}
get totalSlots(): number {
return this.totalHours * this.slotsPerHour;
static get totalSlots(): number {
return CalendarConfig.totalHours * CalendarConfig.slotsPerHour;
}
get slotHeight(): number {
return this.gridSettings.hourHeight / this.slotsPerHour;
static get slotHeight(): number {
return CalendarConfig.gridSettings.hourHeight / CalendarConfig.slotsPerHour;
}
/**
* Validate snap interval
*/
isValidSnapInterval(interval: number): boolean {
static isValidSnapInterval(interval: number): boolean {
return [5, 10, 15, 30, 60].includes(interval);
}
/**
* Get grid display settings
*/
getGridSettings(): GridSettings {
return { ...this.gridSettings };
static getGridSettings(): GridSettings {
return { ...CalendarConfig.gridSettings };
}
/**
* Update grid display settings
* Update grid display settings (no events - use ConfigManager for updates with events)
*/
updateGridSettings(updates: Partial<GridSettings>): void {
this.gridSettings = { ...this.gridSettings, ...updates };
static updateGridSettings(updates: Partial<GridSettings>): void {
CalendarConfig.gridSettings = { ...CalendarConfig.gridSettings, ...updates };
// Update computed values
if (updates.snapInterval) {
this.config.minEventDuration = updates.snapInterval;
CalendarConfig.config.minEventDuration = updates.snapInterval;
}
// Grid settings changes trigger general refresh - avoid specific event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'gridSettings',
value: this.gridSettings
});
}
/**
* Get date view settings
*/
getDateViewSettings(): DateViewSettings {
return { ...this.dateViewSettings };
}
/**
* Update date view settings
*/
updateDateViewSettings(updates: Partial<DateViewSettings>): void {
this.dateViewSettings = { ...this.dateViewSettings, ...updates };
// Date view settings changes trigger general refresh - avoid specific event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'dateViewSettings',
value: this.dateViewSettings
});
static getDateViewSettings(): DateViewSettings {
return { ...CalendarConfig.dateViewSettings };
}
/**
* Get resource view settings
*/
getResourceViewSettings(): ResourceViewSettings {
return { ...this.resourceViewSettings };
}
/**
* Update resource view settings
*/
updateResourceViewSettings(updates: Partial<ResourceViewSettings>): void {
this.resourceViewSettings = { ...this.resourceViewSettings, ...updates };
// Resource view settings changes trigger general refresh - avoid specific event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'resourceViewSettings',
value: this.resourceViewSettings
});
}
/**
* Check if current mode is resource-based
*/
isResourceMode(): boolean {
return this.calendarMode === 'resource';
}
/**
* Check if current mode is date-based
*/
isDateMode(): boolean {
return this.calendarMode === 'date';
static getResourceViewSettings(): ResourceViewSettings {
return { ...CalendarConfig.resourceViewSettings };
}
/**
* Get calendar mode
*/
getCalendarMode(): CalendarMode {
return this.calendarMode;
static getCalendarMode(): CalendarMode {
return CalendarConfig.calendarMode;
}
/**
* Set calendar mode
*/
setCalendarMode(mode: CalendarMode): void {
const oldMode = this.calendarMode;
this.calendarMode = mode;
// Emit calendar mode change event
eventBus.emit(CoreEvents.VIEW_CHANGED, {
oldType: oldMode,
newType: mode
});
static setCalendarMode(mode: CalendarMode): void {
CalendarConfig.calendarMode = mode;
}
/**
* Get selected date
*/
getSelectedDate(): Date | null {
return this.selectedDate;
static getSelectedDate(): Date | null {
return CalendarConfig.selectedDate;
}
/**
* Set selected date
* Note: Does not emit events - caller is responsible for event emission
*/
setSelectedDate(date: Date): void {
this.selectedDate = date;
static setSelectedDate(date: Date): void {
CalendarConfig.selectedDate = date;
}
/**
* Get work week presets
*/
private getWorkWeekPresets(): { [key: string]: WorkWeekSettings } {
private static getWorkWeekPresets(): { [key: string]: WorkWeekSettings } {
return {
'standard': {
id: 'standard',
@ -467,109 +371,115 @@ export class CalendarConfig {
/**
* Get current work week settings
*/
getWorkWeekSettings(): WorkWeekSettings {
const presets = this.getWorkWeekPresets();
return presets[this.currentWorkWeek] || presets['standard'];
static getWorkWeekSettings(): WorkWeekSettings {
const presets = CalendarConfig.getWorkWeekPresets();
return presets[CalendarConfig.currentWorkWeek] || presets['standard'];
}
/**
* Set work week preset
* Note: Does not emit events - caller is responsible for event emission
*/
setWorkWeek(workWeekId: string): void {
const presets = this.getWorkWeekPresets();
static setWorkWeek(workWeekId: string): void {
const presets = CalendarConfig.getWorkWeekPresets();
if (presets[workWeekId]) {
this.currentWorkWeek = workWeekId;
CalendarConfig.currentWorkWeek = workWeekId;
// Update dateViewSettings to match work week
this.dateViewSettings.weekDays = presets[workWeekId].totalDays;
CalendarConfig.dateViewSettings.weekDays = presets[workWeekId].totalDays;
}
}
/**
* Get current work week ID
*/
getCurrentWorkWeek(): string {
return this.currentWorkWeek;
static getCurrentWorkWeek(): string {
return CalendarConfig.currentWorkWeek;
}
/**
* Get time format settings
*/
getTimeFormatSettings(): TimeFormatConfig {
return { ...this.timeFormatConfig };
}
/**
* Update time format settings
*/
updateTimeFormatSettings(updates: Partial<TimeFormatConfig>): void {
this.timeFormatConfig = { ...this.timeFormatConfig, ...updates };
// Update TimeFormatter with new settings
TimeFormatter.configure(this.timeFormatConfig);
// Emit time format change event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'timeFormatSettings',
value: this.timeFormatConfig
});
}
/**
* Set timezone (convenience method)
*/
setTimezone(timezone: string): void {
this.updateTimeFormatSettings({ timezone });
}
/**
* Set 12/24 hour format (convenience method)
*/
set24HourFormat(use24Hour: boolean): void {
this.updateTimeFormatSettings({ use24HourFormat: use24Hour });
static getTimeFormatSettings(): TimeFormatConfig {
return { ...CalendarConfig.timeFormatConfig };
}
/**
* Get configured timezone
*/
getTimezone(): string {
return this.timeFormatConfig.timezone;
static getTimezone(): string {
return CalendarConfig.timeFormatConfig.timezone;
}
/**
* Get configured locale
*/
getLocale(): string {
return this.timeFormatConfig.locale;
static getLocale(): string {
return CalendarConfig.timeFormatConfig.locale;
}
/**
* Check if using 24-hour format
*/
is24HourFormat(): boolean {
return this.timeFormatConfig.use24HourFormat;
}
/**
* Set date format (convenience method)
*/
setDateFormat(format: 'locale' | 'technical'): void {
this.updateTimeFormatSettings({ dateFormat: format });
}
/**
* Set whether to show seconds (convenience method)
*/
setShowSeconds(show: boolean): void {
this.updateTimeFormatSettings({ showSeconds: show });
static is24HourFormat(): boolean {
return CalendarConfig.timeFormatConfig.use24HourFormat;
}
/**
* Get current date format
*/
getDateFormat(): 'locale' | 'technical' {
return this.timeFormatConfig.dateFormat;
static getDateFormat(): 'locale' | 'technical' {
return CalendarConfig.timeFormatConfig.dateFormat;
}
/**
* Load configuration from JSON
*/
static loadFromJSON(json: string): void {
try {
const data = JSON.parse(json);
if (data.gridSettings) CalendarConfig.updateGridSettings(data.gridSettings);
if (data.dateViewSettings) CalendarConfig.dateViewSettings = { ...CalendarConfig.dateViewSettings, ...data.dateViewSettings };
if (data.resourceViewSettings) CalendarConfig.resourceViewSettings = { ...CalendarConfig.resourceViewSettings, ...data.resourceViewSettings };
if (data.timeFormatConfig) {
CalendarConfig.timeFormatConfig = { ...CalendarConfig.timeFormatConfig, ...data.timeFormatConfig };
TimeFormatter.configure(CalendarConfig.timeFormatConfig);
}
} catch (error) {
console.error('Failed to load config from JSON:', error);
}
}
// ========================================================================
// Instance method wrappers for backward compatibility
// These allow injected CalendarConfig to work with existing code
// ========================================================================
get(key: keyof ICalendarConfig) { return CalendarConfig.get(key); }
set(key: keyof ICalendarConfig, value: any) { return CalendarConfig.set(key, value); }
update(updates: Partial<ICalendarConfig>) { return CalendarConfig.update(updates); }
getAll() { return CalendarConfig.getAll(); }
get minuteHeight() { return CalendarConfig.minuteHeight; }
get totalHours() { return CalendarConfig.totalHours; }
get totalMinutes() { return CalendarConfig.totalMinutes; }
get slotsPerHour() { return CalendarConfig.slotsPerHour; }
get totalSlots() { return CalendarConfig.totalSlots; }
get slotHeight() { return CalendarConfig.slotHeight; }
isValidSnapInterval(interval: number) { return CalendarConfig.isValidSnapInterval(interval); }
getGridSettings() { return CalendarConfig.getGridSettings(); }
updateGridSettings(updates: Partial<GridSettings>) { return CalendarConfig.updateGridSettings(updates); }
getDateViewSettings() { return CalendarConfig.getDateViewSettings(); }
getResourceViewSettings() { return CalendarConfig.getResourceViewSettings(); }
getCalendarMode() { return CalendarConfig.getCalendarMode(); }
setCalendarMode(mode: CalendarMode) { return CalendarConfig.setCalendarMode(mode); }
getSelectedDate() { return CalendarConfig.getSelectedDate(); }
setSelectedDate(date: Date) { return CalendarConfig.setSelectedDate(date); }
getWorkWeekSettings() { return CalendarConfig.getWorkWeekSettings(); }
setWorkWeek(workWeekId: string) { return CalendarConfig.setWorkWeek(workWeekId); }
getCurrentWorkWeek() { return CalendarConfig.getCurrentWorkWeek(); }
getTimeFormatSettings() { return CalendarConfig.getTimeFormatSettings(); }
getTimezone() { return CalendarConfig.getTimezone(); }
getLocale() { return CalendarConfig.getLocale(); }
is24HourFormat() { return CalendarConfig.is24HourFormat(); }
getDateFormat() { return CalendarConfig.getDateFormat(); }
}

View file

@ -19,10 +19,11 @@ import { ResizeHandleManager } from './managers/ResizeHandleManager';
import { EdgeScrollManager } from './managers/EdgeScrollManager';
import { DragHoverManager } from './managers/DragHoverManager';
import { HeaderManager } from './managers/HeaderManager';
import { ConfigManager } from './managers/ConfigManager';
// Import renderers
import { DateHeaderRenderer, type HeaderRenderer } from './renderers/HeaderRenderer';
import { DateColumnRenderer, type ColumnRenderer } from './renderers/ColumnRenderer';
import { DateHeaderRenderer, ResourceHeaderRenderer, type HeaderRenderer } from './renderers/HeaderRenderer';
import { DateColumnRenderer, ResourceColumnRenderer, type ColumnRenderer } from './renderers/ColumnRenderer';
import { DateEventRenderer, type EventRendererStrategy } from './renderers/EventRenderer';
import { AllDayEventRenderer } from './renderers/AllDayEventRenderer';
import { GridRenderer } from './renderers/GridRenderer';
@ -66,7 +67,8 @@ async function handleDeepLinking(eventManager: EventManager, urlManager: URLMana
*/
async function initializeCalendar(): Promise<void> {
try {
// Use the singleton calendar configuration
// Initialize static calendar configuration
CalendarConfig.initialize();
// Create NovaDI container
const container = new Container();
@ -75,20 +77,25 @@ async function initializeCalendar(): Promise<void> {
// Enable debug mode for development
eventBus.setDebug(true);
builder.registerType(CalendarConfig).as<CalendarConfig>().singleInstance();
// Register CalendarConfig as singleton instance (static class, not instantiated)
builder.registerInstance(CalendarConfig).as<CalendarConfig>();
// Register ConfigManager for event-driven config updates
builder.registerType(ConfigManager).as<ConfigManager>().singleInstance();
// Bind core services as instances
builder.registerInstance(eventBus).as<IEventBus>();
// Register renderers with keyed registration based on calendar mode
// Date mode renderers
builder.registerType(DateHeaderRenderer).as<HeaderRenderer>().keyed('date');
builder.registerType(DateColumnRenderer).as<ColumnRenderer>().keyed('date');
builder.registerType(DateEventRenderer).as<EventRendererStrategy>().keyed('date');
// Resource mode renderers (using same renderers for now)
builder.registerType(DateHeaderRenderer).as<HeaderRenderer>().keyed('resource');
builder.registerType(DateColumnRenderer).as<ColumnRenderer>().keyed('resource');
builder.registerType(DateEventRenderer).as<EventRendererStrategy>().keyed('resource');
// Register renderers based on calendar mode
const calendarMode = CalendarConfig.getCalendarMode();
if (calendarMode === 'resource') {
builder.registerType(ResourceHeaderRenderer).as<HeaderRenderer>().singleInstance();
builder.registerType(ResourceColumnRenderer).as<ColumnRenderer>().singleInstance();
} else {
builder.registerType(DateHeaderRenderer).as<HeaderRenderer>().singleInstance();
builder.registerType(DateColumnRenderer).as<ColumnRenderer>().singleInstance();
}
builder.registerType(DateEventRenderer).as<EventRendererStrategy>().singleInstance();
// Register core services and utilities
builder.registerType(DateService).as<DateService>().singleInstance();
@ -167,9 +174,11 @@ async function initializeCalendar(): Promise<void> {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
initializeCalendar().catch(error => {
console.error('Calendar initialization failed:', error);
});
});
} else {
initializeCalendar().catch(error => {
console.error('Calendar initialization failed:', error);
});
}

View file

@ -0,0 +1,118 @@
// Configuration manager - handles config updates with event emission
// Uses static CalendarConfig internally but adds event-driven updates
import { IEventBus, ICalendarConfig } from '../types/CalendarTypes';
import { CoreEvents } from '../constants/CoreEvents';
import { CalendarConfig } from '../core/CalendarConfig';
/**
* Grid display settings interface (re-export from CalendarConfig)
*/
interface GridSettings {
dayStartHour: number;
dayEndHour: number;
workStartHour: number;
workEndHour: number;
hourHeight: number;
snapInterval: number;
fitToWidth: boolean;
scrollToHour: number | null;
gridStartThresholdMinutes: number;
showCurrentTime: boolean;
showWorkHours: boolean;
}
/**
* ConfigManager - Handles configuration updates with event emission
* Wraps static CalendarConfig with event-driven functionality for DI system
*/
export class ConfigManager {
constructor(private eventBus: IEventBus) {}
/**
* Set a config value and emit event
*/
set<K extends keyof ICalendarConfig>(key: K, value: ICalendarConfig[K]): void {
const oldValue = CalendarConfig.get(key);
CalendarConfig.set(key, value);
// Emit config update event
this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key,
value,
oldValue
});
}
/**
* Update multiple config values and emit event
*/
update(updates: Partial<ICalendarConfig>): void {
Object.entries(updates).forEach(([key, value]) => {
this.set(key as keyof ICalendarConfig, value);
});
}
/**
* Update grid display settings and emit event
*/
updateGridSettings(updates: Partial<GridSettings>): void {
CalendarConfig.updateGridSettings(updates);
// Emit event after update
this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'gridSettings',
value: CalendarConfig.getGridSettings()
});
}
/**
* Set selected date and emit event
*/
setSelectedDate(date: Date): void {
const oldDate = CalendarConfig.getSelectedDate();
CalendarConfig.setSelectedDate(date);
// Emit date change event if it actually changed
if (!oldDate || oldDate.getTime() !== date.getTime()) {
this.eventBus.emit(CoreEvents.DATE_CHANGED, {
date,
oldDate
});
}
}
/**
* Set work week and emit event
*/
setWorkWeek(workWeekId: string): void {
const oldWorkWeek = CalendarConfig.getCurrentWorkWeek();
CalendarConfig.setWorkWeek(workWeekId);
// Emit event if changed
if (oldWorkWeek !== workWeekId) {
this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'workWeek',
value: workWeekId,
oldValue: oldWorkWeek
});
}
}
/**
* Set calendar mode and emit event
*/
setCalendarMode(mode: 'date' | 'resource'): void {
const oldMode = CalendarConfig.getCalendarMode();
CalendarConfig.setCalendarMode(mode);
// Emit event if changed
if (oldMode !== mode) {
this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'calendarMode',
value: mode,
oldValue: oldMode
});
}
}
}

View file

@ -57,8 +57,8 @@ export class EventManager {
private async loadMockData(): Promise<void> {
const calendarType = this.config.getCalendarMode();
const jsonFile = calendarType === 'resource'
? '/src/data/mock-resource-events.json'
: '/src/data/mock-events.json';
? '/data/mock-resource-events.json'
: '/data/mock-events.json';
const response = await fetch(jsonFile);
if (!response.ok) {

View file

@ -50,7 +50,7 @@ export interface CalendarEvent {
metadata?: Record<string, any>;
}
export interface CalendarConfig {
export interface ICalendarConfig {
// Scrollbar styling
scrollbarWidth: number; // Width of scrollbar in pixels
scrollbarColor: string; // Scrollbar thumb color

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,135 @@
{
"date": "2025-08-05",
"resources": [
{
"name": "karina.knudsen",
"displayName": "Karina Knudsen",
"avatarUrl": "/avatars/karina.jpg",
"employeeId": "EMP001",
"events": [
{
"id": "1",
"title": "Balayage langt hår",
"start": "2025-08-05T10:00:00",
"end": "2025-08-05T11:00:00",
"type": "work",
"allDay": false,
"syncStatus": "synced",
"metadata": { "duration": 60, "color": "#9c27b0" }
},
{
"id": "2",
"title": "Klipning og styling",
"start": "2025-08-05T14:00:00",
"end": "2025-08-05T15:30:00",
"type": "work",
"allDay": false,
"syncStatus": "synced",
"metadata": { "duration": 90, "color": "#e91e63" }
}
]
},
{
"name": "maria.hansen",
"displayName": "Maria Hansen",
"avatarUrl": "/avatars/maria.jpg",
"employeeId": "EMP002",
"events": [
{
"id": "3",
"title": "Permanent",
"start": "2025-08-05T09:00:00",
"end": "2025-08-05T11:00:00",
"type": "work",
"allDay": false,
"syncStatus": "synced",
"metadata": { "duration": 120, "color": "#3f51b5" }
},
{
"id": "4",
"title": "Farve behandling",
"start": "2025-08-05T13:00:00",
"end": "2025-08-05T15:00:00",
"type": "work",
"allDay": false,
"syncStatus": "synced",
"metadata": { "duration": 120, "color": "#ff9800" }
}
]
},
{
"name": "lars.nielsen",
"displayName": "Lars Nielsen",
"avatarUrl": "/avatars/lars.jpg",
"employeeId": "EMP003",
"events": [
{
"id": "5",
"title": "Herreklipning",
"start": "2025-08-05T11:00:00",
"end": "2025-08-05T11:30:00",
"type": "work",
"allDay": false,
"syncStatus": "synced",
"metadata": { "duration": 30, "color": "#795548" }
},
{
"id": "6",
"title": "Skæg trimning",
"start": "2025-08-05T16:00:00",
"end": "2025-08-05T16:30:00",
"type": "work",
"allDay": false,
"syncStatus": "synced",
"metadata": { "duration": 30, "color": "#607d8b" }
}
]
},
{
"name": "anna.petersen",
"displayName": "Anna Petersen",
"avatarUrl": "/avatars/anna.jpg",
"employeeId": "EMP004",
"events": [
{
"id": "7",
"title": "Bryllupsfrisure",
"start": "2025-08-05T08:00:00",
"end": "2025-08-05T10:00:00",
"type": "work",
"allDay": false,
"syncStatus": "synced",
"metadata": { "duration": 120, "color": "#009688" }
}
]
},
{
"name": "thomas.olsen",
"displayName": "Thomas Olsen",
"avatarUrl": "/avatars/thomas.jpg",
"employeeId": "EMP005",
"events": [
{
"id": "8",
"title": "Highlights",
"start": "2025-08-05T12:00:00",
"end": "2025-08-05T14:00:00",
"type": "work",
"allDay": false,
"syncStatus": "synced",
"metadata": { "duration": 120, "color": "#8bc34a" }
},
{
"id": "9",
"title": "Styling konsultation",
"start": "2025-08-05T15:00:00",
"end": "2025-08-05T15:30:00",
"type": "meeting",
"allDay": false,
"syncStatus": "synced",
"metadata": { "duration": 30, "color": "#cddc39" }
}
]
}
]
}