♻️(front) improve React code quality and performance
- Remove all console.log debug statements from production code - Fix useEffect dependency arrays (remove refs, fix dependency cycles) - Add useMemo for sharedCalendars filtering in CalendarList - Improve error handling in handleOpenSubscriptionModal - Add proper cleanup pattern (isMounted) in CalendarContext - Use i18n locale instead of hardcoded French in MiniCalendar Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,8 +23,7 @@ export const LeftPanel = ({
|
|||||||
onDateSelect,
|
onDateSelect,
|
||||||
onCreateEvent,
|
onCreateEvent,
|
||||||
}: LeftPanelProps) => {
|
}: LeftPanelProps) => {
|
||||||
const { davCalendars } = useCalendarContext();
|
useCalendarContext();
|
||||||
console.log("davCalendars LeftPanel", davCalendars);
|
|
||||||
return (
|
return (
|
||||||
<div className="calendar-left-panel">
|
<div className="calendar-left-panel">
|
||||||
<div className="calendar-left-panel__create">
|
<div className="calendar-left-panel__create">
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ import {
|
|||||||
startOfWeek,
|
startOfWeek,
|
||||||
subMonths,
|
subMonths,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import { fr } from "date-fns/locale";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useCalendarContext } from "../contexts";
|
import { useCalendarContext } from "../contexts";
|
||||||
|
import { useCalendarLocale } from "../hooks/useCalendarLocale";
|
||||||
|
|
||||||
interface MiniCalendarProps {
|
interface MiniCalendarProps {
|
||||||
selectedDate: Date;
|
selectedDate: Date;
|
||||||
@@ -41,6 +41,7 @@ export const MiniCalendar = ({
|
|||||||
}: MiniCalendarProps) => {
|
}: MiniCalendarProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { goToDate, currentDate } = useCalendarContext();
|
const { goToDate, currentDate } = useCalendarContext();
|
||||||
|
const { dateFnsLocale, firstDayOfWeek } = useCalendarLocale();
|
||||||
const [viewDate, setViewDate] = useState(selectedDate);
|
const [viewDate, setViewDate] = useState(selectedDate);
|
||||||
|
|
||||||
// Sync viewDate when main calendar navigates (via prev/next buttons)
|
// Sync viewDate when main calendar navigates (via prev/next buttons)
|
||||||
@@ -57,16 +58,33 @@ export const MiniCalendar = ({
|
|||||||
const days = useMemo(() => {
|
const days = useMemo(() => {
|
||||||
const monthStart = startOfMonth(viewDate);
|
const monthStart = startOfMonth(viewDate);
|
||||||
const monthEnd = endOfMonth(viewDate);
|
const monthEnd = endOfMonth(viewDate);
|
||||||
const calendarStart = startOfWeek(monthStart, { weekStartsOn: 1 });
|
const weekStartsOn = firstDayOfWeek as 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
const calendarEnd = endOfWeek(monthEnd, { weekStartsOn: 1 });
|
const calendarStart = startOfWeek(monthStart, { weekStartsOn });
|
||||||
|
const calendarEnd = endOfWeek(monthEnd, { weekStartsOn });
|
||||||
|
|
||||||
return eachDayOfInterval({ start: calendarStart, end: calendarEnd });
|
return eachDayOfInterval({ start: calendarStart, end: calendarEnd });
|
||||||
}, [viewDate]);
|
}, [viewDate, firstDayOfWeek]);
|
||||||
|
|
||||||
// Group days by weeks
|
// Group days by weeks
|
||||||
const weeks = useMemo(() => chunkArray(days, 7), [days]);
|
const weeks = useMemo(() => chunkArray(days, 7), [days]);
|
||||||
|
|
||||||
const weekDays = ["lu", "ma", "me", "je", "ve", "sa", "di"];
|
// Generate weekday labels based on locale and first day of week
|
||||||
|
const weekDays = useMemo(() => {
|
||||||
|
const days = [
|
||||||
|
t('calendar.recurrence.weekdays.mo'),
|
||||||
|
t('calendar.recurrence.weekdays.tu'),
|
||||||
|
t('calendar.recurrence.weekdays.we'),
|
||||||
|
t('calendar.recurrence.weekdays.th'),
|
||||||
|
t('calendar.recurrence.weekdays.fr'),
|
||||||
|
t('calendar.recurrence.weekdays.sa'),
|
||||||
|
t('calendar.recurrence.weekdays.su'),
|
||||||
|
];
|
||||||
|
// Rotate array based on firstDayOfWeek (0 = Sunday, 1 = Monday)
|
||||||
|
if (firstDayOfWeek === 0) {
|
||||||
|
return [days[6], ...days.slice(0, 6)];
|
||||||
|
}
|
||||||
|
return days;
|
||||||
|
}, [t, firstDayOfWeek]);
|
||||||
|
|
||||||
const handlePrevMonth = () => {
|
const handlePrevMonth = () => {
|
||||||
setViewDate(subMonths(viewDate, 1));
|
setViewDate(subMonths(viewDate, 1));
|
||||||
@@ -85,7 +103,7 @@ export const MiniCalendar = ({
|
|||||||
<div className="mini-calendar">
|
<div className="mini-calendar">
|
||||||
<div className="mini-calendar__header">
|
<div className="mini-calendar__header">
|
||||||
<span className="mini-calendar__month-title">
|
<span className="mini-calendar__month-title">
|
||||||
{format(viewDate, "MMMM yyyy", { locale: fr })}
|
{format(viewDate, "MMMM yyyy", { locale: dateFnsLocale })}
|
||||||
</span>
|
</span>
|
||||||
<div className="mini-calendar__nav">
|
<div className="mini-calendar__nav">
|
||||||
<button
|
<button
|
||||||
@@ -121,7 +139,8 @@ export const MiniCalendar = ({
|
|||||||
{/* Calendar body with weeks */}
|
{/* Calendar body with weeks */}
|
||||||
<div className="mini-calendar__body">
|
<div className="mini-calendar__body">
|
||||||
{weeks.map((week, weekIndex) => {
|
{weeks.map((week, weekIndex) => {
|
||||||
const weekNumber = getWeek(week[0], { weekStartsOn: 1 });
|
const weekStartsOn = firstDayOfWeek as 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
|
const weekNumber = getWeek(week[0], { weekStartsOn });
|
||||||
return (
|
return (
|
||||||
<div key={weekIndex} className="mini-calendar__week">
|
<div key={weekIndex} className="mini-calendar__week">
|
||||||
<div className="mini-calendar__week-number">{weekNumber}</div>
|
<div className="mini-calendar__week-number">{weekNumber}</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* CalendarList component - List of calendars with visibility toggles.
|
* CalendarList component - List of calendars with visibility toggles.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState, useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import type { Calendar } from "../../types";
|
import type { Calendar } from "../../types";
|
||||||
@@ -72,6 +72,8 @@ export const CalendarList = ({ calendars }: CalendarListProps) => {
|
|||||||
|
|
||||||
if (calendarsIndex === -1) {
|
if (calendarsIndex === -1) {
|
||||||
console.error("Invalid calendar URL format - 'calendars' segment not found:", davCalendar.url);
|
console.error("Invalid calendar URL format - 'calendars' segment not found:", davCalendar.url);
|
||||||
|
// Reset modal state to avoid stale data
|
||||||
|
setSubscriptionModal({ isOpen: false, calendarName: "", caldavPath: null });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +81,8 @@ export const CalendarList = ({ calendars }: CalendarListProps) => {
|
|||||||
const remainingParts = pathParts.slice(calendarsIndex);
|
const remainingParts = pathParts.slice(calendarsIndex);
|
||||||
if (remainingParts.length < 3) {
|
if (remainingParts.length < 3) {
|
||||||
console.error("Invalid calendar URL format - incomplete path:", davCalendar.url);
|
console.error("Invalid calendar URL format - incomplete path:", davCalendar.url);
|
||||||
|
// Reset modal state to avoid stale data
|
||||||
|
setSubscriptionModal({ isOpen: false, calendarName: "", caldavPath: null });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +96,8 @@ export const CalendarList = ({ calendars }: CalendarListProps) => {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to parse calendar URL:", error);
|
console.error("Failed to parse calendar URL:", error);
|
||||||
|
// Reset modal state on error
|
||||||
|
setSubscriptionModal({ isOpen: false, calendarName: "", caldavPath: null });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,8 +111,10 @@ export const CalendarList = ({ calendars }: CalendarListProps) => {
|
|||||||
// Use translation key for shared marker
|
// Use translation key for shared marker
|
||||||
const sharedMarker = t('calendar.list.shared');
|
const sharedMarker = t('calendar.list.shared');
|
||||||
|
|
||||||
const sharedCalendars = calendarsArray.filter((cal) =>
|
// Memoize filtered calendars to avoid recalculation on every render
|
||||||
cal.name.includes(sharedMarker)
|
const sharedCalendars = useMemo(
|
||||||
|
() => calendarsArray.filter((cal) => cal.name.includes(sharedMarker)),
|
||||||
|
[calendarsArray, sharedMarker]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export const Scheduler = ({ defaultCalendarUrl }: SchedulerProps) => {
|
|||||||
// Now trigger a re-evaluation of eventFilter by calling refetchEvents
|
// Now trigger a re-evaluation of eventFilter by calling refetchEvents
|
||||||
calendarRef.current.refetchEvents();
|
calendarRef.current.refetchEvents();
|
||||||
}
|
}
|
||||||
}, [visibleCalendarUrls, davCalendars, calendarRef]);
|
}, [visibleCalendarUrls, davCalendars]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -68,19 +68,11 @@ export const useSchedulerHandlers = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[EventDrop] Event:', info.event);
|
|
||||||
console.log('[EventDrop] allDay:', info.event.allDay);
|
|
||||||
console.log('[EventDrop] start:', info.event.start);
|
|
||||||
console.log('[EventDrop] end:', info.event.end);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const icsEvent = adapter.toIcsEvent(info.event as EventCalendarEvent, {
|
const icsEvent = adapter.toIcsEvent(info.event as EventCalendarEvent, {
|
||||||
defaultTimezone: extProps.timezone || BROWSER_TIMEZONE,
|
defaultTimezone: extProps.timezone || BROWSER_TIMEZONE,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('[EventDrop] IcsEvent start:', icsEvent.start);
|
|
||||||
console.log('[EventDrop] IcsEvent end:', icsEvent.end);
|
|
||||||
|
|
||||||
const result = await caldavService.updateEvent({
|
const result = await caldavService.updateEvent({
|
||||||
eventUrl: extProps.eventUrl,
|
eventUrl: extProps.eventUrl,
|
||||||
event: icsEvent,
|
event: icsEvent,
|
||||||
|
|||||||
@@ -241,19 +241,23 @@ export const useSchedulerInit = ({
|
|||||||
calendarRef.current = ec as unknown as CalendarApi;
|
calendarRef.current = ec as unknown as CalendarApi;
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
// @event-calendar/core is Svelte-based and uses $destroy
|
||||||
|
// Always call $destroy before clearing the container to avoid memory leaks
|
||||||
if (calendarRef.current) {
|
if (calendarRef.current) {
|
||||||
// @event-calendar/core is Svelte-based and uses $destroy
|
|
||||||
const calendar = calendarRef.current as CalendarApi;
|
const calendar = calendarRef.current as CalendarApi;
|
||||||
if (typeof calendar.$destroy === 'function') {
|
if (typeof calendar.$destroy === 'function') {
|
||||||
calendar.$destroy();
|
calendar.$destroy();
|
||||||
}
|
}
|
||||||
calendarRef.current = null;
|
calendarRef.current = null;
|
||||||
}
|
}
|
||||||
// Also clear the container
|
// Clear the container only after calendar is destroyed
|
||||||
if (containerRef.current) {
|
if (containerRef.current) {
|
||||||
containerRef.current.innerHTML = '';
|
containerRef.current.innerHTML = '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Note: refs (containerRef, calendarRef, visibleCalendarUrlsRef, davCalendarsRef) are excluded
|
||||||
|
// from dependencies as they are stable references that don't trigger re-renders
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
isConnected,
|
isConnected,
|
||||||
calendarUrl,
|
calendarUrl,
|
||||||
@@ -270,15 +274,12 @@ export const useSchedulerInit = ({
|
|||||||
setCurrentDate,
|
setCurrentDate,
|
||||||
t,
|
t,
|
||||||
i18n.language,
|
i18n.language,
|
||||||
containerRef,
|
|
||||||
calendarRef,
|
|
||||||
visibleCalendarUrlsRef,
|
|
||||||
davCalendarsRef,
|
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to check scheduling capabilities on mount.
|
* Hook to check scheduling capabilities on mount.
|
||||||
|
* Silently verifies CalDAV scheduling support without debug output.
|
||||||
*/
|
*/
|
||||||
export const useSchedulingCapabilitiesCheck = (
|
export const useSchedulingCapabilitiesCheck = (
|
||||||
isConnected: boolean,
|
isConnected: boolean,
|
||||||
@@ -291,68 +292,8 @@ export const useSchedulingCapabilitiesCheck = (
|
|||||||
|
|
||||||
hasCheckedRef.current = true;
|
hasCheckedRef.current = true;
|
||||||
|
|
||||||
const checkSchedulingCapabilities = async () => {
|
// Silently check scheduling capabilities
|
||||||
const result = await caldavService.getSchedulingCapabilities();
|
// Debug logging removed for production
|
||||||
|
caldavService.getSchedulingCapabilities();
|
||||||
if (result.success && result.data) {
|
|
||||||
console.group('📅 CalDAV Scheduling Capabilities');
|
|
||||||
console.log(
|
|
||||||
'Scheduling Support:',
|
|
||||||
result.data.hasSchedulingSupport ? '✅ Enabled' : '❌ Disabled'
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
'Schedule Outbox URL:',
|
|
||||||
result.data.scheduleOutboxUrl || '❌ Not found'
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
'Schedule Inbox URL:',
|
|
||||||
result.data.scheduleInboxUrl || '❌ Not found'
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
'Calendar User Addresses:',
|
|
||||||
result.data.calendarUserAddressSet.length > 0
|
|
||||||
? result.data.calendarUserAddressSet
|
|
||||||
: '❌ None'
|
|
||||||
);
|
|
||||||
console.log('');
|
|
||||||
console.log('Raw server response:', result.data.rawResponse);
|
|
||||||
|
|
||||||
if (result.data.hasSchedulingSupport) {
|
|
||||||
console.log('');
|
|
||||||
console.log('✉️ Email Notifications Status:');
|
|
||||||
console.log(' The server supports CalDAV scheduling (RFC 6638).');
|
|
||||||
console.log(
|
|
||||||
' However, this does NOT guarantee email notifications will be sent.'
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
' Email sending requires the IMip plugin to be configured on the server.'
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
' Contact your server administrator to verify IMip plugin configuration.'
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.warn('');
|
|
||||||
console.warn(
|
|
||||||
'⚠️ CalDAV scheduling properties not found on this server.'
|
|
||||||
);
|
|
||||||
console.warn(' This could mean:');
|
|
||||||
console.warn(
|
|
||||||
' 1. The scheduling plugin is not enabled in Sabre/DAV configuration'
|
|
||||||
);
|
|
||||||
console.warn(
|
|
||||||
' 2. The properties are located elsewhere (check raw response above)'
|
|
||||||
);
|
|
||||||
console.warn(
|
|
||||||
' 3. The server does not support CalDAV scheduling (RFC 6638)'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.groupEnd();
|
|
||||||
} else {
|
|
||||||
console.error('Failed to check scheduling capabilities:', result.error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
checkSchedulingCapabilities();
|
|
||||||
}, [isConnected, caldavService]);
|
}, [isConnected, caldavService]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -172,6 +172,8 @@ export const CalendarContextProvider = ({ children }: CalendarContextProviderPro
|
|||||||
|
|
||||||
// Connect to CalDAV server on mount
|
// Connect to CalDAV server on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let isMounted = true;
|
||||||
|
|
||||||
const connect = async () => {
|
const connect = async () => {
|
||||||
try {
|
try {
|
||||||
const result = await caldavService.connect({
|
const result = await caldavService.connect({
|
||||||
@@ -179,18 +181,37 @@ export const CalendarContextProvider = ({ children }: CalendarContextProviderPro
|
|||||||
headers,
|
headers,
|
||||||
fetchOptions,
|
fetchOptions,
|
||||||
});
|
});
|
||||||
if (result.success) {
|
if (isMounted && result.success) {
|
||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
await refreshCalendars();
|
// Fetch calendars after successful connection
|
||||||
} else {
|
const calendarsResult = await caldavService.fetchCalendars();
|
||||||
|
if (isMounted && calendarsResult.success && calendarsResult.data) {
|
||||||
|
setDavCalendars(calendarsResult.data);
|
||||||
|
setVisibleCalendarUrls(new Set(calendarsResult.data.map(cal => cal.url)));
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
} else if (isMounted) {
|
||||||
console.error("Failed to connect to CalDAV:", result.error);
|
console.error("Failed to connect to CalDAV:", result.error);
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error connecting to CalDAV:", error);
|
if (isMounted) {
|
||||||
|
console.error("Error connecting to CalDAV:", error);
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
connect();
|
connect();
|
||||||
}, [caldavService, refreshCalendars]);
|
|
||||||
|
// Cleanup: prevent state updates after unmount
|
||||||
|
return () => {
|
||||||
|
isMounted = false;
|
||||||
|
};
|
||||||
|
// Note: refreshCalendars is excluded to avoid dependency cycle
|
||||||
|
// The initial fetch is done inline in this effect
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [caldavService]);
|
||||||
|
|
||||||
const value: CalendarContextType = {
|
const value: CalendarContextType = {
|
||||||
calendarRef,
|
calendarRef,
|
||||||
|
|||||||
@@ -864,8 +864,6 @@ export class CalDavService {
|
|||||||
? outboxUrl
|
? outboxUrl
|
||||||
: `${this._account!.serverUrl}${outboxUrl.startsWith('/') ? outboxUrl.slice(1) : outboxUrl}`
|
: `${this._account!.serverUrl}${outboxUrl.startsWith('/') ? outboxUrl.slice(1) : outboxUrl}`
|
||||||
|
|
||||||
console.log('[CalDAVService] Sending scheduling request to:', fullOutboxUrl)
|
|
||||||
|
|
||||||
// Use fetch directly to avoid davRequest URL construction issues in dev mode
|
// Use fetch directly to avoid davRequest URL construction issues in dev mode
|
||||||
const response = await fetch(fullOutboxUrl, {
|
const response = await fetch(fullOutboxUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -1208,8 +1206,6 @@ END:VCALENDAR`
|
|||||||
}
|
}
|
||||||
|
|
||||||
return withErrorHandling(async () => {
|
return withErrorHandling(async () => {
|
||||||
console.log('[Scheduling Debug] Requesting from principal URL:', this._account!.principalUrl)
|
|
||||||
|
|
||||||
const response = await propfind({
|
const response = await propfind({
|
||||||
url: this._account!.principalUrl!,
|
url: this._account!.principalUrl!,
|
||||||
props: {
|
props: {
|
||||||
@@ -1222,10 +1218,7 @@ END:VCALENDAR`
|
|||||||
depth: '0',
|
depth: '0',
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('[Scheduling Debug] Full PROPFIND response:', JSON.stringify(response, null, 2))
|
|
||||||
|
|
||||||
const props = response[0]?.props ?? {}
|
const props = response[0]?.props ?? {}
|
||||||
console.log('[Scheduling Debug] Extracted props:', props)
|
|
||||||
|
|
||||||
// Note: tsdav converts XML property names to camelCase
|
// Note: tsdav converts XML property names to camelCase
|
||||||
// schedule-outbox-URL becomes scheduleOutboxURL
|
// schedule-outbox-URL becomes scheduleOutboxURL
|
||||||
|
|||||||
@@ -327,7 +327,6 @@ export class EventCalendarAdapter {
|
|||||||
if (isAllDay && typeof dateValue === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(dateValue)) {
|
if (isAllDay && typeof dateValue === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(dateValue)) {
|
||||||
const [year, month, day] = dateValue.split('-').map(Number)
|
const [year, month, day] = dateValue.split('-').map(Number)
|
||||||
const result = new Date(Date.UTC(year, month - 1, day))
|
const result = new Date(Date.UTC(year, month - 1, day))
|
||||||
console.log('[EventCalendarAdapter] Parsing all-day date:', dateValue, '→', result)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,15 +334,12 @@ export class EventCalendarAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isAllDay = ecEvent.allDay ?? false
|
const isAllDay = ecEvent.allDay ?? false
|
||||||
console.log('[EventCalendarAdapter] toIcsEvent - allDay:', isAllDay, 'start:', ecEvent.start, 'end:', ecEvent.end)
|
|
||||||
|
|
||||||
const startDate = parseDate(ecEvent.start, isAllDay)
|
const startDate = parseDate(ecEvent.start, isAllDay)
|
||||||
const endDate = ecEvent.end
|
const endDate = ecEvent.end
|
||||||
? parseDate(ecEvent.end, isAllDay)
|
? parseDate(ecEvent.end, isAllDay)
|
||||||
: startDate
|
: startDate
|
||||||
|
|
||||||
console.log('[EventCalendarAdapter] Parsed dates - start:', startDate, 'end:', endDate)
|
|
||||||
|
|
||||||
// Determine timezone
|
// Determine timezone
|
||||||
const timezone = extProps.timezone ?? opts.defaultTimezone
|
const timezone = extProps.timezone ?? opts.defaultTimezone
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user