184 lines
5.6 KiB
TypeScript
184 lines
5.6 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { createPortal } from "react-dom";
|
||
import { useNavigate } from "react-router-dom";
|
||
import Icon from "../components/Icon";
|
||
import {
|
||
fetchMyReservationByUuid,
|
||
fetchMyReservations,
|
||
MyReservation,
|
||
} from "../services/campaignApi";
|
||
import { formatPersianNumber } from "../utils/formatNumber";
|
||
import GuestBanner from "../assets/images/banners/Guest-banner.jpg";
|
||
|
||
function formatReservationDateTime(iso: string): string {
|
||
return new Intl.DateTimeFormat("fa-IR", {
|
||
weekday: "long",
|
||
day: "numeric",
|
||
month: "long",
|
||
hour: "2-digit",
|
||
minute: "2-digit",
|
||
}).format(new Date(iso));
|
||
}
|
||
|
||
function MyReservationCard({
|
||
reservation,
|
||
onClick,
|
||
}: {
|
||
reservation: MyReservation;
|
||
onClick: () => void;
|
||
}) {
|
||
return (
|
||
<button
|
||
type="button"
|
||
onClick={onClick}
|
||
className="flex w-full flex-col items-start gap-3 rounded-2xl p-6 text-right shadow-[0px_4px_12px_0px_#0000001F]"
|
||
>
|
||
<h3 className="font-medium text-xl text-gray-900">
|
||
{reservation.event_title}
|
||
</h3>
|
||
<p className="text-lg text-gray-500">
|
||
{formatReservationDateTime(reservation.event_datetime)}
|
||
</p>
|
||
<div className="flex items-center gap-2">
|
||
<span className="font-normal rounded-full bg-[#ECF2F8] px-3 py-1 text-sm text-gray-700 h-9 flex items-center">
|
||
{formatPersianNumber(reservation.ticket_count)} نفر
|
||
</span>
|
||
<span className="font-medium rounded-full bg-[#ECF2F8] px-3 py-1 text-sm text-gray-700 h-9 flex items-center">
|
||
{reservation.reservation_code}
|
||
</span>
|
||
</div>
|
||
</button>
|
||
);
|
||
}
|
||
|
||
function DetailRow({ label, value }: { label: string; value: string }) {
|
||
return (
|
||
<div className="flex items-center justify-between py-3">
|
||
<span className="text-gray-500">{label}</span>
|
||
<span className="text-gray-900">{value}</span>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function ReservationDetailModal({
|
||
uuid,
|
||
onClose,
|
||
}: {
|
||
uuid: string;
|
||
onClose: () => void;
|
||
}) {
|
||
const [reservation, setReservation] = useState<MyReservation | null>(null);
|
||
|
||
useEffect(() => {
|
||
const previousOverflow = document.body.style.overflow;
|
||
document.body.style.overflow = "hidden";
|
||
return () => {
|
||
document.body.style.overflow = previousOverflow;
|
||
};
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
fetchMyReservationByUuid(uuid)
|
||
.then(setReservation)
|
||
.catch(() => {});
|
||
}, [uuid]);
|
||
|
||
return createPortal(
|
||
<div
|
||
className="fixed inset-0 z-1100 flex items-center justify-center bg-black/70 p-4"
|
||
onClick={onClose}
|
||
>
|
||
<div
|
||
className="max-h-[90vh] w-full overflow-y-auto rounded-3xl bg-white p-6"
|
||
onClick={(e) => e.stopPropagation()}
|
||
>
|
||
<Icon name="success" className="mx-auto h-24 w-24" />
|
||
<h2 className="mt-4 text-right font-semibold text-gray-900">
|
||
جزئیات رزرو
|
||
</h2>
|
||
|
||
{reservation && (
|
||
<div className="mt-2">
|
||
<DetailRow
|
||
label="تعداد"
|
||
value={`${formatPersianNumber(reservation.ticket_count)} نفر`}
|
||
/>
|
||
<DetailRow label="به نام" value={reservation.full_name} />
|
||
<DetailRow label="شماره تماس" value={reservation.phone_number} />
|
||
<DetailRow label="کافه" value={reservation.cafe_name} />
|
||
<DetailRow label="بازی" value={reservation.event_title} />
|
||
<DetailRow
|
||
label="تاریخ و ساعت"
|
||
value={formatReservationDateTime(reservation.event_datetime)}
|
||
/>
|
||
<DetailRow label="کد رزرو" value={reservation.reservation_code} />
|
||
</div>
|
||
)}
|
||
|
||
<div className="mt-4 flex items-center justify-end gap-2 rounded-2xl bg-[#ECF2F8] px-4 py-3">
|
||
<Icon name="info" className="h-5 w-5 shrink-0 text-gray-400" />
|
||
<p className="text-right text-sm text-gray-500">
|
||
کد رزرو را برای ورود نزد خود نگه دارید. اطلاعات رزرو و کد ورود در
|
||
بخش رزروها قابل مشاهده است.
|
||
</p>
|
||
</div>
|
||
|
||
<button
|
||
type="button"
|
||
onClick={onClose}
|
||
className="mt-4 w-full rounded-2xl bg-[#223D5D] py-4 text-lg font-semibold text-white"
|
||
>
|
||
بازگشت
|
||
</button>
|
||
</div>
|
||
</div>,
|
||
document.body,
|
||
);
|
||
}
|
||
|
||
export default function MyReservations() {
|
||
const navigate = useNavigate();
|
||
const [reservations, setReservations] = useState<MyReservation[]>([]);
|
||
const [selectedUuid, setSelectedUuid] = useState<string | null>(null);
|
||
|
||
useEffect(() => {
|
||
fetchMyReservations()
|
||
.then(setReservations)
|
||
.catch(() => {});
|
||
}, []);
|
||
|
||
return (
|
||
<div className="min-h-screen bg-white pb-24">
|
||
<img src={GuestBanner} alt="" className="w-full" />
|
||
|
||
<button
|
||
type="button"
|
||
onClick={() => navigate(-1)}
|
||
className="mt-4 flex w-full items-center justify-start gap-2 px-4"
|
||
>
|
||
<Icon
|
||
name="arrowRight"
|
||
className="h-5 w-5 -scale-x-100 text-gray-900"
|
||
/>
|
||
<span className="text-lg font-bold text-gray-900">رزروهای شما</span>
|
||
</button>
|
||
|
||
<div className="mt-4 flex flex-col gap-3 px-4">
|
||
{reservations.map((reservation) => (
|
||
<MyReservationCard
|
||
key={reservation.uuid}
|
||
reservation={reservation}
|
||
onClick={() => setSelectedUuid(reservation.uuid)}
|
||
/>
|
||
))}
|
||
</div>
|
||
|
||
{selectedUuid && (
|
||
<ReservationDetailModal
|
||
uuid={selectedUuid}
|
||
onClose={() => setSelectedUuid(null)}
|
||
/>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|