refactor: 优化配置与奖池页面多语言编辑及管理端列表布局
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type React from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -32,6 +33,51 @@ 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 (
|
||||
<div className="admin-table-shell">
|
||||
<div className="admin-table-toolbar flex items-center justify-between gap-3">
|
||||
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
||||
<AdminTableExportButton
|
||||
tableId={tableId}
|
||||
filename={exportFilename}
|
||||
sheetName={exportSheetName}
|
||||
/>
|
||||
</div>
|
||||
{loading && !hasData ? (
|
||||
<p className="px-4 py-6 text-sm text-muted-foreground">{t("states.loading")}</p>
|
||||
) : (
|
||||
<div className={TABLE_IN_SHELL_CLASS}>{children}</div>
|
||||
)}
|
||||
{footer ? <div className="px-4 pb-4">{footer}</div> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function JackpotRecordsConsole({ embedded = false }: JackpotRecordsConsoleProps) {
|
||||
const { t } = useTranslation(["jackpot", "common"]);
|
||||
const payoutExport = useExportLabels("jackpotPayouts");
|
||||
@@ -147,155 +193,152 @@ export function JackpotRecordsConsole({ embedded = false }: JackpotRecordsConsol
|
||||
</Card>
|
||||
);
|
||||
|
||||
const payoutHeader = embedded ? (
|
||||
<p className="mb-3 text-sm font-semibold">{t("payoutRecords")}</p>
|
||||
) : (
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">{t("payoutRecords")}</CardTitle>
|
||||
</CardHeader>
|
||||
const payoutFooter = 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;
|
||||
|
||||
const contributionFooter = 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;
|
||||
|
||||
const payoutTable = (
|
||||
<JackpotRecordTableSection
|
||||
title={t("payoutRecords")}
|
||||
tableId="jackpot-payout-table"
|
||||
exportFilename={payoutExport.filename}
|
||||
exportSheetName={payoutExport.sheetName}
|
||||
loading={loadingP}
|
||||
hasData={payouts != null}
|
||||
footer={payoutFooter}
|
||||
>
|
||||
<Table id="jackpot-payout-table" className="table-fixed">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-14">{t("table.id", { ns: "common" })}</TableHead>
|
||||
<TableHead className="w-[11rem]">{t("drawNo")}</TableHead>
|
||||
<TableHead className="w-28">{t("trigger")}</TableHead>
|
||||
<TableHead className="w-32 text-right">{t("payoutAmount")}</TableHead>
|
||||
<TableHead className="w-24 text-right">{t("winnerCount")}</TableHead>
|
||||
<TableHead className="w-[11rem]">{t("time")}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{(payouts?.items ?? []).length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className="text-muted-foreground">
|
||||
{t("states.noData", { ns: "common" })}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
(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">{triggerTypeText(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>
|
||||
</JackpotRecordTableSection>
|
||||
);
|
||||
|
||||
const contributionHeader = embedded ? (
|
||||
<p className="mb-3 text-sm font-semibold">{t("contributionRecords")}</p>
|
||||
) : (
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">{t("contributionRecords")}</CardTitle>
|
||||
</CardHeader>
|
||||
const contributionTable = (
|
||||
<JackpotRecordTableSection
|
||||
title={t("contributionRecords")}
|
||||
tableId="jackpot-contribution-table"
|
||||
exportFilename={contributionExport.filename}
|
||||
exportSheetName={contributionExport.sheetName}
|
||||
loading={loadingC}
|
||||
hasData={contribs != null}
|
||||
footer={contributionFooter}
|
||||
>
|
||||
<Table id="jackpot-contribution-table" className="table-fixed">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-14">{t("table.id", { ns: "common" })}</TableHead>
|
||||
<TableHead className="w-[11rem]">{t("drawNo")}</TableHead>
|
||||
<TableHead className="w-[11rem]">{t("ticketNo")}</TableHead>
|
||||
<TableHead>{t("player")}</TableHead>
|
||||
<TableHead className="w-32 text-right">{t("contributionAmount")}</TableHead>
|
||||
<TableHead className="w-[11rem]">{t("time")}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{(contribs?.items ?? []).length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className="text-muted-foreground">
|
||||
{t("states.noData", { ns: "common" })}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
(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-[12rem] 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>
|
||||
</JackpotRecordTableSection>
|
||||
);
|
||||
|
||||
const content = (
|
||||
<>
|
||||
{filterBlock}
|
||||
{err ? <p className="text-destructive text-sm">{err}</p> : null}
|
||||
|
||||
{err ? <p className="text-destructive mb-4 text-sm">{err}</p> : null}
|
||||
|
||||
<Card className={embedded ? "mb-6 border-border/60 shadow-none" : "mb-8"}>
|
||||
{!embedded ? payoutHeader : null}
|
||||
<CardContent className={embedded ? "p-0" : undefined}>
|
||||
{embedded ? payoutHeader : null}
|
||||
{loadingP && !payouts ? (
|
||||
<p className="text-muted-foreground text-sm">{t("states.loading", { ns: "common" })}</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="admin-table-toolbar">
|
||||
<AdminTableExportButton
|
||||
tableId="jackpot-payout-table"
|
||||
filename={payoutExport.filename}
|
||||
sheetName={payoutExport.sheetName}
|
||||
/>
|
||||
</div>
|
||||
<Table id="jackpot-payout-table">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>{t("table.id", { ns: "common" })}</TableHead>
|
||||
<TableHead>{t("drawNo")}</TableHead>
|
||||
<TableHead>{t("trigger")}</TableHead>
|
||||
<TableHead className="text-right">{t("payoutAmount")}</TableHead>
|
||||
<TableHead className="text-right">{t("winnerCount")}</TableHead>
|
||||
<TableHead>{t("time")}</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">{triggerTypeText(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 className={embedded ? "border-border/60 shadow-none" : undefined}>
|
||||
{!embedded ? contributionHeader : null}
|
||||
<CardContent className={embedded ? "p-0" : undefined}>
|
||||
{embedded ? contributionHeader : null}
|
||||
{loadingC && !contribs ? (
|
||||
<p className="text-muted-foreground text-sm">{t("states.loading", { ns: "common" })}</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="admin-table-toolbar">
|
||||
<AdminTableExportButton
|
||||
tableId="jackpot-contribution-table"
|
||||
filename={contributionExport.filename}
|
||||
sheetName={contributionExport.sheetName}
|
||||
/>
|
||||
</div>
|
||||
<Table id="jackpot-contribution-table">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>{t("table.id", { ns: "common" })}</TableHead>
|
||||
<TableHead>{t("drawNo")}</TableHead>
|
||||
<TableHead>{t("ticketNo")}</TableHead>
|
||||
<TableHead>{t("player")}</TableHead>
|
||||
<TableHead className="text-right">{t("contributionAmount")}</TableHead>
|
||||
<TableHead>{t("time")}</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>
|
||||
{embedded ? (
|
||||
<div className="space-y-8">
|
||||
{payoutTable}
|
||||
{contributionTable}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-8">
|
||||
{payoutTable}
|
||||
{contributionTable}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user