Files
nobat724_front/components/appointment/Container.js
T
hamedandClaude Opus 5 f3659f827e feat(booking): take service duration from the backend, not a parallel client sum
The service picker summed duration_minutes itself while the backend already
returns total_duration_minutes. Two sources of truth: when the formula changes to
solo/additional minutes, the site would keep showing the old number and the
patient would see a duration that does not match their appointment.

The picker runs before the date step and appointment-service-slots needs a date,
so it is called with today. total_duration_minutes does not depend on the date —
the backend computes it before touching that day's shifts, so the number is right
even when today is closed and start_times comes back empty.

The label reads "مدت تقریبی" until the server number arrives, then "مدت کل".
A missing field or a failed request falls back to the client sum with a
console.warn rather than blanking the step.

Staleness is derived from the selection key instead of reset in the effect body,
which also clears the set-state-in-effect lint warning.

Task: clinicpro/docs/new_feture/taskes/task-00b-nobat724-service-mode/

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:50:07 +03:30

187 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Date from "./date";
import ServiceSelect from "./service";
import LocationSelect from "./location/LocationSelect";
// Components
import Content from "./Content";
import Layout from "@/components/layout";
import Paying from "@/components/appointment/paying";
import Detail from "@/components/appointment/detail";
import FailedPay from "@/components/appointment/failedPay";
import SuccessPay from "@/components/appointment/successPay";
import LogInPage from "@/components/register/LogInPage";
import VerificationPage from "@/components/register/verificationPage";
import LayoutRegister from "@/components/register/LayoutRegister";
function Container({
step,
data,
doctor,
setData,
setStep,
prevData,
matchedCity,
isForAnother,
setIsForAnother,
disabledDates,
// Login states
num,
setNum,
uuid,
setUuid,
isSendMsg,
setIsSendMsg,
// Appointment slot states
selectedSlot,
setSelectedSlot,
selectedDate,
setSelectedDate,
appointmentId,
setAppointmentId,
appointmentExpiresAt,
setAppointmentExpiresAt,
// Service-based booking
bookingMode,
bookingServices,
selectedServiceUuids,
setSelectedServiceUuids,
// Booking location (مطب شخصی / کلینیک)
bookingLocations,
selectedLocation,
locationConfirmed,
changeLocation,
reopenLocationChoice,
onDateChange,
selectedClosedOnDate,
}) {
const serviceMode = bookingMode === "service";
const clinicUuid = selectedLocation?.clinic_uuid ?? null;
const multiLocation = bookingLocations.length > 1;
let dateStep;
if (bookingLocations.length === 0) {
dateStep = (
<p className="w-full max-w-[520px] mx-auto py-[40px] text-center text-[14px] text-[#7A7A7A]">
نوبتدهی آنلاین برای این پزشک فعال نیست.
</p>
);
} else if (multiLocation && !locationConfirmed) {
dateStep = (
<LocationSelect
locations={bookingLocations}
selected={selectedLocation}
onSelect={changeLocation}
/>
);
} else if (serviceMode && selectedServiceUuids.length === 0) {
dateStep = (
<ServiceSelect
services={bookingServices}
doctorUuid={doctor?.uuid}
clinicUuid={clinicUuid}
onContinue={(uuids) => setSelectedServiceUuids(uuids)}
/>
);
} else {
dateStep = (
<Date
doctor={doctor}
setStep={setStep}
disabledDates={disabledDates}
setSelectedSlot={setSelectedSlot}
setSelectedDate={setSelectedDate}
serviceMode={serviceMode}
selectedServiceUuids={selectedServiceUuids}
onChangeService={() => setSelectedServiceUuids([])}
selectedLocation={selectedLocation}
clinicUuid={clinicUuid}
onChangeLocation={multiLocation ? reopenLocationChoice : null}
onDateChange={onDateChange}
closedOnDate={selectedClosedOnDate}
/>
);
}
const elements = [
dateStep,
<LogInPage
num={num}
setNum={setNum}
setUuid={setUuid}
setStep={setStep}
setIsSendMsg={setIsSendMsg}
matchedCity={matchedCity}
/>,
<VerificationPage
num={num}
uuid={uuid}
setStep={setStep}
setUuid={setUuid}
isSendMsg={isSendMsg}
setIsSendMsg={setIsSendMsg}
link={false}
/>,
<Detail
data={data}
setStep={setStep}
setData={setData}
prevData={prevData}
isForAnother={isForAnother}
setIsForAnother={setIsForAnother}
doctor={doctor}
selectedSlot={selectedSlot}
selectedDate={selectedDate}
setAppointmentId={setAppointmentId}
setAppointmentExpiresAt={setAppointmentExpiresAt}
selectedServiceUuids={selectedServiceUuids}
clinicUuid={clinicUuid}
/>,
<Paying
setStep={setStep}
appointmentId={appointmentId}
expiresAt={appointmentExpiresAt}
doctor={doctor}
data={data}
isForAnother={isForAnother}
selectedSlot={selectedSlot}
selectedDate={selectedDate}
/>,
<SuccessPay setStep={setStep} />,
<FailedPay setStep={setStep} />,
];
return (
<>
{step === 1 || step === 2 ? (
<LayoutRegister matchedCity={matchedCity}>
{elements[step]}
</LayoutRegister>
) : (
<Layout
title="رزرو"
name="booking"
disableFooter={step > 4}
matchedCity={matchedCity}
paddingBottom="pb-[80px]"
>
<Content
disableSide={step === 5 || step === 6}
isFirst={step === 0}
setStep={setStep}
doctor={doctor}
step={step}
selectedSlot={selectedSlot}
selectedDate={selectedDate}
selectedLocation={selectedLocation}
>
{elements[step]}
</Content>
</Layout>
)}
</>
);
}
export default Container;