'use client'
import type { AdminEventDetailData } from './admin-event-detail/types'
import { useEffect, useMemo, useState } from 'react'
import dynamic from 'next/dynamic'
import { useParams, useRouter, useSearchParams } from 'next/navigation'
import type { EventCategory, EventRevision } from '@/services/events'
import type { City, Province } from '@/services/geography'
import type { EventFaq, EventMedia } from '@/services/eventDetail'
import type { EventManagementInsights } from '@/services/eventManagement'
import { Tab } from '@/components/heroui/Tabs'
import { addToast } from '@/lib/toast'
import PageNavbar from '@/components/layouts/PageNavbar'
import { DetailSkeleton } from '@/components/feedback/LoadingState'
import AdminState from '@/components/feedback/AdminState'
import AppTabs from '@/components/ui/AppTabs'
import { APP_ROUTES } from '@/constants/routes'
import { isEventEnded, isLiveEventStatus } from '@/features/events/eventEditPolicy'
import { extractServerErrorDetail } from '@/services/errorHandler'
import {
approveEventAsAdmin,
approveEventRevisionAsAdmin,
cancelEventAsAdmin,
completeEventAsAdmin,
deleteEventAsAdmin,
fetchAdminPendingEventRevision,
fetchEventCategories,
fetchAdminEventDetail,
publishEventAsAdmin,
rejectEventRevisionAsAdmin,
updateEventAsAdmin,
} from '@/services/events'
import { fetchEventInsightsAsAdmin } from '@/services/eventManagement'
import { fetchEventFaqs, fetchEventMedia } from '@/services/eventDetail'
import { fetchAllCities, fetchProvinces } from '@/services/geography'
import useAdminAction from '@/hooks/useAdminAction'
import useAlertModal from '@/hooks/useAlertModal'
import { texts } from '@/texts'
import AdminEventDetailsSection from './admin-event-detail/AdminEventDetailsSection'
import AdminEventLifecycleActions from './admin-event-detail/AdminEventLifecycleActions'
import AdminEventOverviewTab from './admin-event-detail/AdminEventOverviewTab'
import AdminEventPendingRevisionCard from './admin-event-detail/AdminEventPendingRevisionCard'
import AdminEventRevisionRejectModal from './admin-event-detail/AdminEventRevisionRejectModal'
import AdminEventSummaryCard from './admin-event-detail/AdminEventSummaryCard'
const TabContentLoading = () => (
در حال بارگذاری اطلاعات…
)
const AdminEventFinancialTab = dynamic(() => import('./admin-event-detail/AdminEventFinancialTab'), {
loading: TabContentLoading,
ssr: false,
})
const EventDiscountsPanel = dynamic(() => import('@/features/events/detail/EventDiscountsPanel'), {
loading: TabContentLoading,
ssr: false,
})
const AdminEventBookingsTab = dynamic(() => import('./admin-event-detail/AdminEventBookingsTab'), {
loading: TabContentLoading,
ssr: false,
})
const AdminEventReviewsTab = dynamic(() => import('./admin-event-detail/AdminEventReviewsTab'), {
loading: TabContentLoading,
ssr: false,
})
const EventGuestListAccessPanel = dynamic(() => import('@/components/events/EventGuestListAccessPanel'), {
loading: TabContentLoading,
ssr: false,
})
const AdminEventRejectModal = dynamic(() => import('./admin-event-detail/AdminEventRejectModal'), { ssr: false })
const AdminEventCommissionModal = dynamic(() => import('./admin-event-detail/AdminEventCommissionModal'), { ssr: false })
const AdminEventDetail = () => {
const params = useParams<{ id: string }>()
const searchParams = useSearchParams()
const router = useRouter()
const eventId = params.id
const initialTab = searchParams.get('tab') === 'discounts' ? 'discounts' : 'overview'
const [event, setEvent] = useState(null)
const [categories, setCategories] = useState([])
const [cities, setCities] = useState([])
const [provinces, setProvinces] = useState([])
const [media, setMedia] = useState([])
const [faqs, setFaqs] = useState([])
const [insights, setInsights] = useState(null)
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(null)
const [isTogglingDiscoverable, setIsTogglingDiscoverable] = useState(false)
const [isRejectModalOpen, setIsRejectModalOpen] = useState(false)
const [isCommissionModalOpen, setIsCommissionModalOpen] = useState(false)
const [hasOpenedRejectModal, setHasOpenedRejectModal] = useState(false)
const [hasOpenedCommissionModal, setHasOpenedCommissionModal] = useState(false)
const [pendingRevision, setPendingRevision] = useState(null)
const [isRevisionRejectModalOpen, setIsRevisionRejectModalOpen] = useState(false)
const [hasOpenedRevisionRejectModal, setHasOpenedRevisionRejectModal] = useState(false)
const { showAlert } = useAlertModal()
const { pendingId, runAction } = useAdminAction()
const mergeEvent = (updated: AdminEventDetailData) => {
setEvent((prev) => (prev ? { ...prev, ...updated } : prev))
}
const refreshInsights = () => {
if (!eventId) return
void fetchEventInsightsAsAdmin(eventId)
.then(setInsights)
.catch(() => undefined)
}
useEffect(() => {
if (!eventId) return
let cancelled = false
const loadEvent = async () => {
try {
setIsLoading(true)
setError(null)
const [loadedEvent, loadedCategories, loadedCities, loadedProvinces, loadedMedia, loadedFaqs, loadedInsights] = await Promise.all([
fetchAdminEventDetail(eventId) as Promise,
fetchEventCategories().catch(() => []),
fetchAllCities().catch(() => []),
fetchProvinces().catch(() => []),
fetchEventMedia(eventId).catch(() => []),
fetchEventFaqs(eventId).catch(() => []),
fetchEventInsightsAsAdmin(eventId).catch(() => null),
])
if (cancelled) return
setEvent(loadedEvent)
setCategories(loadedCategories)
setCities(loadedCities)
setProvinces(loadedProvinces)
setMedia(loadedMedia)
setFaqs(loadedFaqs)
setInsights(loadedInsights)
} catch {
if (!cancelled) {
setError('رویداد یافت نشد')
setEvent(null)
}
} finally {
if (!cancelled) setIsLoading(false)
}
}
void loadEvent()
return () => {
cancelled = true
}
}, [eventId])
useEffect(() => {
if (!eventId || !event || !isLiveEventStatus(event.status)) {
setPendingRevision(null)
return
}
let cancelled = false
const loadPendingRevision = async () => {
try {
const revision = await fetchAdminPendingEventRevision(eventId)
if (!cancelled) setPendingRevision(revision)
} catch {
if (!cancelled) {
setPendingRevision(null)
addToast({ title: texts.events.revisionLoadFailed, color: 'danger' })
}
}
}
void loadPendingRevision()
return () => {
cancelled = true
}
}, [event, eventId])
const categoryName = useMemo(() => categories.find((item) => item.id === event?.categoryId)?.name ?? '—', [categories, event?.categoryId])
const cityName = useMemo(() => cities.find((item) => item.id === event?.cityId)?.name ?? '—', [cities, event?.cityId])
const provinceName = useMemo(() => provinces.find((item) => item.id === event?.provinceId)?.name ?? '—', [event?.provinceId, provinces])
const handleApprove = () => {
const isPendingReview = event?.status === 'pending_review'
showAlert(isPendingReview ? 'این رویداد تأیید و منتشر شود؟' : 'این رویداد برای انتشار تأیید شود؟', () =>
runAction(
'approve',
async () => {
const updated = (await approveEventAsAdmin(eventId)) as AdminEventDetailData
mergeEvent(updated)
},
isPendingReview ? 'رویداد تأیید و منتشر شد' : 'رویداد برای انتشار تأیید شد'
)
)
}
// Bypasses the whole request/approve flow — publishes immediately
// regardless of whether the host has requested publication or an admin
// has approved yet. Kept as a separate action from "approve" on purpose.
const handleForcePublish = () => {
showAlert('این رویداد فوراً و بدون انتظار برای میزبان منتشر شود؟', () =>
runAction(
'force-publish',
async () => {
const updated = (await publishEventAsAdmin(eventId)) as AdminEventDetailData
mergeEvent(updated)
},
'رویداد فورا منتشر شد'
)
)
}
const handleComplete = () => {
showAlert('آیا این رویداد به پایان رسیده است؟', () =>
runAction(
'complete',
async () => {
const updated = (await completeEventAsAdmin(eventId)) as AdminEventDetailData
mergeEvent(updated)
refreshInsights()
},
'رویداد بهعنوان پایانیافته علامت خورد'
)
)
}
const handleCancel = () => {
showAlert(
'آیا از لغو این رویداد مطمئن هستید؟ همهی رزروها لغو خواهند شد.',
() =>
runAction(
'cancel',
async () => {
const updated = (await cancelEventAsAdmin(eventId)) as AdminEventDetailData
mergeEvent(updated)
refreshInsights()
},
'رویداد لغو شد'
),
undefined,
{ dangerAccept: true }
)
}
const handleDelete = () => {
showAlert(
'آیا از حذف این رویداد مطمئن هستید؟ فقط رویدادهای بدون رزرو فعال قابل حذفاند.',
() =>
runAction(
'delete',
async () => {
await deleteEventAsAdmin(eventId)
router.push(APP_ROUTES.MANAGE_EVENTS)
},
'رویداد حذف شد'
),
undefined,
{ dangerAccept: true }
)
}
const handleApproveRevision = () => {
if (!pendingRevision) return
showAlert('این ویرایش تأیید و روی رویداد اعمال شود؟', () =>
runAction(
'approve-revision',
async () => {
const updated = (await approveEventRevisionAsAdmin(eventId, pendingRevision.id)) as AdminEventDetailData
mergeEvent(updated)
setPendingRevision(null)
refreshInsights()
},
texts.events.revisionApproveSuccess
)
)
}
const handleRejectRevision = (rejectionReason: string) => {
if (!pendingRevision) return
void runAction(
'reject-revision',
async () => {
await rejectEventRevisionAsAdmin(eventId, pendingRevision.id, rejectionReason)
setPendingRevision(null)
setIsRevisionRejectModalOpen(false)
},
texts.events.revisionRejectSuccess
)
}
const handleToggleDiscoverable = (nextValue: boolean) => {
if (!event || isTogglingDiscoverable) return
const previous = event.settings.isDiscoverable
showAlert(nextValue ? 'نمایش این رویداد در جستجوی عمومی فعال شود؟' : 'نمایش این رویداد در جستجوی عمومی غیرفعال شود؟', async () => {
setEvent((prev) => (prev ? { ...prev, settings: { ...prev.settings, isDiscoverable: nextValue } } : prev))
setIsTogglingDiscoverable(true)
try {
await updateEventAsAdmin(eventId, { settings: { isDiscoverable: nextValue } })
addToast({ title: 'وضعیت جستجوی عمومی بهروزرسانی شد', color: 'success' })
} catch (err) {
setEvent((prev) => (prev ? { ...prev, settings: { ...prev.settings, isDiscoverable: previous } } : prev))
const detail = extractServerErrorDetail((err as { response?: { data?: unknown } })?.response?.data)
addToast({
title: 'بهروزرسانی ناموفق بود',
description: detail ?? undefined,
color: 'danger',
})
} finally {
setIsTogglingDiscoverable(false)
}
})
}
return (
void handleApprove()}
onCancel={handleCancel}
onComplete={handleComplete}
onDelete={handleDelete}
onForcePublish={() => void handleForcePublish()}
onOpenReject={() => {
setHasOpenedRejectModal(true)
setIsRejectModalOpen(true)
}}
/>
) : undefined
}
pageTitle={event?.title ?? 'جزئیات رویداد'}
/>
{isLoading &&
}
{!isLoading && error && (
)}
{!isLoading && event && (
<>
{pendingRevision ? (
{
setHasOpenedRevisionRejectModal(true)
setIsRevisionRejectModalOpen(true)
}}
/>
) : null}
{event.isFree ? null : (
)}
{event.isFree ? null : (
)}
{
setHasOpenedCommissionModal(true)
setIsCommissionModalOpen(true)
}}
/>
>
)}
{hasOpenedRejectModal ? (
) : null}
{hasOpenedCommissionModal ? (
) : null}
{hasOpenedRevisionRejectModal ? (
) : null}
)
}
export default AdminEventDetail