feat(appointment): payment timer from real lock + status-driven payment UX
Drive the payment countdown from the booking's expires_at (15-min lock) instead of a fake local 10-min timer; when it hits zero, show an expiry notice and an 'choose time again' button back to slot selection. Show the booked time (Jalali) and doctor on the payment step, and replace the fabricated success amount with a confirmation/SMS note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,19 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import moment from "moment-jalaali";
|
||||
import ArrowLeftB from "@/components/icons/ArrowLeftB";
|
||||
import { Button, MenuItem, Select, FormControl, InputLabel } from "@mui/material";
|
||||
import ButtonFixed from "./ButtonFixed";
|
||||
import { request } from "@/services/response";
|
||||
|
||||
function Paying({ setStep, appointmentId }) {
|
||||
function Paying({ setStep, appointmentId, expiresAt, doctor, selectedSlot, selectedDate }) {
|
||||
const [selectedBank, setSelectedBank] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [timeLeft, setTimeLeft] = useState(600); // 10 minutes in seconds
|
||||
|
||||
const computeLeft = () =>
|
||||
expiresAt
|
||||
? Math.max(0, Math.floor((expiresAt * 1000 - Date.now()) / 1000))
|
||||
: 900;
|
||||
const [timeLeft, setTimeLeft] = useState(computeLeft);
|
||||
|
||||
const banks = [
|
||||
{ id: "mellat", title: "بانک ملت" },
|
||||
@@ -15,14 +21,12 @@ function Paying({ setStep, appointmentId }) {
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
if (timeLeft === 0) return;
|
||||
|
||||
const timer = setInterval(() => {
|
||||
setTimeLeft((prev) => prev - 1);
|
||||
setTimeLeft(computeLeft());
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, [timeLeft]);
|
||||
}, [expiresAt]);
|
||||
|
||||
const formatTime = (seconds) => {
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
@@ -74,6 +78,16 @@ function Paying({ setStep, appointmentId }) {
|
||||
اطلاعات پرداخت
|
||||
</p>
|
||||
|
||||
{selectedSlot?.start && (
|
||||
<div className="flex justify-between items-center mt-[16px] text-[14px] md:text-[16px]">
|
||||
<span className="text-[#616161] font-medium">زمان نوبت:</span>
|
||||
<span className="text-[#3B3B3B] font-bold">
|
||||
{moment.unix(selectedSlot.start).locale("fa").format("jD jMMMM jYYYY - HH:mm")}
|
||||
{doctor?.name ? ` با ${doctor.name}` : ""}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-[12px] sm:mt-[18px] md:mt-[24px] lg:mt-[30px]">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<p className="text-[#616161] text-[14px] md:text-[16px] font-medium">
|
||||
@@ -112,19 +126,35 @@ function Paying({ setStep, appointmentId }) {
|
||||
|
||||
<span className="bg-[#D7D7D7] hidden md:block h-px w-full mt-[32px] mb-[25px]"></span>
|
||||
|
||||
{timeLeft === 0 && (
|
||||
<p className="text-red-500 text-[14px] font-medium mb-3">
|
||||
مهلت پرداخت تمام شد؛ لطفاً دوباره زمان نوبت را انتخاب کنید.
|
||||
</p>
|
||||
)}
|
||||
|
||||
<ButtonFixed>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Button
|
||||
className="!gap-[4px] !mr-auto !flex !px-[12px] !p-[7px] md:!py-[9px] !min-w-[157px]"
|
||||
onClick={handlePayment}
|
||||
variant="contained"
|
||||
disabled={loading || !selectedBank || timeLeft === 0}
|
||||
>
|
||||
<p className={`${loading && "opacity-0"} text-[#EFEFEF] text-[16px] font-medium`}>
|
||||
{timeLeft === 0 ? "زمان پرداخت تمام شد" : "پرداخت نهایی"}
|
||||
</p>
|
||||
{timeLeft > 0 && <ArrowLeftB />}
|
||||
</Button>
|
||||
{timeLeft === 0 ? (
|
||||
<Button
|
||||
className="!gap-[4px] !mr-auto !flex !px-[12px] !p-[7px] md:!py-[9px] !min-w-[157px]"
|
||||
onClick={() => setStep(0)}
|
||||
variant="contained"
|
||||
>
|
||||
<p className="text-[#EFEFEF] text-[16px] font-medium">انتخاب مجدد زمان</p>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className="!gap-[4px] !mr-auto !flex !px-[12px] !p-[7px] md:!py-[9px] !min-w-[157px]"
|
||||
onClick={handlePayment}
|
||||
variant="contained"
|
||||
disabled={loading || !selectedBank}
|
||||
>
|
||||
<p className={`${loading && "opacity-0"} text-[#EFEFEF] text-[16px] font-medium`}>
|
||||
پرداخت نهایی
|
||||
</p>
|
||||
<ArrowLeftB />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</ButtonFixed>
|
||||
</div>
|
||||
|
||||
@@ -18,24 +18,12 @@ function SuccessPay({ setStep }) {
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
className="text-[#3B3B3B] text-[16px] font-normal
|
||||
mt-[24px] sm:mt-[26px] md:mt-[29px] lg:mt-[32px]
|
||||
className="text-[#3B3B3B] text-[16px] font-normal text-center
|
||||
mt-[24px] sm:mt-[26px] md:mt-[29px] lg:mt-[32px]
|
||||
mb-[24px] md:mb-[16px]"
|
||||
>
|
||||
برای نمایش نوبت ها به قسمت نوبت های من بروید.{" "}
|
||||
نوبت شما تأیید شد و پیامک تأیید برای بیمار ارسال میشود. برای نمایش نوبتها به قسمت نوبتهای من بروید.{" "}
|
||||
</p>
|
||||
<div className="flex w-full md:hidden flex-col justify-center items-center mb-[32px]">
|
||||
<span className="w-full block h-px bg-[#EFEFEF]"></span>
|
||||
<div className="flex items-center justify-between my-[20px] gap-[80px]">
|
||||
<p className="text-[#616161] text-[14px] font-medium">
|
||||
مقدار پرداخت شده:{" "}
|
||||
</p>
|
||||
<p className="text-[#05BA58] text-[14px] font-bold">
|
||||
20،000 تومان
|
||||
</p>
|
||||
</div>
|
||||
<span className="w-full block h-px bg-[#EFEFEF]"></span>
|
||||
</div>
|
||||
<Link href="/dashboard">
|
||||
<Button
|
||||
variant="text"
|
||||
|
||||
Reference in New Issue
Block a user