diff --git a/components/dashboard/userAccount/sidebars/turns/Card.js b/components/dashboard/userAccount/sidebars/turns/Card.js
index 5f09ddc..c161bdb 100644
--- a/components/dashboard/userAccount/sidebars/turns/Card.js
+++ b/components/dashboard/userAccount/sidebars/turns/Card.js
@@ -55,6 +55,25 @@ function Card({ data, loading, setIsTurnsDetails }) {
+
+ {/* فیلدهای حالت نوبتدهی سرویسی. شرط اجباری است: نوبت اسلاتی اینها را ندارد و
+ بدون شرط، کارتِ همهٔ نوبتهای اسلاتی میشکند — یعنی کل پنل کاربر. */}
+ {data.service_items?.length > 0 && (
+
+ سرویس:
+
+ {data.service_items.map((s) => s.name).join("، ")}
+
+
+ )}
+ {!data.is_reserve && data.service_total_minutes ? (
+
+ مدت نوبت:
+
+ {data.service_total_minutes} دقیقه
+
+
+ ) : null}
diff --git a/components/dashboard/userAccount/sidebars/turns/Card.test.jsx b/components/dashboard/userAccount/sidebars/turns/Card.test.jsx
new file mode 100644
index 0000000..dcc8355
--- /dev/null
+++ b/components/dashboard/userAccount/sidebars/turns/Card.test.jsx
@@ -0,0 +1,85 @@
+import { describe, it, expect, vi } from "vitest";
+import { render, screen } from "@testing-library/react";
+import Card from "./Card";
+
+// آیکن و لودینگها به این تست ربطی ندارند؛ فقط باید کودکشان را رندر کنند.
+vi.mock("@/app/component/loading/Text", () => ({
+ default: ({ children }) => <>{children}>,
+}));
+vi.mock("@/app/component/loading/Circular", () => ({
+ default: ({ children }) => <>{children}>,
+}));
+vi.mock("@/components/icons/ArrowLeftCardD", () => ({ default: () => null }));
+vi.mock("next/image", () => ({ default: () => null }));
+
+const base = {
+ uuid: "ap1",
+ status: "confirmed",
+ slot_start: 1785567600,
+ doctor: { name: "دکتر تست" },
+};
+
+function renderCard(data) {
+ return render( {}} />);
+}
+
+describe("Card نوبت — فیلدهای حالت سرویسی", () => {
+ // ── ✅ موفق ──────────────────────────────────────────────────────────────
+
+ it("نام همهٔ سرویسها و مدت نوبت را نشان میدهد", () => {
+ renderCard({
+ service_items: [{ uuid: "s1", name: "لیزر صورت" }, { uuid: "s2", name: "لیزر بیکینی" }],
+ service_total_minutes: 35,
+ });
+
+ expect(screen.getByText("سرویس:")).toBeInTheDocument();
+ expect(screen.getByText("لیزر صورت، لیزر بیکینی")).toBeInTheDocument();
+ expect(screen.getByText("مدت نوبت:")).toBeInTheDocument();
+ expect(screen.getByText("35 دقیقه")).toBeInTheDocument();
+ });
+
+ // ── ❌ خط سرخ: نوبت اسلاتی ────────────────────────────────────────────────
+
+ it("نوبت اسلاتی هیچکدام از دو ردیف را نمیگیرد و کرش نمیکند", () => {
+ renderCard({ service_items: [], service_total_minutes: null });
+
+ expect(screen.queryByText("سرویس:")).not.toBeInTheDocument();
+ expect(screen.queryByText("مدت نوبت:")).not.toBeInTheDocument();
+ // ردیفهای موجود سر جایشاناند.
+ expect(screen.getByText("تاریخ نوبت:")).toBeInTheDocument();
+ expect(screen.getByText("ساعت نوبت:")).toBeInTheDocument();
+ });
+
+ it("فیلدهای سرویسی کاملاً غایب (بکاند قدیمی) هم کرش نمیدهد", () => {
+ renderCard({});
+
+ expect(screen.queryByText("سرویس:")).not.toBeInTheDocument();
+ expect(screen.getByText("تاریخ نوبت:")).toBeInTheDocument();
+ });
+
+ // ── ⚠️ مرزی ──────────────────────────────────────────────────────────────
+
+ it("نوبت رزرو سرویس را نشان میدهد ولی مدت را نه (زمان ندارد)", () => {
+ renderCard({
+ is_reserve: true,
+ service_items: [{ uuid: "s1", name: "لیزر" }],
+ service_total_minutes: 45,
+ });
+
+ expect(screen.getByText("لیزر")).toBeInTheDocument();
+ expect(screen.queryByText("مدت نوبت:")).not.toBeInTheDocument();
+ });
+
+ it("سرویس بدون مدت → فقط ردیف سرویس", () => {
+ renderCard({ service_items: [{ uuid: "s1", name: "مشاوره" }], service_total_minutes: null });
+
+ expect(screen.getByText("مشاوره")).toBeInTheDocument();
+ expect(screen.queryByText("مدت نوبت:")).not.toBeInTheDocument();
+ });
+
+ it("مدت صفر ردیف نمیسازد", () => {
+ renderCard({ service_items: [{ uuid: "s1", name: "مشاوره" }], service_total_minutes: 0 });
+
+ expect(screen.queryByText("مدت نوبت:")).not.toBeInTheDocument();
+ });
+});
diff --git a/components/dashboard/userAccount/sidebars/turns/isTurnsDetails/DetailLg.js b/components/dashboard/userAccount/sidebars/turns/isTurnsDetails/DetailLg.js
index 520e848..bf51cf4 100644
--- a/components/dashboard/userAccount/sidebars/turns/isTurnsDetails/DetailLg.js
+++ b/components/dashboard/userAccount/sidebars/turns/isTurnsDetails/DetailLg.js
@@ -90,6 +90,29 @@ function DetailLg({ appointmentData, loading }) {
+ {/* حالت نوبتدهی سرویسی. شرط اجباری: نوبت اسلاتی این فیلدها را ندارد. */}
+ {appointmentData?.service_items?.length > 0 && (
+
+
+
+ سرویس:{" "}
+
+ {appointmentData.service_items.map((s) => s.name).join("، ")}
+
+
+
+ {!appointmentData.is_reserve && appointmentData.service_total_minutes ? (
+
+
+ مدت نوبت:{" "}
+
+ {appointmentData.service_total_minutes} دقیقه
+
+
+
+ ) : null}
+
+ )}
diff --git a/components/dashboard/userAccount/sidebars/turns/isTurnsDetails/DetailSm.js b/components/dashboard/userAccount/sidebars/turns/isTurnsDetails/DetailSm.js
index a346b62..92171fc 100644
--- a/components/dashboard/userAccount/sidebars/turns/isTurnsDetails/DetailSm.js
+++ b/components/dashboard/userAccount/sidebars/turns/isTurnsDetails/DetailSm.js
@@ -85,6 +85,31 @@ function DetailSm({ appointmentData, loading }) {
+ {/* حالت نوبتدهی سرویسی. شرط اجباری: نوبت اسلاتی این فیلدها را ندارد. */}
+ {appointmentData?.service_items?.length > 0 && (
+
+
+ سرویس:
+
+
+
+ {appointmentData.service_items.map((s) => s.name).join("، ")}
+
+
+
+ )}
+ {!appointmentData?.is_reserve && appointmentData?.service_total_minutes ? (
+
+
+ مدت نوبت:
+
+
+
+ {appointmentData.service_total_minutes} دقیقه
+
+
+
+ ) : null}