feat: 更新管理员导航,添加结算和奖池模块,并优化活动匹配逻辑
This commit is contained in:
236
src/modules/jackpot/jackpot-records-console.tsx
Normal file
236
src/modules/jackpot/jackpot-records-console.tsx
Normal file
@@ -0,0 +1,236 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { getAdminJackpotContributions, getAdminJackpotPayoutLogs } from "@/api/admin-jackpot";
|
||||
import { AdminListPaginationFooter } from "@/components/admin/admin-list-pagination-footer";
|
||||
import { ModuleScaffold } from "@/components/admin/module-scaffold";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, 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 { formatAdminMinorUnits } from "@/lib/money";
|
||||
import { LotteryApiBizError } from "@/types/api/errors";
|
||||
import type {
|
||||
AdminJackpotContributionsData,
|
||||
AdminJackpotPayoutLogsData,
|
||||
} from "@/types/api/admin-jackpot";
|
||||
|
||||
export function JackpotRecordsConsole() {
|
||||
const formatDt = useAdminDateTimeFormatter();
|
||||
const [drawNo, setDrawNo] = useState("");
|
||||
const [appliedDrawNo, setAppliedDrawNo] = useState("");
|
||||
|
||||
const [payouts, setPayouts] = useState<AdminJackpotPayoutLogsData | null>(null);
|
||||
const [pPage, setPPage] = useState(1);
|
||||
const [pPer, setPPer] = useState(15);
|
||||
|
||||
const [contribs, setContribs] = useState<AdminJackpotContributionsData | null>(null);
|
||||
const [cPage, setCPage] = useState(1);
|
||||
const [cPer, setCPer] = useState(15);
|
||||
|
||||
const [loadingP, setLoadingP] = useState(true);
|
||||
const [loadingC, setLoadingC] = useState(true);
|
||||
const [err, setErr] = useState<string | null>(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 : "派彩记录加载失败");
|
||||
} finally {
|
||||
setLoadingP(false);
|
||||
}
|
||||
}, [pPage, pPer, appliedDrawNo]);
|
||||
|
||||
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 : "蓄水记录加载失败");
|
||||
} finally {
|
||||
setLoadingC(false);
|
||||
}
|
||||
}, [cPage, cPer, appliedDrawNo]);
|
||||
|
||||
useEffect(() => {
|
||||
queueMicrotask(() => {
|
||||
void loadPayouts();
|
||||
});
|
||||
}, [loadPayouts]);
|
||||
|
||||
useEffect(() => {
|
||||
queueMicrotask(() => {
|
||||
void loadContribs();
|
||||
});
|
||||
}, [loadContribs]);
|
||||
|
||||
const applyDraw = () => {
|
||||
setAppliedDrawNo(drawNo);
|
||||
setPPage(1);
|
||||
setCPage(1);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModuleScaffold>
|
||||
<Card className="mb-6">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base">筛选</CardTitle>
|
||||
<CardDescription>按期号模糊过滤两类记录</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||||
<div className="flex max-w-xs flex-1 flex-col gap-1.5">
|
||||
<Label htmlFor="jk-draw">期号</Label>
|
||||
<Input
|
||||
id="jk-draw"
|
||||
className="font-mono"
|
||||
value={drawNo}
|
||||
onChange={(e) => setDrawNo(e.target.value)}
|
||||
placeholder="可选"
|
||||
/>
|
||||
</div>
|
||||
<Button type="button" onClick={applyDraw}>
|
||||
应用
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{err ? <p className="text-destructive mb-4 text-sm">{err}</p> : null}
|
||||
|
||||
<Card className="mb-8">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Jackpot 派彩记录</CardTitle>
|
||||
<CardDescription>爆池触发与划出总额</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{loadingP && !payouts ? (
|
||||
<p className="text-muted-foreground text-sm">加载中…</p>
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>ID</TableHead>
|
||||
<TableHead>期号</TableHead>
|
||||
<TableHead>触发</TableHead>
|
||||
<TableHead className="text-right">派彩额</TableHead>
|
||||
<TableHead className="text-right">中奖人数</TableHead>
|
||||
<TableHead>时间</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{(payouts?.items ?? []).map((r) => (
|
||||
<TableRow key={r.id}>
|
||||
<TableCell className="font-mono text-xs">{r.id}</TableCell>
|
||||
<TableCell className="font-mono text-xs">{r.draw_no ?? "—"}</TableCell>
|
||||
<TableCell className="text-xs">{r.trigger_type}</TableCell>
|
||||
<TableCell className="text-right font-mono text-xs tabular-nums">
|
||||
{formatAdminMinorUnits(r.total_payout_amount, r.currency_code ?? "NPR")}
|
||||
</TableCell>
|
||||
<TableCell className="text-right tabular-nums">{r.winner_count}</TableCell>
|
||||
<TableCell className="text-xs text-muted-foreground whitespace-nowrap">
|
||||
{formatDt(r.created_at)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
{payouts ? (
|
||||
<AdminListPaginationFooter
|
||||
selectId="jk-payout-per"
|
||||
total={payouts.meta.total}
|
||||
page={payouts.meta.current_page}
|
||||
lastPage={payouts.meta.last_page}
|
||||
perPage={payouts.meta.per_page}
|
||||
loading={loadingP}
|
||||
onPerPageChange={(n) => {
|
||||
setPPer(n);
|
||||
setPPage(1);
|
||||
}}
|
||||
onPageChange={setPPage}
|
||||
/>
|
||||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Jackpot 蓄水记录</CardTitle>
|
||||
<CardDescription>每笔注单蓄水入账流水</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{loadingC && !contribs ? (
|
||||
<p className="text-muted-foreground text-sm">加载中…</p>
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>ID</TableHead>
|
||||
<TableHead>期号</TableHead>
|
||||
<TableHead>注单</TableHead>
|
||||
<TableHead>玩家</TableHead>
|
||||
<TableHead className="text-right">蓄水额</TableHead>
|
||||
<TableHead>时间</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{(contribs?.items ?? []).map((r) => (
|
||||
<TableRow key={r.id}>
|
||||
<TableCell className="font-mono text-xs">{r.id}</TableCell>
|
||||
<TableCell className="font-mono text-xs">{r.draw_no ?? "—"}</TableCell>
|
||||
<TableCell className="font-mono text-xs">{r.ticket_no ?? "—"}</TableCell>
|
||||
<TableCell className="max-w-[10rem] truncate text-xs">
|
||||
{r.player_username ?? "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-right font-mono text-xs tabular-nums">
|
||||
{formatAdminMinorUnits(r.contribution_amount, r.currency_code ?? "NPR")}
|
||||
</TableCell>
|
||||
<TableCell className="text-xs text-muted-foreground whitespace-nowrap">
|
||||
{formatDt(r.created_at)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
{contribs ? (
|
||||
<AdminListPaginationFooter
|
||||
selectId="jk-contrib-per"
|
||||
total={contribs.meta.total}
|
||||
page={contribs.meta.current_page}
|
||||
lastPage={contribs.meta.last_page}
|
||||
perPage={contribs.meta.per_page}
|
||||
loading={loadingC}
|
||||
onPerPageChange={(n) => {
|
||||
setCPer(n);
|
||||
setCPage(1);
|
||||
}}
|
||||
onPageChange={setCPage}
|
||||
/>
|
||||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</ModuleScaffold>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user