calendarFollowUpUtils.ts4.2 KBView on GitHub import type { CalendarEvent } from '../types/calendar-types';
import { useCedarStore } from '@/modules/store';
// ── Helpers (mirrored from EventDetailsPopover) ───────────────────────────────
function detectGoogleMeet(event: CalendarEvent): boolean {
const hasConferenceData = event.conferenceData?.entryPoints?.some(
(ep) => ep.entryPointType === 'video' && ep.uri?.includes('meet.google.com'),
);
if (hasConferenceData) return true;
const location = event.location?.toLowerCase() ?? '';
if (location.includes('google meet') || location.includes('meet.google.com')) return true;
const description = event.description?.toLowerCase() ?? '';
if (description.includes('meet.google.com') || description.includes('hangouts.google.com')) {
return true;
}
return false;
}
function cleanDescription(description: string | null | undefined): string {
if (!description) return '';
let cleaned = description;
cleaned = cleaned.replace(/^.*(?:meet\.google\.com|hangouts\.google\.com).*$/gm, '');
cleaned = cleaned.replace(/^.*(?:calendly\.com).*$/gm, '');
cleaned = cleaned.replace(/^.*Powered by Calendly.*$/gm, '');
cleaned = cleaned.replace(/Need to make changes to this event\?.*$/gms, '');
cleaned = cleaned.replace(/^.*Cancel:.*$/gm, '');
cleaned = cleaned.replace(/^.*Reschedule:.*$/gm, '');
cleaned = cleaned.replace(/^.*Location:.*(?:Google Meet|web conference).*$/gm, '');
cleaned = cleaned.replace(/^.*You can join this meeting from.*$/gm, '');
cleaned = cleaned.replace(/^.*This is a Google Meet.*$/gm, '');
cleaned = cleaned.replace(/\n{3,}/g, '\n\n');
return cleaned.trim();
}
function cleanLocation(location: string | null | undefined): string {
if (!location) return '';
const lower = location.toLowerCase();
const placeholders = [
'google meet',
'google meet (instructions in description)',
'meet.google.com',
'hangouts.google.com',
'zoom',
'microsoft teams',
'web conference',
];
if (placeholders.some((p) => lower.includes(p))) return '';
return location;
}
// ── Public API ────────────────────────────────────────────────────────────────
/**
* Schedules a follow-up event based on an existing calendar event.
* Equivalent to pressing "Schedule Follow-Up" in EventDetailsPopover.
*
* Returns `false` if the event has no dateTime start (all-day event).
*/
export function scheduleFollowUp(event: CalendarEvent): boolean {
if (!event.start?.dateTime) return false;
const currentStart = new Date(event.start.dateTime);
const nextStart = new Date(currentStart);
const nextEnd = new Date(nextStart);
if (event.end?.dateTime) {
const durationMs = new Date(event.end.dateTime).getTime() - currentStart.getTime();
nextEnd.setTime(nextStart.getTime() + durationMs);
} else {
nextEnd.setMinutes(nextEnd.getMinutes() + 30);
}
const nextMeetingEvent: CalendarEvent = {
id: `temp-next-${Date.now()}`,
summary: event.summary ?? '',
calendarId: event.calendarId ?? 'primary',
start: { dateTime: nextStart.toISOString(), timeZone: event.start.timeZone },
end: { dateTime: nextEnd.toISOString(), timeZone: event.end?.timeZone },
attendees: event.attendees,
colorId: event.colorId,
location: cleanLocation(event.location),
description: cleanDescription(event.description),
hadGoogleMeet: detectGoogleMeet(event),
};
const store = useCedarStore.getState();
store.setDraftCalendarEvent(nextMeetingEvent);
// Navigate to the follow-up's week if it's different from the current view week
const viewWeekStart = new Date(store.currentDate);
viewWeekStart.setHours(0, 0, 0, 0);
const dow = viewWeekStart.getDay();
viewWeekStart.setDate(viewWeekStart.getDate() + (dow === 0 ? -6 : 1 - dow));
const nextWeekStart = new Date(nextStart);
nextWeekStart.setHours(0, 0, 0, 0);
const ndow = nextWeekStart.getDay();
nextWeekStart.setDate(nextWeekStart.getDate() + (ndow === 0 ? -6 : 1 - ndow));
if (viewWeekStart.getTime() !== nextWeekStart.getTime()) {
store.setCurrentDate(nextStart);
}
return true;
}