A patient who booked in service mode could not see which services they had reserved or how long the appointment was. Both now appear on the list card and in both detail layouts, reading service_items and service_total_minutes from GET /api/v1/appointments/user. Every field is behind an explicit guard. Slot-mode appointments carry none of them, and an unguarded map would crash the card for every slot-mode appointment, taking the whole panel with it. A reserve entry shows its services but not a duration, because it has no time. Covered by Card.test.jsx: slot-mode appointments render unchanged, absent fields (older backend) do not crash, reserve hides the duration, and a zero duration produces no row. Task: clinicpro/docs/new_feture/taskes/task-00b-nobat724-service-mode/ Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
95 lines
4.0 KiB
JavaScript
95 lines
4.0 KiB
JavaScript
import CircularLoading from "@/app/component/loading/Circular";
|
|
import TextLoading from "@/app/component/loading/Text";
|
|
import ArrowLeftCardD from "@/components/icons/ArrowLeftCardD";
|
|
import { Button } from "@mui/material";
|
|
import Image from "next/image";
|
|
import { convertTimestampToJalali, convertTimestampToTime } from "@/helper";
|
|
|
|
const STATUS_LABELS = {
|
|
pending: "در انتظار پرداخت",
|
|
confirmed: "تأیید شده",
|
|
completed: "انجام شده",
|
|
cancelled_by_user: "لغو شده",
|
|
cancelled_by_doctor: "لغو توسط پزشک",
|
|
expired: "منقضی شده",
|
|
no_show: "عدم مراجعه",
|
|
};
|
|
|
|
function Card({ data, loading, setIsTurnsDetails }) {
|
|
return (
|
|
<li className="shadow-[0px_1px_24.8px_0px_rgba(204,_204,_204,_0.18)] pb-[8px] bg-[#FFF] rounded-[8px]">
|
|
<div className="flex items-center justify-start gap-[8px] p-[12px]">
|
|
<div className="flex flex-col items-start justify-center gap-[8px]">
|
|
<TextLoading loading={loading} width={120} height={18}>
|
|
<p className="text-[#616161] text-[14px] font-medium">
|
|
{data.doctor?.name || "--"}
|
|
</p>
|
|
</TextLoading>
|
|
<TextLoading loading={loading} width={100} height={18}>
|
|
<p className="text-[#7E7E7E] text-[14px] font-normal">
|
|
{STATUS_LABELS[data.status] || data.status || "--"}
|
|
</p>
|
|
</TextLoading>
|
|
</div>
|
|
</div>
|
|
<ul className="w-full py-[8px] px-[12px] bg-[#FAFAFA] flex flex-col gap-[12px]">
|
|
<li className="flex items-center justify-between">
|
|
<TextLoading loading={loading} width={75} height={18}>
|
|
<p className="text-[#7E7E7E] text-[14px] font-normal">
|
|
تاریخ نوبت:
|
|
</p>
|
|
</TextLoading>
|
|
<TextLoading loading={loading} width={110} height={18}>
|
|
<p className="text-[#525252] text-[14px] font-medium">
|
|
{convertTimestampToJalali(data.slot_start)}
|
|
</p>
|
|
</TextLoading>
|
|
</li>
|
|
<li className="flex items-center justify-between">
|
|
<TextLoading loading={loading} width={65} height={18}>
|
|
<p className="text-[#7E7E7E] text-[14px] font-normal">ساعت نوبت:</p>
|
|
</TextLoading>
|
|
<TextLoading loading={loading} width={60} height={18}>
|
|
<p className="text-[#525252] text-[14px] font-medium">
|
|
{convertTimestampToTime(data.slot_start)}
|
|
</p>
|
|
</TextLoading>
|
|
</li>
|
|
|
|
{/* فیلدهای حالت نوبتدهی سرویسی. شرط اجباری است: نوبت اسلاتی اینها را ندارد و
|
|
بدون شرط، کارتِ همهٔ نوبتهای اسلاتی میشکند — یعنی کل پنل کاربر. */}
|
|
{data.service_items?.length > 0 && (
|
|
<li className="flex items-start justify-between gap-[8px]">
|
|
<p className="text-[#7E7E7E] text-[14px] font-normal shrink-0">سرویس:</p>
|
|
<p className="text-[#525252] text-[14px] font-medium text-left">
|
|
{data.service_items.map((s) => s.name).join("، ")}
|
|
</p>
|
|
</li>
|
|
)}
|
|
{!data.is_reserve && data.service_total_minutes ? (
|
|
<li className="flex items-center justify-between">
|
|
<p className="text-[#7E7E7E] text-[14px] font-normal">مدت نوبت:</p>
|
|
<p className="text-[#525252] text-[14px] font-medium">
|
|
{data.service_total_minutes} دقیقه
|
|
</p>
|
|
</li>
|
|
) : null}
|
|
</ul>
|
|
<div className="mt-[12px] flex items-center justify-end px-[12px]">
|
|
<TextLoading loading={loading} width={80} height={25}>
|
|
<Button
|
|
className="!flex !p-1 !items-center !justify-end !gap-[2px]"
|
|
onClick={() => setIsTurnsDetails(data)}
|
|
variant="text"
|
|
>
|
|
مشاهده
|
|
<ArrowLeftCardD />
|
|
</Button>
|
|
</TextLoading>
|
|
</div>
|
|
</li>
|
|
);
|
|
}
|
|
|
|
export default Card;
|