feat(api, agents, i18n): enhance settlement features and multi-language support
Added new types and API functions for settlement period summaries and credit ledgers, improving the management of agent settlements. Updated the admin console to reflect these changes, enhancing user experience with better navigation and data presentation. Additionally, expanded multi-language support by incorporating new translations in English, Nepali, and Chinese for settlement-related terms, ensuring consistency across the platform.
This commit is contained in:
169
src/modules/settlement/settlement-periods-table.tsx
Normal file
169
src/modules/settlement/settlement-periods-table.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
"use client";
|
||||
|
||||
import { Eye, Lock } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import type { SettlementPeriodRow } from "@/api/admin-agent-settlement";
|
||||
import { AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state";
|
||||
import { AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
|
||||
import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu";
|
||||
import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
|
||||
import { formatSettlementPeriodSpan } from "@/lib/agent-settlement-period-range";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { formatDashboardMoneyMinor } from "@/modules/dashboard/use-dashboard-analytics";
|
||||
import { signedSettlementMoneyClass } from "@/modules/settlement/settlement-signed-money";
|
||||
import { settlementPeriodStatusLabel } from "@/modules/settlement/settlement-status-label";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
|
||||
const COL_SPAN = 9;
|
||||
|
||||
type SettlementPeriodsTableProps = {
|
||||
periods: SettlementPeriodRow[];
|
||||
loading?: boolean;
|
||||
canManage: boolean;
|
||||
busy?: boolean;
|
||||
currencyCode?: string;
|
||||
emptyMessage?: string;
|
||||
onViewDetail: (periodId: number) => void;
|
||||
onRequestClose: (row: SettlementPeriodRow) => void;
|
||||
};
|
||||
|
||||
export function SettlementPeriodsTable({
|
||||
periods,
|
||||
loading = false,
|
||||
canManage,
|
||||
busy = false,
|
||||
currencyCode = "NPR",
|
||||
emptyMessage,
|
||||
onViewDetail,
|
||||
onRequestClose,
|
||||
}: SettlementPeriodsTableProps): React.ReactElement {
|
||||
const { t } = useTranslation(["settlementCenter", "agents", "common"]);
|
||||
|
||||
const billCount = (row: SettlementPeriodRow): number =>
|
||||
(row.summary?.player_bills ?? 0)
|
||||
+ (row.summary?.agent_bills ?? 0)
|
||||
+ (row.summary?.adjustment_bills ?? 0);
|
||||
|
||||
const winLossScope = periods.find((row) => row.pipeline?.win_loss_scope)?.pipeline?.win_loss_scope
|
||||
?? "platform";
|
||||
const winLossLabel =
|
||||
winLossScope === "agent"
|
||||
? t("periodTable.agentWinLoss", { defaultValue: "代理输赢" })
|
||||
: t("periodTable.platformWinLoss", { defaultValue: "平台输赢" });
|
||||
|
||||
return (
|
||||
<div className="admin-table-shell overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>{t("periodTable.range", { defaultValue: "账期" })}</TableHead>
|
||||
<TableHead>{t("columns.status", { defaultValue: "状态" })}</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("periodTable.shareLedger", { defaultValue: "占成流水" })}
|
||||
</TableHead>
|
||||
<TableHead className="text-right">{winLossLabel}</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("agents:settlementReports.summary.billCount", { defaultValue: "账单数" })}
|
||||
</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("periodTable.pending", { defaultValue: "待确认" })}
|
||||
</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("periodTable.awaiting", { defaultValue: "待收付" })}
|
||||
</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("agents:settlementReports.summary.totalUnpaid", { defaultValue: "未结合计" })}
|
||||
</TableHead>
|
||||
<TableHead className="sticky right-0 z-10 w-36 bg-muted text-center shadow-[-1px_0_0_rgba(203,213,225,0.7)]">
|
||||
{t("common:table.actions", { defaultValue: "操作" })}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{loading ? (
|
||||
<AdminTableLoadingRow colSpan={COL_SPAN} />
|
||||
) : periods.length === 0 ? (
|
||||
<AdminTableNoResourceRow colSpan={COL_SPAN} message={emptyMessage} />
|
||||
) : (
|
||||
periods.map((row) => {
|
||||
const canCloseRow = canManage && row.status === "open";
|
||||
|
||||
return (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell className="font-medium whitespace-nowrap">
|
||||
{formatSettlementPeriodSpan(row.period_start, row.period_end)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<AdminStatusBadge status={row.status}>
|
||||
{settlementPeriodStatusLabel(row.status, t)}
|
||||
</AdminStatusBadge>
|
||||
</TableCell>
|
||||
<TableCell className="text-right tabular-nums">
|
||||
{row.pipeline?.share_ledger_count ?? "—"}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={cn(
|
||||
"text-right tabular-nums whitespace-nowrap",
|
||||
row.pipeline?.game_win_loss_total != null
|
||||
? signedSettlementMoneyClass(row.pipeline.game_win_loss_total, true)
|
||||
: undefined,
|
||||
)}
|
||||
>
|
||||
{row.pipeline?.game_win_loss_total != null
|
||||
? formatDashboardMoneyMinor(row.pipeline.game_win_loss_total, currencyCode)
|
||||
: "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-right tabular-nums">
|
||||
{billCount(row) > 0 ? billCount(row) : "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-right tabular-nums">
|
||||
{row.summary?.pending_confirm ?? "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-right tabular-nums">
|
||||
{row.summary?.awaiting_payment ?? "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-right tabular-nums whitespace-nowrap">
|
||||
{(row.summary?.total_unpaid ?? 0) > 0
|
||||
? formatDashboardMoneyMinor(row.summary?.total_unpaid ?? 0, currencyCode)
|
||||
: "—"}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className="sticky right-0 z-10 bg-card text-center shadow-[-1px_0_0_rgba(203,213,225,0.7)]"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<AdminRowActionsMenu
|
||||
busy={busy}
|
||||
actions={[
|
||||
{
|
||||
key: "detail",
|
||||
label: t("periodTable.viewDetail", { defaultValue: "查看详情" }),
|
||||
icon: Eye,
|
||||
onClick: () => onViewDetail(row.id),
|
||||
},
|
||||
{
|
||||
key: "close",
|
||||
label: t("periodTable.close", { defaultValue: "关账" }),
|
||||
icon: Lock,
|
||||
hidden: !canCloseRow,
|
||||
onClick: () => onRequestClose(row),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user