From cc6b42c1a5497e4c26417791a9b37a6d6dbdf3fe Mon Sep 17 00:00:00 2001 From: hamed <15238-genius.ha@users.noreply.drupalcode.org> Date: Wed, 24 Jun 2026 19:56:04 +0330 Subject: [PATCH] feat(appointment): add city_id to appointment payload and update ProvinceProvider for city context --- .claude/prompt/booking-send-representation.md | 79 +++++++++++++++++++ components/appointment/detail/SubmitData.js | 4 + context/ProvinceProvider.js | 2 +- 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 .claude/prompt/booking-send-representation.md diff --git a/.claude/prompt/booking-send-representation.md b/.claude/prompt/booking-send-representation.md new file mode 100644 index 0000000..16d6976 --- /dev/null +++ b/.claude/prompt/booking-send-representation.md @@ -0,0 +1,79 @@ +# ارسال city_id دامنه‌ی جاری هنگام رزرو نوبت + +## پروژه + +`nobat724_front` (سایت عمومی). **cross-repo**: پرامپت همتا `clinicpro/.claude/prompt/representation-domain-guard-dashboard-settlement.md` است که **اول** اجرا می‌شود (backend فیلد `city_id` را در `POST /api/v1/appointment` می‌پذیرد و نماینده‌ی شهر را برای گاردِ پورسانت ذخیره می‌کند). این پرامپت front را بعد از آن اجرا کن. + +## زمینه + +هر شهر دامنه‌ی جداگانه دارد و از `data/city.json` تشخیص داده می‌شود (`getStateInfo().matchedCity` سرور، `ProvinceProvider`/`window.location.hostname` کلاینت). هر شهر `id` دارد (مثلاً اراک `129`) و فیلد `representation_id`. نماینده در backend به همان `city_id` خورده است. + +برای اینکه backend بتواند تشخیص دهد نوبت از **دامنه‌ی نماینده** ثبت شده (شرط لازم برای محاسبه‌ی پورسانت)، سایت باید `city_id` دامنه‌ی جاری را در payload رزرو بفرستد. + +## مشکل / هدف + +در حال حاضر `appointmentPayload` در `components/appointment/detail/SubmitData.js` فیلد `city_id` ندارد. باید `matchedCity.id` دامنه‌ی جاری به آن اضافه شود. + +## فایل‌های مرتبط + +| فایل | نقش | +|------|-----| +| `nobat724_front/components/appointment/detail/SubmitData.js` | ساخت `appointmentPayload` و فراخوانی `postAppointment` | +| `nobat724_front/context/ProvinceProvider.js` | منبع client-side شهر جاری (`useProvince`) | +| `nobat724_front/lib/getStateInfo.js` | منبع server-side شهر (`matchedCity.id`) | +| `nobat724_front/data/city.json` | `id` و `domain` هر شهر | +| `nobat724_front/services/response.js` | `postAppointment` (بدون تغییر؛ فقط payload عوض می‌شود) | + +## وضعیت فعلی + +`components/appointment/detail/SubmitData.js` (پس از تغییرِ قبلیِ national_code/gender): +```js +const appointmentPayload = { + doctor_uuid: doctor.uuid, + slot_start: selectedSlot.start, + slot_end: selectedSlot.end, + for_self: !isForAnother, + note: data?.patient_reason?.value || "", + patient_national_code: data?.national_code?.value || "", + patient_gender: data?.gender?.value?.id || data?.gender?.value || "", + ...(isForAnother ? { patient_name: ..., patient_mobile: ..., patient_reason: ... } : {}), +}; +const res = await request.postAppointment(appointmentPayload); +``` + +`ProvinceProvider` (client) شهر را از `window.location.hostname` تشخیص می‌دهد. باید بررسی شود چه چیزی expose می‌کند — اگر فقط `isProvinceInclude` دارد، یک getter برای `matchedCity` (یا حداقل `matchedCity.id`) به آن اضافه شود تا کامپوننت کلاینت به `city.id` دسترسی داشته باشد. + +## وظایف + +### ۱. در دسترس قرار دادن city.id در کلاینت + +اگر `useProvince()` فقط `isProvinceInclude` می‌دهد، آن را گسترش بده تا `matchedCity` (یا `cityId`) هم برگرداند. منطق تطبیق همان `getStateInfo` است: `subdomain = hostname.split('.')[0]`، سپس `citiesData.find(c => c.domain.split('.')[0] === subdomain)`. (در dev، `HOST=yazd-nobat.localhost` ⇒ subdomain = `yazd-nobat`.) + +### ۲. افزودن city_id به payload رزرو + +در `SubmitData.js`: +```js +import { useProvince } from "@/context/ProvinceProvider"; +// ... +const { matchedCity } = useProvince(); // یا cityId مستقیم + +const appointmentPayload = { + doctor_uuid: doctor.uuid, + slot_start: selectedSlot.start, + slot_end: selectedSlot.end, + for_self: !isForAnother, + note: data?.patient_reason?.value || "", + patient_national_code: data?.national_code?.value || "", + patient_gender: data?.gender?.value?.id || data?.gender?.value || "", + city_id: matchedCity?.id ?? null, // ← جدید + ...(isForAnother ? { ... } : {}), +}; +``` + +## نکات مهم + +- `city_id` همان `matchedCity.id` از `city.json` است (نه `representation_id` که ممکن است null باشد). backend خودش نماینده‌ی آن شهر را پیدا می‌کند. +- اگر شهر تشخیص داده نشد یا نماینده نداشت، `city_id` خالی/null ⇒ backend پورسانت محاسبه نمی‌کند. این رفتار مطلوب است؛ خطا نده. +- مبلغ پرداختیِ کاربر تغییر نمی‌کند؛ این فقط متادیتای رزرو است. +- App Router + کامپوننت کلاینت: `useProvince` فقط در client component کار می‌کند؛ `SubmitData` کلاینت است. +- بعد از تغییر: `npm run build` برای صحت. قرارداد `POST /api/v1/appointment` در `clinicpro/docs/api/appointment.md` (سمت backend) به‌روز است؛ فقط مطمئن شو نام فیلد `city_id` با backend یکی است. diff --git a/components/appointment/detail/SubmitData.js b/components/appointment/detail/SubmitData.js index 494c8eb..e1e9f87 100644 --- a/components/appointment/detail/SubmitData.js +++ b/components/appointment/detail/SubmitData.js @@ -5,9 +5,11 @@ import ArrowLeftB from "@/components/icons/ArrowLeftB"; import { request } from "@/services/response"; import Cookies from "js-cookie"; import { setAccessToken } from "@/lib/tokenStore"; +import { useProvince } from "@/context/ProvinceProvider"; function SubmitData({ setStep, data, prevData, setErrors, doctor, isForAnother, selectedSlot, selectedDate, setAppointmentId, setAppointmentExpiresAt }) { const [loading, setLoading] = useState(false); + const { cityId } = useProvince(); const newStep = () => setStep((prev) => prev + 1); const refreshAccessToken = async () => { @@ -142,6 +144,8 @@ function SubmitData({ setStep, data, prevData, setErrors, doctor, isForAnother, // کد ملی و جنسیت در هر دو حالت (خودِ بیمار / شخص دیگر) الزامی است. patient_national_code: data?.national_code?.value || "", patient_gender: data?.gender?.value?.id || data?.gender?.value || "", + // شناسه‌ی شهرِ دامنه‌ی جاری؛ backend برای گاردِ پورسانت نماینده استفاده می‌کند. + city_id: cityId ?? null, ...(isForAnother ? { patient_name: [data?.name?.value, data?.family?.value].filter(Boolean).join(" ").trim(), diff --git a/context/ProvinceProvider.js b/context/ProvinceProvider.js index 3153ce8..1485102 100644 --- a/context/ProvinceProvider.js +++ b/context/ProvinceProvider.js @@ -18,7 +18,7 @@ export const ProvinceProvider = ({ children }) => { }, []); return ( - + {children} );