"use client";
import type React from "react";
import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { getAdminJackpotContributions, getAdminJackpotPayoutLogs } from "@/api/admin-jackpot";
import { AdminListPaginationFooter } from "@/components/admin/admin-list-pagination-footer";
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
import { ModuleScaffold } from "@/components/admin/module-scaffold";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
import { useExportLabels } from "@/hooks/use-export-labels";
import { formatAdminMinorUnits } from "@/lib/money";
import { LotteryApiBizError } from "@/types/api/errors";
import type {
AdminJackpotContributionsData,
AdminJackpotPayoutLogsData,
} from "@/types/api/admin-jackpot";
type JackpotRecordsConsoleProps = {
embedded?: boolean;
};
/** 表格在 admin-table-shell 内时去掉 Table 组件自带的第二层边框 */
const TABLE_IN_SHELL_CLASS =
"[&_[data-slot=table-container]]:rounded-none [&_[data-slot=table-container]]:border-0 [&_[data-slot=table-container]]:bg-transparent [&_[data-slot=table-container]]:shadow-none";
function JackpotRecordTableSection({
title,
tableId,
exportFilename,
exportSheetName,
loading,
hasData,
children,
footer,
}: {
title: string;
tableId: string;
exportFilename: string;
exportSheetName: string;
loading: boolean;
hasData: boolean;
children: React.ReactNode;
footer: React.ReactNode;
}) {
const { t } = useTranslation("common");
return (
{loading && !hasData ? (
{t("states.loading")}
) : (
{children}
)}
{footer ?
{footer}
: null}
);
}
export function JackpotRecordsConsole({ embedded = false }: JackpotRecordsConsoleProps) {
const { t } = useTranslation(["jackpot", "common"]);
const payoutExport = useExportLabels("jackpotPayouts");
const contributionExport = useExportLabels("jackpotContributions");
const formatDt = useAdminDateTimeFormatter();
const [drawNo, setDrawNo] = useState("");
const [appliedDrawNo, setAppliedDrawNo] = useState("");
const [payouts, setPayouts] = useState(null);
const [pPage, setPPage] = useState(1);
const [pPer, setPPer] = useState(10);
const [contribs, setContribs] = useState(null);
const [cPage, setCPage] = useState(1);
const [cPer, setCPer] = useState(10);
const [loadingP, setLoadingP] = useState(true);
const [loadingC, setLoadingC] = useState(true);
const [err, setErr] = useState(null);
const loadPayouts = useCallback(async () => {
setLoadingP(true);
try {
const d = await getAdminJackpotPayoutLogs({
page: pPage,
per_page: pPer,
draw_no: appliedDrawNo.trim() || undefined,
});
setPayouts(d);
} catch (e) {
setErr(e instanceof LotteryApiBizError ? e.message : t("payoutLoadFailed"));
} finally {
setLoadingP(false);
}
}, [pPage, pPer, appliedDrawNo, t]);
const loadContribs = useCallback(async () => {
setLoadingC(true);
try {
const d = await getAdminJackpotContributions({
page: cPage,
per_page: cPer,
draw_no: appliedDrawNo.trim() || undefined,
});
setContribs(d);
} catch (e) {
setErr(e instanceof LotteryApiBizError ? e.message : t("contributionLoadFailed"));
} finally {
setLoadingC(false);
}
}, [cPage, cPer, appliedDrawNo, t]);
useEffect(() => {
queueMicrotask(() => {
void loadPayouts();
});
}, [loadPayouts]);
useEffect(() => {
queueMicrotask(() => {
void loadContribs();
});
}, [loadContribs]);
const applyDraw = () => {
setAppliedDrawNo(drawNo);
setPPage(1);
setCPage(1);
};
const triggerTypeText = (value: string) => {
const key = `triggerTypes.${value}`;
const translated = t(key);
return translated === key ? value : translated;
};
const filterBlock = embedded ? (
) : (
{t("filter")}
setDrawNo(e.target.value)}
placeholder={t("optional")}
/>
);
const payoutFooter = payouts ? (
{
setPPer(n);
setPPage(1);
}}
onPageChange={setPPage}
/>
) : null;
const contributionFooter = contribs ? (
{
setCPer(n);
setCPage(1);
}}
onPageChange={setCPage}
/>
) : null;
const payoutTable = (
{t("table.id", { ns: "common" })}
{t("drawNo")}
{t("trigger")}
{t("payoutAmount")}
{t("winnerCount")}
{t("time")}
{(payouts?.items ?? []).length === 0 ? (
{t("states.noData", { ns: "common" })}
) : (
(payouts?.items ?? []).map((r) => (
{r.id}
{r.draw_no ?? "—"}
{triggerTypeText(r.trigger_type)}
{formatAdminMinorUnits(r.total_payout_amount, r.currency_code ?? "NPR")}
{r.winner_count}
{formatDt(r.created_at)}
))
)}
);
const contributionTable = (
{t("table.id", { ns: "common" })}
{t("drawNo")}
{t("ticketNo")}
{t("player")}
{t("contributionAmount")}
{t("time")}
{(contribs?.items ?? []).length === 0 ? (
{t("states.noData", { ns: "common" })}
) : (
(contribs?.items ?? []).map((r) => (
{r.id}
{r.draw_no ?? "—"}
{r.ticket_no ?? "—"}
{r.player_username ?? "—"}
{formatAdminMinorUnits(r.contribution_amount, r.currency_code ?? "NPR")}
{formatDt(r.created_at)}
))
)}
);
const content = (
<>
{filterBlock}
{err ? {err}
: null}
{embedded ? (
{payoutTable}
{contributionTable}
) : (
{payoutTable}
{contributionTable}
)}
>
);
if (embedded) {
return content;
}
return {content};
}