import { useCallback } from 'react'; import { environment } from '../../../environments/environment.ts'; import { useHttpService } from '../../../services/HttpService.tsx'; import { formatRange, reportQuery, type ReportComparison, type ReportFilters } from './reportPeriod'; export interface SchedulingCounts { scheduled: number; completed: number; cancelled: number; noShow: number; other: number; } export interface SchedulingReport { range: { start: string; end: string }; compare: ReportComparison; locationId?: string; timezone: string; locations: Array<{ id: string; name: string }>; totals: SchedulingCounts; weeks: Array<{ start: string; end: string; completed: number; cancelled: number; noShow: number }>; byLocation: Array; comparison: { range: { start: string; end: string }; totals: SchedulingCounts } | null; } export const useSchedulingReport = () => { const { get } = useHttpService(); return useCallback( (filters: ReportFilters) => get(environment.fhirBaseApi + '/SchedulingReport?' + reportQuery(filters)), [get], ); }; export const locationLabel = (report: SchedulingReport | undefined, locationId: string) => report?.locations.find((location) => location.id === locationId)?.name ?? 'All locations'; // A spreadsheet reads a leading = + - @ as a formula; location names are typed by the practice. const cell = (value: string | number) => { const text = String(value); const safe = /^[=+\-@]/.test(text) ? "'" + text : text; return /[",\r\n]/.test(safe) ? '"' + safe.replace(/"/g, '""') + '"' : safe; }; /** The CSV carries the same numbers the page shows, with the filters that produced them. */ export function schedulingCsv(report: SchedulingReport, locationName: string): string { const rows: Array> = [ ['Scheduling & access'], ['Period', formatRange(report.range.start, report.range.end)], ['Location', locationName], ['Time zone', report.timezone], ]; if (report.comparison) { rows.push(['Compared with', formatRange(report.comparison.range.start, report.comparison.range.end)]); } rows.push([]); const measures: Array<[string, keyof SchedulingCounts]> = [ ['Appointments', 'scheduled'], ['Completed', 'completed'], ['Cancelled', 'cancelled'], ['No-show', 'noShow'], ['Booked, arrived or checked in', 'other'], ]; rows.push(report.comparison ? ['Measure', 'This period', 'Comparison period'] : ['Measure', 'This period']); for (const [label, key] of measures) { rows.push(report.comparison ? [label, report.totals[key], report.comparison.totals[key]] : [label, report.totals[key]]); } rows.push([], ['Week', 'Completed', 'Cancelled', 'No-show']); for (const week of report.weeks) { rows.push([formatRange(week.start, week.end), week.completed, week.cancelled, week.noShow]); } rows.push([], ['Location', 'Appointments', 'Completed', 'Cancelled', 'No-show']); for (const row of report.byLocation) { rows.push([row.name, row.scheduled, row.completed, row.cancelled, row.noShow]); } return rows.map((row) => row.map(cell).join(',')).join('\r\n') + '\r\n'; } export const schedulingCsvFilename = (report: SchedulingReport) => 'scheduling-report-' + report.range.start + '-to-' + report.range.end + '.csv';