深度清理 Polymarket 代码:删除10个文件+3个API路由+客户端引用

删除 MarketOverviewView、MarketDecisionLine、market-scan-state、use-city-market-scan、AnalyticsPanel、TerminalDashboard、polymarket-market-links、lib/types、market-scan 路由、use-ai-city-card-data。scan-terminal-client 移除 getMarketScan 和 skip_polymarket。
This commit is contained in:
2569718930@qq.com
2026-05-25 19:50:28 +08:00
parent a3c4fd522b
commit 2d9e99760f
14 changed files with 0 additions and 2081 deletions
@@ -1,234 +0,0 @@
"use client";
import React from "react";
import {
BarChart2,
Target,
Zap,
Info,
Activity,
ArrowUpRight,
} from "lucide-react";
import { Badge } from "../ui/badge";
import { Sparkline } from "./Sparkline";
import { CityDetail } from "../../lib/types";
interface AnalyticsPanelProps {
data: CityDetail;
t?: any; // Optional translations
}
export function AnalyticsPanel({
data,
t = {}, // Default empty for now, can be expanded via context or props
}: AnalyticsPanelProps) {
const { overview, market_scan, models } = data;
const modelEntries = Object.entries(models)
.filter(([_, v]) => v !== undefined && v !== null)
.map(([label, value]) => ({ label, value: value as number }))
.sort((a, b) => b.value - a.value);
const signalColor =
market_scan.signal_label === "BUY YES"
? "border-emerald-500/30 bg-emerald-500/5 text-emerald-400"
: market_scan.signal_label === "BUY NO"
? "border-rose-500/30 bg-rose-500/5 text-rose-400"
: "border-zinc-800 bg-zinc-900/50 text-zinc-400";
const signalIconColor =
market_scan.signal_label === "BUY YES"
? "text-emerald-500"
: market_scan.signal_label === "BUY NO"
? "text-rose-500"
: "text-zinc-500";
return (
<div className="flex relative h-full w-[400px] flex-col bg-zinc-950/20 backdrop-blur-sm border-l border-zinc-800/50 overflow-hidden">
{/* Header Stat Area */}
<div className="p-4 border-b border-zinc-800 flex flex-col gap-4">
<div className="flex items-center justify-between">
<h2 className="text-[10px] font-black uppercase tracking-[0.2em] text-zinc-500">
{t.marketAnalysis || "MARKET ANALYSIS"}
</h2>
<Badge className="bg-cyan-500/10 text-cyan-400 hover:bg-cyan-500/20 border-cyan-500/30 text-[9px] h-5">
{t.realTime || "LIVE UPLINK"}
</Badge>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="rounded-lg border border-zinc-800 bg-zinc-900/30 p-3 ring-1 ring-inset ring-white/5">
<span className="text-[9px] font-bold text-zinc-500 uppercase tracking-tighter">
{t.liveMetar || "REAL-TIME METAR"}
</span>
<div className="mt-1 flex items-baseline gap-1">
<span className="text-xl font-black text-white">
{overview.current_temp?.toFixed(1) || "--"}
</span>
<span className="text-[10px] font-bold text-zinc-500">
{overview.temp_symbol}
</span>
</div>
</div>
<div className="rounded-lg border border-zinc-800 bg-zinc-900/30 p-3 ring-1 ring-inset ring-white/5">
<span className="text-[9px] font-bold text-zinc-500 uppercase tracking-tighter">
{t.ensembleDeb || "ENSEMBLE DEB"}
</span>
<div className="mt-1 flex items-baseline gap-1">
<span className="text-xl font-black text-cyan-400">
{overview.deb_prediction?.toFixed(1) || "--"}
</span>
<span className="text-[10px] font-bold text-cyan-500">
{overview.temp_symbol}
</span>
</div>
</div>
</div>
{/* Trade Execution Signal */}
<div
className={`rounded-xl border p-4 transition-all duration-500 ${signalColor}`}
>
<div className="flex items-center justify-between mb-3">
<div className="space-y-1">
<span className="text-[10px] font-bold text-zinc-500 uppercase tracking-tighter">
{t.signalStrategy || "SIGNAL STRATEGY"}
</span>
<div className="flex items-center gap-2">
<Zap
className={`h-4 w-4 ${market_scan.available ? "animate-pulse" : ""} ${signalIconColor}`}
/>
<span className="text-sm font-black text-white italic uppercase tracking-wider">
{market_scan.signal_label}
</span>
</div>
</div>
<div className="text-right">
<span className="text-[10px] font-bold text-zinc-500 uppercase tracking-tighter">
{t.confidence || "CONFIDENCE"}
</span>
<div className="mt-1 text-xs font-black text-white opacity-80 uppercase tracking-widest">
{market_scan.confidence}
</div>
</div>
</div>
<div className="h-10 mt-2 flex items-end">
<Sparkline
data={
market_scan.sparkline.length > 0
? market_scan.sparkline
: [1, 1, 1]
}
width={360}
height={35}
color={
market_scan.signal_label === "BUY YES"
? "#10b981"
: market_scan.signal_label === "BUY NO"
? "#f43f5e"
: "#52525b"
}
/>
</div>
</div>
</div>
{/* Probability & Market Pricing */}
<div className="flex-1 overflow-y-auto p-4 space-y-6 custom-scrollbar pb-20">
<section>
<div className="flex items-center gap-2 mb-3">
<Target className="h-3 w-3 text-violet-400" />
<span className="text-[10px] font-black uppercase tracking-[0.15em] text-zinc-400">
{t.yieldArbitrage || "YIELD ARBITRAGE"}
</span>
</div>
<div className="space-y-4">
<div className="space-y-2">
<div className="flex items-center justify-between text-[10px] uppercase font-bold text-zinc-500">
<span>{t.probComparison || "PROB COMPARISON"}</span>
<span className="text-cyan-400">
{t.predictionGap || "GAP"}:{" "}
{market_scan.edge_percent?.toFixed(1) || "0.0"}%
</span>
</div>
{/* Visual Probability Bar */}
<div className="relative h-4 w-full bg-zinc-900 rounded-sm border border-zinc-800 overflow-hidden">
<div
className="absolute h-full bg-zinc-800 border-r border-zinc-700 transition-all duration-700"
style={{ width: `${(market_scan.market_price || 0) * 100}%` }}
/>
<div
className="absolute h-full bg-violet-500/40 border-r border-violet-400 shadow-[0_0_8px_rgba(167,139,250,0.5)] transition-all duration-1000"
style={{
width: `${(market_scan.model_probability || 0) * 100}%`,
}}
/>
</div>
</div>
<div className="grid grid-cols-2 gap-2">
<div className="flex flex-col rounded-md bg-zinc-900/40 px-3 py-2 border border-zinc-800">
<span className="text-[9px] text-zinc-500 uppercase font-black">
{t.marketPriceLabel || "MIDPOINT"}
</span>
<span className="font-mono text-zinc-100 font-bold text-sm">
${market_scan.market_price?.toFixed(2) || "--"}
</span>
</div>
<div className="flex flex-col rounded-md bg-zinc-900/40 px-3 py-2 border border-zinc-800">
<span className="text-[9px] text-violet-500 uppercase font-black">
{t.modelProbLabel || "MODEL PROB"}
</span>
<span className="font-mono text-violet-400 font-bold text-sm">
{((market_scan.model_probability || 0) * 100).toFixed(1)}%
</span>
</div>
</div>
</div>
</section>
<section>
<div className="flex items-center gap-2 mb-4">
<BarChart2 className="h-3 w-3 text-cyan-400" />
<span className="text-[10px] font-black uppercase tracking-[0.15em] text-zinc-400">
{t.multiModelDivergence || "MULTI-MODEL DIVERGENCE"}
</span>
</div>
<div className="space-y-3">
{modelEntries.map((m) => (
<div key={m.label} className="group flex flex-col gap-1 px-1">
<div className="flex justify-between items-center">
<span className="text-[10px] font-bold text-zinc-500 group-hover:text-zinc-300 transition-colors uppercase">
{m.label}
</span>
<span className="text-[10px] font-mono font-bold text-white text-right">
{m.value.toFixed(1)}°
</span>
</div>
<div className="relative h-1 w-full bg-zinc-800/50 rounded-full overflow-hidden">
<div
className={`h-full transition-all duration-500 ${
m.value > 25
? "bg-rose-500 shadow-[0_0_8px_rgba(244,63,94,0.5)]"
: m.value > 15
? "bg-amber-500"
: "bg-cyan-500 shadow-[0_0_8px_rgba(34,211,238,0.5)]"
}`}
style={{ width: `${(m.value / 40) * 100}%` }}
/>
</div>
</div>
))}
</div>
</section>
</div>
{/* Execute Scan Footer */}
<div className="absolute bottom-0 left-0 right-0 p-4 bg-zinc-950/80 backdrop-blur-md border-t border-zinc-800/50">
<button className="w-full rounded-lg bg-cyan-500 py-3 text-[11px] font-black uppercase tracking-widest text-black shadow-[0_0_20px_rgba(34,211,238,0.3)] transition-all hover:bg-cyan-400 hover:scale-[0.98] active:scale-95 disabled:opacity-50 disabled:pointer-events-none">
EXECUTE MARKET RE-SCAN
</button>
</div>
</div>
);
}
-135
View File
@@ -1,135 +0,0 @@
import React from "react";
import { Search, Globe, ChevronRight, Activity } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { CityInfo } from "@/lib/types";
import { Sparkline } from "./Sparkline";
interface CitySidebarProps {
cities: CityInfo[];
activeCity: string;
onSelectCity: (name: string) => void;
t: any;
}
export function CitySidebar({
cities,
activeCity,
onSelectCity,
t,
}: CitySidebarProps) {
return (
<aside className="flex h-full w-[300px] flex-col border-r border-terminal-border bg-terminal/95 transition-all">
{/* Search Header */}
<div className="p-4 border-b border-terminal-border">
<div className="relative group">
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-500 transition-colors group-focus-within:text-neon-cyan" />
<input
type="text"
placeholder={t.lookupPlaceholder || "Search Terminal..."}
className="w-full rounded-lg border border-terminal-border bg-slate-900/50 py-2 pl-9 pr-4 text-xs text-white placeholder:text-slate-600 focus:border-neon-cyan/50 focus:outline-none focus:ring-1 focus:ring-neon-cyan/20"
/>
</div>
</div>
{/* Monitor List */}
<div className="flex-1 overflow-y-auto px-2 py-4 custom-scrollbar">
<div className="mb-4 flex items-center justify-between px-3">
<h3 className="text-[10px] font-black uppercase tracking-[0.2em] text-slate-500">
{t.cityMonitor || "MONITORING NODES"}
</h3>
<Badge
variant="outline"
className="h-5 rounded-md border-terminal-border text-[10px] text-slate-400"
>
{cities.length}
</Badge>
</div>
<div className="space-y-1">
{cities.map((city) => {
const isActive =
activeCity.toLowerCase() === city.name.toLowerCase();
return (
<button
key={city.name}
onClick={() => onSelectCity(city.name)}
className={`group relative flex w-full items-center gap-3 rounded-lg px-3 py-2.5 transition-all ${
isActive
? "bg-neon-cyan/10 ring-1 ring-neon-cyan/30"
: "hover:bg-slate-900/50"
}`}
>
{/* Active Indicator Bar */}
{isActive && (
<div className="absolute left-0 top-1/2 h-4 w-1 -translate-y-1/2 rounded-r bg-neon-cyan shadow-[0_0_8px_rgba(34,211,238,0.8)]" />
)}
<div className="flex flex-1 flex-col items-start overflow-hidden">
<div className="flex w-full items-center justify-between">
<span
className={`truncate text-[11px] font-bold uppercase tracking-wider ${isActive ? "text-neon-cyan" : "text-slate-200"}`}
>
{city.display_name}
</span>
<span className="text-[10px] font-medium text-slate-500">
{city.icao}
</span>
</div>
<div className="mt-1 flex items-center gap-2">
<div
className={`h-1.5 w-1.5 rounded-full ${
city.risk_level === "high"
? "bg-rose-500 shadow-[0_0_4px_rgba(244,63,94,0.6)]"
: city.risk_level === "medium"
? "bg-amber-500"
: "bg-emerald-500"
}`}
/>
<span className="truncate text-[10px] text-slate-500 font-medium">
{city.airport.replace("Airport", "Intl")}
</span>
</div>
</div>
<div className="flex flex-col items-end gap-1 px-1">
<Sparkline
data={[10, 12, 11, 13, 12]}
width={40}
height={15}
strokeWidth={1.5}
color="#475569"
/>
</div>
<div className="flex flex-col items-end gap-1">
<ChevronRight
className={`h-3 w-3 transition-transform ${isActive ? "translate-x-0.5 text-neon-cyan" : "text-slate-700 opacity-0 group-hover:opacity-100"}`}
/>
</div>
</button>
);
})}
</div>
</div>
{/* Terminal Status */}
<div className="border-t border-terminal-border bg-slate-950 p-4">
<div className="flex items-center justify-between mb-3">
<span className="text-[10px] font-black uppercase tracking-widest text-slate-500">
UPLINK STATUS
</span>
<div className="flex items-center gap-1.5">
<div className="h-1.5 w-1.5 animate-pulse rounded-full bg-emerald-500 shadow-[0_0_4px_rgba(16,185,129,0.8)]" />
<span className="text-[10px] font-bold text-emerald-500">LIVE</span>
</div>
</div>
<div className="rounded-md border border-terminal-border bg-slate-900/50 p-2">
<div className="flex items-center gap-2 text-[9px] font-mono text-slate-400">
<Activity className="h-3 w-3 text-neon-cyan" />
<span>LATENCY: 12ms</span>
</div>
</div>
</div>
</aside>
);
}
@@ -1,307 +0,0 @@
"use client";
import React, { useState } from "react";
import { CitySidebar } from "./Sidebar";
import { AnalyticsPanel } from "./AnalyticsPanel";
import {
RefreshCw,
LayoutGrid,
Maximize2,
Layers,
Languages,
Sun,
Moon,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
interface TerminalDashboardProps {
cities: any[];
activeCity: string;
onSelectCity: (name: string) => void;
cityData: any;
marketData: any;
officialData: any;
isLoading: boolean;
refresh: () => void;
lang: any;
setLang: (lang: any) => void;
theme: string;
toggleTheme: () => void;
t: any;
children: React.ReactNode; // For the Map
}
function NavLink({
href,
label,
active = false,
}: {
href: string;
label: string;
active?: boolean;
}) {
return (
<a
href={href}
className={`text-[10px] font-black uppercase tracking-widest transition-colors ${
active
? "text-neon-cyan shadow-[0_0_10px_rgba(34,211,238,0.3)]"
: "text-slate-500 hover:text-white"
}`}
>
{label}
</a>
);
}
export function TerminalDashboard({
cities,
activeCity,
onSelectCity,
cityData,
marketData,
officialData,
isLoading,
refresh,
lang,
setLang,
theme,
toggleTheme,
t,
children,
}: TerminalDashboardProps) {
const currentTemp = cityData?.current?.temp ?? null;
const debPrediction = cityData?.deb?.prediction ?? null;
const marketPrice = marketData?.markets?.[0]?.price ?? 0.62; // Placeholder
const modelEntries = Object.entries(cityData?.multi_model ?? {})
.map(([label, value]) => ({ label, value: value as number }))
.filter((e) => typeof e.value === "number");
return (
<div className="flex h-screen w-full flex-col overflow-hidden bg-terminal font-sans text-slate-200 antialiased">
{/* Top Global Ticker / Nav */}
<div className="flex h-14 items-center justify-between border-b border-terminal-border bg-slate-950/50 px-6 backdrop-blur-md">
<div className="flex items-center gap-6">
<NavLink href="#" label={t.marketScan} active />
<NavLink href="#" label="FORENSICS" />
<NavLink href="#" label="ARBITRAGE" />
</div>
<div className="flex items-center gap-4">
<div className="flex items-center gap-2 rounded-full bg-slate-900 px-3 py-1 border border-terminal-border">
<span className="text-[10px] font-bold text-slate-500 uppercase tracking-tighter">
{t.node || "Node"}:
</span>
<span className="text-[10px] font-mono font-bold text-neon-cyan tracking-widest">
CLOUD-ALPHA-01
</span>
</div>
<button
onClick={refresh}
className="flex items-center gap-2 rounded-lg px-3 py-1.5 text-[10px] font-bold text-slate-400 hover:bg-white/5 hover:text-white transition-all border border-transparent hover:border-terminal-border"
>
<RefreshCw
className={`h-3 w-3 ${isLoading ? "animate-spin" : ""}`}
/>
<span className="uppercase tracking-widest">{t.refresh}</span>
</button>
</div>
<div className="flex items-center gap-3">
<div className="flex items-center border-r border-white/10 pr-3 mr-1 gap-1">
<button
onClick={() => setLang(lang === "en" ? "zh" : "en")}
className="flex h-8 w-8 items-center justify-center rounded-md text-slate-400 hover:bg-white/5 hover:text-neon-cyan transition-all"
title={lang === "en" ? "Switch to Chinese" : "切换为英文"}
>
<Languages className="h-4 w-4" />
</button>
<button
onClick={toggleTheme}
className="flex h-8 w-8 items-center justify-center rounded-md text-slate-400 hover:bg-white/5 hover:text-neon-cyan transition-all"
>
{theme === "dark" ? (
<Sun className="h-4 w-4" />
) : (
<Moon className="h-4 w-4" />
)}
</button>
</div>
<Badge
variant="outline"
className="h-6 border-terminal-border bg-slate-900/50 text-[10px] font-mono text-slate-400"
>
NODE: CLOUD-ALPHA-01
</Badge>
<button
onClick={refresh}
className="flex h-8 items-center gap-2 rounded-md border border-terminal-border bg-slate-900/50 px-3 transition-all hover:bg-slate-800"
>
<RefreshCw
className={`h-3 w-3 text-slate-400 ${isLoading ? "animate-spin" : ""}`}
/>
<span className="text-[10px] font-bold text-slate-300">
REFRESH
</span>
</button>
</div>
</div>
{/* Main Container */}
<div className="flex flex-1 overflow-hidden">
{/* Left: Component List */}
<CitySidebar
cities={cities}
activeCity={activeCity}
onSelectCity={onSelectCity}
t={t}
/>
{/* Center: Interactive Map Area */}
<div className="relative flex-1 bg-slate-950 overflow-hidden flex flex-col">
{/* Map UI Overlay */}
<div className="absolute left-4 top-4 z-10 flex flex-col gap-2">
<div className="rounded-lg border border-terminal-border bg-terminal/80 p-2 backdrop-blur-md shadow-2xl">
<div className="flex flex-col gap-1">
<span className="text-[9px] font-bold text-slate-500 uppercase">
Current Focus
</span>
<span className="text-xs font-black text-white uppercase tracking-widest">
{activeCity}
</span>
</div>
</div>
</div>
<div className="absolute right-4 top-4 z-10 flex flex-col gap-2">
<button className="rounded-md border border-terminal-border bg-terminal/80 p-2 text-slate-400 hover:text-white backdrop-blur-md">
<Layers className="h-4 w-4" />
</button>
<button className="rounded-md border border-terminal-border bg-terminal/80 p-2 text-slate-400 hover:text-white backdrop-blur-md">
<Maximize2 className="h-4 w-4" />
</button>
</div>
{/* The Map */}
<div className="flex-1 h-full min-h-0 relative bg-zinc-900/10">
{children}
</div>
{/* Bottom Map Info Bar */}
<div className="absolute bottom-4 left-4 right-4 z-10 flex items-center justify-between pointer-events-none">
<div className="rounded-md border border-terminal-border bg-terminal/80 px-3 py-1.5 backdrop-blur-md pointer-events-auto">
<span className="text-[9px] font-bold text-slate-500 uppercase tracking-widest">
Projection: Mercator · Layer: Dynamic Thermal Ensemble
</span>
</div>
<div className="flex gap-2 pointer-events-auto">
<Badge className="bg-emerald-500/10 text-emerald-500 border-emerald-500/20">
STABLE VIBE
</Badge>
</div>
</div>
</div>
{/* Right: Intelligence Panel */}
<AnalyticsPanel
data={
{
city: activeCity,
fetched_at: cityData?.updated_at || "",
overview: {
name: activeCity,
display_name: cityData?.display_name || activeCity,
icao: officialData?.aviation_weather?.icao || "",
airport: cityData?.risk?.airport || "",
lat: cityData?.lat || 0,
lon: cityData?.lon || 0,
local_time: cityData?.local_time || "",
local_date: cityData?.local_date || "",
temp_symbol: cityData?.temp_symbol || "°C",
current_temp: currentTemp,
deb_prediction: debPrediction,
risk_level: cityData?.risk?.level || "low",
risk_warning: cityData?.risk?.warning || "",
updated_at: cityData?.updated_at || "",
},
official: {
available: !!officialData?.aviation_weather?.available,
metar: officialData?.aviation_weather?.observation,
weather_gov: officialData?.weather_gov || {},
mgm: cityData?.mgm || {},
mgm_nearby: [],
},
timeseries: {
metar_recent_obs: [],
metar_today_obs: [],
hourly: {},
mgm_hourly: [],
forecast_daily: cityData?.forecast?.multi_day || [],
},
models: cityData?.multi_model || {},
probabilities: cityData?.probabilities || {
mu: null,
distribution: [],
},
market_scan: {
available: !!marketData?.markets?.length,
reason: null,
primary_market: marketData?.markets?.[0] || null,
selected_date: marketData?.target_date || null,
selected_condition_id: null,
selected_slug: null,
temperature_bucket: null,
model_probability: null,
market_price: marketPrice,
edge_percent: null,
signal_label: "MONITOR",
confidence: "low",
yes_token: null,
no_token: null,
yes_buy: null,
yes_sell: null,
no_buy: null,
no_sell: null,
last_trade_price: null,
liquidity: null,
volume: null,
sparkline: [],
recent_trades: [],
websocket: marketData?.websocket || {},
},
risk: cityData?.risk,
ai_analysis: cityData?.ai_analysis || "",
errors: {},
} as any
}
t={t}
/>
</div>
{/* Footer Ticker */}
<footer className="h-8 border-t border-terminal-border bg-terminal-bg flex items-center px-4">
<div className="flex items-center gap-3 overflow-hidden">
<span className="text-[10px] font-bold text-neon-cyan uppercase flex-shrink-0">
TICKER:
</span>
<div className="flex items-center gap-6 animate-marquee whitespace-nowrap">
{cities.slice(0, 10).map((c) => (
<div key={c.name} className="flex items-center gap-2">
<span className="text-[10px] font-bold text-slate-300">
{c.display_name}
</span>
<span className="text-[10px] font-mono text-emerald-500">
24.5°
</span>
<span className="text-[9px] text-slate-600">LTAC</span>
</div>
))}
</div>
</div>
</footer>
</div>
);
}