reservation-front/src/pages/MyReservations.tsx
2026-07-20 13:32:01 +03:30

184 lines
5.6 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 { 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>
);
}