diff --git a/src/lib/admin-prd.ts b/src/lib/admin-prd.ts
index dd9b236..f46cb12 100644
--- a/src/lib/admin-prd.ts
+++ b/src/lib/admin-prd.ts
@@ -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_ODDS_VIEW = "prd.odds.view" as const;
-/** 钱包补单/冲正(冲正 + 手工处理) */
-export const PRD_WALLET_WRITE_ANY = [
- PRD_WALLET_ADJUST_MANAGE,
- PRD_WALLET_RECONCILE_MANAGE,
-] as const;
+/** 钱包补单/冲正(冲正、补入账、手工结案等会影响资金状态的动作) */
+export const PRD_WALLET_WRITE_ANY = [PRD_WALLET_ADJUST_MANAGE] as const;
/** 玩家列表页(与侧栏 requiredAny 一致) */
export const PRD_PLAYERS_ACCESS_ANY = [
diff --git a/src/modules/dashboard/agent-dashboard-console.tsx b/src/modules/dashboard/agent-dashboard-console.tsx
index 08124a2..d52a696 100644
--- a/src/modules/dashboard/agent-dashboard-console.tsx
+++ b/src/modules/dashboard/agent-dashboard-console.tsx
@@ -18,6 +18,7 @@ import {
import { getAdminDashboard } from "@/api/admin-dashboard";
import { useAsyncEffect } from "@/hooks/use-async-effect";
+import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
import { useTranslationRef } from "@/hooks/use-translation-ref";
import { useCachedPlayTypeOptions } from "@/hooks/use-cached-play-type-options";
import { useAdminCurrencyCatalog } from "@/hooks/use-admin-currency-catalog";
@@ -50,6 +51,7 @@ import { LotteryApiBizError } from "@/types/api/errors";
export function AgentDashboardConsole(): ReactElement {
const { t, i18n } = useTranslation(["dashboard", "common", "agents"]);
const tRef = useTranslationRef(["dashboard", "common"]);
+ const formatDt = useAdminDateTimeFormatter();
const profile = useAdminProfile();
const agent = profile?.agent ?? null;
const permissions = useMemo(() => profile?.permissions ?? [], [profile?.permissions]);
@@ -252,7 +254,7 @@ export function AgentDashboardConsole(): ReactElement {
{t("agent.settlementCycle", { cycle: overview.settlement_cycle })}
{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")}
diff --git a/src/modules/dashboard/dashboard-current-draw-card.tsx b/src/modules/dashboard/dashboard-current-draw-card.tsx
index ed0a8fe..9852bb9 100644
--- a/src/modules/dashboard/dashboard-current-draw-card.tsx
+++ b/src/modules/dashboard/dashboard-current-draw-card.tsx
@@ -9,7 +9,9 @@ import { buttonVariants } from "@/components/ui/button";
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
import { Card, CardContent } from "@/components/ui/card";
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 type { DrawCurrentSnapshot } from "@/types/api/public-draw";
@@ -62,7 +64,11 @@ export function DashboardCurrentDrawCard({
loading = false,
}: DashboardCurrentDrawCardProps): ReactElement {
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) {
return (
diff --git a/src/modules/draws/draw-create-dialog.tsx b/src/modules/draws/draw-create-dialog.tsx
index a18ce85..370877b 100644
--- a/src/modules/draws/draw-create-dialog.tsx
+++ b/src/modules/draws/draw-create-dialog.tsx
@@ -84,7 +84,7 @@ export function DrawCreateDialog({
{t("createDraw.title")}
- {t("createDraw.description", { tz: "Local" })}
+ {t("createDraw.description", { tz: scheduleTimezone ?? LOTTERY_SCHEDULE_TIMEZONE })}
diff --git a/src/modules/draws/draw-detail-console.tsx b/src/modules/draws/draw-detail-console.tsx
index b334c80..0986d5c 100644
--- a/src/modules/draws/draw-detail-console.tsx
+++ b/src/modules/draws/draw-detail-console.tsx
@@ -20,7 +20,9 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-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 { LotteryApiBizError } from "@/types/api/errors";
import type { AdminDrawFinanceSummaryData } from "@/types/api/admin-draw-finance";
@@ -46,7 +48,14 @@ type 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 (
@@ -112,7 +121,7 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
} finally {
setLoading(false);
}
- }, [idNum]);
+ }, [idNum, tRef]);
async function runAction(name: string, action: () => Promise): Promise {
if (!Number.isFinite(idNum)) return;
diff --git a/src/modules/draws/draw-edit-dialog.tsx b/src/modules/draws/draw-edit-dialog.tsx
index 02b7f1f..b690908 100644
--- a/src/modules/draws/draw-edit-dialog.tsx
+++ b/src/modules/draws/draw-edit-dialog.tsx
@@ -17,8 +17,9 @@ import {
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
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 { LOTTERY_SCHEDULE_TIMEZONE } from "@/lib/lottery-schedule-timezone";
import { LotteryApiBizError } from "@/types/api/errors";
import type { AdminDrawListItem } from "@/types/api/admin-draws";
@@ -30,9 +31,10 @@ type DrawEditDialogProps = {
onSaved: () => void | Promise;
};
-function isoToScheduleValue(iso: string | null): string {
- return formatAdminInstant(iso, {
+function isoToScheduleValue(iso: string | null, timeZone: string): string {
+ return formatAdminInstantInTimeZone(iso, {
locale: getAdminRequestLocale(),
+ timeZone,
});
}
@@ -55,12 +57,13 @@ export function DrawEditDialog({
if (!open || draw == null) {
return;
}
- setDrawTime(isoToScheduleValue(draw.draw_time));
- setCloseTime(isoToScheduleValue(draw.close_time));
- setStartTime(isoToScheduleValue(draw.start_time));
+ const tz = scheduleTimezone ?? LOTTERY_SCHEDULE_TIMEZONE;
+ setDrawTime(isoToScheduleValue(draw.draw_time, tz));
+ setCloseTime(isoToScheduleValue(draw.close_time, tz));
+ setStartTime(isoToScheduleValue(draw.start_time, tz));
setDrawNo(draw.draw_no);
});
- }, [open, draw]);
+ }, [open, draw, scheduleTimezone]);
async function submit(): Promise {
if (draw == null) {
@@ -95,7 +98,7 @@ export function DrawEditDialog({
{t("editDraw.title")}
{t("editDraw.description", {
- tz: "Local",
+ tz: scheduleTimezone ?? LOTTERY_SCHEDULE_TIMEZONE,
drawNo: draw?.draw_no ?? "",
})}
diff --git a/src/modules/draws/draws-index-console.tsx b/src/modules/draws/draws-index-console.tsx
index cd5fda9..82818b1 100644
--- a/src/modules/draws/draws-index-console.tsx
+++ b/src/modules/draws/draws-index-console.tsx
@@ -14,10 +14,11 @@ import {
postAdminCancelDraw,
postAdminGenerateDrawPlan,
} from "@/api/admin-draws";
-import { formatAdminInstant } from "@/lib/admin-datetime";
+import { formatAdminInstantInTimeZone } from "@/lib/admin-datetime";
import { getAdminRequestLocale } from "@/lib/admin-locale";
+import { LOTTERY_SCHEDULE_TIMEZONE } from "@/lib/lottery-schedule-timezone";
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 { Button } from "@/components/ui/button";
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
@@ -52,7 +53,6 @@ import {
import { formatAdminMinorUnits } from "@/lib/money";
import { useConfirmAction } from "@/hooks/use-confirm-action";
import { useExportLabels } from "@/hooks/use-export-labels";
-import { adminHasAnyPermission } from "@/lib/admin-permissions";
import { cn } from "@/lib/utils";
import { useAdminProfile } from "@/stores/admin-session";
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 { canManageDrawResults, canViewDrawFinance } from "@/lib/draw-access";
-import { PRD_DRAW_RESULT_MANAGE } from "./draw-prd";
import { DrawStatusBadge } from "./draw-status-badge";
/** 下拉「不限」;请求时不传 status */
@@ -102,8 +101,9 @@ export function DrawsIndexConsole() {
const [data, setData] = useState(null);
const formatDt = useCallback(
(iso: string | null | undefined) =>
- formatAdminInstant(iso, {
+ formatAdminInstantInTimeZone(iso, {
locale: getAdminRequestLocale(),
+ timeZone: LOTTERY_SCHEDULE_TIMEZONE,
}),
[],
);
@@ -156,7 +156,7 @@ export function DrawsIndexConsole() {
} finally {
setLoading(false);
}
- }, [page, perPage, appliedDrawNo, appliedStatus, appliedAgentNodeId]);
+ }, [page, perPage, appliedDrawNo, appliedStatus, appliedAgentNodeId, tRef]);
async function generatePlan(): Promise {
setGenerating(true);
@@ -559,6 +559,7 @@ export function DrawsIndexConsole() {
) : null}
@@ -571,6 +572,7 @@ export function DrawsIndexConsole() {
}
}}
draw={editDraw}
+ scheduleTimezone={LOTTERY_SCHEDULE_TIMEZONE}
onSaved={load}
/>
) : null}
diff --git a/src/modules/settlement/settlement-adjustments-table.tsx b/src/modules/settlement/settlement-adjustments-table.tsx
index c8a3478..f781c47 100644
--- a/src/modules/settlement/settlement-adjustments-table.tsx
+++ b/src/modules/settlement/settlement-adjustments-table.tsx
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
import type { SettlementAdjustmentRow } from "@/api/admin-agent-settlement";
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-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 { formatDashboardMoneyMinor } from "@/modules/dashboard/use-dashboard-analytics";
import {
@@ -30,6 +31,7 @@ export function SettlementAdjustmentsTable({
onOpenBill,
}: SettlementAdjustmentsTableProps): React.ReactElement {
const { t } = useTranslation(["settlementCenter", "common"]);
+ const formatTs = useAdminDateTimeFormatter();
if (loading) {
return ;
@@ -71,7 +73,7 @@ export function SettlementAdjustmentsTable({
{formatDashboardMoneyMinor(row.amount, currencyCode)}
{row.reason ?? "—"}
- {row.created_at ?? "—"}
+ {formatTs(row.created_at)}
{row.original_bill_id != null ? (
;
@@ -77,9 +79,13 @@ export function SettlementPaymentsTable({
{formatDashboardMoneyMinor(row.amount, currencyCode)}
{row.method ?? "—"}
- {row.status}
+
+ {t(`paymentStatus.${row.status}`, {
+ defaultValue: row.status === "confirmed" ? "已确认" : row.status,
+ })}
+
- {row.confirmed_at ?? row.created_at ?? "—"}
+ {formatTs(row.confirmed_at ?? row.created_at)}