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;
}