Introduces settings service for managing tenant-specific calendar configurations Enables dynamic workweek presets with configurable work days Improves view switching with enhanced UI components Adds flexible calendar rendering based on tenant settings Extends DateService to support workweek date generation
177 lines
5.4 KiB
TypeScript
177 lines
5.4 KiB
TypeScript
import dayjs from 'dayjs';
|
|
import utc from 'dayjs/plugin/utc';
|
|
import timezone from 'dayjs/plugin/timezone';
|
|
import isoWeek from 'dayjs/plugin/isoWeek';
|
|
import { ITimeFormatConfig } from './ITimeFormatConfig';
|
|
|
|
// Enable dayjs plugins
|
|
dayjs.extend(utc);
|
|
dayjs.extend(timezone);
|
|
dayjs.extend(isoWeek);
|
|
|
|
export class DateService {
|
|
private timezone: string;
|
|
private baseDate: dayjs.Dayjs;
|
|
|
|
constructor(private config: ITimeFormatConfig, baseDate?: Date) {
|
|
this.timezone = config.timezone;
|
|
// Allow setting a fixed base date for demo/testing purposes
|
|
this.baseDate = baseDate ? dayjs(baseDate) : dayjs();
|
|
}
|
|
|
|
/**
|
|
* Set a fixed base date (useful for demos with static mock data)
|
|
*/
|
|
setBaseDate(date: Date): void {
|
|
this.baseDate = dayjs(date);
|
|
}
|
|
|
|
/**
|
|
* Get the current base date (either fixed or today)
|
|
*/
|
|
getBaseDate(): Date {
|
|
return this.baseDate.toDate();
|
|
}
|
|
|
|
parseISO(isoString: string): Date {
|
|
return dayjs(isoString).toDate();
|
|
}
|
|
|
|
getDayName(date: Date, format: 'short' | 'long' = 'short'): string {
|
|
return new Intl.DateTimeFormat(this.config.locale, { weekday: format }).format(date);
|
|
}
|
|
|
|
getWeekDates(offset = 0, days = 7): string[] {
|
|
const monday = this.baseDate.startOf('week').add(1, 'day').add(offset, 'week');
|
|
return Array.from({ length: days }, (_, i) =>
|
|
monday.add(i, 'day').format('YYYY-MM-DD')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get dates for specific weekdays within a week
|
|
* @param offset - Week offset from base date (0 = current week)
|
|
* @param workDays - Array of ISO weekday numbers (1=Monday, 7=Sunday)
|
|
* @returns Array of date strings in YYYY-MM-DD format
|
|
*/
|
|
getWorkWeekDates(offset: number, workDays: number[]): string[] {
|
|
const monday = this.baseDate.startOf('week').add(1, 'day').add(offset, 'week');
|
|
return workDays.map(isoDay => {
|
|
// ISO: 1=Monday, 7=Sunday → days from Monday: 0-6
|
|
const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1;
|
|
return monday.add(daysFromMonday, 'day').format('YYYY-MM-DD');
|
|
});
|
|
}
|
|
|
|
// ============================================
|
|
// FORMATTING
|
|
// ============================================
|
|
|
|
formatTime(date: Date, showSeconds = false): string {
|
|
const pattern = showSeconds ? 'HH:mm:ss' : 'HH:mm';
|
|
return dayjs(date).format(pattern);
|
|
}
|
|
|
|
formatTimeRange(start: Date, end: Date): string {
|
|
return `${this.formatTime(start)} - ${this.formatTime(end)}`;
|
|
}
|
|
|
|
formatDate(date: Date): string {
|
|
return dayjs(date).format('YYYY-MM-DD');
|
|
}
|
|
|
|
getDateKey(date: Date): string {
|
|
return this.formatDate(date);
|
|
}
|
|
|
|
// ============================================
|
|
// COLUMN KEY
|
|
// ============================================
|
|
|
|
/**
|
|
* Build a uniform columnKey from grouping segments
|
|
* Handles any combination of date, resource, team, etc.
|
|
*
|
|
* @example
|
|
* buildColumnKey({ date: '2025-12-09' }) → "2025-12-09"
|
|
* buildColumnKey({ date: '2025-12-09', resource: 'EMP001' }) → "2025-12-09:EMP001"
|
|
*/
|
|
buildColumnKey(segments: Record<string, string>): string {
|
|
// Always put date first if present, then other segments alphabetically
|
|
const date = segments.date;
|
|
const others = Object.entries(segments)
|
|
.filter(([k]) => k !== 'date')
|
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
.map(([, v]) => v);
|
|
|
|
return date ? [date, ...others].join(':') : others.join(':');
|
|
}
|
|
|
|
/**
|
|
* Parse a columnKey back into segments
|
|
* Assumes format: "date:resource:..." or just "date"
|
|
*/
|
|
parseColumnKey(columnKey: string): { date: string; resource?: string } {
|
|
const parts = columnKey.split(':');
|
|
return {
|
|
date: parts[0],
|
|
resource: parts[1]
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Extract dateKey from columnKey (first segment)
|
|
*/
|
|
getDateFromColumnKey(columnKey: string): string {
|
|
return columnKey.split(':')[0];
|
|
}
|
|
|
|
// ============================================
|
|
// TIME CALCULATIONS
|
|
// ============================================
|
|
|
|
timeToMinutes(timeString: string): number {
|
|
const parts = timeString.split(':').map(Number);
|
|
const hours = parts[0] || 0;
|
|
const minutes = parts[1] || 0;
|
|
return hours * 60 + minutes;
|
|
}
|
|
|
|
minutesToTime(totalMinutes: number): string {
|
|
const hours = Math.floor(totalMinutes / 60);
|
|
const minutes = totalMinutes % 60;
|
|
return dayjs().hour(hours).minute(minutes).format('HH:mm');
|
|
}
|
|
|
|
getMinutesSinceMidnight(date: Date): number {
|
|
const d = dayjs(date);
|
|
return d.hour() * 60 + d.minute();
|
|
}
|
|
|
|
// ============================================
|
|
// UTC CONVERSIONS
|
|
// ============================================
|
|
|
|
toUTC(localDate: Date): string {
|
|
return dayjs.tz(localDate, this.timezone).utc().toISOString();
|
|
}
|
|
|
|
fromUTC(utcString: string): Date {
|
|
return dayjs.utc(utcString).tz(this.timezone).toDate();
|
|
}
|
|
|
|
// ============================================
|
|
// DATE CREATION
|
|
// ============================================
|
|
|
|
createDateAtTime(baseDate: Date | string, timeString: string): Date {
|
|
const totalMinutes = this.timeToMinutes(timeString);
|
|
const hours = Math.floor(totalMinutes / 60);
|
|
const minutes = totalMinutes % 60;
|
|
return dayjs(baseDate).startOf('day').hour(hours).minute(minutes).toDate();
|
|
}
|
|
|
|
getISOWeekDay(date: Date | string): number {
|
|
return dayjs(date).isoWeekday(); // 1=Monday, 7=Sunday
|
|
}
|
|
}
|