diff --git a/src/managers/GridManager.ts b/src/managers/GridManager.ts index 22e7861..d248b34 100644 --- a/src/managers/GridManager.ts +++ b/src/managers/GridManager.ts @@ -1,294 +1,256 @@ -// Grid structure management - Simple CSS Grid Implementation with Strategy Pattern +/** + * GridManager - Simplified grid manager using Strategy Pattern + * Now delegates view-specific logic to strategy implementations + */ import { eventBus } from '../core/EventBus'; import { calendarConfig } from '../core/CalendarConfig'; import { EventTypes } from '../constants/EventTypes'; -import { DateCalculator } from '../utils/DateCalculator'; -import { ResourceCalendarData } from '../types/CalendarTypes'; +import { CoreEvents } from '../constants/CoreEvents'; +import { ResourceCalendarData, CalendarView } from '../types/CalendarTypes'; import { AllDayEvent } from '../types/EventTypes'; -import { GridRenderer } from '../renderers/GridRenderer'; -import { GridStyleManager } from '../renderers/GridStyleManager'; +import { ViewStrategy, ViewContext } from '../strategies/ViewStrategy'; +import { WeekViewStrategy } from '../strategies/WeekViewStrategy'; +import { MonthViewStrategy } from '../strategies/MonthViewStrategy'; /** - * Grid position interface - */ -interface GridPosition { - minutes: number; - time: string; - y: number; -} - -/** - * Manages the calendar grid structure using simple CSS Grid with Strategy Pattern + * Simplified GridManager focused on coordination, not implementation */ export class GridManager { private container: HTMLElement | null = null; - private grid: HTMLElement | null = null; - private currentWeek: Date | null = null; - private allDayEvents: AllDayEvent[] = []; // Store all-day events for current week - private resourceData: ResourceCalendarData | null = null; // Store resource data for resource calendar - private gridRenderer: GridRenderer; - private styleManager: GridStyleManager; - private dateCalculator: DateCalculator; + private currentDate: Date = new Date(); + private allDayEvents: AllDayEvent[] = []; + private resourceData: ResourceCalendarData | null = null; + private currentStrategy: ViewStrategy; + private eventCleanup: (() => void)[] = []; constructor() { - console.log('🏗️ GridManager: Constructor called'); - this.gridRenderer = new GridRenderer(calendarConfig); - this.styleManager = new GridStyleManager(calendarConfig); - this.dateCalculator = new DateCalculator(calendarConfig); + console.log('🏗️ GridManager: Constructor called with Strategy Pattern'); + + // Default to week view strategy + this.currentStrategy = new WeekViewStrategy(); + this.init(); } - + private init(): void { this.findElements(); this.subscribeToEvents(); - // Set initial current week to today if not set - if (!this.currentWeek) { - this.currentWeek = this.dateCalculator.getISOWeekStart(new Date()); - console.log('GridManager: Set initial currentWeek to', this.currentWeek); - // Don't render immediately - wait for proper initialization event - console.log('GridManager: Waiting for initialization complete before rendering'); - } + console.log('GridManager: Initialized with strategy pattern'); } - - private getWeekStart(date: Date): Date { - // Use DateCalculator for ISO week (Monday = start) - return this.dateCalculator.getISOWeekStart(date); - } - + private findElements(): void { - this.grid = document.querySelector('swp-calendar-container'); - console.log('GridManager: findElements called, found swp-calendar-container:', !!this.grid); + this.container = document.querySelector('swp-calendar-container'); + console.log('GridManager: Found container:', !!this.container); } - + private subscribeToEvents(): void { - // State-driven events removed - render() is now called directly by CalendarManager - - // Re-render grid on config changes - eventBus.on(EventTypes.CONFIG_UPDATE, (e: Event) => { - const detail = (e as CustomEvent).detail; - if (['dayStartHour', 'dayEndHour', 'hourHeight', 'view', 'weekDays', 'fitToWidth'].includes(detail.key)) { + // Listen for view changes to switch strategies + this.eventCleanup.push( + eventBus.on(EventTypes.VIEW_CHANGED, (e: Event) => { + const detail = (e as CustomEvent).detail; + this.switchViewStrategy(detail.currentView); + }) + ); + + // Listen for data changes + this.eventCleanup.push( + eventBus.on(EventTypes.DATE_CHANGED, (e: Event) => { + const detail = (e as CustomEvent).detail; + this.currentDate = detail.currentDate; this.render(); - } - }); - - // Re-render on calendar type change - eventBus.on(EventTypes.CALENDAR_TYPE_CHANGED, () => { - this.render(); - }); - - // Re-render on view change - eventBus.on(EventTypes.VIEW_CHANGE, () => { - this.render(); - }); - - // Re-render on period change - eventBus.on(EventTypes.PERIOD_CHANGE, (e: Event) => { - const detail = (e as CustomEvent).detail; - this.currentWeek = this.dateCalculator.getISOWeekStart(detail.week); - this.render(); - }); - - // Handle week changes from NavigationManager - eventBus.on(EventTypes.WEEK_CHANGED, (e: Event) => { - const detail = (e as CustomEvent).detail; - this.currentWeek = this.dateCalculator.getISOWeekStart(detail.weekStart); - this.render(); - }); - - // Handle date changes from CalendarManager - eventBus.on(EventTypes.DATE_CHANGED, (e: Event) => { - const detail = (e as CustomEvent).detail; - this.currentWeek = this.dateCalculator.getISOWeekStart(detail.currentDate); - this.render(); - }); - - - // Handle events loaded - eventBus.on(EventTypes.EVENTS_LOADED, (e: Event) => { - const detail = (e as CustomEvent).detail; - this.updateAllDayEvents(detail.events); - }); - - - // Handle grid clicks - this.setupGridInteractions(); + }) + ); + + this.eventCleanup.push( + eventBus.on(EventTypes.WEEK_CHANGED, (e: Event) => { + const detail = (e as CustomEvent).detail; + this.currentDate = detail.weekStart; + this.render(); + }) + ); + + this.eventCleanup.push( + eventBus.on(EventTypes.EVENTS_LOADED, (e: Event) => { + const detail = (e as CustomEvent).detail; + this.updateAllDayEvents(detail.events); + }) + ); + + // Listen for config changes that affect rendering + this.eventCleanup.push( + eventBus.on(EventTypes.CONFIG_UPDATE, (e: Event) => { + this.render(); + }) + ); + + this.eventCleanup.push( + eventBus.on(EventTypes.WORKWEEK_CHANGED, () => { + this.render(); + }) + ); } - + + /** + * Switch to a different view strategy + */ + public switchViewStrategy(view: CalendarView): void { + console.log(`GridManager: Switching to ${view} strategy`); + + // Clean up current strategy + this.currentStrategy.destroy(); + + // Create new strategy based on view + switch (view) { + case 'week': + case 'day': + this.currentStrategy = new WeekViewStrategy(); + break; + case 'month': + this.currentStrategy = new MonthViewStrategy(); + break; + default: + console.warn(`GridManager: Unknown view type ${view}, defaulting to week`); + this.currentStrategy = new WeekViewStrategy(); + } + + // Re-render with new strategy + this.render(); + } + /** * Set resource data for resource calendar mode */ public setResourceData(resourceData: ResourceCalendarData | null): void { this.resourceData = resourceData; - console.log('GridManager: Set resource data:', resourceData ? `${resourceData.resources.length} resources` : 'null'); + console.log('GridManager: Updated resource data'); + this.render(); } - + /** - * Render the complete grid structure - now returns Promise for direct calls + * Main render method - delegates to current strategy */ - async render(): Promise { - if (!this.grid) { - console.warn('GridManager: render() called but this.grid is null, re-finding elements'); - this.findElements(); - if (!this.grid) { - throw new Error('GridManager: swp-calendar-container not found, cannot render'); - } + public async render(): Promise { + if (!this.container) { + console.warn('GridManager: No container found, cannot render'); + return; } - - console.group(`🏗️ GRID RENDER: ${this.currentWeek?.toDateString()}`); - console.log('Updating grid styles and rendering structure...'); - this.styleManager.updateGridStyles(this.resourceData); - this.gridRenderer.renderGrid(this.grid, this.currentWeek!, this.resourceData, this.allDayEvents); + console.group(`🎨 GRID RENDER: ${this.currentDate.toDateString()}`); + console.log(`Using strategy: ${this.currentStrategy.constructor.name}`); - const columnCount = this.styleManager.getColumnCount(this.resourceData); - console.log(`Grid structure complete - ${columnCount} columns created`); + // Create context for strategy + const context: ViewContext = { + currentDate: this.currentDate, + container: this.container, + allDayEvents: this.allDayEvents, + resourceData: this.resourceData + }; - // Emit GRID_RENDERED event to trigger event rendering - const weekEnd = this.currentWeek ? new Date(this.currentWeek.getTime() + 6 * 24 * 60 * 60 * 1000) : null; - eventBus.emit(EventTypes.GRID_RENDERED, { - container: this.grid, - currentWeek: this.currentWeek, - startDate: this.currentWeek, - endDate: weekEnd, - columnCount: columnCount + // Delegate to current strategy + this.currentStrategy.renderGrid(context); + + // Get layout info from strategy + const layoutConfig = this.currentStrategy.getLayoutConfig(); + + // Emit grid rendered event + eventBus.emit(CoreEvents.GRID_RENDERED, { + container: this.container, + currentDate: this.currentDate, + layoutConfig: layoutConfig, + columnCount: layoutConfig.columnCount }); + console.log(`Grid rendered with ${layoutConfig.columnCount} columns`); console.groupEnd(); } - - // Column count calculation moved to GridStyleManager - - // Grid rendering methods moved to GridRenderer - + /** - * Update all-day events data and re-render if needed + * Update all-day events and re-render */ private updateAllDayEvents(events: AllDayEvent[]): void { - if (!this.currentWeek) return; - - // Filter all-day events for current week - const weekStart = this.currentWeek; - const weekEnd = new Date(weekStart); - weekEnd.setDate(weekStart.getDate() + 6); - - this.allDayEvents = events.filter(event => { - if (!event.allDay) return false; - - const eventDate = new Date(event.start); - return eventDate >= weekStart && eventDate <= weekEnd; - }); - - console.log('GridManager: Updated all-day events:', this.allDayEvents.length); - - // Update only the calendar header if grid is already rendered - if (this.grid && this.grid.children.length > 0) { - this.gridRenderer.renderGrid(this.grid, this.currentWeek!, this.resourceData, this.allDayEvents); - } + console.log(`GridManager: Updating ${events.length} all-day events`); + this.allDayEvents = events.filter(event => event.allDay); + this.render(); } - - // CSS management methods moved to GridStyleManager - - /** - * Setup grid interaction handlers for POC structure - */ - private setupGridInteractions(): void { - if (!this.grid) return; - - // Click handler for day columns (works for both date and resource columns) - this.grid.addEventListener('click', (e: MouseEvent) => { - // Ignore if clicking on an event - if ((e.target as Element).closest('swp-event')) return; - - const dayColumn = (e.target as Element).closest('swp-day-column, swp-resource-column') as HTMLElement; - if (!dayColumn) return; - - const position = this.getClickPosition(e, dayColumn); - - eventBus.emit(EventTypes.GRID_CLICK, { - date: (dayColumn as any).dataset.date, - resource: (dayColumn as any).dataset.resource, - employeeId: (dayColumn as any).dataset.employeeId, - time: position.time, - minutes: position.minutes - }); - }); - - // Double click handler for day columns - this.grid.addEventListener('dblclick', (e: MouseEvent) => { - // Ignore if clicking on an event - if ((e.target as Element).closest('swp-event')) return; - - const dayColumn = (e.target as Element).closest('swp-day-column, swp-resource-column') as HTMLElement; - if (!dayColumn) return; - - const position = this.getClickPosition(e, dayColumn); - - eventBus.emit(EventTypes.GRID_DBLCLICK, { - date: (dayColumn as any).dataset.date, - resource: (dayColumn as any).dataset.resource, - employeeId: (dayColumn as any).dataset.employeeId, - time: position.time, - minutes: position.minutes - }); - }); - } - - /** - * Get click position in day column (POC structure) - */ - private getClickPosition(event: MouseEvent, dayColumn: HTMLElement): GridPosition { - const rect = dayColumn.getBoundingClientRect(); - const y = event.clientY - rect.top; - - const gridSettings = calendarConfig.getGridSettings(); - const hourHeight = gridSettings.hourHeight; - const minuteHeight = hourHeight / 60; - const snapInterval = gridSettings.snapInterval; - const dayStartHour = gridSettings.dayStartHour; - - // Calculate total minutes from day start - let totalMinutes = Math.floor(y / minuteHeight); - - // Snap to interval - totalMinutes = Math.round(totalMinutes / snapInterval) * snapInterval; - - // Add day start offset - totalMinutes += dayStartHour * 60; - - return { - minutes: totalMinutes, - time: this.minutesToTime(totalMinutes), - y: y - }; - } - - /** - * Scroll to specific hour - */ - scrollToHour(hour: number): void { - if (!this.grid) return; - - const gridSettings = calendarConfig.getGridSettings(); - const hourHeight = gridSettings.hourHeight; - const dayStartHour = gridSettings.dayStartHour; - const headerHeight = 80; // Header row height - const scrollTop = headerHeight + ((hour - dayStartHour) * hourHeight); - - this.grid.scrollTop = scrollTop; - } - - /** - * Utility methods - */ - private minutesToTime(totalMinutes: number): string { - const hours = Math.floor(totalMinutes / 60); - const minutes = totalMinutes % 60; - const period = hours >= 12 ? 'PM' : 'AM'; - const displayHour = hours > 12 ? hours - 12 : (hours === 0 ? 12 : hours); + /** + * Get current period label from strategy + */ + public getCurrentPeriodLabel(): string { + return this.currentStrategy.getPeriodLabel(this.currentDate); + } + + /** + * Navigate to next period using strategy + */ + public navigateNext(): void { + const nextDate = this.currentStrategy.getNextPeriod(this.currentDate); + this.currentDate = nextDate; - return `${displayHour}:${minutes.toString().padStart(2, '0')} ${period}`; + eventBus.emit(CoreEvents.PERIOD_CHANGED, { + direction: 'next', + newDate: nextDate, + periodLabel: this.getCurrentPeriodLabel() + }); + + this.render(); + } + + /** + * Navigate to previous period using strategy + */ + public navigatePrevious(): void { + const prevDate = this.currentStrategy.getPreviousPeriod(this.currentDate); + this.currentDate = prevDate; + + eventBus.emit(CoreEvents.PERIOD_CHANGED, { + direction: 'previous', + newDate: prevDate, + periodLabel: this.getCurrentPeriodLabel() + }); + + this.render(); + } + + /** + * Navigate to today + */ + public navigateToToday(): void { + this.currentDate = new Date(); + + eventBus.emit(CoreEvents.DATE_CHANGED, { + newDate: this.currentDate, + periodLabel: this.getCurrentPeriodLabel() + }); + + this.render(); + } + + /** + * Get current view's display dates + */ + public getDisplayDates(): Date[] { + return this.currentStrategy.getDisplayDates(this.currentDate); + } + + /** + * Clean up all resources + */ + public destroy(): void { + console.log('GridManager: Cleaning up'); + + // Clean up event listeners + this.eventCleanup.forEach(cleanup => cleanup()); + this.eventCleanup = []; + + // Clean up current strategy + this.currentStrategy.destroy(); + + // Clear references + this.container = null; + this.allDayEvents = []; + this.resourceData = null; } } \ No newline at end of file diff --git a/src/renderers/EventRendererManager.ts b/src/renderers/EventRendererManager.ts index 294bbd9..743fca5 100644 --- a/src/renderers/EventRendererManager.ts +++ b/src/renderers/EventRendererManager.ts @@ -1,6 +1,7 @@ import { EventBus } from '../core/EventBus'; import { IEventBus, CalendarEvent, RenderContext } from '../types/CalendarTypes'; import { EventTypes } from '../constants/EventTypes'; +import { CoreEvents } from '../constants/CoreEvents'; import { calendarConfig } from '../core/CalendarConfig'; import { CalendarTypeFactory } from '../factories/CalendarTypeFactory'; import { EventManager } from '../managers/EventManager'; @@ -54,7 +55,13 @@ export class EventRenderingService { private setupEventListeners(): void { // Event-driven rendering: React to grid and container events this.eventBus.on(EventTypes.GRID_RENDERED, (event: Event) => { - console.log('EventRenderer: Received GRID_RENDERED event'); + console.log('EventRenderer: Received GRID_RENDERED event (legacy)'); + this.handleGridRendered(event as CustomEvent); + }); + + // Listen to new CoreEvents system as well + this.eventBus.on(CoreEvents.GRID_RENDERED, (event: Event) => { + console.log('EventRenderer: Received GRID_RENDERED event (core)'); this.handleGridRendered(event as CustomEvent); }); @@ -82,16 +89,32 @@ export class EventRenderingService { * Handle GRID_RENDERED event - render events in the current grid */ private handleGridRendered(event: CustomEvent): void { - const { container, startDate, endDate } = event.detail; + const { container, startDate, endDate, currentDate } = event.detail; if (!container) { console.error('EventRenderer: No container in GRID_RENDERED event', event.detail); return; } - // Use period from event or fallback to calculated period - const periodStart = startDate; - const periodEnd = endDate; + let periodStart: Date; + let periodEnd: Date; + + if (startDate && endDate) { + // Legacy format with explicit dates + periodStart = startDate; + periodEnd = endDate; + } else if (currentDate) { + // New format - calculate period from current date + // For now, use a week period. This could be improved by getting the actual period from the strategy + const baseDate = new Date(currentDate); + periodStart = new Date(baseDate); + periodStart.setDate(baseDate.getDate() - baseDate.getDay() + 1); // Monday + periodEnd = new Date(periodStart); + periodEnd.setDate(periodStart.getDate() + 6); // Sunday + } else { + console.error('EventRenderer: No date information in GRID_RENDERED event', event.detail); + return; + } this.renderEvents({ container: container, diff --git a/src/strategies/MonthViewStrategy.ts b/src/strategies/MonthViewStrategy.ts new file mode 100644 index 0000000..b921fe8 --- /dev/null +++ b/src/strategies/MonthViewStrategy.ts @@ -0,0 +1,148 @@ +/** + * MonthViewStrategy - Strategy for month view rendering + * Completely different from week view - no time axis, cell-based events + */ + +import { ViewStrategy, ViewContext, ViewLayoutConfig } from './ViewStrategy'; +import { DateCalculator } from '../utils/DateCalculator'; +import { calendarConfig } from '../core/CalendarConfig'; + +export class MonthViewStrategy implements ViewStrategy { + private dateCalculator: DateCalculator; + + constructor() { + this.dateCalculator = new DateCalculator(calendarConfig); + } + + getLayoutConfig(): ViewLayoutConfig { + return { + needsTimeAxis: false, // No time axis in month view! + columnCount: 7, // Always 7 days (Mon-Sun) + scrollable: false, // Month fits in viewport + eventPositioning: 'cell-based' // Events go in day cells + }; + } + + renderGrid(context: ViewContext): void { + console.group(`📅 MONTH VIEW: Rendering grid for ${context.currentDate.toDateString()}`); + + // Clear existing content + context.container.innerHTML = ''; + + // Create month grid (completely different from week!) + this.createMonthGrid(context); + + console.log('Month grid rendered with 7x6 layout'); + console.groupEnd(); + } + + private createMonthGrid(context: ViewContext): void { + const monthGrid = document.createElement('div'); + monthGrid.className = 'month-grid'; + monthGrid.style.display = 'grid'; + monthGrid.style.gridTemplateColumns = 'repeat(7, 1fr)'; + monthGrid.style.gridTemplateRows = 'auto repeat(6, 1fr)'; + monthGrid.style.height = '100%'; + + // Add day headers (Mon, Tue, Wed, etc.) + this.createDayHeaders(monthGrid); + + // Add 6 weeks of day cells + this.createDayCells(monthGrid, context.currentDate); + + // Render events in day cells + this.renderMonthEvents(monthGrid, context.allDayEvents); + + context.container.appendChild(monthGrid); + } + + private createDayHeaders(container: HTMLElement): void { + const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; + + dayNames.forEach(dayName => { + const header = document.createElement('div'); + header.className = 'month-day-header'; + header.textContent = dayName; + header.style.padding = '8px'; + header.style.fontWeight = 'bold'; + header.style.textAlign = 'center'; + header.style.borderBottom = '1px solid #e0e0e0'; + container.appendChild(header); + }); + } + + private createDayCells(container: HTMLElement, monthDate: Date): void { + const dates = this.getMonthDates(monthDate); + + dates.forEach(date => { + const cell = document.createElement('div'); + cell.className = 'month-day-cell'; + cell.dataset.date = this.dateCalculator.formatISODate(date); + cell.style.border = '1px solid #e0e0e0'; + cell.style.minHeight = '100px'; + cell.style.padding = '4px'; + cell.style.position = 'relative'; + + // Day number + const dayNumber = document.createElement('div'); + dayNumber.className = 'month-day-number'; + dayNumber.textContent = date.getDate().toString(); + dayNumber.style.fontWeight = 'bold'; + dayNumber.style.marginBottom = '4px'; + + // Check if today + if (this.dateCalculator.isToday(date)) { + dayNumber.style.color = '#1976d2'; + cell.style.backgroundColor = '#f5f5f5'; + } + + cell.appendChild(dayNumber); + container.appendChild(cell); + }); + } + + private getMonthDates(monthDate: Date): Date[] { + // Get first day of month + const firstOfMonth = new Date(monthDate.getFullYear(), monthDate.getMonth(), 1); + + // Get Monday of the week containing first day + const startDate = this.dateCalculator.getISOWeekStart(firstOfMonth); + + // Generate 42 days (6 weeks) + const dates: Date[] = []; + for (let i = 0; i < 42; i++) { + dates.push(this.dateCalculator.addDays(startDate, i)); + } + + return dates; + } + + private renderMonthEvents(container: HTMLElement, events: any[]): void { + // TODO: Implement month event rendering + // Events will be small blocks in day cells + console.log(`MonthViewStrategy: Would render ${events.length} events`); + } + + getNextPeriod(currentDate: Date): Date { + return new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1); + } + + getPreviousPeriod(currentDate: Date): Date { + return new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1); + } + + getPeriodLabel(date: Date): string { + const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December']; + + return `${monthNames[date.getMonth()]} ${date.getFullYear()}`; + } + + getDisplayDates(baseDate: Date): Date[] { + return this.getMonthDates(baseDate); + } + + destroy(): void { + console.log('MonthViewStrategy: Cleaning up'); + } +} \ No newline at end of file diff --git a/src/strategies/ViewStrategy.ts b/src/strategies/ViewStrategy.ts new file mode 100644 index 0000000..577bf87 --- /dev/null +++ b/src/strategies/ViewStrategy.ts @@ -0,0 +1,67 @@ +/** + * ViewStrategy - Strategy pattern for different calendar view types + * Allows clean separation between week view, month view, day view etc. + */ + +import { AllDayEvent } from '../types/EventTypes'; +import { ResourceCalendarData } from '../types/CalendarTypes'; + +/** + * Context object passed to strategy methods + */ +export interface ViewContext { + currentDate: Date; + container: HTMLElement; + allDayEvents: AllDayEvent[]; + resourceData: ResourceCalendarData | null; +} + +/** + * Layout configuration specific to each view type + */ +export interface ViewLayoutConfig { + needsTimeAxis: boolean; + columnCount: number; + scrollable: boolean; + eventPositioning: 'time-based' | 'cell-based'; +} + +/** + * Base strategy interface for all view types + */ +export interface ViewStrategy { + /** + * Get the layout configuration for this view + */ + getLayoutConfig(): ViewLayoutConfig; + + /** + * Render the grid structure for this view + */ + renderGrid(context: ViewContext): void; + + /** + * Calculate next period for navigation + */ + getNextPeriod(currentDate: Date): Date; + + /** + * Calculate previous period for navigation + */ + getPreviousPeriod(currentDate: Date): Date; + + /** + * Get display label for current period + */ + getPeriodLabel(date: Date): string; + + /** + * Get the dates that should be displayed in this view + */ + getDisplayDates(baseDate: Date): Date[]; + + /** + * Clean up any view-specific resources + */ + destroy(): void; +} \ No newline at end of file diff --git a/src/strategies/WeekViewStrategy.ts b/src/strategies/WeekViewStrategy.ts new file mode 100644 index 0000000..1eaf839 --- /dev/null +++ b/src/strategies/WeekViewStrategy.ts @@ -0,0 +1,75 @@ +/** + * WeekViewStrategy - Strategy for week/day view rendering + * Extracts the time-based grid logic from GridManager + */ + +import { ViewStrategy, ViewContext, ViewLayoutConfig } from './ViewStrategy'; +import { DateCalculator } from '../utils/DateCalculator'; +import { calendarConfig } from '../core/CalendarConfig'; +import { GridRenderer } from '../renderers/GridRenderer'; +import { GridStyleManager } from '../renderers/GridStyleManager'; + +export class WeekViewStrategy implements ViewStrategy { + private dateCalculator: DateCalculator; + private gridRenderer: GridRenderer; + private styleManager: GridStyleManager; + + constructor() { + this.dateCalculator = new DateCalculator(calendarConfig); + this.gridRenderer = new GridRenderer(calendarConfig); + this.styleManager = new GridStyleManager(calendarConfig); + } + + getLayoutConfig(): ViewLayoutConfig { + return { + needsTimeAxis: true, + columnCount: calendarConfig.getWorkWeekSettings().totalDays, + scrollable: true, + eventPositioning: 'time-based' + }; + } + + renderGrid(context: ViewContext): void { + console.group(`🗓️ WEEK VIEW: Rendering grid for ${context.currentDate.toDateString()}`); + + // Update grid styles + this.styleManager.updateGridStyles(context.resourceData); + + // Render the grid structure (time axis + day columns) + this.gridRenderer.renderGrid( + context.container, + context.currentDate, + context.resourceData, + context.allDayEvents + ); + + console.log(`Week grid rendered with ${this.getLayoutConfig().columnCount} columns`); + console.groupEnd(); + } + + getNextPeriod(currentDate: Date): Date { + return this.dateCalculator.addWeeks(currentDate, 1); + } + + getPreviousPeriod(currentDate: Date): Date { + return this.dateCalculator.addWeeks(currentDate, -1); + } + + getPeriodLabel(date: Date): string { + const weekStart = this.dateCalculator.getISOWeekStart(date); + const weekEnd = this.dateCalculator.addDays(weekStart, 6); + const weekNumber = this.dateCalculator.getWeekNumber(date); + + return `Week ${weekNumber}: ${this.dateCalculator.formatDateRange(weekStart, weekEnd)}`; + } + + getDisplayDates(baseDate: Date): Date[] { + return this.dateCalculator.getWorkWeekDates(baseDate); + } + + destroy(): void { + // Clean up any week-specific resources + // For now, just log + console.log('WeekViewStrategy: Cleaning up'); + } +} \ No newline at end of file diff --git a/wwwroot/index.html b/wwwroot/index.html index 2219578..e72f644 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -46,7 +46,7 @@ Day Week - Month + Month