Initial commit
This commit is contained in:
175
lib/calendar.ts
Normal file
175
lib/calendar.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user