'use client' import { useState } from 'react' import dynamic from 'next/dynamic' import { useQueryClient } from '@tanstack/react-query' import { addToast } from '@/lib/toast' import type { PaginationListColumnType } from '@/types' import PaginatedList from '@/components/PaginatedList' import PageNavbar from '@/components/layouts/PageNavbar' import Button from '@/components/formElements/Button' import FileCheckIcon from '@/components/icons/FileCheckIcon' import Modal from '@/components/modals/Modal' import { ListSkeleton } from '@/components/feedback/LoadingState' import AdminTableActions from '@/components/ui/AdminTableActions' import AdminTableViewButton from '@/components/ui/AdminTableViewButton' import StatusChip from '@/components/ui/StatusChip' import axiosInstance from '@/config/axios' import { getSettlementStatus, type SettlementStatus, SETTLEMENT_STATUS_FILTER_ITEMS } from '@/constants/status' import { formatCurrency, coerceToString } from '@/helpers' import { unwrapApiPayload } from '@/helpers/listResponse' import useAdminMutation from '@/hooks/useAdminMutation' import { formatPersianDate } from '@/lib/formatters' import { adminKeys } from '@/queries/admin/adminKeys' import { API_ROUTES } from '@/services/config' const CreateSettlementModal = dynamic(() => import('@/app/(dashboard)/settlements/_components/CreateSettlementModal'), { ssr: false }) const ManualPayoutModal = dynamic(() => import('@/components/admin/ManualPayoutModal'), { ssr: false }) interface SettlementItem { id: string amount: number | string organizerEarningId: string eventId: string eventTitle: string } interface SettlementRow { id: string settlementCode: string organizerId: string bankAccountId: string periodStart: string periodEnd: string totalAmount: number | string status: SettlementStatus processedAt?: string | null failureReason?: string | null manualTrackingCode?: string | null manualReceiptUrl?: string | null manualNote?: string | null createdAt: string updatedAt: string items?: SettlementItem[] [key: string]: unknown } const COMPLETABLE_STATUSES: SettlementStatus[] = ['pending', 'processing'] const columns: PaginationListColumnType[] = [ { field: 'settlementCode', label: 'کد تسویه', filterable: false, sortable: false, type: 'text', }, { field: 'organizerId', label: 'میزبان', filterable: true, sortable: false, type: 'text', hideInTable: true, }, { field: 'period', label: 'بازه زمانی', filterable: false, sortable: false, }, { field: 'totalAmount', label: 'مبلغ کل', filterable: false, sortable: true, }, { field: 'status', label: 'وضعیت', filterable: true, sortable: true, type: 'select', filterItems: SETTLEMENT_STATUS_FILTER_ITEMS, }, { field: 'createdAt', label: 'تاریخ ثبت', filterable: false, sortable: true, type: 'date', }, { field: 'actions', label: 'عملیات', }, ] const formatDate = (value: unknown, withTime = false) => formatPersianDate(value, withTime ? { dateStyle: 'medium', timeStyle: 'short' } : { dateStyle: 'medium' }) const SettlementsPage = () => { const queryClient = useQueryClient() const { pendingId, runAction } = useAdminMutation({ errorMessage: 'تکمیل تسویه با خطا مواجه شد', url: API_ROUTES.SETTLEMENTS.ADMIN_LIST, }) const [detailTarget, setDetailTarget] = useState(null) const [detailItems, setDetailItems] = useState([]) const [isLoadingDetail, setIsLoadingDetail] = useState(false) const [isCreateOpen, setIsCreateOpen] = useState(false) const [manualTarget, setManualTarget] = useState(null) const openDetail = async (row: SettlementRow) => { setDetailTarget(row) setDetailItems([]) setIsLoadingDetail(true) try { const response = await axiosInstance.get(API_ROUTES.SETTLEMENTS.ADMIN_DETAIL(row.id)) const payload = unwrapApiPayload<{ items?: SettlementItem[] }>(response.data) setDetailItems(Array.isArray(payload.items) ? payload.items : []) } catch { addToast({ title: 'بارگذاری جزئیات تسویه ناموفق بود', color: 'danger' }) } finally { setIsLoadingDetail(false) } } const closeDetail = () => { setDetailTarget(null) setDetailItems([]) } return (
{ setIsCreateOpen(true) }} > ایجاد تسویه } pageTitle="تسویه‌ها" />
{{ period: (row) => { const settlement = row as SettlementRow return ( {formatDate(settlement.periodStart)} — {formatDate(settlement.periodEnd)} ) }, totalAmount: (_row, cellValue) => formatCurrency(Number(cellValue ?? 0)), createdAt: (_row, cellValue) => formatDate(cellValue, true), status: (row, cellValue) => { const settlement = row as SettlementRow const { label, chipColor } = getSettlementStatus(coerceToString(cellValue)) return ( ) }, actions: (row) => { const settlement = row as SettlementRow const isBusy = pendingId === settlement.id const canComplete = COMPLETABLE_STATUSES.includes(settlement.status) return ( openDetail(settlement)} /> {canComplete ? ( ) : null} ) }, }}
{ if (!open) closeDetail() }} onReject={closeDetail} > {isLoadingDetail ? ( ) : (
{detailTarget?.status === 'failed' && detailTarget?.failureReason ? (
دلیل شکست: {detailTarget.failureReason}
) : null} {detailTarget?.status === 'completed' ? (

شماره پیگیری: {detailTarget.manualTrackingCode ?? '—'}

{detailTarget.manualNote ? (

یادداشت: {detailTarget.manualNote}

) : null} {detailTarget.manualReceiptUrl ? ( مشاهده رسید پرداخت ) : null}
) : null} {detailItems.length === 0 ? (
هیچ قلمی برای این تسویه ثبت نشده است.
) : (
رویداد مبلغ
{detailItems.map((item) => (
{item.eventTitle} {formatCurrency(Number(item.amount ?? 0))}
))}
)}
)}
{isCreateOpen ? ( { setIsCreateOpen(false) }} onCreated={() => { setIsCreateOpen(false) void queryClient.invalidateQueries({ queryKey: adminKeys.listByUrl(API_ROUTES.SETTLEMENTS.ADMIN_LIST) }) }} /> ) : null} {manualTarget ? ( { setManualTarget(null) }} onSubmit={async (payload) => { return runAction( manualTarget.id, () => axiosInstance.patch(API_ROUTES.SETTLEMENTS.ADMIN_COMPLETE(manualTarget.id), payload), 'تسویه با رسید دستی تکمیل شد' ) }} /> ) : null}
) } export default SettlementsPage