import type { ApiEvent, CalendarEvent, EventStatus } from "@/lib/types"; const STATUSES: readonly EventStatus[] = ["PUBLIC", "PRIVATE", "DRAFT", "DELETED"]; const parseDate = (value: string | null | undefined): Date | null => { if (!value) { return null; } const date = new Date(value); return Number.isNaN(date.getTime()) ? null : date; }; /** * Turns one row of the API's JSON into a `CalendarEvent`, or null for a row * the list should not show. * * Null covers deleted events - the admin feed returns them, because deleting * only appends a version with `status = 'DELETED'` - and anything without a * usable start and end, which a deleted row also is (all its other columns are * null). The Angular app filtered on status alone and would have rendered such * a row as "Invalid Date". * * A missing status (the anonymous public listing omits it) means PUBLIC, since * that listing only returns published events. An unknown one is treated as * PRIVATE (and saved back as such) - "not public" is the safe reading; it is * never silently promoted to PUBLIC. */ export const parseApiEvent = (row: ApiEvent): CalendarEvent | null => { const status: EventStatus = row.status == null ? "PUBLIC" : STATUSES.includes(row.status as EventStatus) ? (row.status as EventStatus) : "PRIVATE"; if (status === "DELETED") { return null; } const start = parseDate(row.startDateTime); const end = parseDate(row.endDateTime); if (!start || !end) { return null; } return { eventId: row.eventId, calendarId: row.calendarId, uuid: row.uuid ?? "", name: row.name ?? "", description: row.description ?? "", start, end, created: parseDate(row.createdDate), lastModified: parseDate(row.lastModifiedDate), location: row.location ?? "", createdBy: row.createdBy ?? "", lastModifiedBy: row.lastModifiedBy ?? "", url: row.url ?? "", wholeDay: Boolean(row.wholeDay), repeatFrequency: row.repeatFrequency ?? "", status, }; }; export const parseApiEvents = (rows: ApiEvent[]): CalendarEvent[] => rows.map(parseApiEvent).filter((e): e is CalendarEvent => e !== null);