- Updated DoctorPage component to accept bookingResources prop for appointment list. - Added serviceQuery function to serialize service item UUIDs for API requests. - Introduced new API endpoints for fetching booking resources and resource slots. - Enhanced tests for new resource-based booking functionality, including resource selection and service availability. - Created ResourceSelect component for selecting appointment types, including doctor and resource options. - Updated appointment submission logic to include resource_uuid in payload when applicable. - Ensured UI reflects changes in booking flow without disrupting existing doctor-centric experience.
85 lines
3.4 KiB
JavaScript
85 lines
3.4 KiB
JavaScript
"use client";
|
||
|
||
const MAX_PREVIEW_SERVICES = 3;
|
||
|
||
/**
|
||
* زیرنویس کارت: نام سرویسها تا سهتا و بعد «+n».
|
||
*
|
||
* شمارِ خالی («۳ سرویس») به بیمار نمیگوید این دستگاه به کارش میآید یا نه، و او را
|
||
* مجبور میکند کورکورانه کلیک کند.
|
||
*/
|
||
function servicesLabel(services = []) {
|
||
if (services.length === 0) return null;
|
||
|
||
const shown = services.slice(0, MAX_PREVIEW_SERVICES).map((s) => s.name);
|
||
const rest = services.length - shown.length;
|
||
|
||
return rest > 0 ? `${shown.join("، ")} +${rest}` : shown.join("، ");
|
||
}
|
||
|
||
/**
|
||
* انتخاب نوع نوبت — پیش از انتخاب سرویس و روز، و فقط وقتی این پزشک در این محل
|
||
* دستکم یک منبعِ قابل رزرو دارد.
|
||
*
|
||
* «منبع» واژهٔ داخلی است و به بیمار نشان داده نمیشود؛ کارتها نام واقعی دستگاه/اتاق
|
||
* و نوعشان را نشان میدهند. کارت اول همیشه جریان همیشگی است: نوبت با خودِ پزشک.
|
||
*/
|
||
function ResourceSelect({ resources = [], selected = null, onSelect, allowDoctorOption = true }) {
|
||
const cardClass = (active) =>
|
||
`w-full text-right p-[16px] rounded-[8px] border border-solid transition-colors ${
|
||
active
|
||
? "border-[#5559CE] bg-[rgba(85,89,206,0.06)]"
|
||
: "border-[#EFEFEF] bg-[#FFF] hover:border-[#C7C9EC]"
|
||
}`;
|
||
|
||
return (
|
||
<div className="w-full max-w-[520px] mx-auto">
|
||
<h2 className="text-[16px] font-bold text-[#3B3B3B] mb-4">۱. انتخاب نوع نوبت</h2>
|
||
|
||
<div className="flex flex-col gap-2">
|
||
{/* پزشکی که برنامهٔ هفتگی ندارد، ویزیتِ مستقیم هم ندارد؛ نشاندادن این کارت
|
||
کاربر را به فهرست ساعتِ همیشهخالی میبرد. */}
|
||
{allowDoctorOption && (
|
||
<button type="button" onClick={() => onSelect(null)} className={cardClass(selected === null)}>
|
||
<p className="text-[14px] md:text-[16px] font-bold text-[#3B3B3B]">نوبت با پزشک</p>
|
||
<p className="mt-[6px] text-[13px] text-[#616161] font-normal">
|
||
ویزیت معمول در ساعتهای کاری پزشک
|
||
</p>
|
||
</button>
|
||
)}
|
||
|
||
{resources.map((resource) => {
|
||
const active = selected?.uuid === resource.uuid;
|
||
const label = servicesLabel(resource.services);
|
||
|
||
return (
|
||
<button
|
||
key={resource.uuid}
|
||
type="button"
|
||
onClick={() => onSelect(resource)}
|
||
className={cardClass(active)}
|
||
>
|
||
<div className="flex items-center justify-between gap-[8px]">
|
||
<p className="text-[14px] md:text-[16px] font-bold text-[#3B3B3B]">
|
||
{resource.name}
|
||
</p>
|
||
{resource.type?.name && (
|
||
<span className="text-[11px] text-[#5559CE] bg-[rgba(85,89,206,0.10)] rounded-[4px] px-[6px] py-[2px] shrink-0">
|
||
{resource.type.name}
|
||
</span>
|
||
)}
|
||
</div>
|
||
|
||
{label && (
|
||
<p className="mt-[6px] text-[13px] text-[#616161] font-normal">{label}</p>
|
||
)}
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default ResourceSelect;
|