import { IconSpan } from './icon-span'; import { departmentIcon } from './icons'; import type { BookingPrefill, ChatToolPart, ToolOutputs } from './types'; type WidgetProps = { part: ChatToolPart; onDepartmentClick: (department: string) => void; onShowDoctorSlots: (doctorName: string) => void; onSuggestBooking: () => void; onPickSlot: (prefill: BookingPrefill) => void; onPickBranch: (branch: string) => void; }; // Dispatcher — renders the right widget for a tool part based on its name and state. export const ChatToolWidget = ({ part, onDepartmentClick, onShowDoctorSlots, onSuggestBooking, onPickSlot, onPickBranch, }: WidgetProps) => { if (part.state === 'input-streaming' || part.state === 'input-available') { return ; } if (part.state === 'output-error') { return
Couldn't load: {part.errorText ?? 'unknown error'}
; } switch (part.toolName) { case 'pick_branch': { const out = part.output as ToolOutputs['pick_branch'] | undefined; if (!out?.branches?.length) return null; return ; } case 'list_departments': { const out = part.output as ToolOutputs['list_departments'] | undefined; if (!out?.departments?.length) return null; return ; } case 'show_clinic_timings': { const out = part.output as ToolOutputs['show_clinic_timings'] | undefined; if (!out?.departments?.length) return null; return ; } case 'show_doctors': { const out = part.output as ToolOutputs['show_doctors'] | undefined; if (!out?.doctors?.length) { return (
No doctors found in {out?.department ?? 'this department'}.
); } return ( ); } case 'show_doctor_slots': { const out = part.output as ToolOutputs['show_doctor_slots'] | undefined; if (!out) return null; if (out.error || !out.doctor) { return
{out.error ?? 'Doctor not found.'}
; } return ; } case 'suggest_booking': { const out = part.output as ToolOutputs['suggest_booking'] | undefined; return ( ); } default: return null; } }; const TOOL_LABELS: Record = { pick_branch: 'Fetching branches…', list_departments: 'Looking up departments…', show_clinic_timings: 'Fetching clinic hours…', show_doctors: 'Looking up doctors…', show_doctor_slots: 'Checking availability…', suggest_booking: 'Thinking about booking options…', }; const ToolLoadingRow = ({ toolName }: { toolName: string }) => (
); type BranchPickerProps = { branches: ToolOutputs['pick_branch']['branches']; onPick: (branch: string) => void; }; const BranchPickerWidget = ({ branches, onPick }: BranchPickerProps) => (
Which branch?
{branches.map(b => ( ))}
); type DepartmentListProps = { departments: string[]; onPick: (department: string) => void; }; const DepartmentListWidget = ({ departments, onPick }: DepartmentListProps) => (
Departments
{departments.map(dept => ( ))}
); type ClinicTimingsProps = { departments: ToolOutputs['show_clinic_timings']['departments']; }; const ClinicTimingsWidget = ({ departments }: ClinicTimingsProps) => (
Clinic hours
{departments.map(dept => (
{dept.name}
{dept.entries.map(entry => (
{entry.name}
{entry.hours}
{entry.clinic && (
{entry.clinic}
)}
))}
))}
); type DoctorsListProps = { department: string; doctors: ToolOutputs['show_doctors']['doctors']; onPickDoctor: (doctorName: string) => void; }; const DoctorsListWidget = ({ department, doctors, onPickDoctor }: DoctorsListProps) => (
{department}
{doctors.map(doc => (
{doc.name}
{doc.specialty &&
{doc.specialty}
} {doc.visitingHours &&
{doc.visitingHours}
} {doc.clinic &&
{doc.clinic}
}
))}
); type DoctorSlotsProps = { data: ToolOutputs['show_doctor_slots']; onPickSlot: (prefill: BookingPrefill) => void; }; const DoctorSlotsWidget = ({ data, onPickSlot }: DoctorSlotsProps) => { if (!data.doctor) return null; const doctor = data.doctor; const available = data.slots.filter(s => s.available); const hasAny = available.length > 0; return (
Available slots
{doctor.name}
{formatDate(data.date)} {doctor.clinic ? ` • ${doctor.clinic}` : ''}
{hasAny ? (
{data.slots.map(s => ( ))}
) : (
No slots available on this date.
)}
); }; const formatDate = (iso: string): string => { // iso is YYYY-MM-DD from the backend. Render as e.g. "Mon, 6 Apr". const d = new Date(iso + 'T00:00:00'); if (isNaN(d.getTime())) return iso; return d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }); }; type BookingSuggestionProps = { reason: string; department: string | null; onBook: () => void; }; const BookingSuggestionWidget = ({ reason, department, onBook }: BookingSuggestionProps) => (
Book an appointment
{reason}
{department &&
Suggested: {department}
}
);