diff --git a/src/components/call-desk/appointment-form.tsx b/src/components/call-desk/appointment-form.tsx index b19f886..9427c32 100644 --- a/src/components/call-desk/appointment-form.tsx +++ b/src/components/call-desk/appointment-form.tsx @@ -44,22 +44,8 @@ const genderItems = [ { id: 'other', label: 'Other' }, ]; -const timeSlotItems = [ - { id: '09:00', label: '9:00 AM' }, - { id: '09:30', label: '9:30 AM' }, - { id: '10:00', label: '10:00 AM' }, - { id: '10:30', label: '10:30 AM' }, - { id: '11:00', label: '11:00 AM' }, - { id: '11:30', label: '11:30 AM' }, - { id: '14:00', label: '2:00 PM' }, - { id: '14:30', label: '2:30 PM' }, - { id: '15:00', label: '3:00 PM' }, - { id: '15:30', label: '3:30 PM' }, - { id: '16:00', label: '4:00 PM' }, -]; - -const formatDeptLabel = (dept: string) => - dept.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); +// Time slots are fetched from /api/masterdata/slots based on +// doctor + date. No hardcoded times. export const AppointmentForm = ({ isOpen, @@ -111,6 +97,24 @@ export const AppointmentForm = ({ const [chiefComplaint, setChiefComplaint] = useState(existingAppointment?.reasonForVisit ?? ''); const [source, setSource] = useState('Inbound Call'); const [agentNotes, setAgentNotes] = useState(''); + const [timeSlotItems, setTimeSlotItems] = useState>([]); + + // Fetch available time slots when doctor + date change + useEffect(() => { + if (!doctor || !date) { + setTimeSlotItems([]); + return; + } + apiClient.get>( + `/api/masterdata/slots?doctorId=${doctor}&date=${date}`, + ).then(slots => { + setTimeSlotItems(slots.map(s => ({ id: s.time, label: s.label }))); + // Auto-select clinic from the slot's clinic + if (slots.length > 0 && !clinic) { + setClinic(slots[0].clinicId); + } + }).catch(() => setTimeSlotItems([])); + }, [doctor, date]); // Availability state const [bookedSlots, setBookedSlots] = useState([]); @@ -121,56 +125,27 @@ export const AppointmentForm = ({ // Fetch doctors on mount. Doctors are hospital-wide — no single // `clinic` field anymore. We pull the full visit-slot list via the - // doctorVisitSlots reverse relation so the agent can see which - // clinics + days this doctor covers in the picker. - // Fetch clinics from platform + // Fetch clinics + doctors from the master data endpoint (Redis-cached). + // This is faster than direct GraphQL and returns pre-formatted data. useEffect(() => { if (!isOpen) return; - apiClient.graphql<{ clinics: { edges: Array<{ node: { id: string; clinicName: string } }> } }>( - `{ clinics(first: 50) { edges { node { id clinicName } } } }`, - ).then(data => { - setClinicItems( - data.clinics.edges.map(e => ({ id: e.node.id, label: e.node.clinicName || 'Unnamed Clinic' })), - ); - }).catch(() => {}); + apiClient.get>('/api/masterdata/clinics') + .then(clinics => { + setClinicItems(clinics.map(c => ({ id: c.id, label: c.name || 'Unnamed Clinic' }))); + }).catch(() => {}); }, [isOpen]); useEffect(() => { if (!isOpen) return; - apiClient.graphql<{ doctors: { edges: Array<{ node: any }> } }>( - `{ doctors(first: 50) { edges { node { - id name fullName { firstName lastName } department - doctorVisitSlots(first: 50) { - edges { node { id clinic { id clinicName } dayOfWeek startTime endTime } } - } - } } } }`, - ).then(data => { - const docs = data.doctors.edges.map(e => { - // Flatten the visit-slot list into a comma-separated - // clinic summary for display. Keep full slot data on - // the record in case future UX needs it (e.g., show - // only slots matching the selected date's weekday). - const slotEdges: Array<{ node: any }> = e.node.doctorVisitSlots?.edges ?? []; - const clinicNames = Array.from( - new Set( - slotEdges - .map((se) => se.node.clinic?.clinicName ?? se.node.clinicId) - .filter((n): n is string => !!n), - ), - ); - return { - id: e.node.id, - name: e.node.fullName - ? `Dr. ${e.node.fullName.firstName} ${e.node.fullName.lastName}`.trim() - : e.node.name, - department: e.node.department ?? '', - // `clinic` here is a display-only summary: "Koramangala, Whitefield" - // or empty if the doctor has no slots yet. - clinic: clinicNames.join(', '), - }; - }); - setDoctors(docs); - }).catch(() => {}); + apiClient.get>('/api/masterdata/doctors') + .then(docs => { + setDoctors(docs.map(d => ({ + id: d.id, + name: d.name, + department: d.department, + clinic: '', // clinic assignment via visit slots, not on doctor directly + }))); + }).catch(() => {}); }, [isOpen]); // Fetch booked slots when doctor + date selected @@ -219,9 +194,18 @@ export const AppointmentForm = ({ setTimeSlot(null); }, [doctor, date]); - // Derive department and doctor lists from fetched data - const departmentItems = [...new Set(doctors.map(d => d.department).filter(Boolean))] - .map(dept => ({ id: dept, label: formatDeptLabel(dept) })); + // Departments from master data (or fallback to deriving from doctors) + const [departmentItems, setDepartmentItems] = useState>([]); + useEffect(() => { + if (!isOpen) return; + apiClient.get('/api/masterdata/departments') + .then(depts => setDepartmentItems(depts.map(d => ({ id: d, label: d })))) + .catch(() => { + // Fallback: derive from doctor list + const derived = [...new Set(doctors.map(d => d.department).filter(Boolean))]; + setDepartmentItems(derived.map(d => ({ id: d, label: d }))); + }); + }, [isOpen, doctors]); const filteredDoctors = department ? doctors.filter(d => d.department === department)