From 1d8f166e889c67614cc59ce419e190b2614dda99 Mon Sep 17 00:00:00 2001 From: Dana Medical Developer Date: Fri, 23 Oct 2026 08:18:22 +0000 Subject: [PATCH] fix(booking): preserve selected doctor when changing calendar date --- web/scripts/test-booking-flow.mjs | 26 ++++++++++++++++++++++---- web/src/components/LeadModal.jsx | 25 +++++++++++++++---------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/web/scripts/test-booking-flow.mjs b/web/scripts/test-booking-flow.mjs index 0ad6cee..a09a645 100644 --- a/web/scripts/test-booking-flow.mjs +++ b/web/scripts/test-booking-flow.mjs @@ -128,15 +128,33 @@ try { await chooseDoctor(2); await reply(pending.at(-1), [free('09:00'), { ...free('14:15'), start: '2070-10-24T14:15:00+03:00' }]); assert.deepEqual(times(), ['14:15'], 'Selected date restricts the actual time list too'); + await click(container.querySelector('.slot-chip--time')); + assert.ok(container.querySelector('input[type="tel"]')); await act(async () => Simulate.change(container.querySelector('input[type="date"]'), { target: { value: '2070-10-25' } })); assert.deepEqual(times(), []); - assert.equal(container.querySelector('.doctor-picker__trigger .doctor-picker__name').textContent, 'Выберите врача', 'Changing date clears the selected doctor and window'); - assert.deepEqual(await availableDoctorIDs(), []); - assert.match(text(), /На эту дату свободных окон нет/); - assert.equal(container.querySelector('.lead-modal__availability--warning'), null, 'An empty selected date keeps its informational style'); + assert.equal(container.querySelector('input[type="tel"]'), null, 'Changing date clears the chosen time and contacts step'); + assert.match(container.querySelector('.doctor-picker__trigger .doctor-picker__name').textContent, /Второй врач/, 'Changing date preserves the doctor even when no windows are available'); + assert.deepEqual(await availableDoctorIDs(), ['2']); + assert.match(text(), /У выбранного врача на эту дату свободных окон нет/); + assert.ok(container.querySelector('.lead-modal__availability--warning')); + await act(async () => Simulate.change(container.querySelector('input[type="date"]'), { target: { value: '2070-10-23' } })); + assert.match(container.querySelector('.doctor-picker__trigger .doctor-picker__name').textContent, /Второй врач/); + assert.equal(pending.at(-1).slug, 'second'); + await reply(pending.at(-1), [free('11:45')]); + assert.deepEqual(times(), ['11:45'], 'Available date opens the preserved doctor schedule without a second selection'); await act(async () => Simulate.change(container.querySelector('input[type="date"]'), { target: { value: '' } })); + await reply(pending.at(-1), [free('12:45')]); + assert.deepEqual(times(), ['12:45'], 'Clearing date keeps the doctor and restores all their dates'); assert.deepEqual(await availableDoctorIDs(), ['1','2','3'], 'Clearing date restores the doctor-first flow'); + await act(async () => root.render(null)); + await render({ doctor: doctors[0] }); + await reply(pending.at(-1), [free('10:30')]); + await act(async () => Simulate.input(container.querySelector('input[type="date"]'), { target: { value: '2070-10-23' } })); + await reply(pending.at(-1), [free('13:30')]); + assert.match(container.querySelector('.doctor-picker__trigger .doctor-picker__name').textContent, /Первый врач/); + assert.deepEqual(times(), ['13:30'], 'Doctor preselected from a card also survives calendar selection'); + const originalDoctorLoader = api.doctors; for (const fail of [false, true]) { await act(async () => root.render(null)); diff --git a/web/src/components/LeadModal.jsx b/web/src/components/LeadModal.jsx index 420c0c7..a0f012e 100644 --- a/web/src/components/LeadModal.jsx +++ b/web/src/components/LeadModal.jsx @@ -63,9 +63,7 @@ export default function LeadModal({ form = 'appointment', doctor, slot, service, const [status, setStatus] = useState({ state: 'idle', message: '' }); const chosenDoctor = doctors.find((d) => String(d.id) === values.doctorId) || (doctor && String(doctor.id) === values.doctorId ? doctor : null); - const selectedDoctor = form === 'appointment' && date - && (availability.loading || !availability.days.find((day) => day.date === date)?.doctor_ids.includes(chosenDoctor?.id)) - ? null : chosenDoctor; + const selectedDoctor = chosenDoctor; const native = form === 'appointment' && selectedDoctor?.booking?.provider === 'native' && unavailableDoctorId !== selectedDoctor.id; const onNativeUnavailable = useCallback( @@ -90,7 +88,6 @@ export default function LeadModal({ form = 'appointment', doctor, slot, service, setDate(e.target.value); setRequestedSlot(undefined); setUnavailableDoctorId(null); - setValues((previous) => ({ ...previous, doctorId: '' })); setStatus({ state: 'idle', message: '' }); }; @@ -180,6 +177,14 @@ export default function LeadModal({ form = 'appointment', doctor, slot, service, : doctorsLoaded && doctorOptions.length === 0 ? 'Список врачей сейчас недоступен. Позвоните в клинику, чтобы уточнить запись.' : date ? 'Выберите врача из списка — покажем его свободные часы на выбранную дату.' : ''; + // Дата фильтрует окна, но не отменяет сделанный посетителем выбор врача. + const selectedDateMessage = selectedDoctor && date + ? availability.loading ? 'Проверяем свободные окна выбранного врача…' + : availability.error ? 'Не удалось проверить расписание. Попробуйте выбрать дату ещё раз.' + : !availability.days.find((day) => day.date === date)?.doctor_ids.includes(selectedDoctor.id) + ? 'У выбранного врача на эту дату свободных окон нет. Выберите другую дату или другого специалиста.' : '' + : ''; + const appointmentMessage = selectedDoctor ? selectedDateMessage : availabilityMessage; const submit = async (e) => { e.preventDefault(); @@ -271,13 +276,13 @@ export default function LeadModal({ form = 'appointment', doctor, slot, service, onInput={changeDate} onChange={changeDate} /> - Можно выбрать дату и увидеть только свободных в этот день врачей. + {selectedDoctor ? 'Выберите дату — покажем свободные окна выбранного врача.' : 'Можно выбрать дату и увидеть только свободных в этот день врачей.'} )} {form === 'appointment' && status.state !== 'done' && ( d.id === doctor.id) ? [doctor, ...doctorOptions] : doctorOptions} + doctors={selectedDoctor && !doctorOptions.some((d) => d.id === selectedDoctor.id) ? [selectedDoctor, ...doctorOptions] : doctorOptions} value={values.doctorId} onChange={set('doctorId')} date={date} @@ -286,10 +291,10 @@ export default function LeadModal({ form = 'appointment', doctor, slot, service, /> )} - {form === 'appointment' && !selectedDoctor ? ( - availabilityMessage ? ( -

- {availabilityMessage} + {form === 'appointment' && (!selectedDoctor || selectedDateMessage) ? ( + appointmentMessage ? ( +

+ {appointmentMessage}

) : null ) : native ? (