Files
clinicpro/assets/admin/components/AppointmentActions.tsx
T

1027 lines
38 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
ArrowDownOnSquareIcon,
ArrowPathIcon,
ArrowsRightLeftIcon,
ClockIcon,
EllipsisHorizontalIcon,
EyeIcon,
PencilIcon,
PhoneIcon,
PlusIcon,
Squares2X2Icon,
TagIcon,
UserIcon,
WalletIcon,
} from "@heroicons/react/24/outline";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import React, { useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom";
import { useNavigate } from "react-router-dom";
import { toast } from "sonner";
import type { ApiResponse } from "../lib/api";
import { api } from "../lib/api";
import { formatRial, tehranWallClockToUnix } from "../lib/utils";
import type { Appointment } from "../types";
import AppointmentStatusDropdown from "./ui/AppointmentStatusDropdown";
import Modal from "./ui/Modal";
import PersianDateInput from "./ui/PersianDateInput";
import PriceInput from "./ui/PriceInput";
/** Row actions for the appointments table (Figma عملیات menu). */
type ModalKind = null | "info" | "move" | "transfer" | "replace";
const toEpoch = (isoDate: string, time: string) => tehranWallClockToUnix(isoDate, time);
/**
* Resolve the patient-record uuid behind an appointment via the patient list
* search (mobile is unique per user). Returns null when no record exists yet.
*/
export async function findRecordUuid(mobile: string): Promise<string | null> {
const res: any = await api.get(
`/api/v1/patient?search=${encodeURIComponent(mobile)}&limit=1`,
);
return res?.data?.[0]?.uuid ?? null;
}
/**
* «شارژ کیف پول» accent link (appointment create/edit forms) — deep-links the
* patient's wallet tab, where the manual top-up modal lives.
*/
export function WalletChargeLink({ mobile }: { mobile?: string }) {
const navigate = useNavigate();
const go = async () => {
if (!mobile || mobile.trim().length < 10) {
toast.error("ابتدا شماره تماس مراجعه کننده را وارد کنید");
return;
}
try {
const recordUuid = await findRecordUuid(mobile.trim());
if (!recordUuid) {
toast.error("پرونده‌ای برای این بیمار یافت نشد");
return;
}
navigate(`/admin/patients/${recordUuid}?tab=wallet`);
} catch {
toast.error("خطا در یافتن پرونده بیمار");
}
};
return (
<button
type="button"
className="btn sm ghost"
style={{ color: "var(--accent)" }}
onClick={go}
>
شارژ کیف پول
</button>
);
}
export default function AppointmentActionsMenu({
appointment,
queryKey,
}: {
appointment: Appointment;
queryKey: unknown[];
}) {
const [open, setOpen] = useState(false);
const [modal, setModal] = useState<ModalKind>(null);
const [menuPos, setMenuPos] = useState<{
top: number;
right: number;
} | null>(null);
const btnRef = useRef<HTMLButtonElement>(null);
const menuRef = useRef<HTMLDivElement>(null);
const navigate = useNavigate();
useEffect(() => {
if (!open) return;
const handler = (e: MouseEvent) => {
const t = e.target as Node;
if (!btnRef.current?.contains(t) && !menuRef.current?.contains(t))
setOpen(false);
};
document.addEventListener("mousedown", handler);
return () => document.removeEventListener("mousedown", handler);
}, [open]);
const openMenu = () => {
if (!open && btnRef.current) {
const r = btnRef.current.getBoundingClientRect();
setMenuPos({
top: r.bottom + 4,
right: window.innerWidth - r.right,
});
}
setOpen((o) => !o);
};
const goToServiceRegistration = async () => {
setOpen(false);
try {
const recordUuid = await findRecordUuid(appointment.patient_mobile);
if (!recordUuid) {
toast.error("پرونده‌ای برای این بیمار یافت نشد");
return;
}
navigate(`/admin/patients/${recordUuid}/session/new`);
} catch {
toast.error("خطا در یافتن پرونده بیمار");
}
};
const items: {
label: string;
icon: React.ElementType;
onClick: () => void;
}[] = [
{
label: "ویرایش",
icon: PencilIcon,
onClick: () => {
setOpen(false);
navigate(`/admin/appointments/${appointment.uuid}/edit`);
},
},
{
label: "ثبت سرویس",
icon: PlusIcon,
onClick: goToServiceRegistration,
},
{
label: "مشاهده",
icon: EyeIcon,
onClick: () => {
setOpen(false);
setModal("info");
},
},
{
label: "جا به جایی نوبت",
icon: ArrowsRightLeftIcon,
onClick: () => {
setOpen(false);
setModal("move");
},
},
{
label: appointment.is_reserve
? "انتقال به لیست نوبت‌ها"
: "انتقال به لیست رزرو",
icon: ArrowDownOnSquareIcon,
onClick: () => {
setOpen(false);
setModal("transfer");
},
},
{
label: "جایگزینی نوبت",
icon: ArrowPathIcon,
onClick: () => {
setOpen(false);
setModal("replace");
},
},
];
return (
<>
<button
ref={btnRef}
onClick={openMenu}
aria-label="عملیات"
className="btn sm ghost"
style={{ color: "var(--primary)", gap: 4 }}
>
<EllipsisHorizontalIcon style={{ width: 18 }} /> عملیات
</button>
{open &&
menuPos &&
ReactDOM.createPortal(
<div
ref={menuRef}
style={{
position: "fixed",
top: menuPos.top,
right: menuPos.right,
zIndex: 9000,
background: "var(--surface)",
border: "1px solid var(--border)",
borderRadius: "var(--r)",
boxShadow: "var(--shadow-lg)",
minWidth: 190,
overflow: "hidden",
}}
>
{items.map(({ label, icon: Icon, onClick }) => (
<button
key={label}
onClick={onClick}
style={{
display: "flex",
alignItems: "center",
gap: 8,
width: "100%",
padding: "9px 12px",
fontSize: 13,
background: "transparent",
border: "none",
cursor: "pointer",
color: "var(--text)",
fontFamily: "inherit",
textAlign: "right",
}}
onMouseEnter={(e) =>
(e.currentTarget.style.background =
"var(--surface-2)")
}
onMouseLeave={(e) =>
(e.currentTarget.style.background =
"transparent")
}
>
<Icon
style={{
width: 15,
color: "var(--text-2)",
}}
/>{" "}
{label}
</button>
))}
</div>,
document.body,
)}
{modal === "info" && (
<AppointmentInfoModal
appointment={appointment}
queryKey={queryKey}
onClose={() => setModal(null)}
/>
)}
{modal === "move" && (
<MoveAppointmentModal
appointment={appointment}
queryKey={queryKey}
onClose={() => setModal(null)}
/>
)}
{modal === "transfer" && (
<TransferReserveModal
appointment={appointment}
queryKey={queryKey}
onClose={() => setModal(null)}
/>
)}
{modal === "replace" && (
<ReplaceAppointmentModal
appointment={appointment}
queryKey={queryKey}
onClose={() => setModal(null)}
/>
)}
</>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// مشاهده — appointment info + patient wallet balance (Figma appointments-info)
// ─────────────────────────────────────────────────────────────────────────────
function InfoRow({
icon: Icon,
label,
value,
ltr,
}: {
icon: React.ElementType;
label: string;
value?: string | null;
ltr?: boolean;
}) {
return (
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "11px 0",
borderBottom: "1px solid var(--border)",
gap: 12,
}}
>
<span
style={{
display: "inline-flex",
alignItems: "center",
gap: 7,
fontSize: 13,
color: "var(--text-2)",
}}
>
<Icon style={{ width: 16, color: "var(--text-3)" }} /> {label}
</span>
<span
style={{
fontSize: 13.5,
fontWeight: 600,
color: "var(--text)",
direction: ltr ? "ltr" : undefined,
}}
>
{value || "—"}
</span>
</div>
);
}
export function AppointmentInfoModal({
appointment: a,
queryKey,
onClose,
}: {
appointment: Appointment;
queryKey: unknown[];
onClose: () => void;
}) {
const navigate = useNavigate();
// record uuid → wallet balance + «مشاهده پرونده» target (both need the record)
const recordQ = useQuery<string | null>({
queryKey: ["appt-record", a.patient_mobile],
queryFn: () => findRecordUuid(a.patient_mobile),
});
const walletQ = useQuery<ApiResponse<{ balance_rials: number }>>({
queryKey: ["appt-wallet", recordQ.data],
queryFn: () => api.get(`/api/v1/patient/${recordQ.data}/wallet`),
enabled: !!recordQ.data,
});
const durationMin = Math.max(
0,
Math.round((a.slot_end - a.slot_start) / 60),
);
return (
<Modal open title={a.patient_name || "نوبت"} onClose={onClose}>
<div>
<InfoRow
icon={ClockIcon}
label="ساعت شروع:"
value={a.appointment_time}
ltr
/>
<InfoRow
icon={ClockIcon}
label="مدت انجام:"
value={`${durationMin.toLocaleString("fa-IR")} دقیقه`}
/>
<InfoRow
icon={PhoneIcon}
label="تلفن:"
value={a.patient_mobile}
ltr
/>
<InfoRow
icon={Squares2X2Icon}
label="بخش:"
value={a.service_section?.name}
/>
<InfoRow
icon={TagIcon}
label="سرویس:"
value={a.service_item?.name}
/>
<InfoRow
icon={UserIcon}
label="پرسنل:"
value={a.staff?.full_name}
/>
<InfoRow
icon={WalletIcon}
label="موجودی کیف پول:"
value={
walletQ.data?.data
? `${formatRial(walletQ.data.data.balance_rials)}`
: undefined
}
/>
<div style={{ margin: "14px 0" }}>
<AppointmentStatusDropdown
uuid={a.uuid}
currentStatus={a.status}
version={a.version}
queryKey={queryKey}
/>
</div>
<button
className="btn"
style={{
width: "100%",
color: "var(--primary)",
border: "1px solid var(--primary-soft2, var(--border))",
background: "var(--primary-soft)",
}}
disabled={!recordQ.data}
onClick={() =>
recordQ.data &&
navigate(`/admin/patients/${recordQ.data}`)
}
>
مشاهده پرونده
</button>
</div>
</Modal>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// جا به جایی نوبت — pick a new date + start/end time
// ─────────────────────────────────────────────────────────────────────────────
export function MoveAppointmentModal({
appointment: a,
queryKey,
onClose,
}: {
appointment: Appointment;
queryKey: unknown[];
onClose: () => void;
}) {
const qc = useQueryClient();
const [date, setDate] = useState(a.appointment_date);
const [start, setStart] = useState(a.appointment_time);
const [end, setEnd] = useState(a.end_time);
const move = useMutation({
mutationFn: () =>
api.patch(`/api/v1/appointment/${a.uuid}`, {
slot_start: toEpoch(date, start),
slot_end: toEpoch(date, end),
version: a.version,
}),
onSuccess: () => {
qc.invalidateQueries({ queryKey });
toast.success("نوبت جا به جا شد");
onClose();
},
onError: (e: any) => toast.error(e.message || "خطا در جا به جایی نوبت"),
});
return (
<Modal open title="جا به جایی نوبت" onClose={onClose}>
<div>
<label style={{ fontSize: 12.5, color: "var(--text-3)" }}>
انتخاب تاریخ
</label>
<div style={{ margin: "6px 0 12px" }}>
<PersianDateInput value={date} onChange={setDate} />
</div>
<div style={{ display: "flex", gap: 10, marginBottom: 16 }}>
<div style={{ flex: 1 }}>
<label
style={{ fontSize: 12.5, color: "var(--text-3)" }}
>
ساعت شروع
</label>
<div className="field" style={{ marginTop: 6 }}>
<input
aria-label="ساعت شروع"
type="time"
value={start}
onChange={(e) => setStart(e.target.value)}
dir="ltr"
/>
</div>
</div>
<div style={{ flex: 1 }}>
<label
style={{ fontSize: 12.5, color: "var(--text-3)" }}
>
ساعت پایان
</label>
<div className="field" style={{ marginTop: 6 }}>
<input
aria-label="ساعت پایان"
type="time"
value={end}
onChange={(e) => setEnd(e.target.value)}
dir="ltr"
/>
</div>
</div>
</div>
<button
className="btn primary"
style={{ width: "100%" }}
disabled={!date || !start || !end || move.isPending}
onClick={() => move.mutate()}
>
اعمال تغییرات
</button>
</div>
</Modal>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// انتقال به لیست رزرو (و بازگشت) — flips is_reserve for a chosen day
// ─────────────────────────────────────────────────────────────────────────────
export function TransferReserveModal({
appointment: a,
queryKey,
onClose,
}: {
appointment: Appointment;
queryKey: unknown[];
onClose: () => void;
}) {
const qc = useQueryClient();
const [date, setDate] = useState(a.appointment_date);
const toReserve = !a.is_reserve;
const transfer = useMutation({
mutationFn: () => {
const day = toEpoch(date, "00:00");
return api.patch(
`/api/v1/appointment/${a.uuid}`,
toReserve
? // reserve entries are day-level: midnight-to-midnight, no slot occupation
{
is_reserve: true,
slot_start: day,
slot_end: day,
version: a.version,
}
: {
is_reserve: false,
slot_start: toEpoch(date, a.appointment_time),
slot_end: toEpoch(date, a.end_time),
version: a.version,
},
);
},
onSuccess: () => {
qc.invalidateQueries({ queryKey });
toast.success(
toReserve
? "به لیست رزرو منتقل شد"
: "به لیست نوبت‌ها منتقل شد",
);
onClose();
},
onError: (e: any) => toast.error(e.message || "خطا در انتقال"),
});
return (
<Modal
open
title={toReserve ? "انتقال به لیست رزرو" : "انتقال به لیست نوبت‌ها"}
onClose={onClose}
>
<div>
<div
style={{
display: "flex",
gap: 8,
alignItems: "flex-start",
background: "var(--warning-bg)",
border: "1px solid var(--border)",
borderRadius: "var(--r)",
padding: "10px 12px",
marginBottom: 14,
fontSize: 12.5,
color: "var(--text-2)",
}}
>
<span style={{ color: "var(--warning)", fontWeight: 800 }}>
!
</span>
{toReserve
? "نوبت از لیست نوبت ها حذف شده و به لیست نوبت های رزرو شده منتقل می شود."
: "نوبت از لیست رزرو حذف شده و به لیست نوبت ها منتقل می شود."}
</div>
<label style={{ fontSize: 12.5, color: "var(--text-3)" }}>
انتخاب تاریخ
</label>
<div style={{ margin: "6px 0 16px" }}>
<PersianDateInput value={date} onChange={setDate} />
</div>
<div style={{ display: "flex", gap: 8 }}>
<button
className="btn primary"
style={{ flex: 1 }}
disabled={!date || transfer.isPending}
onClick={() => transfer.mutate()}
>
انتقال و حذف از لیست
</button>
<button
className="btn"
style={{ flex: 1 }}
onClick={onClose}
>
انصراف
</button>
</div>
</div>
</Modal>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// جایگزینی نوبت — put a different patient into the same slot
// (appointments-replace.pdf: patient search-or-new, بخش/سرویس, deposit,
// locked date/time, پرسنل, وضعیت, توضیحات)
// ─────────────────────────────────────────────────────────────────────────────
interface PickerOption {
uuid: string;
name?: string;
full_name?: string;
}
interface PickedPatient {
uuid: string;
user_name?: string;
user_mobile?: string;
}
export function ReplaceAppointmentModal({
appointment: a,
queryKey,
onClose,
}: {
appointment: Appointment;
queryKey: unknown[];
onClose: () => void;
}) {
const qc = useQueryClient();
// patient: search an existing record or enter a new person
const [patientSearch, setPatientSearch] = useState("");
const [picked, setPicked] = useState<PickedPatient | null>(null);
const [name, setName] = useState("");
const [mobile, setMobile] = useState("");
const patientsQ = useQuery<ApiResponse<PickedPatient[]>>({
queryKey: ["replace-patients", patientSearch],
queryFn: () =>
api.get(
`/api/v1/patient?search=${encodeURIComponent(patientSearch)}&limit=10`,
),
enabled: patientSearch.trim().length >= 2,
});
// service specs + staff + status
const [sectionUuid, setSectionUuid] = useState(
a.service_section?.uuid ?? "",
);
const [itemUuid, setItemUuid] = useState(a.service_item?.uuid ?? "");
const [staffUuid, setStaffUuid] = useState(a.staff?.uuid ?? "");
const [status, setStatus] = useState(a.status);
const sectionsQ = useQuery<ApiResponse<PickerOption[]>>({
queryKey: ["service-sections"],
queryFn: () => api.get("/api/v1/service-sections"),
});
const itemsQ = useQuery<ApiResponse<PickerOption[]>>({
queryKey: ["service-items", sectionUuid],
queryFn: () => api.get(`/api/v1/service-items/${sectionUuid}`),
enabled: !!sectionUuid,
});
const staffQ = useQuery<ApiResponse<PickerOption[]>>({
queryKey: ["staff-list"],
queryFn: () => api.get("/api/v1/staff"),
});
// deposit
const [depositRequired, setDepositRequired] = useState(
!!a.deposit_required,
);
const [depositRials, setDepositRials] = useState(
a.deposit_amount_rials ?? 0,
);
const [note, setNote] = useState("");
const effectiveName = picked?.user_name || name.trim();
const effectiveMobile = picked?.user_mobile || mobile.trim();
const replace = useMutation({
mutationFn: () =>
api.patch(`/api/v1/appointment/${a.uuid}`, {
patient_name: effectiveName,
patient_mobile: effectiveMobile,
service_section_uuid: sectionUuid,
service_item_uuid: itemUuid,
staff_uuid: staffUuid,
deposit_required: depositRequired,
deposit_amount_rials: depositRequired ? depositRials : null,
...(note.trim() ? { note: note.trim() } : {}),
...(status !== a.status ? { status } : {}),
version: a.version,
}),
onSuccess: () => {
qc.invalidateQueries({ queryKey });
toast.success("نوبت جایگزین شد");
onClose();
},
onError: (e: any) => toast.error(e.message || "خطا در جایگزینی نوبت"),
});
const label = { fontSize: 12.5, color: "var(--text-3)" } as const;
const sel = {
width: "100%",
height: 38,
borderRadius: "var(--r-sm)",
border: "1px solid var(--border)",
background: "var(--surface)",
fontSize: 13,
fontFamily: "inherit",
padding: "0 10px",
} as const;
const lockedField = { margin: "6px 0 12px", opacity: 0.6 } as const;
const patients = patientsQ.data?.data ?? [];
const statusOptions: [string, string][] = [
["pending", "ثبت شده"],
["confirmed", "قطعی شده"],
["following_up", "در حال پیگیری"],
["salon", "سالن"],
["completed", "ویزیت شده"],
["cancelled_by_doctor", "لغو شده"],
];
return (
<Modal open title="جایگزینی نوبت" onClose={onClose}>
<div>
<label style={label}>انتخاب مراجعه کننده</label>
<div className="field" style={{ margin: "6px 0 8px" }}>
<input
value={
picked
? `${picked.user_name ?? ""}${picked.user_mobile ?? ""}`
: patientSearch
}
onChange={(e) => {
setPicked(null);
setPatientSearch(e.target.value);
}}
placeholder="جستجوی نام، شماره تماس، شماره پرونده..."
/>
</div>
{!picked && patients.length > 0 && (
<div
style={{
border: "1px solid var(--border)",
borderRadius: "var(--r-sm)",
marginBottom: 10,
overflow: "hidden",
}}
>
{patients.map((p) => (
<button
key={p.uuid}
onClick={() => setPicked(p)}
style={{
display: "block",
width: "100%",
padding: "8px 10px",
fontSize: 13,
textAlign: "right",
background: "transparent",
border: "none",
cursor: "pointer",
fontFamily: "inherit",
}}
>
{p.user_name}{" "}
<span
style={{
color: "var(--text-3)",
direction: "ltr",
}}
>
{p.user_mobile}
</span>
</button>
))}
</div>
)}
{picked === null && (
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: 10,
marginBottom: 12,
}}
>
<div className="field">
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="نام و نام خانوادگی"
/>
</div>
<div className="field">
<input
value={mobile}
onChange={(e) => setMobile(e.target.value)}
placeholder="شماره تماس"
dir="ltr"
/>
</div>
</div>
)}
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: 10,
marginBottom: 12,
}}
>
<div>
<label style={label}>بخش</label>
<select
aria-label="بخش"
style={{ ...sel, marginTop: 6 }}
value={sectionUuid}
onChange={(e) => {
setSectionUuid(e.target.value);
setItemUuid("");
}}
>
<option value="">انتخاب بخش</option>
{(sectionsQ.data?.data ?? []).map((o) => (
<option key={o.uuid} value={o.uuid}>
{o.name}
</option>
))}
</select>
</div>
<div>
<label style={label}>سرویس</label>
<select
aria-label="سرویس"
style={{ ...sel, marginTop: 6 }}
value={itemUuid}
onChange={(e) => setItemUuid(e.target.value)}
disabled={!sectionUuid}
>
<option value="">انتخاب زیر بخش</option>
{(itemsQ.data?.data ?? []).map((o) => (
<option key={o.uuid} value={o.uuid}>
{o.name}
</option>
))}
</select>
</div>
</div>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: 10,
flexWrap: "wrap",
marginBottom: 12,
}}
>
<label
style={{
display: "inline-flex",
alignItems: "center",
gap: 8,
fontSize: 13,
cursor: "pointer",
}}
>
<input
type="checkbox"
checked={depositRequired}
onChange={(e) =>
setDepositRequired(e.target.checked)
}
/>
بیعانه مورد نیاز است.
</label>
{depositRequired && (
<WalletChargeLink mobile={effectiveMobile} />
)}
</div>
{depositRequired && (
<div style={{ marginBottom: 12 }}>
<label style={label}>مبلغ بیعانه (تومان)</label>
<div style={{ marginTop: 6 }}>
<PriceInput
value={depositRials}
onChange={setDepositRials}
/>
</div>
</div>
)}
{/* the replacement keeps the original slot — date/time locked */}
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: 10,
}}
>
<div>
<label style={label}>انتخاب تاریخ</label>
<div className="field" style={lockedField}>
<input
value={a.appointment_date}
disabled
dir="ltr"
/>
</div>
</div>
<div>
<label style={label}>ساعت شروع</label>
<div className="field" style={lockedField}>
<input
value={a.appointment_time}
disabled
dir="ltr"
/>
</div>
</div>
</div>
<label style={label}>انتخاب پرسنل</label>
<select
aria-label="پرسنل"
style={{ ...sel, margin: "6px 0 12px" }}
value={staffUuid}
onChange={(e) => setStaffUuid(e.target.value)}
>
<option value="">انتخاب...</option>
{(staffQ.data?.data ?? []).map((o) => (
<option key={o.uuid} value={o.uuid}>
{o.full_name}
</option>
))}
</select>
<label style={label}>انتخاب وضعیت</label>
<select
aria-label="وضعیت"
style={{ ...sel, margin: "6px 0 12px" }}
value={status}
onChange={(e) =>
setStatus(e.target.value as Appointment["status"])
}
>
{statusOptions.map(([v, l]) => (
<option key={v} value={v}>
{l}
</option>
))}
</select>
<label style={label}>توضیحات</label>
<div
className="field"
style={{ height: "auto", margin: "6px 0 16px" }}
>
<textarea
value={note}
onChange={(e) => setNote(e.target.value)}
rows={3}
placeholder="توضیحات..."
style={{
width: "100%",
border: "none",
background: "transparent",
fontFamily: "inherit",
resize: "vertical",
}}
/>
</div>
<button
className="btn primary"
style={{ width: "100%" }}
disabled={
effectiveName.length < 2 ||
effectiveMobile.length < 10 ||
replace.isPending
}
onClick={() => replace.mutate()}
>
ثبت نوبت
</button>
</div>
</Modal>
);
}