feat: info icon's modal on reservation modal added

This commit is contained in:
Afra ‌ 2026-07-22 12:07:35 +03:30
parent 594cc75165
commit 434b5b5235

View file

@ -4,7 +4,11 @@ import { useNavigate } from "react-router-dom";
import { Cafe } from "../services/campaignApi"; import { Cafe } from "../services/campaignApi";
import { chargePageUrl, createCharge } from "../services/ipgApi"; import { chargePageUrl, createCharge } from "../services/ipgApi";
import { fetchWalletBalances } from "../services/walletApi"; import { fetchWalletBalances } from "../services/walletApi";
import { toEnglishDigits, toPersianDigits, formatPersianNumber } from "../utils/formatNumber"; import {
toEnglishDigits,
toPersianDigits,
formatPersianNumber,
} from "../utils/formatNumber";
import { toE164 } from "../utils/phone"; import { toE164 } from "../utils/phone";
import { setPendingReservation } from "../utils/pendingReservation"; import { setPendingReservation } from "../utils/pendingReservation";
import { getUserPhone } from "../utils/userPhone"; import { getUserPhone } from "../utils/userPhone";
@ -39,6 +43,8 @@ function PaymentRow({
bottomBorder?: boolean; bottomBorder?: boolean;
infoIconParam?: boolean; infoIconParam?: boolean;
}) { }) {
const [showInfo, setShowInfo] = useState(false);
return ( return (
<div <div
className={`flex items-center justify-between py-3 ${bottomBorder ? "border-b border-gray-100" : ""}`} className={`flex items-center justify-between py-3 ${bottomBorder ? "border-b border-gray-100" : ""}`}
@ -52,7 +58,14 @@ function PaymentRow({
{label} {label}
</span> </span>
{infoIconParam && ( {infoIconParam && (
<Icon name="info" className="h-5 w-5 shrink-0 text-[#376195]" /> <button
type="button"
onClick={() => setShowInfo(true)}
className="shrink-0"
aria-label="توضیحات کارمزد"
>
<Icon name="info" className="h-5 w-5 text-[#376195]" />
</button>
)} )}
</div> </div>
<span <span
@ -60,6 +73,31 @@ function PaymentRow({
> >
{formatPersianNumber(amount)} تومان {formatPersianNumber(amount)} تومان
</span> </span>
{showInfo &&
createPortal(
<>
<div
className="fixed inset-0 z-9998 bg-black/40"
onClick={() => setShowInfo(false)}
/>
<div className="fixed inset-x-6 top-1/2 z-9999 -translate-y-1/2 rounded-2xl bg-white px-5 py-5 shadow-xl">
<button
type="button"
onClick={() => setShowInfo(false)}
className="absolute left-4 top-4 flex h-7 w-7 items-center justify-center text-gray-400 hover:text-gray-600"
aria-label="بستن"
>
✕
</button>
<p className="pt-4 text-right text-sm leading-7 text-gray-800">
مطابق مقررات نظام مالیاتی کشور، کارمزد درگاه پرداخت در تمامی
درگاه‌های پرداخت اعمال می‌شود.
</p>
</div>
</>,
document.body,
)}
</div> </div>
); );
} }
@ -182,14 +220,19 @@ export default function ReservationModal({
<input <input
type="text" type="text"
value={fullName} value={fullName}
onChange={(e) => { setFullName(e.target.value); clearFieldError("name"); }} onChange={(e) => {
setFullName(e.target.value);
clearFieldError("name");
}}
placeholder="نام و نام خانوادگی" placeholder="نام و نام خانوادگی"
className={`w-full rounded-2xl border px-4 py-3 text-right placeholder:font-light placeholder:text-[#999999] ${ className={`w-full rounded-2xl border px-4 py-3 text-right placeholder:font-light placeholder:text-[#999999] ${
fieldErrors.name ? "border-red-400" : "border-gray-200" fieldErrors.name ? "border-red-400" : "border-gray-200"
}`} }`}
/> />
{fieldErrors.name && ( {fieldErrors.name && (
<p className="mt-1 text-right text-sm text-red-500">{fieldErrors.name}</p> <p className="mt-1 text-right text-sm text-red-500">
{fieldErrors.name}
</p>
)} )}
</div> </div>
@ -203,7 +246,9 @@ export default function ReservationModal({
inputMode="numeric" inputMode="numeric"
value={phone} value={phone}
onChange={(e) => { onChange={(e) => {
const digits = toEnglishDigits(e.target.value).replace(/\D/g, "").slice(0, 11); const digits = toEnglishDigits(e.target.value)
.replace(/\D/g, "")
.slice(0, 11);
setPhone(toPersianDigits(digits)); setPhone(toPersianDigits(digits));
clearFieldError("phone"); clearFieldError("phone");
}} }}
@ -213,7 +258,9 @@ export default function ReservationModal({
}`} }`}
/> />
{fieldErrors.phone && ( {fieldErrors.phone && (
<p className="mt-1 text-right text-sm text-red-500">{fieldErrors.phone}</p> <p className="mt-1 text-right text-sm text-red-500">
{fieldErrors.phone}
</p>
)} )}
</div> </div>
@ -242,10 +289,14 @@ export default function ReservationModal({
</span> </span>
<button <button
type="button" type="button"
onClick={() => setCount((prev) => Math.min(remainingTickets, prev + 1))} onClick={() =>
setCount((prev) => Math.min(remainingTickets, prev + 1))
}
disabled={atMaxCount} disabled={atMaxCount}
className={`flex h-6 w-6 items-center justify-center ${ className={`flex h-6 w-6 items-center justify-center ${
atMaxCount ? "cursor-not-allowed text-[#999999]" : "text-[#223D5D]" atMaxCount
? "cursor-not-allowed text-[#999999]"
: "text-[#223D5D]"
}`} }`}
> >
<Icon name="plus" className="h-4 w-4" /> <Icon name="plus" className="h-4 w-4" />