refactor: 完成全站国际化改造,统一多语言支持
此提交完成了全项目的国际化适配: 1. 新增多语言翻译文件与基础配置 2. 替换所有硬编码文本为i18n调用 3. 优化语言切换与文档语言同步逻辑 4. 重构部分业务逻辑以支持动态翻译 5. 移除过时代码与硬编码配置
This commit is contained in:
@@ -4,22 +4,26 @@ export function ticketStatusDisplay(
|
||||
status: string,
|
||||
winMinor: number,
|
||||
jackpotMinor: number,
|
||||
t?: (key: string, options?: { defaultValue?: string; status?: string }) => string,
|
||||
): { label: string; dotClass: string; ring?: boolean } {
|
||||
const total = winMinor + jackpotMinor;
|
||||
if (status === "success") {
|
||||
return { label: "待开奖", dotClass: "bg-sky-500" };
|
||||
return { label: t?.("ticketStatus.success") ?? "待开奖", dotClass: "bg-sky-500" };
|
||||
}
|
||||
if (status === "settled_win" && total > 0) {
|
||||
return { label: "已派彩", dotClass: "bg-emerald-500" };
|
||||
return { label: t?.("ticketStatus.settled_win") ?? "已派彩", dotClass: "bg-emerald-500" };
|
||||
}
|
||||
if (status === "settled_lose" || status === "settled_win") {
|
||||
return {
|
||||
label: "未中奖",
|
||||
label: t?.("ticketStatus.settled_lose") ?? "未中奖",
|
||||
dotClass: "bg-background",
|
||||
ring: true,
|
||||
};
|
||||
}
|
||||
return { label: status, dotClass: "bg-red-500" };
|
||||
return {
|
||||
label: t?.("ticketStatus.unknown", { status, defaultValue: status }) ?? status,
|
||||
dotClass: "bg-red-500",
|
||||
};
|
||||
}
|
||||
|
||||
export function StatusDot({
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { getTicketItemDetail } from "@/api/ticket-items";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import { PlayerPanel } from "@/components/layout/player-panel";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -18,46 +20,31 @@ import { StatusDot, ticketStatusDisplay } from "@/features/orders/ticket-item-st
|
||||
import { formatLotteryInstant } from "@/lib/player-datetime";
|
||||
import { formatMinorAsCurrency } from "@/lib/money";
|
||||
import { norm4d } from "@/lib/norm-4d";
|
||||
import { playLabelZh } from "@/lib/play-labels";
|
||||
import { playLabel } from "@/lib/play-labels";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { TicketItemDetailPayload } from "@/types/api/ticket-items";
|
||||
|
||||
type OddsSnapRow = { prize_scope?: string; odds_value?: number };
|
||||
|
||||
function formatOddsSnapshot(json: unknown): string {
|
||||
function formatOddsSnapshot(
|
||||
json: unknown,
|
||||
t: (key: string, options?: { defaultValue?: string }) => string,
|
||||
): string {
|
||||
if (!Array.isArray(json)) return "—";
|
||||
const parts = (json as OddsSnapRow[])
|
||||
.filter((r) => r.prize_scope && r.odds_value != null)
|
||||
.map((r) => {
|
||||
const scope = String(r.prize_scope);
|
||||
const label =
|
||||
scope === "first"
|
||||
? "头奖"
|
||||
: scope === "second"
|
||||
? "二奖"
|
||||
: scope === "third"
|
||||
? "三奖"
|
||||
: scope === "starter"
|
||||
? "特别奖"
|
||||
: scope === "consolation"
|
||||
? "安慰奖"
|
||||
: scope;
|
||||
const label = t(`prizeTier.${scope}`, { defaultValue: scope });
|
||||
const mult = Number(r.odds_value) / 10_000;
|
||||
return `${label} ${mult}x`;
|
||||
});
|
||||
return parts.length ? parts.join(" · ") : "—";
|
||||
}
|
||||
|
||||
const TIER_ZH: Record<string, string> = {
|
||||
first: "头奖",
|
||||
second: "二奖",
|
||||
third: "三奖",
|
||||
starter: "特别奖",
|
||||
consolation: "安慰奖",
|
||||
};
|
||||
|
||||
/** 界面文档 §4.8 注单详情 */
|
||||
export function TicketOrderDetailScreen({ ticketNo }: { ticketNo: string }) {
|
||||
const { t } = useTranslation("player");
|
||||
const [data, setData] = useState<TicketItemDetailPayload | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -70,11 +57,11 @@ export function TicketOrderDetailScreen({ ticketNo }: { ticketNo: string }) {
|
||||
setData(row);
|
||||
} catch {
|
||||
setData(null);
|
||||
setError("注单不存在或无权查看");
|
||||
setError(t("orders.notFound"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [ticketNo]);
|
||||
}, [ticketNo, t]);
|
||||
|
||||
useEffect(() => {
|
||||
queueMicrotask(() => {
|
||||
@@ -84,39 +71,42 @@ export function TicketOrderDetailScreen({ ticketNo }: { ticketNo: string }) {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Skeleton className="h-6 w-40" />
|
||||
<Skeleton className="h-4 w-56" />
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
<Skeleton className="h-32 w-full" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<PlayerPanel
|
||||
title={t("orders.betDetail")}
|
||||
subtitle={ticketNo}
|
||||
eyebrow={t("orders.title")}
|
||||
backHref="/orders"
|
||||
backLabel={t("orders.title")}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<Skeleton className="h-12 rounded-xl" />
|
||||
<Skeleton className="h-56 rounded-xl" />
|
||||
</div>
|
||||
</PlayerPanel>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<Card className="border-destructive/40">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">注单详情</CardTitle>
|
||||
<CardDescription>{error ?? "无数据"}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-wrap gap-2">
|
||||
<PlayerPanel
|
||||
title={t("orders.betDetail")}
|
||||
subtitle={ticketNo}
|
||||
eyebrow={t("orders.title")}
|
||||
backHref="/orders"
|
||||
backLabel={t("orders.title")}
|
||||
>
|
||||
<div className="rounded-xl border border-red-200 bg-red-50 px-4 py-4 text-sm text-red-700">
|
||||
<p>{error ?? t("orders.noData")}</p>
|
||||
<Button type="button" size="sm" variant="secondary" onClick={() => void load()}>
|
||||
重试
|
||||
{t("actions.retry")}
|
||||
</Button>
|
||||
<Link href="/orders" className={cn(buttonVariants({ variant: "outline", size: "sm" }))}>
|
||||
返回列表
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</PlayerPanel>
|
||||
);
|
||||
}
|
||||
|
||||
const cur = data.currency_code ?? "NPR";
|
||||
const st = ticketStatusDisplay(data.status, data.win_amount, data.jackpot_win_amount);
|
||||
const st = ticketStatusDisplay(data.status, data.win_amount, data.jackpot_win_amount, t);
|
||||
const totalWin = data.win_amount + data.jackpot_win_amount;
|
||||
const pub = data.published_draw_results;
|
||||
const first = pub?.results?.["1st"] ?? "";
|
||||
@@ -146,119 +136,144 @@ export function TicketOrderDetailScreen({ ticketNo }: { ticketNo: string }) {
|
||||
: null;
|
||||
|
||||
const tierLabel = data.settlement?.matched_prize_tier
|
||||
? TIER_ZH[data.settlement.matched_prize_tier] ?? data.settlement.matched_prize_tier
|
||||
? t(`prizeTier.${data.settlement.matched_prize_tier}`, {
|
||||
defaultValue: data.settlement.matched_prize_tier,
|
||||
})
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<Card>
|
||||
<CardHeader className="space-y-2 pb-2">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<CardTitle className="text-base">注单详情</CardTitle>
|
||||
<StatusDot label={st.label} dotClass={st.dotClass} ring={st.ring} />
|
||||
</div>
|
||||
<CardDescription className="font-mono text-xs">
|
||||
注单号 {data.ticket_no} · 订单 {data.order_no ?? "—"}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3 text-sm">
|
||||
<div className="grid gap-1 text-xs">
|
||||
<p>
|
||||
<span className="text-muted-foreground">期号</span>{" "}
|
||||
<span className="font-mono font-medium">{data.draw_no ?? "—"}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">下单时间</span>{" "}
|
||||
{formatLotteryInstant(data.placed_at ?? null)}
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">号码</span>{" "}
|
||||
<span className="font-mono">{data.original_number ?? "—"}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">玩法</span> {playLabelZh(data.play_code)} (
|
||||
{data.dimension ?? "—"}D)
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">下注金额</span>{" "}
|
||||
{formatMinorAsCurrency(data.total_bet_amount, cur)}
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">回水率</span>{" "}
|
||||
{(Number(data.rebate_rate_snapshot) * 100).toFixed(1)}%
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">实扣金额</span>{" "}
|
||||
{formatMinorAsCurrency(data.actual_deduct_amount, cur)}
|
||||
</p>
|
||||
</div>
|
||||
<PlayerPanel
|
||||
title={t("orders.betDetail")}
|
||||
subtitle={data.ticket_no}
|
||||
eyebrow={t("orders.title")}
|
||||
backHref="/orders"
|
||||
backLabel={t("orders.title")}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<Card className="rounded-xl border-[#e5edf8] shadow-[0_8px_24px_rgba(15,23,42,0.05)]">
|
||||
<CardHeader className="space-y-2 pb-2">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<CardTitle className="text-base">{t("orders.detailTitle")}</CardTitle>
|
||||
<StatusDot label={st.label} dotClass={st.dotClass} ring={st.ring} />
|
||||
</div>
|
||||
<CardDescription className="font-mono text-xs">
|
||||
{t("orders.ticketNo", { ticketNo: data.ticket_no })} ·{" "}
|
||||
{t("orders.orderNo", { orderNo: data.order_no ?? "—" })}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3 text-sm">
|
||||
<div className="grid gap-1 text-xs">
|
||||
<p>
|
||||
<span className="text-muted-foreground">{t("orders.drawNo")}</span>{" "}
|
||||
<span className="font-mono font-medium">{data.draw_no ?? "—"}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">{t("orders.placedAt")}</span>{" "}
|
||||
{formatLotteryInstant(data.placed_at ?? null)}
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">{t("orders.number")}</span>{" "}
|
||||
<span className="font-mono">{data.original_number ?? "—"}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">{t("orders.play")}</span>{" "}
|
||||
{playLabel(data.play_code, t)} (
|
||||
{data.dimension ?? "—"}D)
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">{t("orders.amount")}</span>{" "}
|
||||
{formatMinorAsCurrency(data.total_bet_amount, cur)}
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">{t("orders.rebateRate")}</span>{" "}
|
||||
{(Number(data.rebate_rate_snapshot) * 100).toFixed(1)}%
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-muted-foreground">{t("orders.actualDeduct")}</span>{" "}
|
||||
{formatMinorAsCurrency(data.actual_deduct_amount, cur)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border bg-muted/30 px-3 py-2 text-xs">
|
||||
<p className="font-medium text-foreground">赔率快照</p>
|
||||
<p className="mt-1 text-muted-foreground">{formatOddsSnapshot(data.odds_snapshot_json)}</p>
|
||||
<p className="font-medium text-foreground">{t("orders.oddsSnapshot")}</p>
|
||||
<p className="mt-1 text-muted-foreground">
|
||||
{formatOddsSnapshot(data.odds_snapshot_json, t)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{pub?.results ? (
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium">开奖号码(本期)</p>
|
||||
<p className="text-sm font-medium">{t("orders.drawNumbers")}</p>
|
||||
<TwentyThreeResultsGrid numbers={pub.results} highlighted4d={highlight} />
|
||||
{first ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
头奖{" "}
|
||||
{t("orders.firstPrize")}{" "}
|
||||
<span className="font-mono font-semibold text-foreground">{first}</span>
|
||||
{comboHits.length > 0 ? (
|
||||
<span className="text-emerald-600 dark:text-emerald-400"> ← 命中</span>
|
||||
<span className="text-emerald-600 dark:text-emerald-400">
|
||||
{" "}
|
||||
← {t("orders.hit")}
|
||||
</span>
|
||||
) : null}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground">本期开奖号码尚未发布或不可展示。</p>
|
||||
<p className="text-xs text-muted-foreground">{t("orders.notPublished")}</p>
|
||||
)}
|
||||
|
||||
{data.settlement && tierLabel ? (
|
||||
<div className="rounded-md border border-emerald-500/20 bg-emerald-500/5 px-3 py-2 text-xs">
|
||||
<p className="font-medium text-emerald-900 dark:text-emerald-100">
|
||||
匹配结果:命中 {tierLabel}
|
||||
{t("orders.matchWin", { tier: tierLabel })}
|
||||
</p>
|
||||
<p className="mt-1 font-mono text-muted-foreground">
|
||||
中奖金额 {formatMinorAsCurrency(data.settlement.win_amount_minor, cur)}
|
||||
{t("orders.winAmount", {
|
||||
amount: formatMinorAsCurrency(data.settlement.win_amount_minor, cur),
|
||||
})}
|
||||
{data.settlement.jackpot_allocation_minor > 0 ? (
|
||||
<>
|
||||
{" "}
|
||||
· Jackpot {formatMinorAsCurrency(data.settlement.jackpot_allocation_minor, cur)}
|
||||
·{" "}
|
||||
{t("orders.jackpotAmount", {
|
||||
amount: formatMinorAsCurrency(
|
||||
data.settlement.jackpot_allocation_minor,
|
||||
cur,
|
||||
),
|
||||
})}
|
||||
</>
|
||||
) : null}
|
||||
</p>
|
||||
<p className="mt-1 font-mono text-muted-foreground">
|
||||
派彩合计 {formatMinorAsCurrency(totalWin, cur)}
|
||||
{t("orders.payoutTotal", { amount: formatMinorAsCurrency(totalWin, cur) })}
|
||||
</p>
|
||||
</div>
|
||||
) : data.status === "settled_lose" ? (
|
||||
<p className="text-xs text-muted-foreground">匹配结果:未中奖</p>
|
||||
<p className="text-xs text-muted-foreground">{t("orders.matchLose")}</p>
|
||||
) : null}
|
||||
|
||||
{data.settled_at ? (
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
结算时间 {formatLotteryInstant(data.settled_at)}
|
||||
{t("orders.settledAt", { time: formatLotteryInstant(data.settled_at) })}
|
||||
</p>
|
||||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{data.draw_no ? (
|
||||
<Link
|
||||
href={`/results/${encodeURIComponent(data.draw_no)}`}
|
||||
className={cn(buttonVariants({ variant: "outline", size: "sm" }))}
|
||||
>
|
||||
查看本期开奖
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{data.draw_no ? (
|
||||
<Link
|
||||
href={`/results/${encodeURIComponent(data.draw_no)}`}
|
||||
className={cn(buttonVariants({ variant: "outline", size: "sm" }))}
|
||||
>
|
||||
{t("orders.viewDraw")}
|
||||
</Link>
|
||||
) : null}
|
||||
<Link href="/orders" className={cn(buttonVariants({ variant: "secondary", size: "sm" }))}>
|
||||
{t("orders.backToOrders")}
|
||||
</Link>
|
||||
) : null}
|
||||
<Link href="/orders" className={cn(buttonVariants({ variant: "secondary", size: "sm" }))}>
|
||||
返回我的注单
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PlayerPanel>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Link from "next/link";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { getTicketItems } from "@/api/ticket-items";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -11,11 +12,12 @@ import { PlayerPanel } from "@/components/layout/player-panel";
|
||||
import { StatusDot, ticketStatusDisplay } from "@/features/orders/ticket-item-status";
|
||||
import { formatMinorAsCurrency } from "@/lib/money";
|
||||
import { formatLotteryInstant } from "@/lib/player-datetime";
|
||||
import { playLabelZh } from "@/lib/play-labels";
|
||||
import { playLabel } from "@/lib/play-labels";
|
||||
import type { TicketItemListRow } from "@/types/api/ticket-items";
|
||||
|
||||
export function TicketOrdersListScreen() {
|
||||
const searchParams = useSearchParams();
|
||||
const { t } = useTranslation("player");
|
||||
const drawNoFilter = useMemo(
|
||||
() => (searchParams.get("draw_no") ?? "").trim(),
|
||||
[searchParams],
|
||||
@@ -45,14 +47,14 @@ export function TicketOrdersListScreen() {
|
||||
setLastPage(res.last_page);
|
||||
setTotal(res.total);
|
||||
} catch {
|
||||
setError("加载失败");
|
||||
setError(t("orders.loadFailed"));
|
||||
if (!append) setItems([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setLoadingMore(false);
|
||||
}
|
||||
},
|
||||
[drawNoFilter],
|
||||
[drawNoFilter, t],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -67,13 +69,13 @@ export function TicketOrdersListScreen() {
|
||||
};
|
||||
|
||||
return (
|
||||
<PlayerPanel title="My Bets" subtitle="Recent ticket records" eyebrow="N lotto">
|
||||
<PlayerPanel title={t("orders.title")} subtitle={t("orders.subtitle")} eyebrow={t("brand.name")}>
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-xl border border-[#e6edf8] bg-[#f8fbff] p-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs font-bold text-[#32518d]">
|
||||
{drawNoFilter ? "Filtered Issue" : "Total Records"}
|
||||
{drawNoFilter ? t("orders.filteredIssue") : t("orders.totalRecords")}
|
||||
</p>
|
||||
<p className="mt-1 truncate font-mono text-lg font-black text-[#0b3f96]">
|
||||
{drawNoFilter || total}
|
||||
@@ -84,14 +86,14 @@ export function TicketOrdersListScreen() {
|
||||
href="/orders"
|
||||
className="shrink-0 rounded-full border border-[#dce7f7] bg-white px-3 py-1.5 text-xs font-bold text-[#0b56b7]"
|
||||
>
|
||||
Clear
|
||||
{t("actions.clear")}
|
||||
</Link>
|
||||
) : (
|
||||
<Link
|
||||
href="/hall"
|
||||
className="shrink-0 rounded-full bg-[#e5002c] px-3 py-1.5 text-xs font-bold text-white"
|
||||
>
|
||||
Bet Now
|
||||
{t("orders.betNow")}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
@@ -112,17 +114,17 @@ export function TicketOrdersListScreen() {
|
||||
className="mt-3 bg-[#e5002c] text-white hover:bg-[#d10028]"
|
||||
onClick={() => void fetchPage(1, false)}
|
||||
>
|
||||
Retry
|
||||
{t("actions.retry")}
|
||||
</Button>
|
||||
</div>
|
||||
) : items.length === 0 ? (
|
||||
<div className="rounded-xl border border-dashed border-[#dce7f7] bg-[#f8fbff] px-4 py-10 text-center">
|
||||
<p className="text-sm font-bold text-slate-700">No bet records yet.</p>
|
||||
<p className="text-sm font-bold text-slate-700">{t("orders.empty")}</p>
|
||||
<Link
|
||||
href="/hall"
|
||||
className="mt-4 inline-flex h-9 items-center rounded-lg bg-[#e5002c] px-4 text-sm font-bold text-white"
|
||||
>
|
||||
Submit Bet
|
||||
{t("orders.submitBet")}
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
@@ -134,6 +136,7 @@ export function TicketOrdersListScreen() {
|
||||
row.status,
|
||||
row.win_amount,
|
||||
row.jackpot_win_amount,
|
||||
t,
|
||||
);
|
||||
const totalWin = row.win_amount + row.jackpot_win_amount;
|
||||
return (
|
||||
@@ -148,20 +151,24 @@ export function TicketOrdersListScreen() {
|
||||
{row.draw_no ?? "—"}
|
||||
</p>
|
||||
<p className="mt-1 truncate text-xs text-slate-500">
|
||||
{playLabelZh(row.play_code)} · {row.original_number ?? row.play_code}
|
||||
{playLabel(row.play_code, t)} · {row.original_number ?? row.play_code}
|
||||
</p>
|
||||
</div>
|
||||
<StatusDot label={st.label} dotClass={st.dotClass} ring={st.ring} />
|
||||
</div>
|
||||
<div className="mt-3 grid grid-cols-2 gap-2">
|
||||
<div className="rounded-lg bg-[#f8fbff] px-3 py-2">
|
||||
<p className="text-[10px] font-bold uppercase text-[#7890b8]">Stake</p>
|
||||
<p className="text-[10px] font-bold uppercase text-[#7890b8]">
|
||||
{t("orders.stake")}
|
||||
</p>
|
||||
<p className="mt-1 text-sm font-black text-slate-900">
|
||||
{formatMinorAsCurrency(row.total_bet_amount, cur)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg bg-[#f8fbff] px-3 py-2">
|
||||
<p className="text-[10px] font-bold uppercase text-[#7890b8]">Deduction</p>
|
||||
<p className="text-[10px] font-bold uppercase text-[#7890b8]">
|
||||
{t("orders.deduction")}
|
||||
</p>
|
||||
<p className="mt-1 text-sm font-black text-[#0b3f96]">
|
||||
{formatMinorAsCurrency(row.actual_deduct_amount, cur)}
|
||||
</p>
|
||||
@@ -169,7 +176,7 @@ export function TicketOrdersListScreen() {
|
||||
</div>
|
||||
{totalWin > 0 && row.status === "settled_win" ? (
|
||||
<p className="mt-2 text-xs font-bold text-emerald-600">
|
||||
Win {formatMinorAsCurrency(totalWin, cur)}
|
||||
{t("orders.win", { amount: formatMinorAsCurrency(totalWin, cur) })}
|
||||
</p>
|
||||
) : null}
|
||||
<p className="mt-2 text-[11px] text-slate-500">
|
||||
@@ -187,7 +194,7 @@ export function TicketOrdersListScreen() {
|
||||
disabled={loadingMore}
|
||||
onClick={() => loadMore()}
|
||||
>
|
||||
{loadingMore ? "Loading..." : "Load More"}
|
||||
{loadingMore ? t("actions.loading") : t("actions.loadMore")}
|
||||
</Button>
|
||||
) : null}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user