diff --git a/src/core/CalendarConfig.ts b/src/core/CalendarConfig.ts
index cef5e5c..1231c7a 100644
--- a/src/core/CalendarConfig.ts
+++ b/src/core/CalendarConfig.ts
@@ -40,10 +40,9 @@ interface DateViewSettings {
*/
interface WorkWeekSettings {
id: string;
- workDays: number[]; // [1,2,3,4,5] for mon-fri
- dayNames: string[]; // ['Mon','Tue','Wed','Thu','Fri']
+ workDays: number[]; // ISO 8601: [1,2,3,4,5] for mon-fri (1=Mon, 7=Sun)
totalDays: number; // 5
- firstWorkDay: number; // 1 = Monday
+ firstWorkDay: number; // ISO: 1 = Monday, 7 = Sunday
}
/**
@@ -442,38 +441,33 @@ export class CalendarConfig {
return {
'standard': {
id: 'standard',
- workDays: [1,2,3,4,5],
- dayNames: ['Mon','Tue','Wed','Thu','Fri'],
+ workDays: [1,2,3,4,5], // Monday-Friday (ISO)
totalDays: 5,
firstWorkDay: 1
},
'compressed': {
id: 'compressed',
- workDays: [1,2,3,4],
- dayNames: ['Mon','Tue','Wed','Thu'],
+ workDays: [1,2,3,4], // Monday-Thursday (ISO)
totalDays: 4,
firstWorkDay: 1
},
'midweek': {
id: 'midweek',
- workDays: [3,4,5],
- dayNames: ['Wed','Thu','Fri'],
+ workDays: [3,4,5], // Wednesday-Friday (ISO)
totalDays: 3,
firstWorkDay: 3
},
'weekend': {
id: 'weekend',
- workDays: [6,0],
- dayNames: ['Sat','Sun'],
+ workDays: [6,7], // Saturday-Sunday (ISO)
totalDays: 2,
firstWorkDay: 6
},
'fullweek': {
id: 'fullweek',
- workDays: [0,1,2,3,4,5,6],
- dayNames: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
+ workDays: [1,2,3,4,5,6,7], // Monday-Sunday (ISO)
totalDays: 7,
- firstWorkDay: 0
+ firstWorkDay: 1
}
};
}
@@ -511,6 +505,7 @@ export class CalendarConfig {
getCurrentWorkWeek(): string {
return this.currentWorkWeek;
}
+
}
// Create singleton instance
diff --git a/src/factories/CalendarTypeFactory.ts b/src/factories/CalendarTypeFactory.ts
index 844b63a..45a66cc 100644
--- a/src/factories/CalendarTypeFactory.ts
+++ b/src/factories/CalendarTypeFactory.ts
@@ -4,6 +4,7 @@ import { CalendarType } from '../types/CalendarTypes';
import { HeaderRenderer, DateHeaderRenderer, ResourceHeaderRenderer } from '../renderers/HeaderRenderer';
import { ColumnRenderer, DateColumnRenderer, ResourceColumnRenderer } from '../renderers/ColumnRenderer';
import { EventRendererStrategy, DateEventRenderer, ResourceEventRenderer } from '../renderers/EventRenderer';
+import { calendarConfig } from '../core/CalendarConfig';
/**
* Renderer configuration for a calendar type
@@ -34,13 +35,13 @@ export class CalendarTypeFactory {
this.registerRenderers('date', {
headerRenderer: new DateHeaderRenderer(),
columnRenderer: new DateColumnRenderer(),
- eventRenderer: new DateEventRenderer()
+ eventRenderer: new DateEventRenderer(calendarConfig)
});
this.registerRenderers('resource', {
headerRenderer: new ResourceHeaderRenderer(),
columnRenderer: new ResourceColumnRenderer(),
- eventRenderer: new ResourceEventRenderer()
+ eventRenderer: new ResourceEventRenderer(calendarConfig)
});
this.isInitialized = true;
diff --git a/src/factories/ManagerFactory.ts b/src/factories/ManagerFactory.ts
index 055c2d4..db24bb7 100644
--- a/src/factories/ManagerFactory.ts
+++ b/src/factories/ManagerFactory.ts
@@ -42,7 +42,7 @@ export class ManagerFactory {
const eventRenderer = new EventRenderingService(eventBus, eventManager);
const gridManager = new GridManager();
const scrollManager = new ScrollManager();
- const navigationManager = new NavigationManager(eventBus);
+ const navigationManager = new NavigationManager(eventBus, eventRenderer);
const viewManager = new ViewManager(eventBus);
// CalendarManager depends on all other managers
diff --git a/src/managers/GridManager.ts b/src/managers/GridManager.ts
index 358e09d..17150f6 100644
--- a/src/managers/GridManager.ts
+++ b/src/managers/GridManager.ts
@@ -4,7 +4,6 @@ import { eventBus } from '../core/EventBus';
import { calendarConfig } from '../core/CalendarConfig';
import { EventTypes } from '../constants/EventTypes';
import { StateEvents } from '../types/CalendarState';
-import { DateUtils } from '../utils/DateUtils';
import { DateCalculator } from '../utils/DateCalculator';
import { ResourceCalendarData } from '../types/CalendarTypes';
import { GridRenderer } from '../renderers/GridRenderer';
@@ -105,6 +104,7 @@ export class GridManager {
this.render();
});
+
// Handle events loaded
eventBus.on(EventTypes.EVENTS_LOADED, (e: Event) => {
const detail = (e as CustomEvent).detail;
@@ -147,12 +147,14 @@ export class GridManager {
}
}
- console.log('GridManager: Starting render with grid element:', this.grid);
+ 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);
const columnCount = this.styleManager.getColumnCount(this.resourceData);
- console.log(`GridManager: Render complete - created ${columnCount} columns`);
+ console.log(`Grid structure complete - ${columnCount} columns created`);
// Emit GRID_RENDERED event to trigger event rendering
const weekEnd = this.currentWeek ? new Date(this.currentWeek.getTime() + 6 * 24 * 60 * 60 * 1000) : null;
@@ -163,6 +165,8 @@ export class GridManager {
endDate: weekEnd,
columnCount: columnCount
});
+
+ console.groupEnd();
}
// Column count calculation moved to GridStyleManager
diff --git a/src/managers/NavigationManager.ts b/src/managers/NavigationManager.ts
index 2208b42..4c2d789 100644
--- a/src/managers/NavigationManager.ts
+++ b/src/managers/NavigationManager.ts
@@ -1,5 +1,6 @@
import { IEventBus } from '../types/CalendarTypes.js';
-import { DateUtils } from '../utils/DateUtils.js';
+import { EventRenderingService } from '../renderers/EventRendererManager.js';
+import { DateCalculator } from '../utils/DateCalculator.js';
import { EventTypes } from '../constants/EventTypes.js';
import { NavigationRenderer } from '../renderers/NavigationRenderer.js';
import { calendarConfig } from '../core/CalendarConfig.js';
@@ -11,15 +12,17 @@ import { calendarConfig } from '../core/CalendarConfig.js';
export class NavigationManager {
private eventBus: IEventBus;
private navigationRenderer: NavigationRenderer;
+ private dateCalculator: DateCalculator;
private currentWeek: Date;
private targetWeek: Date;
private animationQueue: number = 0;
- constructor(eventBus: IEventBus) {
+ constructor(eventBus: IEventBus, eventRenderer: EventRenderingService) {
console.log('🧭 NavigationManager: Constructor called');
this.eventBus = eventBus;
- this.navigationRenderer = new NavigationRenderer(eventBus, calendarConfig);
- this.currentWeek = DateUtils.getWeekStart(new Date(), 0); // Sunday start like POC
+ this.dateCalculator = new DateCalculator(calendarConfig);
+ this.navigationRenderer = new NavigationRenderer(eventBus, calendarConfig, eventRenderer);
+ this.currentWeek = this.dateCalculator.getISOWeekStart(new Date());
this.targetWeek = new Date(this.currentWeek);
this.init();
}
@@ -83,7 +86,7 @@ export class NavigationManager {
private navigateToToday(): void {
const today = new Date();
- const todayWeekStart = DateUtils.getWeekStart(today, 0);
+ const todayWeekStart = this.dateCalculator.getISOWeekStart(today);
// Reset to today
this.targetWeek = new Date(todayWeekStart);
@@ -101,7 +104,7 @@ export class NavigationManager {
}
private navigateToDate(date: Date): void {
- const weekStart = DateUtils.getWeekStart(date, 0);
+ const weekStart = this.dateCalculator.getISOWeekStart(date);
this.targetWeek = new Date(weekStart);
const currentTime = this.currentWeek.getTime();
@@ -128,14 +131,16 @@ export class NavigationManager {
return;
}
- console.log(`NavigationManager: Starting ${direction} animation to ${targetWeek.toDateString()}`);
-
+ console.group(`🎬 NAVIGATION ANIMATION: ${direction} to ${targetWeek.toDateString()}`);
+ console.log('1. Creating new container with events...');
+
let newGrid: HTMLElement;
// Always create a fresh container for consistent behavior
- console.log('NavigationManager: Creating new container');
newGrid = this.navigationRenderer.renderContainer(container as HTMLElement, targetWeek);
+ console.log('2. Starting slide animation...');
+
// Clear any existing transforms before animation
newGrid.style.transform = '';
(currentGrid as HTMLElement).style.transform = '';
@@ -161,6 +166,8 @@ export class NavigationManager {
// Handle animation completion
slideInAnimation.addEventListener('finish', () => {
+ console.log('3. Animation finished, cleaning up...');
+
// Cleanup: Remove all old grids except the new one
const allGrids = container.querySelectorAll('swp-grid-container');
for (let i = 0; i < allGrids.length - 1; i++) {
@@ -184,24 +191,24 @@ export class NavigationManager {
this.updateWeekInfo();
this.eventBus.emit(EventTypes.WEEK_CHANGED, {
weekStart: this.currentWeek,
- weekEnd: DateUtils.addDays(this.currentWeek, 6)
+ weekEnd: this.dateCalculator.addDays(this.currentWeek, 6)
});
-
// Emit animation complete event for ScrollManager
this.eventBus.emit(EventTypes.NAVIGATION_ANIMATION_COMPLETE, {
direction,
weekStart: this.currentWeek
});
- console.log(`NavigationManager: Completed ${direction} animation`);
+ console.log('✅ Animation completed successfully');
+ console.groupEnd();
});
}
private updateWeekInfo(): void {
- const weekNumber = DateUtils.getWeekNumber(this.currentWeek);
- const weekEnd = DateUtils.addDays(this.currentWeek, 6);
- const dateRange = DateUtils.formatDateRange(this.currentWeek, weekEnd);
+ const weekNumber = this.dateCalculator.getWeekNumber(this.currentWeek);
+ const weekEnd = this.dateCalculator.addDays(this.currentWeek, 6);
+ const dateRange = this.dateCalculator.formatDateRange(this.currentWeek, weekEnd);
// Notify other managers about week info update - DOM manipulation should happen via events
this.eventBus.emit(EventTypes.WEEK_INFO_UPDATED, {
diff --git a/src/managers/ViewManager.ts b/src/managers/ViewManager.ts
index 1f9c8f3..3b010b0 100644
--- a/src/managers/ViewManager.ts
+++ b/src/managers/ViewManager.ts
@@ -36,6 +36,7 @@ export class ViewManager {
// Setup workweek preset button handlers
this.setupWorkweekButtonHandlers();
+
}
private setupViewButtonHandlers(): void {
@@ -64,6 +65,7 @@ export class ViewManager {
});
}
+
private initializeView(): void {
this.updateViewButtons();
this.updateWorkweekButtons();
diff --git a/src/renderers/EventRenderer.ts b/src/renderers/EventRenderer.ts
index 1c71a39..4847a45 100644
--- a/src/renderers/EventRenderer.ts
+++ b/src/renderers/EventRenderer.ts
@@ -2,7 +2,7 @@
import { CalendarEvent } from '../types/CalendarTypes';
import { CalendarConfig } from '../core/CalendarConfig';
-import { DateUtils } from '../utils/DateUtils';
+import { DateCalculator } from '../utils/DateCalculator';
/**
* Interface for event rendering strategies
@@ -16,6 +16,11 @@ export interface EventRendererStrategy {
* Base class for event renderers with common functionality
*/
export abstract class BaseEventRenderer implements EventRendererStrategy {
+ protected dateCalculator: DateCalculator;
+
+ constructor(config: CalendarConfig) {
+ this.dateCalculator = new DateCalculator(config);
+ }
renderEvents(events: CalendarEvent[], container: HTMLElement, config: CalendarConfig): void {
console.log('BaseEventRenderer: renderEvents called with', events.length, 'events');
@@ -70,8 +75,8 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
eventElement.style.backgroundColor = event.metadata?.color || '#3498db';
// Format time for display
- const startTime = this.formatTime(event.start);
- const endTime = this.formatTime(event.end);
+ const startTime = this.dateCalculator.formatTime(new Date(event.start));
+ const endTime = this.dateCalculator.formatTime(new Date(event.end));
// Create event content
eventElement.innerHTML = `
@@ -160,15 +165,24 @@ export class DateEventRenderer extends BaseEventRenderer {
protected getEventsForColumn(column: HTMLElement, events: CalendarEvent[]): CalendarEvent[] {
const columnDate = column.dataset.date;
- if (!columnDate) return [];
+ if (!columnDate) {
+ console.log(`DateEventRenderer: Column has no dataset.date`);
+ return [];
+ }
const columnEvents = events.filter(event => {
const eventDate = new Date(event.start);
- const eventDateStr = DateUtils.formatDate(eventDate);
- return eventDateStr === columnDate;
+ const eventDateStr = this.dateCalculator.formatISODate(eventDate);
+ const matches = eventDateStr === columnDate;
+
+ if (!matches) {
+ console.log(`DateEventRenderer: Event ${event.title} (${eventDateStr}) does not match column (${columnDate})`);
+ }
+
+ return matches;
});
- console.log(`DateEventRenderer: Column ${columnDate} has ${columnEvents.length} events`);
+ console.log(`DateEventRenderer: Column ${columnDate} has ${columnEvents.length} events from ${events.length} total`);
return columnEvents;
}
}
diff --git a/src/renderers/EventRendererManager.ts b/src/renderers/EventRendererManager.ts
index 6590a08..13d3753 100644
--- a/src/renderers/EventRendererManager.ts
+++ b/src/renderers/EventRendererManager.ts
@@ -31,11 +31,7 @@ export class EventRenderingService {
* Render events in a specific container for a given period
*/
public renderEvents(context: RenderContext): void {
- console.log('EventRenderer: Rendering events for period', {
- startDate: context.startDate,
- endDate: context.endDate,
- container: context.container
- });
+ console.log(` 📅 Getting events for ${context.startDate.toDateString()} - ${context.endDate.toDateString()}`);
// Get events from EventManager for the period
const events = this.eventManager.getEventsForPeriod(
@@ -43,17 +39,17 @@ export class EventRenderingService {
context.endDate
);
- console.log(`EventRenderer: Found ${events.length} events for period`);
+ console.log(` 📊 Found ${events.length} events for period`);
if (events.length === 0) {
- console.log('EventRenderer: No events to render for this period');
+ console.log(' ⚠️ No events to render for this period');
return;
}
// Use cached strategy to render events in the specific container
this.strategy.renderEvents(events, context.container, calendarConfig);
- console.log(`EventRenderer: Successfully rendered ${events.length} events`);
+ console.log(` ✅ Rendered ${events.length} events successfully`);
}
private setupEventListeners(): void {
@@ -63,10 +59,11 @@ export class EventRenderingService {
this.handleGridRendered(event as CustomEvent);
});
- this.eventBus.on(EventTypes.CONTAINER_READY_FOR_EVENTS, (event: Event) => {
- console.log('EventRenderer: Received CONTAINER_READY_FOR_EVENTS event');
- this.handleContainerReady(event as CustomEvent);
- });
+ // CONTAINER_READY_FOR_EVENTS removed - events are now pre-rendered synchronously
+ // this.eventBus.on(EventTypes.CONTAINER_READY_FOR_EVENTS, (event: Event) => {
+ // console.log('EventRenderer: Received CONTAINER_READY_FOR_EVENTS event');
+ // this.handleContainerReady(event as CustomEvent);
+ // });
this.eventBus.on(EventTypes.VIEW_CHANGED, (event: Event) => {
console.log('EventRenderer: Received VIEW_CHANGED event');
diff --git a/src/renderers/HeaderRenderer.ts b/src/renderers/HeaderRenderer.ts
index 95488a0..7650206 100644
--- a/src/renderers/HeaderRenderer.ts
+++ b/src/renderers/HeaderRenderer.ts
@@ -37,15 +37,16 @@ export class DateHeaderRenderer implements HeaderRenderer {
const weekDays = config.get('weekDays');
const daysToShow = dates.slice(0, weekDays);
- const workWeekSettings = config.getWorkWeekSettings();
daysToShow.forEach((date, index) => {
const header = document.createElement('swp-day-header');
if (this.dateCalculator.isToday(date)) {
(header as any).dataset.today = 'true';
}
+ const dayName = this.dateCalculator.getDayName(date, 'short');
+
header.innerHTML = `
- ${workWeekSettings.dayNames[index]}
+ ${dayName}
${date.getDate()}
`;
(header as any).dataset.date = this.dateCalculator.formatISODate(date);
diff --git a/src/renderers/NavigationRenderer.ts b/src/renderers/NavigationRenderer.ts
index 0d3f614..88cf69d 100644
--- a/src/renderers/NavigationRenderer.ts
+++ b/src/renderers/NavigationRenderer.ts
@@ -1,8 +1,8 @@
import { IEventBus } from '../types/CalendarTypes';
import { EventTypes } from '../constants/EventTypes';
-import { DateUtils } from '../utils/DateUtils';
import { CalendarConfig } from '../core/CalendarConfig';
import { DateCalculator } from '../utils/DateCalculator';
+import { EventRenderingService } from './EventRendererManager';
/**
* NavigationRenderer - Handles DOM rendering for navigation containers
@@ -12,10 +12,12 @@ export class NavigationRenderer {
private eventBus: IEventBus;
private config: CalendarConfig;
private dateCalculator: DateCalculator;
+ private eventRenderer: EventRenderingService;
- constructor(eventBus: IEventBus, config: CalendarConfig) {
+ constructor(eventBus: IEventBus, config: CalendarConfig, eventRenderer: EventRenderingService) {
this.eventBus = eventBus;
this.config = config;
+ this.eventRenderer = eventRenderer;
this.dateCalculator = new DateCalculator(config);
this.setupEventListeners();
}
@@ -51,7 +53,10 @@ export class NavigationRenderer {
* Render a complete container with content and events
*/
public renderContainer(parentContainer: HTMLElement, weekStart: Date): HTMLElement {
- console.log('NavigationRenderer: Rendering new container for week:', weekStart.toDateString());
+ const weekEnd = this.dateCalculator.addDays(weekStart, 6);
+
+ console.group(`🎨 RENDERING CONTAINER: ${weekStart.toDateString()} - ${weekEnd.toDateString()}`);
+ console.log('1. Creating grid structure...');
// Create new grid container
const newGrid = document.createElement('swp-grid-container');
@@ -75,17 +80,18 @@ export class NavigationRenderer {
// Add to parent container
parentContainer.appendChild(newGrid);
- // Render week content (headers and columns)
+ console.log('2. Rendering headers and columns...');
this.renderWeekContentInContainer(newGrid, weekStart);
- // Emit event to trigger event rendering
- const weekEnd = DateUtils.addDays(weekStart, 6);
- this.eventBus.emit(EventTypes.CONTAINER_READY_FOR_EVENTS, {
+ console.log('3. Pre-rendering events synchronously...');
+ this.eventRenderer.renderEvents({
container: newGrid,
startDate: weekStart,
endDate: weekEnd
});
+ console.log('✅ Container ready with pre-rendered events');
+ console.groupEnd();
return newGrid;
}
@@ -104,7 +110,6 @@ export class NavigationRenderer {
// Get dates using DateCalculator
const dates = this.dateCalculator.getWorkWeekDates(weekStart);
- const workWeekSettings = this.config.getWorkWeekSettings();
// Render headers for target week
dates.forEach((date, i) => {
@@ -113,8 +118,10 @@ export class NavigationRenderer {
headerElement.dataset.today = 'true';
}
+ const dayName = this.dateCalculator.getDayName(date, 'short');
+
headerElement.innerHTML = `
- ${workWeekSettings.dayNames[i]}
+ ${dayName}
${date.getDate()}
`;
headerElement.dataset.date = this.dateCalculator.formatISODate(date);
diff --git a/src/utils/DateCalculator.ts b/src/utils/DateCalculator.ts
index be1574d..a795c53 100644
--- a/src/utils/DateCalculator.ts
+++ b/src/utils/DateCalculator.ts
@@ -13,7 +13,7 @@ export class DateCalculator {
}
/**
- * Get dates for work week based on ISO week (starts Monday)
+ * Get dates for work week using ISO 8601 day numbering (Monday=1, Sunday=7)
* @param weekStart - Any date in the week
* @returns Array of dates for the configured work days
*/
@@ -21,21 +21,15 @@ export class DateCalculator {
const dates: Date[] = [];
const workWeekSettings = this.config.getWorkWeekSettings();
- // Get Monday of the ISO week
- const monday = this.getISOWeekStart(weekStart);
+ // Always use ISO week start (Monday)
+ const mondayOfWeek = this.getISOWeekStart(weekStart);
- // Calculate dates for each work day
- workWeekSettings.workDays.forEach(dayOfWeek => {
- const date = new Date(monday);
-
- if (dayOfWeek === 0) {
- // Sunday is 6 days after Monday
- date.setDate(monday.getDate() + 6);
- } else {
- // Monday=1 becomes 0 days after, Tuesday=2 becomes 1 day after, etc.
- date.setDate(monday.getDate() + dayOfWeek - 1);
- }
-
+ // Calculate dates for each work day using ISO numbering
+ workWeekSettings.workDays.forEach(isoDay => {
+ const date = new Date(mondayOfWeek);
+ // ISO day 1=Monday is +0 days, ISO day 7=Sunday is +6 days
+ const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1;
+ date.setDate(mondayOfWeek.getDate() + daysFromMonday);
dates.push(date);
});
@@ -56,24 +50,14 @@ export class DateCalculator {
return monday;
}
- /**
- * Get the start of the week for a given date (legacy method)
- * @param date - Any date in the week
- * @param firstDayOfWeek - 0 for Sunday, 1 for Monday
- * @returns The start date of the week
- */
- getWeekStart(date: Date, firstDayOfWeek: number = 1): Date {
- return this.getISOWeekStart(date);
- }
/**
- * Get the end of the week for a given date
+ * Get the end of the ISO week for a given date
* @param date - Any date in the week
- * @param firstDayOfWeek - 0 for Sunday, 1 for Monday
- * @returns The end date of the week
+ * @returns The end date of the ISO week (Sunday)
*/
- getWeekEnd(date: Date, firstDayOfWeek: number = 1): Date {
- const weekStart = this.getWeekStart(date, firstDayOfWeek);
+ getWeekEnd(date: Date): Date {
+ const weekStart = this.getISOWeekStart(date);
const weekEnd = new Date(weekStart);
weekEnd.setDate(weekStart.getDate() + 6);
weekEnd.setHours(23, 59, 59, 999);
@@ -94,28 +78,40 @@ export class DateCalculator {
}
/**
- * Format a date range for display
+ * Format a date range with customizable options
* @param start - Start date
* @param end - End date
+ * @param options - Formatting options
* @returns Formatted date range string
*/
- formatDateRange(start: Date, end: Date): string {
- const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+ formatDateRange(
+ start: Date,
+ end: Date,
+ options: {
+ locale?: string;
+ month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow';
+ day?: 'numeric' | '2-digit';
+ year?: 'numeric' | '2-digit';
+ } = {}
+ ): string {
+ const { locale = 'en-US', month = 'short', day = 'numeric' } = options;
- const startMonth = months[start.getMonth()];
- const endMonth = months[end.getMonth()];
- const startDay = start.getDate();
- const endDay = end.getDate();
const startYear = start.getFullYear();
const endYear = end.getFullYear();
- if (startYear !== endYear) {
- return `${startMonth} ${startDay}, ${startYear} - ${endMonth} ${endDay}, ${endYear}`;
- } else if (startMonth === endMonth) {
- return `${startMonth} ${startDay} - ${endDay}, ${startYear}`;
- } else {
- return `${startMonth} ${startDay} - ${endMonth} ${endDay}, ${startYear}`;
+ const formatter = new Intl.DateTimeFormat(locale, {
+ month,
+ day,
+ year: startYear !== endYear ? 'numeric' : undefined
+ });
+
+ // @ts-ignore
+ if (typeof formatter.formatRange === 'function') {
+ // @ts-ignore
+ return formatter.formatRange(start, end);
}
+
+ return `${formatter.format(start)} - ${formatter.format(end)}`;
}
/**
@@ -173,18 +169,110 @@ export class DateCalculator {
}
/**
- * Get the day name for a date
+ * Get the day name for a date using Intl.DateTimeFormat
* @param date - Date to get day name for
* @param format - 'short' or 'long'
* @returns Day name
*/
getDayName(date: Date, format: 'short' | 'long' = 'short'): string {
- const days = {
- short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
- long: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
- };
- return days[format][date.getDay()];
+ const formatter = new Intl.DateTimeFormat('en-US', {
+ weekday: format
+ });
+ return formatter.format(date);
}
+
+ /**
+ * Format time to HH:MM
+ * @param date - Date to format
+ * @returns Time string
+ */
+ formatTime(date: Date): string {
+ return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
+ }
+
+ /**
+ * Format time to 12-hour format
+ * @param date - Date to format
+ * @returns 12-hour time string
+ */
+ formatTime12(date: Date): string {
+ const hours = date.getHours();
+ const minutes = date.getMinutes();
+ const period = hours >= 12 ? 'PM' : 'AM';
+ const displayHours = hours % 12 || 12;
+
+ return `${displayHours}:${String(minutes).padStart(2, '0')} ${period}`;
+ }
+
+ /**
+ * Convert minutes since midnight to time string
+ * @param minutes - Minutes since midnight
+ * @returns Time string
+ */
+ minutesToTime(minutes: number): string {
+ const hours = Math.floor(minutes / 60);
+ const mins = minutes % 60;
+ const period = hours >= 12 ? 'PM' : 'AM';
+ const displayHours = hours % 12 || 12;
+
+ return `${displayHours}:${String(mins).padStart(2, '0')} ${period}`;
+ }
+
+ /**
+ * Convert time string to minutes since midnight
+ * @param timeStr - Time string
+ * @returns Minutes since midnight
+ */
+ timeToMinutes(timeStr: string): number {
+ const [time] = timeStr.split('T').pop()!.split('.');
+ const [hours, minutes] = time.split(':').map(Number);
+ return hours * 60 + minutes;
+ }
+
+ /**
+ * Get minutes since start of day
+ * @param date - Date or ISO string
+ * @returns Minutes since midnight
+ */
+ getMinutesSinceMidnight(date: Date | string): number {
+ const d = typeof date === 'string' ? new Date(date) : date;
+ return d.getHours() * 60 + d.getMinutes();
+ }
+
+ /**
+ * Calculate duration in minutes between two dates
+ * @param start - Start date or ISO string
+ * @param end - End date or ISO string
+ * @returns Duration in minutes
+ */
+ getDurationMinutes(start: Date | string, end: Date | string): number {
+ const startDate = typeof start === 'string' ? new Date(start) : start;
+ const endDate = typeof end === 'string' ? new Date(end) : end;
+ return Math.floor((endDate.getTime() - startDate.getTime()) / 60000);
+ }
+
+ /**
+ * Check if two dates are on the same day
+ * @param date1 - First date
+ * @param date2 - Second date
+ * @returns True if same day
+ */
+ isSameDay(date1: Date, date2: Date): boolean {
+ return date1.toDateString() === date2.toDateString();
+ }
+
+ /**
+ * Check if event spans multiple days
+ * @param start - Start date or ISO string
+ * @param end - End date or ISO string
+ * @returns True if spans multiple days
+ */
+ isMultiDay(start: Date | string, end: Date | string): boolean {
+ const startDate = typeof start === 'string' ? new Date(start) : start;
+ const endDate = typeof end === 'string' ? new Date(end) : end;
+ return !this.isSameDay(startDate, endDate);
+ }
+
}
// Create singleton instance with config
diff --git a/src/utils/DateUtils.ts b/src/utils/DateUtils.ts
deleted file mode 100644
index 93ffc8d..0000000
--- a/src/utils/DateUtils.ts
+++ /dev/null
@@ -1,230 +0,0 @@
-// Date and time utility functions
-
-/**
- * Date and time utility functions
- */
-export class DateUtils {
- /**
- * Get start of week for a given date
- */
- static getWeekStart(date: Date, firstDayOfWeek: number = 1): Date {
- const d = new Date(date);
- const day = d.getDay();
- const diff = (day - firstDayOfWeek + 7) % 7;
- d.setDate(d.getDate() - diff);
- d.setHours(0, 0, 0, 0);
- return d;
- }
-
- /**
- * Get end of week for a given date
- */
- static getWeekEnd(date: Date, firstDayOfWeek: number = 1): Date {
- const start = this.getWeekStart(date, firstDayOfWeek);
- const end = new Date(start);
- end.setDate(end.getDate() + 6);
- end.setHours(23, 59, 59, 999);
- return end;
- }
-
- /**
- * Format date to YYYY-MM-DD
- */
- static formatDate(date: Date): string {
- return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
- }
-
- /**
- * Format time to HH:MM
- */
- static formatTime(date: Date): string {
- return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
- }
-
- /**
- * Format time to 12-hour format
- */
- static formatTime12(date: Date): string {
- const hours = date.getHours();
- const minutes = date.getMinutes();
- const period = hours >= 12 ? 'PM' : 'AM';
- const displayHours = hours % 12 || 12;
-
- return `${displayHours}:${String(minutes).padStart(2, '0')} ${period}`;
- }
-
- /**
- * Convert minutes since midnight to time string
- */
- static minutesToTime(minutes: number): string {
- const hours = Math.floor(minutes / 60);
- const mins = minutes % 60;
- const period = hours >= 12 ? 'PM' : 'AM';
- const displayHours = hours % 12 || 12;
-
- return `${displayHours}:${String(mins).padStart(2, '0')} ${period}`;
- }
-
- /**
- * Convert time string to minutes since midnight
- */
- static timeToMinutes(timeStr: string): number {
- const [time] = timeStr.split('T').pop()!.split('.');
- const [hours, minutes] = time.split(':').map(Number);
- return hours * 60 + minutes;
- }
-
- /**
- * Get minutes since start of day
- */
- static getMinutesSinceMidnight(date: Date | string): number {
- const d = typeof date === 'string' ? new Date(date) : date;
- return d.getHours() * 60 + d.getMinutes();
- }
-
- /**
- * Calculate duration in minutes between two dates
- */
- static getDurationMinutes(start: Date | string, end: Date | string): number {
- const startDate = typeof start === 'string' ? new Date(start) : start;
- const endDate = typeof end === 'string' ? new Date(end) : end;
- return Math.floor((endDate.getTime() - startDate.getTime()) / 60000);
- }
-
- /**
- * Check if date is today
- */
- static isToday(date: Date): boolean {
- const today = new Date();
- return date.toDateString() === today.toDateString();
- }
-
- /**
- * Check if two dates are on the same day
- */
- static isSameDay(date1: Date, date2: Date): boolean {
- return date1.toDateString() === date2.toDateString();
- }
-
- /**
- * Check if event spans multiple days
- */
- static isMultiDay(start: Date | string, end: Date | string): boolean {
- const startDate = typeof start === 'string' ? new Date(start) : start;
- const endDate = typeof end === 'string' ? new Date(end) : end;
- return !this.isSameDay(startDate, endDate);
- }
-
- /**
- * Get day name
- */
- static getDayName(date: Date, format: 'short' | 'long' = 'short'): string {
- const days = {
- short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
- long: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
- };
- return days[format][date.getDay()];
- }
-
- /**
- * Add days to date
- */
- static addDays(date: Date, days: number): Date {
- const result = new Date(date);
- result.setDate(result.getDate() + days);
- return result;
- }
-
- /**
- * Add minutes to date
- */
- static addMinutes(date: Date, minutes: number): Date {
- const result = new Date(date);
- result.setMinutes(result.getMinutes() + minutes);
- return result;
- }
-
- /**
- * Snap time to nearest interval
- */
- static snapToInterval(date: Date, intervalMinutes: number): Date {
- const minutes = date.getMinutes();
- const snappedMinutes = Math.round(minutes / intervalMinutes) * intervalMinutes;
- const result = new Date(date);
- result.setMinutes(snappedMinutes);
- result.setSeconds(0);
- result.setMilliseconds(0);
- return result;
- }
-
- /**
- * Get current time in minutes since day start
- */
- static getCurrentTimeMinutes(dayStartHour: number = 0): number {
- const now = new Date();
- const minutesSinceMidnight = now.getHours() * 60 + now.getMinutes();
- return minutesSinceMidnight - (dayStartHour * 60);
- }
-
- /**
- * Format duration to human readable string
- */
- static formatDuration(minutes: number): string {
- if (minutes < 60) {
- return `${minutes} min`;
- }
-
- const hours = Math.floor(minutes / 60);
- const mins = minutes % 60;
-
- if (mins === 0) {
- return `${hours} hour${hours > 1 ? 's' : ''}`;
- }
-
- return `${hours} hour${hours > 1 ? 's' : ''} ${mins} min`;
- }
-
- /**
- * Get ISO week number for a given date
- */
- static getWeekNumber(date: Date): number {
- const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
- const dayNum = d.getUTCDay() || 7;
- d.setUTCDate(d.getUTCDate() + 4 - dayNum);
- const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
- return Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
- }
-
- /**
- * Get month names array
- */
- static getMonthNames(format: 'short' | 'long' = 'short'): string[] {
- const months = {
- short: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
- long: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
- };
- return months[format];
- }
-
- /**
- * Format date range for display (e.g., "Jan 15 - 21, 2024" or "Jan 15 - Feb 2, 2024")
- */
- static formatDateRange(startDate: Date, endDate: Date): string {
- const monthNames = this.getMonthNames('short');
-
- const startMonth = monthNames[startDate.getMonth()];
- const endMonth = monthNames[endDate.getMonth()];
- const startDay = startDate.getDate();
- const endDay = endDate.getDate();
- const startYear = startDate.getFullYear();
- const endYear = endDate.getFullYear();
-
- if (startMonth === endMonth && startYear === endYear) {
- return `${startMonth} ${startDay} - ${endDay}, ${startYear}`;
- } else if (startYear !== endYear) {
- return `${startMonth} ${startDay}, ${startYear} - ${endMonth} ${endDay}, ${endYear}`;
- } else {
- return `${startMonth} ${startDay} - ${endMonth} ${endDay}, ${startYear}`;
- }
- }
-}
\ No newline at end of file
diff --git a/wwwroot/css/calendar-components-css.css b/wwwroot/css/calendar-components-css.css
index df8ff1c..950bfc4 100644
--- a/wwwroot/css/calendar-components-css.css
+++ b/wwwroot/css/calendar-components-css.css
@@ -125,6 +125,7 @@ swp-preset-button {
}
}
+
/* Search container */
swp-search-container {
margin-left: auto;