diff --git a/src/components/features/calendar/index.ts b/src/components/features/calendar/index.ts index 309c3849..2b88bd3d 100644 --- a/src/components/features/calendar/index.ts +++ b/src/components/features/calendar/index.ts @@ -25,6 +25,7 @@ import { import { Calendar, type DateAdapter, + DateFnsAdapter, hideWeekends, workingHours, onlyDays, @@ -82,171 +83,6 @@ export interface CalendarEvent extends Task { badge?: boolean; } -class NormalizedDateFnsAdapter implements DateAdapter { - constructor(private readonly fns: typeof dateFns) {} - - private normalizeFormat(formatStr?: string) { - if (!formatStr) return formatStr; - // Align legacy moment-style tokens with date-fns v4 requirements - return formatStr - .replace(/Y{2,4}/g, (token) => token.toLowerCase()) - .replace(/D{1,2}/g, (token) => token.toLowerCase()); - } - - create(date?: string | Date) { - if (!date) return new Date(); - if (date instanceof Date) return new Date(date); - return this.fns.parseISO(date); - } - - parse(dateStr: string, formatStr?: string) { - const normalizedFormat = this.normalizeFormat(formatStr); - if (!normalizedFormat) { - return this.fns.parseISO(dateStr); - } - return this.fns.parse(dateStr, normalizedFormat, new Date()); - } - - format(date: Date, formatStr: string) { - return this.fns.format( - date, - this.normalizeFormat(formatStr) ?? formatStr, - ); - } - - year(date: Date) { - return this.fns.getYear(date); - } - - month(date: Date) { - return this.fns.getMonth(date); - } - - date(date: Date) { - return this.fns.getDate(date); - } - - day(date: Date) { - return this.fns.getDay(date); - } - - hour(date: Date) { - return this.fns.getHours(date); - } - - minute(date: Date) { - return this.fns.getMinutes(date); - } - - setHour(date: Date, hour: number) { - return this.fns.setHours(date, hour); - } - - setMinute(date: Date, minute: number) { - return this.fns.setMinutes(date, minute); - } - - add(date: Date, amount: number, unit: string) { - switch (unit) { - case "year": - return this.fns.addYears(date, amount); - case "month": - return this.fns.addMonths(date, amount); - case "week": - return this.fns.addWeeks(date, amount); - case "day": - return this.fns.addDays(date, amount); - case "hour": - return this.fns.addHours(date, amount); - case "minute": - return this.fns.addMinutes(date, amount); - default: - return date; - } - } - - diff(date1: Date, date2: Date, unit: string) { - switch (unit) { - case "year": - return this.fns.differenceInYears(date1, date2); - case "month": - return this.fns.differenceInMonths(date1, date2); - case "week": - return this.fns.differenceInWeeks(date1, date2); - case "day": - return this.fns.differenceInDays(date1, date2); - case "hour": - return this.fns.differenceInHours(date1, date2); - case "minute": - return this.fns.differenceInMinutes(date1, date2); - default: - return 0; - } - } - - startOf(date: Date, unit: string) { - switch (unit) { - case "year": - return this.fns.startOfYear(date); - case "month": - return this.fns.startOfMonth(date); - case "week": - return this.fns.startOfWeek(date); - case "day": - return this.fns.startOfDay(date); - case "hour": - return this.fns.startOfHour(date); - case "minute": - return this.fns.startOfMinute(date); - default: - return date; - } - } - - endOf(date: Date, unit: string) { - switch (unit) { - case "year": - return this.fns.endOfYear(date); - case "month": - return this.fns.endOfMonth(date); - case "week": - return this.fns.endOfWeek(date); - case "day": - return this.fns.endOfDay(date); - case "hour": - return this.fns.endOfHour(date); - case "minute": - return this.fns.endOfMinute(date); - default: - return date; - } - } - - isBefore(date1: Date, date2: Date, unit?: string) { - if (!unit) return this.fns.isBefore(date1, date2); - return this.fns.isBefore( - this.startOf(date1, unit), - this.startOf(date2, unit), - ); - } - - isAfter(date1: Date, date2: Date, unit?: string) { - if (!unit) return this.fns.isAfter(date1, date2); - return this.fns.isAfter( - this.startOf(date1, unit), - this.startOf(date2, unit), - ); - } - - isSame(date1: Date, date2: Date, unit?: string) { - if (!unit) return this.fns.isEqual(date1, date2); - return this.fns.isEqual( - this.startOf(date1, unit), - this.startOf(date2, unit), - ); - } -} - /** * Main CalendarComponent class */ @@ -886,7 +722,7 @@ export class CalendarComponent extends Component { }, // Use custom view registry for agenda/year views viewRegistry: isAgendaOrYear ? this.viewRegistry : undefined, - dateAdapter: new NormalizedDateFnsAdapter(dateFns), + dateAdapter: new DateFnsAdapter(dateFns), events: this.convertTasksToTGEvents(), showEventCounts: cc.showEventCounts ?? true, dateFormats: { @@ -974,7 +810,7 @@ export class CalendarComponent extends Component { ) : undefined, }, - dateAdapter: new NormalizedDateFnsAdapter(dateFns), + dateAdapter: new DateFnsAdapter(dateFns), events: this.convertTasksToTGEvents(), showEventCounts: true, dateFormats: { diff --git a/src/utils/adapters/DateFnsAdapter.ts b/src/utils/adapters/DateFnsAdapter.ts deleted file mode 100644 index 23438ee1..00000000 --- a/src/utils/adapters/DateFnsAdapter.ts +++ /dev/null @@ -1,309 +0,0 @@ -/** - * DateFnsAdapter - Adapter for @taskgenius/calendar using date-fns - * - * This adapter bridges date-fns with @taskgenius/calendar's DateAdapter interface, - * allowing the calendar to use date-fns for all date operations. - */ - -import { - parse, - format, - startOfDay, - startOfWeek, - startOfMonth, - startOfYear, - endOfDay, - endOfWeek, - endOfMonth, - endOfYear, - addDays, - addWeeks, - addMonths, - addYears, - subDays, - subWeeks, - subMonths, - subYears, - differenceInDays, - differenceInWeeks, - differenceInMonths, - differenceInYears, - isSameDay, - isSameWeek, - isSameMonth, - isSameYear, - isBefore, - isAfter, - getDay, - getDaysInMonth, - isValid, - parseISO, -} from "date-fns"; - -/** - * Time unit types supported by the adapter - */ -type TimeUnit = "day" | "week" | "month" | "year" | "hour" | "minute"; - -/** - * DateAdapter interface that must be implemented - * This is the contract expected by @taskgenius/calendar - */ -interface DateAdapter { - create(date?: string | Date | T): T; - parse(dateStr: string, format?: string): T; - format(date: T, format: string): string; - startOf(date: T, unit: TimeUnit): T; - endOf(date: T, unit: TimeUnit): T; - add(date: T, amount: number, unit: TimeUnit): T; - subtract(date: T, amount: number, unit: TimeUnit): T; - diff(date1: T, date2: T, unit: TimeUnit): number; - isSame(date1: T, date2: T, unit?: TimeUnit): boolean; - isBefore(date: T, compare: T): boolean; - isAfter(date: T, compare: T): boolean; - getDay(date: T): number; - getDaysInMonth(date: T): number; -} - -/** - * DateFnsAdapter implementation using date-fns library - */ -export class DateFnsAdapter implements DateAdapter { - /** - * Create a Date object from various input types - */ - create(date?: string | Date): Date { - if (!date) { - return new Date(); - } - if (date instanceof Date) { - return new Date(date); - } - if (typeof date === "string") { - // Try to parse ISO format first - const parsed = parseISO(date); - if (isValid(parsed)) { - return parsed; - } - // Fallback to native Date constructor - return new Date(date); - } - return new Date(); - } - - /** - * Parse a date string with optional format - */ - parse(dateStr: string, formatStr?: string): Date { - if (!formatStr) { - // Default to ISO format - const parsed = parseISO(dateStr); - if (isValid(parsed)) { - return parsed; - } - return new Date(dateStr); - } - - // Use date-fns parse with format - const parsed = parse(dateStr, formatStr, new Date()); - if (!isValid(parsed)) { - throw new Error(`Invalid date string: ${dateStr}`); - } - return parsed; - } - - /** - * Format a date to string - */ - format(date: Date, formatStr: string): string { - return format(date, formatStr); - } - - /** - * Get the start of a time unit - */ - startOf(date: Date, unit: TimeUnit): Date { - switch (unit) { - case "day": - return startOfDay(date); - case "week": - return startOfWeek(date); - case "month": - return startOfMonth(date); - case "year": - return startOfYear(date); - case "hour": - // date-fns doesn't have startOfHour, so we set minutes and seconds to 0 - const hourStart = new Date(date); - hourStart.setMinutes(0, 0, 0); - return hourStart; - case "minute": - // Set seconds and milliseconds to 0 - const minuteStart = new Date(date); - minuteStart.setSeconds(0, 0); - return minuteStart; - default: - return startOfDay(date); - } - } - - /** - * Get the end of a time unit - */ - endOf(date: Date, unit: TimeUnit): Date { - switch (unit) { - case "day": - return endOfDay(date); - case "week": - return endOfWeek(date); - case "month": - return endOfMonth(date); - case "year": - return endOfYear(date); - case "hour": - // Set to last millisecond of the hour - const hourEnd = new Date(date); - hourEnd.setMinutes(59, 59, 999); - return hourEnd; - case "minute": - // Set to last millisecond of the minute - const minuteEnd = new Date(date); - minuteEnd.setSeconds(59, 999); - return minuteEnd; - default: - return endOfDay(date); - } - } - - /** - * Add time to a date - */ - add(date: Date, amount: number, unit: TimeUnit): Date { - switch (unit) { - case "day": - return addDays(date, amount); - case "week": - return addWeeks(date, amount); - case "month": - return addMonths(date, amount); - case "year": - return addYears(date, amount); - case "hour": - return new Date(date.getTime() + amount * 60 * 60 * 1000); - case "minute": - return new Date(date.getTime() + amount * 60 * 1000); - default: - return addDays(date, amount); - } - } - - /** - * Subtract time from a date - */ - subtract(date: Date, amount: number, unit: TimeUnit): Date { - switch (unit) { - case "day": - return subDays(date, amount); - case "week": - return subWeeks(date, amount); - case "month": - return subMonths(date, amount); - case "year": - return subYears(date, amount); - case "hour": - return new Date(date.getTime() - amount * 60 * 60 * 1000); - case "minute": - return new Date(date.getTime() - amount * 60 * 1000); - default: - return subDays(date, amount); - } - } - - /** - * Calculate difference between two dates - */ - diff(date1: Date, date2: Date, unit: TimeUnit): number { - switch (unit) { - case "day": - return differenceInDays(date1, date2); - case "week": - return differenceInWeeks(date1, date2); - case "month": - return differenceInMonths(date1, date2); - case "year": - return differenceInYears(date1, date2); - case "hour": - return Math.floor((date1.getTime() - date2.getTime()) / (60 * 60 * 1000)); - case "minute": - return Math.floor((date1.getTime() - date2.getTime()) / (60 * 1000)); - default: - return differenceInDays(date1, date2); - } - } - - /** - * Check if two dates are the same - */ - isSame(date1: Date, date2: Date, unit?: TimeUnit): boolean { - if (!unit) { - return date1.getTime() === date2.getTime(); - } - - switch (unit) { - case "day": - return isSameDay(date1, date2); - case "week": - return isSameWeek(date1, date2); - case "month": - return isSameMonth(date1, date2); - case "year": - return isSameYear(date1, date2); - case "hour": - return ( - isSameDay(date1, date2) && - date1.getHours() === date2.getHours() - ); - case "minute": - return ( - isSameDay(date1, date2) && - date1.getHours() === date2.getHours() && - date1.getMinutes() === date2.getMinutes() - ); - default: - return isSameDay(date1, date2); - } - } - - /** - * Check if date is before compare date - */ - isBefore(date: Date, compare: Date): boolean { - return isBefore(date, compare); - } - - /** - * Check if date is after compare date - */ - isAfter(date: Date, compare: Date): boolean { - return isAfter(date, compare); - } - - /** - * Get day of week (0 = Sunday, 6 = Saturday) - */ - getDay(date: Date): number { - return getDay(date); - } - - /** - * Get number of days in the month - */ - getDaysInMonth(date: Date): number { - return getDaysInMonth(date); - } -} - -/** - * Export a singleton instance for convenience - */ -export const dateFnsAdapter = new DateFnsAdapter();