fix(appointment-form): filter past pills + confirm modal + view-only mode

- 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) <noreply@anthropic.com>
This commit is contained in:
2026-04-15 13:39:54 +05:30
parent ffb8bcb6ad
commit 04f559037c
2 changed files with 93 additions and 12 deletions

View File

@@ -35,6 +35,11 @@ type AppointmentFormProps = {
// CANCELLED each map to distinct disposition outcomes).
onSaved?: (outcome: 'BOOKED' | 'RESCHEDULED' | 'CANCELLED') => 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}
/>
</div>
{!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) => <Select.Item id={item.id} label={item.label} />}
</Select>
@@ -555,7 +561,7 @@ export const AppointmentForm = ({
items={doctorSelectItems}
selectedKey={doctor}
onSelectionChange={(key) => setDoctor(key as string)}
isDisabled={!department}
isDisabled={readOnly || !department}
>
{(item) => <Select.Item id={item.id} label={item.label} />}
</Select>
@@ -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}
/>
</div>
@@ -585,7 +591,7 @@ export const AppointmentForm = ({
<button
key={slot.id}
type="button"
disabled={isBooked}
disabled={readOnly || isBooked}
onClick={() => setTimeSlot(slot.id)}
className={cx(
'rounded-lg py-2 px-1 text-xs font-medium transition duration-100 ease-linear',
@@ -613,6 +619,7 @@ export const AppointmentForm = ({
placeholder="Describe the reason for visit..."
value={chiefComplaint}
onChange={setChiefComplaint}
isDisabled={readOnly}
rows={2}
/>
@@ -649,7 +656,7 @@ export const AppointmentForm = ({
{/* Footer — pinned */}
<div className="shrink-0 flex items-center justify-between pt-4 border-t border-secondary">
<div>
{isEditMode && (
{isEditMode && !readOnly && (
<Button size="sm" color="primary-destructive" isLoading={isSaving} onClick={handleCancel}>
Cancel Appointment
</Button>
@@ -659,9 +666,11 @@ export const AppointmentForm = ({
<Button size="sm" color="secondary" onClick={() => onOpenChange(false)}>
Close
</Button>
<Button size="sm" color="primary" isLoading={isSaving} showTextWhileLoading onClick={handleSave}>
{isSaving ? 'Saving...' : isEditMode ? 'Update Appointment' : 'Book Appointment'}
</Button>
{!readOnly && (
<Button size="sm" color="primary" isLoading={isSaving} showTextWhileLoading onClick={handleSave}>
{isSaving ? 'Saving...' : isEditMode ? 'Update Appointment' : 'Book Appointment'}
</Button>
)}
</div>
</div>