// S1 - mirrors aws/sam/services/api/src/utils/appointmentReminderPolicy.ts. // Keep the type and the defaults in step with the server copy, exactly as // config/communicationPreferences.ts mirrors its server twin. Divergence here means the editor // offers values the server rejects. import type { Organization } from 'fhir/r4'; export interface QuietHours { start: string; end: string; behavior: 'defer' | 'suppress'; } export interface ReminderPolicy { enabled: boolean; offsetsMinutes: number[]; maxPerAppointment: number; quietHours: QuietHours | null; } export const MAX_OFFSET_MINUTES = 10080; export const DEFAULT_REMINDER_POLICY: ReminderPolicy = { enabled: false, offsetsMinutes: [120, 1440], maxPerAppointment: 2, quietHours: null, }; /** Offsets the editor offers. Every value must be one the server's validator accepts. */ export const OFFSET_PRESETS = [60, 120, 240, 1440, 2880, 4320, 10080]; export function formatOffset(minutes: number): string { if (minutes % 1440 === 0) { const days = minutes / 1440; return `${days} ${days === 1 ? 'day' : 'days'} before`; } if (minutes % 60 === 0) { const hours = minutes / 60; return `${hours} ${hours === 1 ? 'hour' : 'hours'} before`; } return `${minutes} minutes before`; } // There is no GET /R4/appointment-reminder-settings - the org's saved policy travels only on the // Organization resource, as this extension. The server WRITES this exact URL (setReminderPolicy in // its own copy of this file); the plugin READS it back here, so the literal MUST match byte for byte. export const ORG_APPOINTMENT_REMINDER_EXTENSION_URL = 'https://akashichealthrecords.com/fhir/StructureDefinition/organization-appointment-reminder-policy'; /** * Pure reader - mirrors the server's resolveEffectiveReminderPolicy exactly (same extension URL, * same child urls), so the editor seeds from whatever the org last saved. Falls back to * DEFAULT_REMINDER_POLICY when the org has never saved a policy, same as the server default. */ export function resolveOrgReminderPolicy(organization?: Organization | null): ReminderPolicy { const ext = organization?.extension?.find((e) => e.url === ORG_APPOINTMENT_REMINDER_EXTENSION_URL); if (!ext) return { ...DEFAULT_REMINDER_POLICY, offsetsMinutes: [...DEFAULT_REMINDER_POLICY.offsetsMinutes] }; const children = ext.extension ?? []; const offsets = children .filter((c) => c.url === 'offsetMinutes') .map((c) => c.valueInteger) .filter((v): v is number => typeof v === 'number') .sort((a, b) => a - b); const quietExt = children.find((c) => c.url === 'quietHours'); const quietChildren = quietExt?.extension ?? []; const start = quietChildren.find((c) => c.url === 'start')?.valueString; const end = quietChildren.find((c) => c.url === 'end')?.valueString; const behavior = quietChildren.find((c) => c.url === 'behavior')?.valueString; return { enabled: children.find((c) => c.url === 'enabled')?.valueBoolean ?? DEFAULT_REMINDER_POLICY.enabled, offsetsMinutes: offsets.length ? offsets : [...DEFAULT_REMINDER_POLICY.offsetsMinutes], maxPerAppointment: children.find((c) => c.url === 'maxPerAppointment')?.valueInteger ?? DEFAULT_REMINDER_POLICY.maxPerAppointment, quietHours: start && end && (behavior === 'defer' || behavior === 'suppress') ? { start, end, behavior } : null, }; }