mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-05-18 20:08:19 +00:00
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:
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user