From 04f559037c890386eb5cf73bf6008a05e1047f04 Mon Sep 17 00:00:00 2001 From: saridsa2 Date: Wed, 15 Apr 2026 13:39:54 +0530 Subject: [PATCH] fix(appointment-form): filter past pills + confirm modal + view-only mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - leadAppointments now filters out past appointments — past dates can't be rescheduled, so "14 Apr · Meena Patel" shouldn't appear in the pill row today. Uses scheduledAt >= now. - Click Edit pill → reschedule-confirm modal: "Yes, reschedule" → form opens in edit mode (prefilled + editable) "No, just view" → form opens read-only (prefilled + disabled) - Prefill was broken — AppointmentForm's useState initializers only run at mount, so switching pills didn't re-seed state. Added key={editingApptId}-{apptMode} so the form fully remounts whenever the selection or mode changes. - Thread readOnly prop through every form control (patient name, phone, age, gender, clinic, department, doctor, date, time slots, chief complaint). In view mode all inputs are disabled and the Update Appointment + Cancel Appointment buttons hide — only Close remains. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/call-desk/active-call-card.tsx | 78 ++++++++++++++++++- src/components/call-desk/appointment-form.tsx | 27 ++++--- 2 files changed, 93 insertions(+), 12 deletions(-) diff --git a/src/components/call-desk/active-call-card.tsx b/src/components/call-desk/active-call-card.tsx index b92a9bf..c0529a9 100644 --- a/src/components/call-desk/active-call-card.tsx +++ b/src/components/call-desk/active-call-card.tsx @@ -14,6 +14,7 @@ import { setOutboundPending } from '@/state/sip-manager'; import { useSip } from '@/providers/sip-provider'; import { DispositionModal } from './disposition-modal'; import type { CallAction } from './disposition-modal'; +import { Dialog, Modal, ModalOverlay } from '@/components/application/modals/modal'; import { AppointmentForm } from './appointment-form'; import { TransferDialog } from './transfer-dialog'; import { EnquiryForm } from './enquiry-form'; @@ -74,12 +75,17 @@ export const ActiveCallCard = ({ lead, callerPhone, missedCallId, onCallComplete const leadAppointments = useMemo(() => { const patientId = (lead as any)?.patientId; if (!patientId) return []; + const now = Date.now(); return appointments .filter((a) => a.patientId === patientId && a.appointmentStatus !== 'CANCELLED' && a.appointmentStatus !== 'NO_SHOW' - && a.appointmentStatus !== 'COMPLETED', + && a.appointmentStatus !== 'COMPLETED' + // Only future appointments make sense as reschedule targets. + // Past ones can't be edited — they already happened. + && a.scheduledAt + && new Date(a.scheduledAt).getTime() >= now, ) .sort((a, b) => new Date(a.scheduledAt ?? '').getTime() - new Date(b.scheduledAt ?? '').getTime()); }, [appointments, lead]); @@ -89,6 +95,12 @@ export const ActiveCallCard = ({ lead, callerPhone, missedCallId, onCallComplete [leadAppointments, editingApptId], ); + // Pending pill click awaiting the reschedule-confirm modal. When the + // agent clicks a pill, we store the appointment id here + open the modal. + // Yes → promote to editingApptId in edit mode. No → promote in view mode. + const [pendingApptId, setPendingApptId] = useState(null); + const [apptMode, setApptMode] = useState<'edit' | 'view'>('edit'); + const agentConfig = localStorage.getItem('helix_agent_config'); const agentIdForState = agentConfig ? (() => { try { return JSON.parse(agentConfig).ozonetelAgentId; } catch { return null; } })() : null; const { supervisorPresence } = useAgentState(agentIdForState); @@ -397,7 +409,7 @@ export const ActiveCallCard = ({ lead, callerPhone, missedCallId, onCallComplete + + + + )} + + + + {/* Disposition Modal — the ONLY path to end a call */} void; existingAppointment?: ExistingAppointment | null; + // When true, the form shows the existing appointment's data in a + // disabled state — no input editing, no Save/Cancel. Only a Close + // button. Used by the reschedule-confirm flow when the agent picks + // "No, just view" on an upcoming-appointment pill. + readOnly?: boolean; }; type DoctorRecord = { id: string; name: string; department: string; clinic: string }; @@ -60,6 +65,7 @@ export const AppointmentForm = ({ patientId, onSaved, existingAppointment, + readOnly = false, }: AppointmentFormProps) => { const isEditMode = !!existingAppointment; @@ -471,7 +477,7 @@ export const AppointmentForm = ({ placeholder="Full name" value={patientName} onChange={setPatientName} - isDisabled={!isNameEditable} + isDisabled={readOnly || !isNameEditable} /> {!isNameEditable && initialLeadName.length > 0 && ( @@ -544,7 +550,7 @@ export const AppointmentForm = ({ items={departmentItems} selectedKey={department} onSelectionChange={(key) => setDepartment(key as string)} - isDisabled={doctors.length === 0} + isDisabled={readOnly || doctors.length === 0} > {(item) => } @@ -555,7 +561,7 @@ export const AppointmentForm = ({ items={doctorSelectItems} selectedKey={doctor} onSelectionChange={(key) => setDoctor(key as string)} - isDisabled={!department} + isDisabled={readOnly || !department} > {(item) => } @@ -567,7 +573,7 @@ export const AppointmentForm = ({ value={date ? parseDate(date) : null} onChange={(val) => setDate(val ? val.toString() : '')} granularity="day" - isDisabled={!doctor} + isDisabled={readOnly || !doctor} /> @@ -585,7 +591,7 @@ export const AppointmentForm = ({ @@ -659,9 +666,11 @@ export const AppointmentForm = ({ - + {!readOnly && ( + + )}