176 lines
4.5 KiB
TypeScript
176 lines
4.5 KiB
TypeScript
import { loadSystemConfig } from "./system-config";
|
|
|
|
export type CalendarEvent = {
|
|
summary: string;
|
|
description: string;
|
|
startTime: Date;
|
|
endTime: Date;
|
|
location?: string;
|
|
attendees?: string[];
|
|
};
|
|
|
|
/**
|
|
* Generate ICS file content for calendar invitation
|
|
*/
|
|
export function generateIcsFile(event: CalendarEvent): string {
|
|
const formatDate = (date: Date): string => {
|
|
return date
|
|
.toISOString()
|
|
.replace(/[-:]/g, "")
|
|
.replace(/\.\d{3}/, "");
|
|
};
|
|
|
|
const startDateStr = formatDate(event.startTime);
|
|
const endDateStr = formatDate(event.endTime);
|
|
const now = formatDate(new Date());
|
|
const uid = `${now}@estate-platform.com`;
|
|
|
|
// Escape special characters in text
|
|
const escape = (text: string) => {
|
|
return text
|
|
.replace(/\\/g, "\\\\")
|
|
.replace(/;/g, "\\;")
|
|
.replace(/,/g, "\\,")
|
|
.replace(/\n/g, "\\n");
|
|
};
|
|
|
|
let icsContent = [
|
|
"BEGIN:VCALENDAR",
|
|
"VERSION:2.0",
|
|
"PRODID:-//Estate Platform//Webinar Calendar//EN",
|
|
"CALSCALE:GREGORIAN",
|
|
"METHOD:REQUEST",
|
|
"BEGIN:VEVENT",
|
|
`UID:${uid}`,
|
|
`DTSTAMP:${now}`,
|
|
`DTSTART:${startDateStr}`,
|
|
`DTEND:${endDateStr}`,
|
|
`SUMMARY:${escape(event.summary)}`,
|
|
`DESCRIPTION:${escape(event.description)}`,
|
|
];
|
|
|
|
if (event.location) {
|
|
icsContent.push(`LOCATION:${escape(event.location)}`);
|
|
}
|
|
|
|
// Add organizer
|
|
icsContent.push("ORGANIZER;CN=Estate Platform:mailto:noreply@estate-platform.com");
|
|
|
|
// Add attendees
|
|
if (event.attendees && event.attendees.length > 0) {
|
|
event.attendees.forEach((email) => {
|
|
icsContent.push(
|
|
`ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:${email}`
|
|
);
|
|
});
|
|
}
|
|
|
|
icsContent.push(
|
|
"STATUS:CONFIRMED",
|
|
"SEQUENCE:0",
|
|
"BEGIN:VALARM",
|
|
"TRIGGER:-PT15M",
|
|
"ACTION:DISPLAY",
|
|
"DESCRIPTION:Reminder: Webinar starts in 15 minutes",
|
|
"END:VALARM",
|
|
"END:VEVENT",
|
|
"END:VCALENDAR",
|
|
);
|
|
|
|
return icsContent.join("\r\n");
|
|
}
|
|
|
|
/**
|
|
* Create a Google Calendar event (if service account is configured)
|
|
*/
|
|
export async function createGoogleCalendarEvent(
|
|
event: CalendarEvent
|
|
): Promise<{ success: boolean; eventId?: string; error?: string }> {
|
|
try {
|
|
const config = await loadSystemConfig();
|
|
const calendarConfig = config.googleCalendar;
|
|
|
|
if (!calendarConfig?.enabled) {
|
|
return {
|
|
success: false,
|
|
error: "Google Calendar is not enabled",
|
|
};
|
|
}
|
|
|
|
if (
|
|
!calendarConfig.serviceAccountEmail ||
|
|
!calendarConfig.serviceAccountKey ||
|
|
!calendarConfig.calendarId
|
|
) {
|
|
return {
|
|
success: false,
|
|
error: "Google Calendar credentials are not configured",
|
|
};
|
|
}
|
|
|
|
// In a real implementation, you would use the googleapis library
|
|
// to create the event using service account credentials
|
|
// For now, we'll just return a mock success
|
|
// TODO: Implement actual Google Calendar API integration
|
|
|
|
console.log("[CALENDAR] Would create event:", event);
|
|
|
|
return {
|
|
success: true,
|
|
eventId: `mock-event-${Date.now()}`,
|
|
};
|
|
} catch (error) {
|
|
console.error("[CALENDAR] Error creating event:", error);
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : "Failed to create calendar event",
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper to calculate end time based on duration in minutes
|
|
*/
|
|
export function calculateEndTime(startTime: Date, durationMinutes: number): Date {
|
|
return new Date(startTime.getTime() + durationMinutes * 60 * 1000);
|
|
}
|
|
|
|
/**
|
|
* Create a calendar event for a webinar registration
|
|
*/
|
|
export async function createWebinarCalendarEvent(
|
|
webinar: {
|
|
title: string;
|
|
description: string;
|
|
startAt: Date;
|
|
duration: number;
|
|
meetingInfo: any;
|
|
},
|
|
attendeeEmail: string
|
|
): Promise<{ icsContent: string; googleEventId?: string }> {
|
|
const endTime = calculateEndTime(webinar.startAt, webinar.duration);
|
|
|
|
const meetingLink = webinar.meetingInfo?.meetingLink || "TBD";
|
|
const description = `${webinar.description}\n\nJoin Link: ${meetingLink}`;
|
|
|
|
const event: CalendarEvent = {
|
|
summary: webinar.title,
|
|
description,
|
|
startTime: webinar.startAt,
|
|
endTime,
|
|
location: meetingLink,
|
|
attendees: [attendeeEmail],
|
|
};
|
|
|
|
// Generate ICS file
|
|
const icsContent = generateIcsFile(event);
|
|
|
|
// Optionally create Google Calendar event
|
|
const googleResult = await createGoogleCalendarEvent(event);
|
|
|
|
return {
|
|
icsContent,
|
|
googleEventId: googleResult.success ? googleResult.eventId : undefined,
|
|
};
|
|
}
|