'use client' import type { ReactNode } from 'react' import Link from 'next/link' import { useEffect, useState } from 'react' import { Card, CardBody, CardHeader } from '@/components/heroui/Card' import PageNavbar from '@/components/layouts/PageNavbar' import { DetailSkeleton, TableSkeleton } from '@/components/feedback/LoadingState' import AdminState from '@/components/feedback/AdminState' import { GET_ADMIN_REPORTS_OVERVIEW, type AdminReportsOverview } from '@/services/adminReports' import { APP_ROUTES } from '@/constants/routes' import { formatNumber as formatNumberBase } from '@/helpers' import UserFillIcon from '@/components/icons/UserFillIcon' import CalendarIcon from '@/components/icons/CalendarIcon' import BookmarkIcon from '@/components/icons/BookmarkIcon' import UserIdCardIcon from '@/components/icons/UserIdCardIcon' import CoinDollarIcon from '@/components/icons/CoinDollarIcon' import AngleLeftIcon from '@/components/icons/AngleLeftIcon' interface StatItem { label: string value: number | string href?: string } type KpiItem = Omit & { href: string icon: ReactNode } const formatNumber = (value: number | string) => { const num = typeof value === 'string' ? Number(value) : value if (!Number.isFinite(num)) return String(value) return formatNumberBase(num) } const StatCell = ({ item }: { item: StatItem }) => { const content = ( <> {item.label} {formatNumber(item.value)} {item.href ? ( ) : null} ) const className = 'group flex h-full min-h-24 flex-col justify-center gap-2 bg-white p-3 text-right transition-colors hover:bg-secondary-50/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500' return item.href ? ( {content} ) : (
{content}
) } const KpiCard = ({ item }: { item: KpiItem }) => ( {item.icon}

{item.label}

{formatNumber(item.value)}

) const StatGroup = ({ title, description, icon, items }: { title: string; description: string; icon: ReactNode; items: StatItem[] }) => ( {icon}

{title}

{description}

{items.map((item) => ( ))}
) const ActionQueue = ({ overview }: { overview: AdminReportsOverview }) => { const items = [ { label: 'احراز هویت نیازمند بررسی', value: overview.identityVerifications.pending, href: `${APP_ROUTES.IDENTITY_VERIFICATIONS}?filters[status]=pending`, }, { label: 'رویداد در انتظار بررسی', value: overview.events.byStatus.pending_review, href: `${APP_ROUTES.MANAGE_EVENTS}?filters[status]=pending_review`, }, { label: 'رزرو در انتظار پرداخت', value: overview.bookings.byStatus.pending_payment, href: `${APP_ROUTES.BOOKINGS}?filters[status]=pending_payment`, }, ] const total = items.reduce((sum, item) => sum + item.value, 0) return (

صف اقدامات امروز

مواردی که برای ادامه فرایند به توجه ادمین نیاز دارند

{formatNumber(total)} باز
{items.map((item) => (

{item.label}

{formatNumber(item.value)}

))}
) } const DashboardPage = () => { const [overview, setOverview] = useState(null) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) const [reloadKey, setReloadKey] = useState(0) useEffect(() => { let cancelled = false const load = async () => { setIsLoading(true) setError(null) const result = await GET_ADMIN_REPORTS_OVERVIEW() if (cancelled) return if (!result.ok) { setOverview(null) setError(result.error?.message ?? 'دریافت گزارش‌ها ناموفق بود') } else { setOverview(result.data) } setIsLoading(false) } void load() return () => { cancelled = true } }, [reloadKey]) return (
{isLoading && (
)} {!isLoading && error && (
{ setReloadKey((current) => current + 1) }} />
)} {!isLoading && overview && ( <>

نمای کلی سامانه

سلام، به پنل مدیریت قبیله خوش آمدید

مهم‌ترین شاخص‌های کاربران، رویدادها، رزروها و عملیات مالی را در یک نگاه بررسی کنید.

بررسی احرازهای در انتظار
{( [ { label: 'کل کاربران', value: overview.users.total, icon: , href: APP_ROUTES.USERS, }, { label: 'رویدادهای فعال', value: overview.events.active, icon: , href: `${APP_ROUTES.MANAGE_EVENTS}?filters[status]=published`, }, { label: 'کل رزروها', value: overview.bookings.total, icon: , href: APP_ROUTES.BOOKINGS, }, { label: 'در انتظار احراز', value: overview.identityVerifications.pending, icon: , href: `${APP_ROUTES.IDENTITY_VERIFICATIONS}?filters[status]=pending`, }, ] satisfies KpiItem[] ).map((item) => ( ))}
} items={[ { label: 'کل کمیسیون پلتفرم (تومان)', value: overview.financial.totalCommissionCollected, href: APP_ROUTES.SETTLEMENTS, }, { label: 'کل موجودی کیف‌پول‌ها (تومان)', value: overview.financial.totalWalletBalance, href: APP_ROUTES.WITHDRAWAL_REQUESTS, }, ]} title="مالی" /> } items={[ { label: 'کل رویدادها', value: overview.events.total }, // «فعال» شامل هر دو وضعیت published و full است، اما فیلتر لیست // رویدادها تک‌مقداری است (کاما در PaginatedList به‌عنوان بازه‌ی // from/to تفسیر می‌شود، نه چند مقدار) — طبق تصمیم کاربر به published // لینک می‌شود؛ رکورد‌های full را می‌توان جدا از ردیف «تکمیل ظرفیت» دید. { label: 'منتشر‌ یا تکمیل ظرفیت', value: overview.events.active, href: `${APP_ROUTES.MANAGE_EVENTS}?filters[status]=published`, }, { label: 'کل برگذار‌شده', value: overview.events.totalHeld }, { label: 'پیش‌نویس', value: overview.events.byStatus.draft, href: `${APP_ROUTES.MANAGE_EVENTS}?filters[status]=draft`, }, { label: 'در انتظار بررسی', value: overview.events.byStatus.pending_review, href: `${APP_ROUTES.MANAGE_EVENTS}?filters[status]=pending_review`, }, { label: 'منتشر‌شده', value: overview.events.byStatus.published }, { label: 'تکمیل ظرفیت', value: overview.events.byStatus.full }, { label: 'لغو‌شده', value: overview.events.byStatus.cancelled }, ]} title="رویدادها" /> } items={[ { label: 'کل رزروها', value: overview.bookings.total }, // «فعال» = pending_payment + confirmed (رزروهای زنده‌ای که ظرفیت را نگه می‌دارند). // فیلتر لیست تک‌مقداری است؛ مثل رویدادهای فعال به confirmed لینک می‌شود. { label: 'فعال', value: overview.bookings.byStatus.pending_payment + overview.bookings.byStatus.confirmed, href: `${APP_ROUTES.BOOKINGS}?filters[status]=confirmed`, }, { label: 'در انتظار پرداخت', value: overview.bookings.byStatus.pending_payment, href: `${APP_ROUTES.BOOKINGS}?filters[status]=pending_payment`, }, { label: 'تأیید‌شده', value: overview.bookings.byStatus.confirmed, href: `${APP_ROUTES.BOOKINGS}?filters[status]=confirmed`, }, { label: 'لغو‌شده', value: overview.bookings.byStatus.cancelled }, { label: 'منقضی‌شده', value: overview.bookings.byStatus.expired }, { label: 'بازپرداخت‌شده', value: overview.bookings.byStatus.refunded }, { label: 'عدم حضور', value: overview.bookings.byStatus.no_show }, ]} title="رزروها" /> } items={[ { label: 'کل کاربران', value: overview.users.total }, { label: 'فعال', value: overview.users.active, href: `${APP_ROUTES.USERS}?filters[status]=active`, }, { label: 'در انتظار تکمیل پروفایل', value: overview.users.pending, href: `${APP_ROUTES.USERS}?filters[status]=pending`, }, { label: 'معلق', value: overview.users.suspended, href: `${APP_ROUTES.USERS}?filters[status]=suspended`, }, ]} title="کاربران" /> } items={[ { label: 'بدون احراز', value: overview.identityVerifications.none, href: `${APP_ROUTES.USERS}?filters[identityStatus]=none`, }, { label: 'در انتظار', value: overview.identityVerifications.pending, href: `${APP_ROUTES.IDENTITY_VERIFICATIONS}?filters[status]=pending`, }, { label: 'تأیید‌شده', value: overview.identityVerifications.verified, href: `${APP_ROUTES.USERS}?filters[identityStatus]=verified`, }, { label: 'رد‌شده', value: overview.identityVerifications.rejected, href: `${APP_ROUTES.USERS}?filters[identityStatus]=rejected`, }, ]} title="احراز هویت" />
)}
) } export default DashboardPage