refactor(admin, draws, settlement): unify admin datetime display and tighten wallet write permission
This commit is contained in:
@@ -47,11 +47,8 @@ export const PRD_RISK_VIEW = "prd.risk.view" as const;
|
|||||||
export const PRD_RISK_MANAGE = "prd.risk.manage" as const;
|
export const PRD_RISK_MANAGE = "prd.risk.manage" as const;
|
||||||
export const PRD_ODDS_VIEW = "prd.odds.view" as const;
|
export const PRD_ODDS_VIEW = "prd.odds.view" as const;
|
||||||
|
|
||||||
/** 钱包补单/冲正(冲正 + 手工处理) */
|
/** 钱包补单/冲正(冲正、补入账、手工结案等会影响资金状态的动作) */
|
||||||
export const PRD_WALLET_WRITE_ANY = [
|
export const PRD_WALLET_WRITE_ANY = [PRD_WALLET_ADJUST_MANAGE] as const;
|
||||||
PRD_WALLET_ADJUST_MANAGE,
|
|
||||||
PRD_WALLET_RECONCILE_MANAGE,
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
/** 玩家列表页(与侧栏 requiredAny 一致) */
|
/** 玩家列表页(与侧栏 requiredAny 一致) */
|
||||||
export const PRD_PLAYERS_ACCESS_ANY = [
|
export const PRD_PLAYERS_ACCESS_ANY = [
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
|
|
||||||
import { getAdminDashboard } from "@/api/admin-dashboard";
|
import { getAdminDashboard } from "@/api/admin-dashboard";
|
||||||
import { useAsyncEffect } from "@/hooks/use-async-effect";
|
import { useAsyncEffect } from "@/hooks/use-async-effect";
|
||||||
|
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
||||||
import { useTranslationRef } from "@/hooks/use-translation-ref";
|
import { useTranslationRef } from "@/hooks/use-translation-ref";
|
||||||
import { useCachedPlayTypeOptions } from "@/hooks/use-cached-play-type-options";
|
import { useCachedPlayTypeOptions } from "@/hooks/use-cached-play-type-options";
|
||||||
import { useAdminCurrencyCatalog } from "@/hooks/use-admin-currency-catalog";
|
import { useAdminCurrencyCatalog } from "@/hooks/use-admin-currency-catalog";
|
||||||
@@ -50,6 +51,7 @@ import { LotteryApiBizError } from "@/types/api/errors";
|
|||||||
export function AgentDashboardConsole(): ReactElement {
|
export function AgentDashboardConsole(): ReactElement {
|
||||||
const { t, i18n } = useTranslation(["dashboard", "common", "agents"]);
|
const { t, i18n } = useTranslation(["dashboard", "common", "agents"]);
|
||||||
const tRef = useTranslationRef(["dashboard", "common"]);
|
const tRef = useTranslationRef(["dashboard", "common"]);
|
||||||
|
const formatDt = useAdminDateTimeFormatter();
|
||||||
const profile = useAdminProfile();
|
const profile = useAdminProfile();
|
||||||
const agent = profile?.agent ?? null;
|
const agent = profile?.agent ?? null;
|
||||||
const permissions = useMemo(() => profile?.permissions ?? [], [profile?.permissions]);
|
const permissions = useMemo(() => profile?.permissions ?? [], [profile?.permissions]);
|
||||||
@@ -252,7 +254,7 @@ export function AgentDashboardConsole(): ReactElement {
|
|||||||
<span>{t("agent.settlementCycle", { cycle: overview.settlement_cycle })}</span>
|
<span>{t("agent.settlementCycle", { cycle: overview.settlement_cycle })}</span>
|
||||||
<span>
|
<span>
|
||||||
{overview.latest_bet_at
|
{overview.latest_bet_at
|
||||||
? t("agent.latestBetAt", { time: new Date(overview.latest_bet_at).toLocaleString() })
|
? t("agent.latestBetAt", { time: formatDt(overview.latest_bet_at) })
|
||||||
: t("agent.noBetToday")}
|
: t("agent.noBetToday")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import { buttonVariants } from "@/components/ui/button";
|
|||||||
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
import { formatAdminInstantInTimeZone } from "@/lib/admin-datetime";
|
||||||
|
import { getAdminRequestLocale } from "@/lib/admin-locale";
|
||||||
|
import { LOTTERY_SCHEDULE_TIMEZONE } from "@/lib/lottery-schedule-timezone";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import type { DrawCurrentSnapshot } from "@/types/api/public-draw";
|
import type { DrawCurrentSnapshot } from "@/types/api/public-draw";
|
||||||
|
|
||||||
@@ -62,7 +64,11 @@ export function DashboardCurrentDrawCard({
|
|||||||
loading = false,
|
loading = false,
|
||||||
}: DashboardCurrentDrawCardProps): ReactElement {
|
}: DashboardCurrentDrawCardProps): ReactElement {
|
||||||
const { t } = useTranslation(["dashboard", "draws"]);
|
const { t } = useTranslation(["dashboard", "draws"]);
|
||||||
const formatDt = useAdminDateTimeFormatter();
|
const formatDt = (iso: string | null | undefined): string =>
|
||||||
|
formatAdminInstantInTimeZone(iso, {
|
||||||
|
locale: getAdminRequestLocale(),
|
||||||
|
timeZone: LOTTERY_SCHEDULE_TIMEZONE,
|
||||||
|
});
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export function DrawCreateDialog({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{t("createDraw.title")}</DialogTitle>
|
<DialogTitle>{t("createDraw.title")}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{t("createDraw.description", { tz: "Local" })}
|
{t("createDraw.description", { tz: scheduleTimezone ?? LOTTERY_SCHEDULE_TIMEZONE })}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
||||||
import { AdminLoadingState } from "@/components/admin/admin-loading-state";
|
import { AdminLoadingState } from "@/components/admin/admin-loading-state";
|
||||||
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
import { formatAdminInstantInTimeZone } from "@/lib/admin-datetime";
|
||||||
|
import { getAdminRequestLocale } from "@/lib/admin-locale";
|
||||||
|
import { LOTTERY_SCHEDULE_TIMEZONE } from "@/lib/lottery-schedule-timezone";
|
||||||
import { useConfirmAction } from "@/hooks/use-confirm-action";
|
import { useConfirmAction } from "@/hooks/use-confirm-action";
|
||||||
import { LotteryApiBizError } from "@/types/api/errors";
|
import { LotteryApiBizError } from "@/types/api/errors";
|
||||||
import type { AdminDrawFinanceSummaryData } from "@/types/api/admin-draw-finance";
|
import type { AdminDrawFinanceSummaryData } from "@/types/api/admin-draw-finance";
|
||||||
@@ -46,7 +48,14 @@ type ScheduleStep = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function ScheduleTimeline({ steps }: { steps: ScheduleStep[] }) {
|
function ScheduleTimeline({ steps }: { steps: ScheduleStep[] }) {
|
||||||
const formatDt = useAdminDateTimeFormatter();
|
const formatDt = useCallback(
|
||||||
|
(iso: string | null | undefined) =>
|
||||||
|
formatAdminInstantInTimeZone(iso, {
|
||||||
|
locale: getAdminRequestLocale(),
|
||||||
|
timeZone: LOTTERY_SCHEDULE_TIMEZONE,
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ol className="grid gap-3 sm:grid-cols-3">
|
<ol className="grid gap-3 sm:grid-cols-3">
|
||||||
@@ -112,7 +121,7 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [idNum]);
|
}, [idNum, tRef]);
|
||||||
|
|
||||||
async function runAction(name: string, action: () => Promise<unknown>): Promise<void> {
|
async function runAction(name: string, action: () => Promise<unknown>): Promise<void> {
|
||||||
if (!Number.isFinite(idNum)) return;
|
if (!Number.isFinite(idNum)) return;
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ import {
|
|||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { formatAdminInstant } from "@/lib/admin-datetime";
|
import { formatAdminInstantInTimeZone } from "@/lib/admin-datetime";
|
||||||
import { getAdminRequestLocale } from "@/lib/admin-locale";
|
import { getAdminRequestLocale } from "@/lib/admin-locale";
|
||||||
|
import { LOTTERY_SCHEDULE_TIMEZONE } from "@/lib/lottery-schedule-timezone";
|
||||||
import { LotteryApiBizError } from "@/types/api/errors";
|
import { LotteryApiBizError } from "@/types/api/errors";
|
||||||
import type { AdminDrawListItem } from "@/types/api/admin-draws";
|
import type { AdminDrawListItem } from "@/types/api/admin-draws";
|
||||||
|
|
||||||
@@ -30,9 +31,10 @@ type DrawEditDialogProps = {
|
|||||||
onSaved: () => void | Promise<void>;
|
onSaved: () => void | Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
function isoToScheduleValue(iso: string | null): string {
|
function isoToScheduleValue(iso: string | null, timeZone: string): string {
|
||||||
return formatAdminInstant(iso, {
|
return formatAdminInstantInTimeZone(iso, {
|
||||||
locale: getAdminRequestLocale(),
|
locale: getAdminRequestLocale(),
|
||||||
|
timeZone,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,12 +57,13 @@ export function DrawEditDialog({
|
|||||||
if (!open || draw == null) {
|
if (!open || draw == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setDrawTime(isoToScheduleValue(draw.draw_time));
|
const tz = scheduleTimezone ?? LOTTERY_SCHEDULE_TIMEZONE;
|
||||||
setCloseTime(isoToScheduleValue(draw.close_time));
|
setDrawTime(isoToScheduleValue(draw.draw_time, tz));
|
||||||
setStartTime(isoToScheduleValue(draw.start_time));
|
setCloseTime(isoToScheduleValue(draw.close_time, tz));
|
||||||
|
setStartTime(isoToScheduleValue(draw.start_time, tz));
|
||||||
setDrawNo(draw.draw_no);
|
setDrawNo(draw.draw_no);
|
||||||
});
|
});
|
||||||
}, [open, draw]);
|
}, [open, draw, scheduleTimezone]);
|
||||||
|
|
||||||
async function submit(): Promise<void> {
|
async function submit(): Promise<void> {
|
||||||
if (draw == null) {
|
if (draw == null) {
|
||||||
@@ -95,7 +98,7 @@ export function DrawEditDialog({
|
|||||||
<DialogTitle>{t("editDraw.title")}</DialogTitle>
|
<DialogTitle>{t("editDraw.title")}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{t("editDraw.description", {
|
{t("editDraw.description", {
|
||||||
tz: "Local",
|
tz: scheduleTimezone ?? LOTTERY_SCHEDULE_TIMEZONE,
|
||||||
drawNo: draw?.draw_no ?? "",
|
drawNo: draw?.draw_no ?? "",
|
||||||
})}
|
})}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ import {
|
|||||||
postAdminCancelDraw,
|
postAdminCancelDraw,
|
||||||
postAdminGenerateDrawPlan,
|
postAdminGenerateDrawPlan,
|
||||||
} from "@/api/admin-draws";
|
} from "@/api/admin-draws";
|
||||||
import { formatAdminInstant } from "@/lib/admin-datetime";
|
import { formatAdminInstantInTimeZone } from "@/lib/admin-datetime";
|
||||||
import { getAdminRequestLocale } from "@/lib/admin-locale";
|
import { getAdminRequestLocale } from "@/lib/admin-locale";
|
||||||
|
import { LOTTERY_SCHEDULE_TIMEZONE } from "@/lib/lottery-schedule-timezone";
|
||||||
import { AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
|
import { AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
|
||||||
import { AdminNoResourceState, AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state";
|
import { AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state";
|
||||||
import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu";
|
import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
|
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
|
||||||
@@ -52,7 +53,6 @@ import {
|
|||||||
import { formatAdminMinorUnits } from "@/lib/money";
|
import { formatAdminMinorUnits } from "@/lib/money";
|
||||||
import { useConfirmAction } from "@/hooks/use-confirm-action";
|
import { useConfirmAction } from "@/hooks/use-confirm-action";
|
||||||
import { useExportLabels } from "@/hooks/use-export-labels";
|
import { useExportLabels } from "@/hooks/use-export-labels";
|
||||||
import { adminHasAnyPermission } from "@/lib/admin-permissions";
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useAdminProfile } from "@/stores/admin-session";
|
import { useAdminProfile } from "@/stores/admin-session";
|
||||||
import { LotteryApiBizError } from "@/types/api/errors";
|
import { LotteryApiBizError } from "@/types/api/errors";
|
||||||
@@ -60,7 +60,6 @@ import type { AdminDrawListData, AdminDrawListItem } from "@/types/api/admin-dra
|
|||||||
|
|
||||||
import { drawStatusLabel } from "./draw-display";
|
import { drawStatusLabel } from "./draw-display";
|
||||||
import { canManageDrawResults, canViewDrawFinance } from "@/lib/draw-access";
|
import { canManageDrawResults, canViewDrawFinance } from "@/lib/draw-access";
|
||||||
import { PRD_DRAW_RESULT_MANAGE } from "./draw-prd";
|
|
||||||
import { DrawStatusBadge } from "./draw-status-badge";
|
import { DrawStatusBadge } from "./draw-status-badge";
|
||||||
|
|
||||||
/** 下拉「不限」;请求时不传 status */
|
/** 下拉「不限」;请求时不传 status */
|
||||||
@@ -102,8 +101,9 @@ export function DrawsIndexConsole() {
|
|||||||
const [data, setData] = useState<AdminDrawListData | null>(null);
|
const [data, setData] = useState<AdminDrawListData | null>(null);
|
||||||
const formatDt = useCallback(
|
const formatDt = useCallback(
|
||||||
(iso: string | null | undefined) =>
|
(iso: string | null | undefined) =>
|
||||||
formatAdminInstant(iso, {
|
formatAdminInstantInTimeZone(iso, {
|
||||||
locale: getAdminRequestLocale(),
|
locale: getAdminRequestLocale(),
|
||||||
|
timeZone: LOTTERY_SCHEDULE_TIMEZONE,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@@ -156,7 +156,7 @@ export function DrawsIndexConsole() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [page, perPage, appliedDrawNo, appliedStatus, appliedAgentNodeId]);
|
}, [page, perPage, appliedDrawNo, appliedStatus, appliedAgentNodeId, tRef]);
|
||||||
|
|
||||||
async function generatePlan(): Promise<void> {
|
async function generatePlan(): Promise<void> {
|
||||||
setGenerating(true);
|
setGenerating(true);
|
||||||
@@ -559,6 +559,7 @@ export function DrawsIndexConsole() {
|
|||||||
<DrawCreateDialog
|
<DrawCreateDialog
|
||||||
open={createOpen}
|
open={createOpen}
|
||||||
onOpenChange={setCreateOpen}
|
onOpenChange={setCreateOpen}
|
||||||
|
scheduleTimezone={LOTTERY_SCHEDULE_TIMEZONE}
|
||||||
onCreated={load}
|
onCreated={load}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -571,6 +572,7 @@ export function DrawsIndexConsole() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
draw={editDraw}
|
draw={editDraw}
|
||||||
|
scheduleTimezone={LOTTERY_SCHEDULE_TIMEZONE}
|
||||||
onSaved={load}
|
onSaved={load}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import type { SettlementAdjustmentRow } from "@/api/admin-agent-settlement";
|
import type { SettlementAdjustmentRow } from "@/api/admin-agent-settlement";
|
||||||
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
||||||
import { AdminLoadingState } from "@/components/admin/admin-loading-state";
|
import { AdminLoadingState } from "@/components/admin/admin-loading-state";
|
||||||
|
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
||||||
import { formatSettlementPeriodSpan } from "@/lib/agent-settlement-period-range";
|
import { formatSettlementPeriodSpan } from "@/lib/agent-settlement-period-range";
|
||||||
import { formatDashboardMoneyMinor } from "@/modules/dashboard/use-dashboard-analytics";
|
import { formatDashboardMoneyMinor } from "@/modules/dashboard/use-dashboard-analytics";
|
||||||
import {
|
import {
|
||||||
@@ -30,6 +31,7 @@ export function SettlementAdjustmentsTable({
|
|||||||
onOpenBill,
|
onOpenBill,
|
||||||
}: SettlementAdjustmentsTableProps): React.ReactElement {
|
}: SettlementAdjustmentsTableProps): React.ReactElement {
|
||||||
const { t } = useTranslation(["settlementCenter", "common"]);
|
const { t } = useTranslation(["settlementCenter", "common"]);
|
||||||
|
const formatTs = useAdminDateTimeFormatter();
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <AdminLoadingState />;
|
return <AdminLoadingState />;
|
||||||
@@ -71,7 +73,7 @@ export function SettlementAdjustmentsTable({
|
|||||||
{formatDashboardMoneyMinor(row.amount, currencyCode)}
|
{formatDashboardMoneyMinor(row.amount, currencyCode)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="max-w-[200px] truncate text-sm">{row.reason ?? "—"}</TableCell>
|
<TableCell className="max-w-[200px] truncate text-sm">{row.reason ?? "—"}</TableCell>
|
||||||
<TableCell className="text-xs text-muted-foreground">{row.created_at ?? "—"}</TableCell>
|
<TableCell className="text-xs text-muted-foreground">{formatTs(row.created_at)}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{row.original_bill_id != null ? (
|
{row.original_bill_id != null ? (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import type { SettlementPaymentRow } from "@/api/admin-agent-settlement";
|
import type { SettlementPaymentRow } from "@/api/admin-agent-settlement";
|
||||||
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
||||||
import { AdminLoadingState } from "@/components/admin/admin-loading-state";
|
import { AdminLoadingState } from "@/components/admin/admin-loading-state";
|
||||||
|
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
||||||
import { formatSettlementPeriodSpan } from "@/lib/agent-settlement-period-range";
|
import { formatSettlementPeriodSpan } from "@/lib/agent-settlement-period-range";
|
||||||
import { formatDashboardMoneyMinor } from "@/modules/dashboard/use-dashboard-analytics";
|
import { formatDashboardMoneyMinor } from "@/modules/dashboard/use-dashboard-analytics";
|
||||||
import { settlementBillTypeLabel } from "@/modules/settlement/settlement-status-label";
|
import { settlementBillTypeLabel } from "@/modules/settlement/settlement-status-label";
|
||||||
@@ -31,6 +32,7 @@ export function SettlementPaymentsTable({
|
|||||||
onOpenBill,
|
onOpenBill,
|
||||||
}: SettlementPaymentsTableProps): React.ReactElement {
|
}: SettlementPaymentsTableProps): React.ReactElement {
|
||||||
const { t } = useTranslation(["settlementCenter", "agents", "common"]);
|
const { t } = useTranslation(["settlementCenter", "agents", "common"]);
|
||||||
|
const formatTs = useAdminDateTimeFormatter();
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <AdminLoadingState />;
|
return <AdminLoadingState />;
|
||||||
@@ -77,9 +79,13 @@ export function SettlementPaymentsTable({
|
|||||||
{formatDashboardMoneyMinor(row.amount, currencyCode)}
|
{formatDashboardMoneyMinor(row.amount, currencyCode)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{row.method ?? "—"}</TableCell>
|
<TableCell>{row.method ?? "—"}</TableCell>
|
||||||
<TableCell>{row.status}</TableCell>
|
<TableCell>
|
||||||
|
{t(`paymentStatus.${row.status}`, {
|
||||||
|
defaultValue: row.status === "confirmed" ? "已确认" : row.status,
|
||||||
|
})}
|
||||||
|
</TableCell>
|
||||||
<TableCell className="text-xs text-muted-foreground">
|
<TableCell className="text-xs text-muted-foreground">
|
||||||
{row.confirmed_at ?? row.created_at ?? "—"}
|
{formatTs(row.confirmed_at ?? row.created_at)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -414,11 +414,11 @@ export function TransferOrdersPanel(): React.ReactElement {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [page, perPage, applied]);
|
}, [page, perPage, applied, tRef]);
|
||||||
|
|
||||||
useAsyncEffect(() => {
|
useAsyncEffect(() => {
|
||||||
void load();
|
void load();
|
||||||
}, [page, perPage, applied]);
|
}, [page, perPage, applied, tRef]);
|
||||||
|
|
||||||
const runSearch = () => {
|
const runSearch = () => {
|
||||||
setApplied({ ...draft });
|
setApplied({ ...draft });
|
||||||
@@ -696,7 +696,7 @@ export function WalletTxnsPanel(): React.ReactElement {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [page, perPage, applied]);
|
}, [page, perPage, applied, tRef]);
|
||||||
|
|
||||||
useAsyncEffect(() => {
|
useAsyncEffect(() => {
|
||||||
void load();
|
void load();
|
||||||
@@ -971,7 +971,7 @@ export function PlayerWalletPanel(): React.ReactElement {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [playerId]);
|
}, [playerId, tRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user