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

@@ -421,6 +421,9 @@ export function DashboardAgentRankingCard({
const v = metricValue(row);
const pct = (Math.abs(v) / maxAbs) * 100;
const color = barColor(row);
const agentName = row.agent_name?.trim() || "-";
const agentCode = row.agent_code?.trim() || "";
const showCode = agentCode !== "" && agentCode !== agentName;
return (
<div key={row.agent_node_id} className="rounded-lg bg-muted/20 px-2 py-2">
<div className="flex items-start justify-between gap-2">
@@ -429,8 +432,10 @@ export function DashboardAgentRankingCard({
#{idx + 1}
</span>
<div className="min-w-0">
<p className="truncate text-xs font-medium">{row.agent_name || "-"}</p>
<p className="truncate text-[11px] text-muted-foreground">{row.agent_code || ""}</p>
<p className="truncate text-xs font-medium">{agentName}</p>
{showCode ? (
<p className="truncate text-[11px] text-muted-foreground">{agentCode}</p>
) : null}
</div>
</div>
<div className="shrink-0 text-right text-xs font-semibold tabular-nums">

View File

@@ -13,6 +13,38 @@ import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter"
import { cn } from "@/lib/utils";
import type { DrawCurrentSnapshot } from "@/types/api/public-draw";
function formatDashboardDrawStatus(
status: string,
t: (key: string, options?: Record<string, unknown>) => string,
): string {
const lower = status.trim().toLowerCase();
switch (lower) {
case "pending":
return t("statusOptions.pending", { ns: "draws" });
case "open":
return t("statusOptions.open", { ns: "draws" });
case "closing":
return t("statusOptions.closing", { ns: "draws" });
case "closed":
return t("statusOptions.closed", { ns: "draws" });
case "drawing":
return t("statusOptions.drawing", { ns: "draws" });
case "review":
return t("statusOptions.review", { ns: "draws" });
case "cooldown":
return t("statusOptions.cooldown", { ns: "draws" });
case "settling":
return t("statusOptions.settling", { ns: "draws" });
case "settled":
return t("statusOptions.settled", { ns: "draws" });
case "cancelled":
return t("statusOptions.cancelled", { ns: "draws" });
default:
return status;
}
}
function isOpenLikeStatus(status: string): boolean {
const lower = status.toLowerCase();
return lower.includes("open") || lower.includes("sale");
@@ -29,7 +61,7 @@ export function DashboardCurrentDrawCard({
drawId,
loading = false,
}: DashboardCurrentDrawCardProps): ReactElement {
const { t } = useTranslation("dashboard");
const { t } = useTranslation(["dashboard", "draws"]);
const formatDt = useAdminDateTimeFormatter();
if (loading) {
@@ -54,6 +86,7 @@ export function DashboardCurrentDrawCard({
}
const openLike = isOpenLikeStatus(hall.status);
const statusLabel = formatDashboardDrawStatus(hall.status, t);
return (
<Card className="admin-list-card overflow-hidden border-primary/15 py-0 shadow-sm">
@@ -95,7 +128,7 @@ export function DashboardCurrentDrawCard({
openLike ? "bg-emerald-500" : "bg-muted-foreground/70",
)}
/>
{hall.status}
{statusLabel}
</span>
</div>
</div>

View File

@@ -817,6 +817,11 @@ export function HotUsageBars({
}): ReactElement {
const { t } = useTranslation("dashboard");
const chartConfig = useMemo(() => buildUsageBarConfig(t("riskCapUsage")), [t]);
const shortenPoolLabel = (value: string): string => {
const normalized = value.trim();
const trimmedMeta = normalized.replace(/\s*\(.+\)$/, "");
return trimmedMeta.length > 10 ? `${trimmedMeta.slice(0, 10)}` : trimmedMeta;
};
const chartData = useMemo(
() =>
@@ -824,6 +829,7 @@ export function HotUsageBars({
const pct = Math.min(100, Math.max(0, (row.usage_ratio ?? 0) * 100));
return {
number: row.normalized_number.trim(),
displayNumber: shortenPoolLabel(row.normalized_number),
usage: pct,
fill: usageBarFill(pct),
};
@@ -855,15 +861,23 @@ export function HotUsageBars({
<YAxis
type="category"
dataKey="number"
width={72}
width={compact ? 92 : 112}
tickLine={false}
axisLine={false}
tickFormatter={(value) => {
const row = chartData.find((item) => item.number === value);
return row?.displayNumber ?? value;
}}
tick={{ fontSize: 11, fontFamily: "var(--font-mono)" }}
/>
<ChartTooltip
content={
<ChartTooltipContent
formatter={(value) => `${Number(value).toFixed(1)}%`}
formatter={(value, _name, item) => {
const payload = item?.payload as { number?: string } | undefined;
const number = payload?.number ? `${payload.number} · ` : "";
return `${number}${Number(value).toFixed(1)}%`;
}}
/>
}
/>