"use client"; import { useEffect, useMemo, useState } from "react"; import { CalendarDays, Clock3, MapPin, Ticket, UsersRound } from "lucide-react"; import { Link, useNavigate, useParams } from "@/lib/router"; import { api } from "@/lib/api"; import type * as Types from "@/lib/types"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import GalleryLightbox, { type GalleryLightboxItem } from "@/components/GalleryLightbox"; import ProgressiveImage from "@/components/ProgressiveImage"; import { useToast } from "@/hooks/use-toast"; import Markdown from "@/components/Markdown"; import { formatJalali, formatNumberPersian, formatToman, getEventHeroImageUrl, getEventSeoImageUrl, getGalleryImageBlurUrl, getGalleryImageFullUrl, getGalleryImagePreviewUrl, resolveErrorMessage, toPersianDigits, } from "@/lib/utils"; import { useAuth } from "@/contexts/AuthContext"; import { buildAuthPath } from "@/lib/auth-return"; import { getEventPath, getEventRegistrationState } from "@/lib/event-ui"; const typeLabel: Record = { online: "آنلاین", on_site: "حضوری", hybrid: "آنلاین و حضوری", }; function formatDateTimeRange(start?: string | null, end?: string | null) { if (!start) return end ? formatJalali(end) : "—"; if (!end) return formatJalali(start); const startDate = formatJalali(start, false); if (startDate !== "—" && startDate === formatJalali(end, false)) { const formatTime = (value: string) => new Intl.DateTimeFormat("fa-IR", { hour: "2-digit", minute: "2-digit", }).format(new Date(value)); return `${startDate}، ساعت ${formatTime(start)} تا ${formatTime(end)}`; } return `${formatJalali(start)} تا ${formatJalali(end)}`; } type EventDetailProps = { initialEvent?: Types.EventDetailSchema | null; }; export default function EventDetail({ initialEvent = null }: EventDetailProps) { const { slug } = useParams<{ slug: string }>(); const { isAuthenticated } = useAuth(); const navigate = useNavigate(); const { toast } = useToast(); const [event, setEvent] = useState(initialEvent); const [lightboxIndex, setLightboxIndex] = useState(null); const [loading, setLoading] = useState(!initialEvent); const [eventLoadError, setEventLoadError] = useState(false); const [eventLoadAttempt, setEventLoadAttempt] = useState(0); const [nowTs, setNowTs] = useState(() => Date.now()); const basePrice = Number(event?.price ?? 0); const isFree = useMemo(() => basePrice <= 0, [basePrice]); const handleMainCTA = () => { if (!event) return; if (!isAuthenticated) { navigate(buildAuthPath(`${getEventPath(event)}/registration`)); return; } navigate(`${getEventPath(event)}/registration`); }; useEffect(() => { let cancelled = false; async function loadEvent() { try { setEventLoadError(false); setLoading(true); if (!slug) return; if (initialEvent && (initialEvent.slug === slug || String(initialEvent.id) === slug)) { setEvent(initialEvent); return; } setEvent(null); const data = /^\d+$/.test(slug) ? await api.getEventById(Number(slug)) : await api.getEventBySlug(slug); if (cancelled) return; setEvent(data); } catch (error: unknown) { if (cancelled) return; setEventLoadError(true); toast({ title: "خطا در بارگذاری رویداد", description: resolveErrorMessage(error, "لطفاً دوباره تلاش کنید."), variant: "destructive", }); } finally { if (!cancelled) { setLoading(false); } } } loadEvent(); return () => { cancelled = true; }; }, [eventLoadAttempt, initialEvent, slug, toast]); useEffect(() => { const timer = window.setInterval(() => setNowTs(Date.now()), 1000); return () => window.clearInterval(timer); }, []); const deadlineTs = useMemo( () => (event?.registration_end_date ? new Date(event.registration_end_date).getTime() : null), [event?.registration_end_date], ); const remainingMs = useMemo( () => (deadlineTs != null ? Math.max(0, deadlineTs - nowTs) : null), [deadlineTs, nowTs], ); const formatCountdownTwoDigit = (value: number) => toPersianDigits(value.toString().padStart(2, "0")); const formatCountdownNumber = (value: number) => formatNumberPersian(value); const formatRemainingWords = (ms: number) => { const total = Math.max(0, Math.floor(ms / 1000)); const days = Math.floor(total / 86400); const hours = Math.floor((total % 86400) / 3600); const minutes = Math.floor((total % 3600) / 60); const seconds = total % 60; if (days === 0) { return `${formatCountdownTwoDigit(hours)} ساعت و ${formatCountdownTwoDigit(minutes)} دقیقه و ${formatCountdownTwoDigit(seconds)} ثانیه`; } return `${formatCountdownNumber(days)} روز و ${formatCountdownTwoDigit(hours)} ساعت و ${formatCountdownTwoDigit(minutes)} دقیقه و ${formatCountdownTwoDigit(seconds)} ثانیه`; }; const registration = event ? getEventRegistrationState(event, nowTs) : null; const galleryItems = useMemo( () => (event?.gallery_images ?? []).map((image) => ({ id: image.id, alt: image.title || event?.title || "تصویر رویداد", title: image.title, previewSrc: getGalleryImagePreviewUrl(image), blurSrc: getGalleryImageBlurUrl(image), fullSrc: getGalleryImageFullUrl(image), })), [event], ); if (loading) { return
در حال بارگذاری رویداد...
; } if (!event) { return (

{eventLoadError ? "بارگذاری رویداد ناموفق بود" : "رویداد مورد نظر یافت نشد"}

{eventLoadError ? "اتصال را بررسی کنید و دوباره تلاش کنید." : "ممکن است نشانی رویداد تغییر کرده باشد یا رویداد در دسترس نباشد."}

{eventLoadError ? ( ) : null}
); } const beforeStart = registration?.state === "not_started"; const ended = registration?.state === "closed"; const showCountdown = !beforeStart && !ended && deadlineTs !== null && (remainingMs ?? 0) > 0; return (
{beforeStart && (
ثبت‌نام از {formatJalali(event.registration_start_date!)} آغاز می‌شود.
)} {showCountdown && remainingMs != null && (
زمان باقی‌مانده تا پایان ثبت‌نام: {formatRemainingWords(remainingMs)}
)} {ended && (
مهلت ثبت‌نام به پایان رسیده است.
)}
{event.title} {formatDateTimeRange(event.start_time, event.end_time)}
{typeLabel[event.event_type] || event.event_type}
{galleryItems.length ? (

گالری تصاویر

{galleryItems.map((image, index) => ( ))}
) : null}
اطلاعات ثبت‌نام جزئیات دسترسی به رویداد
{event.registration_end_date ? (
) : null}
{ if (!isOpen) { setLightboxIndex(null); } }} />
); }