"use client"; import Link from "next/link"; import { useCallback, useState } from "react"; import { useTranslation } from "react-i18next"; import { useAsyncEffect } from "@/hooks/use-async-effect"; import { useTranslationRef } from "@/hooks/use-translation-ref"; import { toast } from "sonner"; import { downloadAdminSettlementBatchExport, getAdminSettlementBatch, getAdminSettlementBatchDetails, postAdminApproveSettlementBatch, postAdminPayoutSettlementBatch, postAdminRejectSettlementBatch, } from "@/api/admin-settlement"; import { AdminListPaginationFooter } from "@/components/admin/admin-list-pagination-footer"; import { AdminAgentFilter } from "@/components/admin/admin-agent-filter"; import { AdminAgentIdentityCells, AdminAgentIdentityHeads } from "@/components/admin/admin-agent-columns"; import { AdminPlayerIdentityCells, AdminPlayerIdentityHeads } from "@/components/admin/admin-player-identity-columns"; import { AdminStatusBadge } from "@/components/admin/admin-status-badge"; import { ModuleScaffold } from "@/components/admin/module-scaffold"; import { Button, buttonVariants } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Textarea } from "@/components/ui/textarea"; import { AdminLoadingState, AdminLoadingInline, AdminTableLoadingRow } from "@/components/admin/admin-loading-state"; import { useAdminCurrencyCatalog } from "@/hooks/use-admin-currency-catalog"; import { useAdminPlayCodeLabel } from "@/hooks/use-admin-play-type-catalog"; import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter"; import { adminHasAnyPermission } from "@/lib/admin-permissions"; import { formatAdminMinorUnits } from "@/lib/money"; import { cn } from "@/lib/utils"; import { PRD_PAYOUT_MANAGE, PRD_PAYOUT_REVIEW } from "@/lib/admin-prd"; import { useAdminProfile } from "@/stores/admin-session"; import { LotteryApiBizError } from "@/types/api/errors"; import type { AdminSettlementBatchDetailsData, AdminSettlementBatchShowData, } from "@/types/api/admin-settlement"; type Props = { batchId: number; }; type SettlementAction = "approve" | "reject" | "payout"; function settlementStatusText(value: string, t: (key: string) => string): string { const key = `statusOptions.${value}`; const translated = t(key); return translated === key ? value : translated; } function settlementReviewStatusText(value: string | null, t: (key: string) => string): string { if (!value) return "—"; const key = `reviewStatusOptions.${value}`; const translated = t(key); return translated === key ? value : translated; } export function SettlementBatchDetailsConsole({ batchId }: Props) { const { t } = useTranslation(["settlement", "common"]); const tRef = useTranslationRef(["settlement", "common"]); const profile = useAdminProfile(); useAdminCurrencyCatalog(); const playCodeLabel = useAdminPlayCodeLabel(); const canReviewSettlement = adminHasAnyPermission(profile?.permissions, [PRD_PAYOUT_REVIEW]); const canManagePayout = adminHasAnyPermission(profile?.permissions, [PRD_PAYOUT_MANAGE]); const formatDt = useAdminDateTimeFormatter(); const [summary, setSummary] = useState(null); const [details, setDetails] = useState(null); const [loading, setLoading] = useState(true); const [err, setErr] = useState(null); const [page, setPage] = useState(1); const [perPage, setPerPage] = useState(10); const [agentNodeId, setAgentNodeId] = useState(undefined); const [appliedAgentNodeId, setAppliedAgentNodeId] = useState(undefined); const [acting, setActing] = useState(null); const [pendingAction, setPendingAction] = useState(null); const [reviewRemark, setReviewRemark] = useState(""); const batchCurrency = summary?.currency_code ?? "NPR"; const load = useCallback(async () => { setLoading(true); setErr(null); try { const [s, d] = await Promise.all([ getAdminSettlementBatch(batchId), getAdminSettlementBatchDetails(batchId, { page, per_page: perPage, agent_node_id: appliedAgentNodeId, }), ]); setSummary(s); setDetails(d); } catch (e) { setErr(e instanceof LotteryApiBizError ? e.message : tRef.current("loadFailed")); setSummary(null); setDetails(null); } finally { setLoading(false); } }, [batchId, page, perPage, appliedAgentNodeId]); async function runAction(label: string, action: () => Promise): Promise { setActing(label); try { await action(); toast.success(t("actionSuccess", { name: label })); setPendingAction(null); setReviewRemark(""); await load(); } catch (e) { toast.error(e instanceof LotteryApiBizError ? e.message : t("actionFailed", { name: label })); } finally { setActing(null); } } const actionLabel = (action: SettlementAction): string => { if (action === "approve") { return t("approve"); } if (action === "reject") { return t("reject"); } return t("runPayout"); }; const confirmPendingAction = (): void => { if (!summary || pendingAction === null) { return; } const remark = reviewRemark.trim() || undefined; if (pendingAction === "approve") { void runAction(actionLabel(pendingAction), () => postAdminApproveSettlementBatch(batchId, remark)); return; } if (pendingAction === "reject") { void runAction(actionLabel(pendingAction), () => postAdminRejectSettlementBatch(batchId, remark)); return; } void runAction(actionLabel(pendingAction), () => postAdminPayoutSettlementBatch(batchId)); }; const openActionDialog = (action: SettlementAction): void => { setReviewRemark(""); setPendingAction(action); }; async function exportCsv(): Promise { setActing(t("export")); try { const blob = await downloadAdminSettlementBatchExport(batchId); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `settlement-${batchId}.csv`; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); } catch (e) { toast.error(e instanceof LotteryApiBizError ? e.message : t("exportFailed")); } finally { setActing(null); } } useAsyncEffect(() => { void load(); }, [batchId, page, perPage, appliedAgentNodeId]); return (
← {t("backToList")}
{err ? ( {t("errorTitle")}

{err}

) : null} {summary ? ( {t("batchSummary", { id: summary.id })}

{t("summaryMeta", { drawNo: summary.draw_no ?? "—", drawStatus: summary.draw_status ?? "—", version: summary.result_batch_version ?? "—", })}

{t("settlementStatus")} {settlementStatusText(summary.status, t)}

{t("reviewState")} {summary.review_status ? ( {settlementReviewStatusText(summary.review_status, t)} ) : ( )}

{t("ticketTotal")}{" "} {summary.total_ticket_count}

{t("winTotal")}{" "} {summary.total_win_count}

{t("totalBet")}{" "} {formatAdminMinorUnits(summary.total_bet_amount, summary.currency_code ?? "NPR")}

{t("actualDeduct")}{" "} {formatAdminMinorUnits(summary.total_actual_deduct, summary.currency_code ?? "NPR")}

{t("payoutAmount")}{" "} {formatAdminMinorUnits(summary.total_payout_amount, summary.currency_code ?? "NPR")}

{t("jackpotPayout")}{" "} {formatAdminMinorUnits(summary.total_jackpot_payout_amount, summary.currency_code ?? "NPR")}

{t("platformProfit")}{" "} {formatAdminMinorUnits(summary.platform_profit, summary.currency_code ?? "NPR")}

{t("startedAt")} {formatDt(summary.started_at)}

{t("endedAt")} {formatDt(summary.finished_at)}

{canReviewSettlement ? ( ) : null} {canReviewSettlement ? ( ) : null} {canManagePayout ? ( ) : null}
) : loading ? ( ) : null} {t("detailTitle")} {details ? ( <>
{t("ticketNo")} {t("playCode")} {t("matchedTier")} {t("regularPayout")} {t("jackpot")} {details.items.map((r) => ( {r.ticket_no ?? "—"} {playCodeLabel(r.play_code)} {r.matched_prize_tier ?? "—"} {formatAdminMinorUnits(r.win_amount, r.currency_code ?? batchCurrency)} {formatAdminMinorUnits( r.jackpot_allocation_amount, r.currency_code ?? batchCurrency, )} ))}
{ setPerPage(n); setPage(1); }} onPageChange={setPage} /> ) : (

{loading ? ( ) : ( t("states.noData", { ns: "common" }) )}

)}
{ if (!open && acting === null) { setPendingAction(null); setReviewRemark(""); } }} > {summary && pendingAction ? ( <> {t("confirmAction", { name: actionLabel(pendingAction) })} {pendingAction === "payout" ? t("confirmPayoutDesc") : t("confirmActionDesc", { drawNo: summary.draw_no ?? "—" })}

{t("confirmAmountLine", { actual: formatAdminMinorUnits(summary.total_actual_deduct, summary.currency_code ?? "NPR"), payout: formatAdminMinorUnits(summary.total_payout_amount, summary.currency_code ?? "NPR"), profit: formatAdminMinorUnits(summary.platform_profit, summary.currency_code ?? "NPR"), })}

{pendingAction !== "payout" ? (