diff --git a/frontend/components/dashboard/ScanTerminalDashboard.tsx b/frontend/components/dashboard/ScanTerminalDashboard.tsx
index 36dc5408..64a47bd9 100644
--- a/frontend/components/dashboard/ScanTerminalDashboard.tsx
+++ b/frontend/components/dashboard/ScanTerminalDashboard.tsx
@@ -99,6 +99,66 @@ function edgeClass(value: unknown) {
return n > 0 ? "text-emerald-700" : "text-red-600";
}
+const TERM = {
+ cityContract: { en: "City / Contract", zh: "城市 / 合约" },
+ live: { en: "Live", zh: "实测" },
+ deb: { en: "DEB", zh: "DEB" },
+ mkt: { en: "Mkt", zh: "市场" },
+ edge: { en: "Edge", zh: "优势" },
+ liq: { en: "Liq", zh: "流动性" },
+ signal: { en: "Signal", zh: "信号" },
+ searchPlaceholder: { en: "Search city, contract, station, or signal", zh: "搜索城市、合约、站点或信号" },
+ weatherContracts: { en: "Weather Contracts", zh: "天气合约" },
+ approvedSignals: { en: "Approved Signals", zh: "已确认信号" },
+ selectedContractMonitor: { en: "Selected Contract Monitor", zh: "选中合约监控" },
+ probabilityDistribution: { en: "Probability Distribution", zh: "概率分布" },
+ marketList: { en: "Market List", zh: "市场列表" },
+ watchlist: { en: "Watchlist", zh: "观察列表" },
+ rows: { en: "Rows", zh: "行数" },
+ avgEdge: { en: "Avg Edge", zh: "平均优势" },
+ liquidity: { en: "Liquidity", zh: "流动性" },
+ intradayPerformance: { en: "Intraday Performance", zh: "日内表现" },
+ spread: { en: "Spread", zh: "价差" },
+ model: { en: "Model", zh: "模型" },
+ noData: { en: "No data", zh: "无数据" },
+ noDistributionData: { en: "No distribution data", zh: "无分布数据" },
+ selectContract: {
+ en: "Select a weather contract to inspect model edge, market price, and live evidence.",
+ zh: "选择天气合约以查看模型优势、市场价格和实况证据。",
+ },
+ signInToContinue: { en: "Sign in to continue", zh: "请先登录" },
+ signInHint: {
+ en: "The terminal is only available to registered users. Please sign in or create an account.",
+ zh: "决策台仅对注册用户开放。请登录或创建账号。",
+ },
+ logIn: { en: "Log in", zh: "登录" },
+ createAccount: { en: "Create an account", zh: "注册账号" },
+ learnAbout: { en: "Learn about PolyWeather", zh: "了解 PolyWeather" },
+ proAccessRequired: { en: "Pro Access Required", zh: "需要付费订阅" },
+ proDesc: {
+ en: "The PolyWeather terminal is a paid product. Subscribe to unlock real-time weather-market intelligence.",
+ zh: "PolyWeather 决策台为付费产品。订阅以解锁实时天气市场情报。",
+ },
+ subscriptionTerms: {
+ en: "Billed monthly. Cancel anytime. Payment via USDC on Polygon.",
+ zh: "按月计费,随时可取消。通过 Polygon 链 USDC 支付。",
+ },
+ month: { en: "/ month", zh: "/ 月" },
+ subscribeNow: { en: "Subscribe Now — $10/mo", zh: "立即订阅 — $10/月" },
+ subscribePrompt: {
+ en: "You need an active subscription to access the terminal.",
+ zh: "你需要开通有效订阅才能访问决策台。",
+ },
+ backToProduct: { en: "Back to product overview", zh: "返回产品介绍页" },
+ dashboard: { en: "Weather Market Dashboard", zh: "天气市场数据仪表盘" },
+ refresh: { en: "Refresh", zh: "刷新" },
+ switchLang: { en: "Switch to Chinese", zh: "切换到英文" },
+} as const;
+
+function t(key: keyof typeof TERM, isEn: boolean) {
+ return isEn ? TERM[key].en : TERM[key].zh;
+}
+
function decisionLabel(row?: ScanOpportunityRow | null) {
const raw =
row?.ai_decision ||
@@ -128,14 +188,16 @@ function decisionClass(label: string) {
function SparkArea({
color = "#2563eb",
data,
+ isEn = true,
}: {
color?: string;
data: { v: number }[];
+ isEn?: boolean;
}) {
if (!data.length) {
return (
- No data
+ {t("noData", isEn)}
);
}
@@ -164,13 +226,15 @@ function SparkArea({
function ProbabilityDistributionChart({
points,
+ isEn = true,
}: {
points?: ScanOpportunityRow["distribution_preview"];
+ isEn?: boolean;
}) {
if (!points?.length) {
return (
- No distribution data
+ {t("noDistributionData", isEn)}
);
}
@@ -206,8 +270,8 @@ function ProbabilityDistributionChart({
`${Number(value).toFixed(1)}%`
}
/>
-
-
+
+
);
@@ -246,23 +310,25 @@ function MarketTable({
onSelect,
rows,
selectedId,
+ isEn = true,
}: {
onSelect: (row: ScanOpportunityRow) => void;
rows: ScanOpportunityRow[];
selectedId?: string | null;
+ isEn?: boolean;
}) {
return (
- | City / Contract |
- Live |
- DEB |
- Mkt |
- Edge |
- Liq |
- Signal |
+ {t("cityContract", isEn)} |
+ {t("live", isEn)} |
+ {t("deb", isEn)} |
+ {t("mkt", isEn)} |
+ {t("edge", isEn)} |
+ {t("liq", isEn)} |
+ {t("signal", isEn)} |
@@ -395,7 +461,7 @@ function KoyfinWeatherTerminal({
- Search city, contract, station, or signal
+ {t("searchPlaceholder", isEn)}
/
@@ -403,7 +469,7 @@ function KoyfinWeatherTerminal({
- {isEn ? "Weather Market Dashboard" : "天气市场数据仪表盘"}
+ {t("dashboard", isEn)}
@@ -411,8 +477,8 @@ function KoyfinWeatherTerminal({
{/* Language toggle — matches landing page style */}
-
+
- Rows
+ {t("rows", isEn)}
{rows.length}
- Avg Edge
+ {t("avgEdge", isEn)}
{pct(avgEdge)}
@@ -476,7 +542,7 @@ function KoyfinWeatherTerminal({
- Liquidity
+ {t("liquidity", isEn)}
{money(totalLiquidity)}
@@ -487,10 +553,11 @@ function KoyfinWeatherTerminal({
rows={topRows}
selectedId={selectedRow?.id}
onSelect={setSelectedRow}
+ isEn={isEn}
/>
-
+
{(approveRows.length ? approveRows : topRows.slice(0, 5)).map((row) => (