删除 _unused_CityGroupedTable(137行) + KoyfinRowsTable 改为外部导入

ScanTerminalDashboard 1060→841 行。
This commit is contained in:
2569718930@qq.com
2026-05-25 17:23:36 +08:00
parent dc5b747385
commit 3732dafb95
2 changed files with 11 additions and 223 deletions
@@ -47,6 +47,7 @@ import { Panel } from "@/components/dashboard/scan-terminal/Panel";
import { TrainingDashboard } from "@/components/dashboard/scan-terminal/TrainingDashboard";
import { LiveTemperatureThresholdChart } from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart";
import { MarketOverviewView } from "@/components/dashboard/scan-terminal/MarketOverviewView";
import { KoyfinRowsTable } from "@/components/dashboard/scan-terminal/KoyfinRowsTable";
import { rowName, pct, money, temp, edgeClass } from "@/components/dashboard/scan-terminal/utils";
function createEmptyAccess(loading = true): ProAccessState {
@@ -168,89 +169,6 @@ function tablePrice(row: ScanOpportunityRow) {
return formatPrice(row.midpoint, row.ask, row.bid);
}
function KoyfinRowsTable({
compact = false,
isEn,
onSelect,
rows,
selectedId,
}: {
compact?: boolean;
isEn: boolean;
onSelect: (row: ScanOpportunityRow) => void;
rows: ScanOpportunityRow[];
selectedId?: string | null;
}) {
return (
<table className="w-full border-collapse text-[11px]">
<thead>
<tr className="border-b border-slate-200 bg-[#f3f5f7] text-[11px] uppercase tracking-wide text-slate-500">
<th className="w-5 px-2 py-1 text-left font-black">
<span className="block h-3 w-3 rounded-[2px] border border-slate-300 bg-white" />
</th>
<th className="px-1.5 py-1 text-left font-black">
{isEn ? "City" : "城市"}
</th>
<th className="px-1.5 py-1 text-right font-black">
{isEn ? "Price" : "价格"}
</th>
<th className="px-1.5 py-1 text-right font-black">
{isEn ? "Chg" : "变化"}
</th>
<th className="px-2 py-1 text-right font-black">%</th>
</tr>
</thead>
<tbody>
{rows.map((row) => {
const edge = Number(row.edge_percent ?? row.signed_gap ?? row.gap ?? 0);
const positive = edge >= 0;
return (
<tr
key={row.id}
onClick={() => onSelect(row)}
className={clsx(
"cursor-pointer border-b border-slate-100 hover:bg-blue-50/70",
selectedId === row.id && "bg-blue-50",
)}
>
<td className="px-2 py-1">
<span className="block h-3 w-3 rounded-[2px] border border-slate-300 bg-white" />
</td>
<td className="px-1.5 py-1">
<div className="truncate font-bold text-slate-800">
{rowName(row)}
</div>
<div className="truncate text-[10px] font-medium text-slate-400">
{row.target_label || row.market_question || row.airport || "--"}
</div>
</td>
<td className="px-1.5 py-1 text-right font-mono font-bold text-slate-800">
{tablePrice(row)}
</td>
<td
className={clsx(
"px-1.5 py-1 text-right font-mono font-bold",
positive ? "text-emerald-700" : "text-red-600",
)}
>
{Number.isFinite(edge) ? `${positive ? "+" : ""}${edge.toFixed(1)}` : "--"}
</td>
<td
className={clsx(
"px-2 py-1 text-right font-mono font-bold",
positive ? "text-emerald-700" : "text-red-600",
)}
>
{pct(row.market_probability ?? row.market_event_probability ?? row.model_probability)}
</td>
</tr>
);
})}
</tbody>
</table>
);
}
function CityRegionList({
isEn,
rows,
@@ -347,143 +265,6 @@ function CityContractDetail({
);
}
function _unused_CityGroupedTable({
compact,
isEn,
onSelect,
rows,
selectedId,
}: {
compact?: boolean;
isEn: boolean;
onSelect: (row: ScanOpportunityRow) => void;
rows: ScanOpportunityRow[];
selectedId?: string | null;
}) {
const [expandedCities, setExpandedCities] = useState<Set<string>>(new Set());
const cityGroups = useMemo(() => {
const map = new Map<string, ScanOpportunityRow[]>();
rows.forEach((row) => {
const key = String(row.city || "").toLowerCase();
if (!map.has(key)) map.set(key, []);
map.get(key)!.push(row);
});
return Array.from(map.entries())
.map(([city, cityRows]) => {
const best = cityRows.reduce((a, b) =>
Math.abs(Number(b.edge_percent || 0)) > Math.abs(Number(a.edge_percent || 0)) ? b : a
);
const buckets = [...cityRows].sort((a, b) =>
Number(b.edge_percent || 0) - Number(a.edge_percent || 0)
);
return { city, displayName: rowName(best), best, buckets };
})
.sort((a, b) =>
Math.abs(Number(b.best.edge_percent || 0)) - Math.abs(Number(a.best.edge_percent || 0))
);
}, [rows]);
const toggle = (city: string) => {
setExpandedCities((prev) => {
const next = new Set(prev);
if (next.has(city)) next.delete(city);
else next.add(city);
return next;
});
};
const labelExpand = isEn ? "Expand" : "展开";
const labelBuckets = isEn ? "buckets" : "档位";
return (
<div className="overflow-auto h-full">
<table className="w-full min-w-[780px] border-collapse text-[11px]">
<thead>
<tr className="border-b border-slate-200 bg-[#f3f5f7] text-[9px] uppercase tracking-wide text-slate-500">
<th className="w-6 px-2 py-1.5 text-left font-black" />
<th className="px-1.5 py-1.5 text-left font-black">
{isEn ? "City / Best Signal" : "城市 / 最佳信号"}
</th>
<th className="px-1.5 py-1.5 text-right font-black">{isEn ? "Edge" : "优势"}</th>
<th className="px-1.5 py-1.5 text-right font-black">{isEn ? "Model" : "模型"}</th>
<th className="px-1.5 py-1.5 text-right font-black">Live</th>
<th className="px-1.5 py-1.5 text-right font-black">DEB</th>
<th className="px-1.5 py-1.5 text-right font-black">{isEn ? "Mkt" : "盘口"}</th>
<th className="px-1.5 py-1.5 text-right font-black">{isEn ? "Spr/Liq" : "价差/流动性"}</th>
</tr>
</thead>
<tbody>
{cityGroups.map(({ city, displayName, best, buckets }) => {
const isExpanded = expandedCities.has(city);
const hasMultiple = buckets.length > 1;
return (
<Fragment key={city}>
<tr
className={clsx(
"cursor-pointer border-b border-slate-100 hover:bg-slate-50/80",
selectedId === best.id && "bg-blue-50/50"
)}
onClick={() => { onSelect(best); if (hasMultiple) toggle(city); }}
>
<td className="px-2 py-1.5">
<button
type="button"
className="grid h-4 w-4 place-items-center text-slate-400 hover:text-slate-700"
onClick={(e) => { e.stopPropagation(); toggle(city); }}
title={`${labelExpand} ${hasMultiple ? `${buckets.length} ${labelBuckets}` : ""}`}
>
{hasMultiple ? (
isExpanded ? <ChevronDown size={11} /> : <ChevronRight size={11} />
) : (
<span className="text-[8px] text-slate-300"></span>
)}
</button>
</td>
<td className="px-1.5 py-1.5">
<div className="font-bold text-slate-800">{displayName}</div>
<div className="text-[9px] text-slate-400">
{best.target_label || ""}
{hasMultiple && !isExpanded ? ` +${buckets.length - 1}` : ""}
</div>
</td>
<td className={clsx("px-1.5 py-1.5 text-right font-mono font-bold", edgeClass(best.edge_percent))}>
{pct(best.edge_percent)}
</td>
<td className="px-1.5 py-1.5 text-right font-mono text-blue-700">{pct(best.model_probability ?? best.model_event_probability)}</td>
<td className="px-1.5 py-1.5 text-right font-mono">{temp(best.current_temp || best.current_max_so_far, best.temp_symbol)}</td>
<td className="px-1.5 py-1.5 text-right font-mono">{temp(best.deb_prediction, best.temp_symbol)}</td>
<td className="px-1.5 py-1.5 text-right font-mono">{formatPrice(best.midpoint, best.ask, best.bid)}</td>
<td className="px-1.5 py-1.5 text-right font-mono">{formatSpreadLiquidity(best.spread, best.book_liquidity ?? best.market_liquidity)}</td>
</tr>
{isExpanded && buckets.map((row) => (
<tr
key={row.id}
className="border-b border-slate-50 bg-slate-50/30 text-slate-500"
>
<td className="px-2 py-1 pl-2" />
<td className="px-1.5 py-1 pl-3">
<span className="text-[10px]">{row.target_label || "--"}</span>
</td>
<td className={clsx("px-1.5 py-1 text-right font-mono text-[10px] font-bold", edgeClass(row.edge_percent))}>
{pct(row.edge_percent)}
</td>
<td className="px-1.5 py-1 text-right font-mono text-[10px] text-blue-700">{pct(row.model_probability ?? row.model_event_probability)}</td>
<td className="px-1.5 py-1 text-right font-mono text-[10px]">{temp(row.current_temp || row.current_max_so_far, row.temp_symbol)}</td>
<td className="px-1.5 py-1 text-right font-mono text-[10px]">{temp(row.deb_prediction, row.temp_symbol)}</td>
<td className="px-1.5 py-1 text-right font-mono text-[10px]">{formatPrice(row.midpoint, row.ask, row.bid)}</td>
<td className="px-1.5 py-1 text-right font-mono text-[10px]">{formatSpreadLiquidity(row.spread, row.book_liquidity ?? row.market_liquidity)}</td>
</tr>
))}
</Fragment>
);
})}
</tbody>
</table>
</div>
);
}
function PolyWeatherTerminal({
generatedText,
isEn,
+10 -3
View File
@@ -236,6 +236,11 @@ def _build_quick_row(
distribution = probs.get("distribution") or []
local_date = str(data.get("local_date") or "")
local_time = str(data.get("local_time") or "")
city_meta = CITIES.get(city) or {}
tz_offset = data.get("utc_offset_seconds")
if tz_offset is None:
tz_offset = _safe_int(city_meta.get("tz"), 0)
market_region = _market_region_from_tz_offset(tz_offset)
id_parts = [city, local_date or "today"]
if data.get("temp_symbol") == "°F":
@@ -249,7 +254,7 @@ def _build_quick_row(
"airport": str(risk.get("airport") or ""),
"local_date": local_date,
"local_time": local_time,
"tz_offset_seconds": data.get("utc_offset_seconds"),
"tz_offset_seconds": tz_offset,
"temp_symbol": data.get("temp_symbol"),
"risk_level": risk.get("level"),
"current_temp": curr.get("temp"),
@@ -260,8 +265,10 @@ def _build_quick_row(
if v is not None
},
"distribution_preview": distribution[:6] if distribution else [],
"trading_region": data.get("trading_region"),
"trading_region_sort": data.get("trading_region_sort"),
"trading_region": market_region["key"],
"trading_region_label": market_region["label_en"],
"trading_region_label_zh": market_region["label_zh"],
"trading_region_sort": market_region.get("sort_order", 0),
"active": True,
"closed": False,
"tradable": False,