feat(admin, i18n): enhance reports, draws, config, and player workflows

This commit is contained in:
2026-06-08 17:41:55 +08:00
parent af982bb9f7
commit 7e65c53732
55 changed files with 1986 additions and 804 deletions

View File

@@ -23,13 +23,13 @@ import { AdminPlayerIdentityCells, AdminPlayerIdentityHeads } from "@/components
import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu";
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
import { Button, buttonVariants } from "@/components/ui/button";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state";
import { AdminLoadingState, AdminLoadingInline, AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
import { AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
import {
Select,
SelectContent,
@@ -55,7 +55,6 @@ import { useExportLabels } from "@/hooks/use-export-labels";
import { PlayerLedgerSourceBadge } from "@/components/admin/player-funding-badges";
import { formatAdminMinorUnits } from "@/lib/money";
import { creditLedgerReasonLabel } from "@/modules/settlement/settlement-status-label";
import { cn } from "@/lib/utils";
import { LotteryApiBizError } from "@/types/api/errors";
import type {
AdminPlayerWalletsData,
@@ -323,13 +322,23 @@ export function TransferOrdersPanel(): React.ReactElement {
const exportLabels = useExportLabels("walletTransferOrders");
useAdminCurrencyCatalog();
const formatTs = useAdminDateTimeFormatter();
const searchParams = useSearchParams();
const playerIdFromUrl = (searchParams.get("player_id") ?? "").trim();
const transferNoFromUrl = (searchParams.get("transfer_no") ?? "").trim();
const externalRefNoFromUrl = (searchParams.get("external_ref_no") ?? "").trim();
const [data, setData] = useState<AdminTransferOrderListData | null>(null);
const [loading, setLoading] = useState(true);
const [err, setErr] = useState<string | null>(null);
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(10);
const [draft, setDraft] = useState<TransferFilters>(emptyTransferFilters);
const [applied, setApplied] = useState<TransferFilters>(emptyTransferFilters);
const initialTransferFilters: TransferFilters = {
...emptyTransferFilters,
playerId: playerIdFromUrl,
transferNo: transferNoFromUrl,
externalRefNo: externalRefNoFromUrl,
};
const [draft, setDraft] = useState<TransferFilters>(initialTransferFilters);
const [applied, setApplied] = useState<TransferFilters>(initialTransferFilters);
const [actionLoading, setActionLoading] = useState<Set<string>>(new Set());
const doAction = async (
@@ -645,10 +654,18 @@ export function WalletTxnsPanel(): React.ReactElement {
const [err, setErr] = useState<string | null>(null);
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(10);
const [draft, setDraft] = useState<TxnFilters>(emptyTxnFilters);
const [applied, setApplied] = useState<TxnFilters>(emptyTxnFilters);
const searchParams = useSearchParams();
const playerIdFromUrl = (searchParams.get("player_id") ?? "").trim();
const txnNoFromUrl = (searchParams.get("txn_no") ?? "").trim();
const externalRefNoFromUrl = (searchParams.get("external_ref_no") ?? "").trim();
const initialTxnFilters: TxnFilters = {
...emptyTxnFilters,
playerId: playerIdFromUrl,
txnNo: txnNoFromUrl,
externalRefNo: externalRefNoFromUrl,
};
const [draft, setDraft] = useState<TxnFilters>(initialTxnFilters);
const [applied, setApplied] = useState<TxnFilters>(initialTxnFilters);
const load = useCallback(async () => {
setLoading(true);
@@ -685,15 +702,6 @@ export function WalletTxnsPanel(): React.ReactElement {
void load();
}, [page, perPage, applied]);
useAsyncEffect(() => {
if (!playerIdFromUrl) {
return;
}
setDraft((d) => ({ ...d, playerId: playerIdFromUrl }));
setApplied((d) => ({ ...d, playerId: playerIdFromUrl }));
setPage(1);
}, [playerIdFromUrl]);
const runSearch = () => {
setApplied({ ...draft });
setPage(1);