From f3659f827e1d5f4383283d8d513c78263048f4b5 Mon Sep 17 00:00:00 2001 From: hamed <15238-genius.ha@users.noreply.drupalcode.org> Date: Thu, 30 Jul 2026 15:50:07 +0330 Subject: [PATCH] feat(booking): take service duration from the backend, not a parallel client sum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- components/appointment/Container.js | 2 + components/appointment/service/index.js | 61 ++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/components/appointment/Container.js b/components/appointment/Container.js index c2fea86..4ebddb2 100644 --- a/components/appointment/Container.js +++ b/components/appointment/Container.js @@ -77,6 +77,8 @@ function Container({ dateStep = ( setSelectedServiceUuids(uuids)} /> ); diff --git a/components/appointment/service/index.js b/components/appointment/service/index.js index 9728ebb..f897b94 100644 --- a/components/appointment/service/index.js +++ b/components/appointment/service/index.js @@ -1,24 +1,70 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState } from "react"; +import moment from "jalali-moment"; +import { request } from "@/services/response"; function toToman(rials) { if (!rials) return null; return Math.round(rials / 10).toLocaleString("fa-IR"); } +/** + * جمعِ سمتِ کلاینت — فقط fallback برای بک‌اندی که `total_duration_minutes` نمی‌دهد. + * مدت، دادهٔ سرور است: فرمولش قرار است به «زمان تنها / زمان اضافه» تغییر کند و هر + * محاسبهٔ موازی در فرانت از آن روز عددِ غلط نشان می‌دهد. + */ +function fallbackSum(services, uuids) { + return services + .filter((s) => uuids.includes(s.uuid)) + .reduce((sum, s) => sum + (Number(s.duration_minutes) || 0), 0); +} + // مرحلهٔ انتخاب سرویس (فقط حالت نوبت‌دهی سرویسی) — پیش از انتخاب روز. -function ServiceSelect({ services = [], onContinue }) { +function ServiceSelect({ services = [], onContinue, doctorUuid, clinicUuid = null }) { const [draft, setDraft] = useState([]); + /** `{ key, minutes }` — کلید همان انتخابی است که عدد به آن تعلق دارد. */ + const [serverDuration, setServerDuration] = useState(null); const toggle = (uuid) => setDraft((prev) => prev.includes(uuid) ? prev.filter((u) => u !== uuid) : [...prev, uuid] ); - const totalMinutes = services - .filter((s) => draft.includes(s.uuid)) - .reduce((sum, s) => sum + (Number(s.duration_minutes) || 0), 0); + const draftKey = draft.join(","); + + // مدت را سرور حساب می‌کند. این مرحله پیش از انتخاب روز است، ولی + // `total_duration_minutes` به تاریخ وابسته نیست — بک‌اند آن را پیش از لمسِ + // شیفت‌های آن روز برمی‌گرداند، پس حتی اگر امروز تعطیل باشد و `start_times` + // خالی بیاید، عدد درست است. + useEffect(() => { + if (!doctorUuid || draftKey === "") return; + + let cancelled = false; + const today = moment().format("YYYY-MM-DD"); + + request + .getServiceSlots(doctorUuid, today, draftKey.split(","), clinicUuid) + .then((res) => { + if (cancelled) return; + const minutes = (res?.data?.data ?? res?.data)?.total_duration_minutes; + if (typeof minutes !== "number") { + console.warn("[booking] total_duration_minutes missing — falling back to client sum"); + return; + } + setServerDuration({ key: draftKey, minutes }); + }) + .catch(() => {}); + + return () => { + cancelled = true; + }; + }, [doctorUuid, clinicUuid, draftKey]); + + // کهنه‌بودن **مشتق** می‌شود، نه با reset: عددِ متعلق به انتخاب قبلی خودکار نادیده + // گرفته می‌شود و effect لازم نیست در بدنه‌اش setState صدا بزند. + const serverMinutes = serverDuration?.key === draftKey ? serverDuration.minutes : null; + const totalMinutes = serverMinutes ?? fallbackSum(services, draft); return (
@@ -70,7 +116,10 @@ function ServiceSelect({ services = [], onContinue }) { {draft.length > 0 && (
- مدت کل: {totalMinutes} دقیقه + {/* تا وقتی عدد سرور نرسیده، «تقریبی» است — عددِ جمعِ کلاینت ممکن است با + مدت واقعی نوبت یکی نباشد. */} + {serverMinutes === null ? "مدت تقریبی" : "مدت کل"}:{" "} + {totalMinutes} دقیقه
)}