移除 Polymarket 价格层 UI:删除 MarketDecisionLine 组件及相关数据流

This commit is contained in:
2569718930@qq.com
2026-05-23 12:16:19 +08:00
parent eeb5f563db
commit 348dfe132a
6 changed files with 3 additions and 129 deletions
@@ -10,7 +10,6 @@ import { MobileDecisionCard } from "@/components/dashboard/scan-terminal/MobileD
import { ModelEvidencePanel } from "@/components/dashboard/scan-terminal/ModelEvidencePanel";
import { WeatherDecisionBand } from "@/components/dashboard/scan-terminal/WeatherDecisionBand";
import {
buildMarketDecisionView,
buildWeatherDecisionView,
resolveExpectedHighCandidate,
} from "@/components/dashboard/scan-terminal/city-card-decision-utils";
@@ -24,7 +23,6 @@ import { LoadingSignal } from "@/components/dashboard/scan-terminal/LoadingSigna
import type { AiPinnedCity } from "@/components/dashboard/scan-terminal/types";
import {
useAiCityForecast,
useCityMarketScan,
} from "@/components/dashboard/scan-terminal/use-ai-city-card-data";
import { getDisplayAirportPrimary } from "@/lib/airport-observation-display";
import type { CityDetail, ScanOpportunityRow } from "@/lib/dashboard-types";
@@ -184,28 +182,6 @@ function buildModelFreshnessValue(detail: CityDetail | null, locale: string, isE
);
}
function buildMarketFreshnessValue({
isEn,
marketScan,
marketStatus,
}: {
isEn: boolean;
marketScan: ReturnType<typeof useCityMarketScan>["marketScan"];
marketStatus: ReturnType<typeof useCityMarketScan>["marketStatus"];
}) {
if (marketStatus === "loading") return isEn ? "syncing" : "同步中";
if (!marketScan?.available) return isEn ? "temporarily unavailable" : "暂不可用";
const quoteAgeMs = Number(
marketScan.quote_age_ms ??
marketScan.yes_token?.quote_age_ms ??
marketScan.no_token?.quote_age_ms,
);
if (Number.isFinite(quoteAgeMs) && quoteAgeMs >= 0) {
return formatFreshnessAge(quoteAgeMs / 60_000, isEn);
}
return isEn ? "synced" : "已同步";
}
export function AiPinnedCityCard({
item,
detail,
@@ -287,11 +263,6 @@ export function AiPinnedCityCard({
locale,
report,
});
const { marketScan, marketStatus } = useCityMarketScan({
detail,
detailCityName,
enabled: Boolean(detail),
});
const isRefreshing = refreshingDetail || aiForecast.status === "loading";
const [isCompactCard, setIsCompactCard] = useState(false);
@@ -382,13 +353,6 @@ export function AiPinnedCityCard({
debNumber != null
? formatTemperatureValue(debNumber, tempSymbol, { digits: 1 })
: "--";
const marketDecisionView = buildMarketDecisionView({
expectedHigh: decisionExpectedHighNumber,
isEn,
marketScan,
marketStatus,
tempSymbol,
});
const aiMeta = aiCityForecast?._polyweather_meta || null;
const guardReason = aiMeta?.deterministic_guard_reason || {};
const observationStale = Boolean(
@@ -436,7 +400,6 @@ export function AiPinnedCityCard({
aiRuleEvidenceMode,
isEn,
isHkoObservation,
marketDecisionView,
modelHighlyConsistent,
needsNextBulletin,
observationStale,
@@ -451,16 +414,6 @@ export function AiPinnedCityCard({
value: buildModelFreshnessValue(detail, locale, isEn),
tone: "fresh" as const,
},
{
label: isEn ? "Market" : "市场价格",
value: buildMarketFreshnessValue({ isEn, marketScan, marketStatus }),
tone:
marketDecisionView.status === "ready"
? ("fresh" as const)
: marketDecisionView.status === "loading"
? ("loading" as const)
: ("stale" as const),
},
];
const freshnessSeparator = isEn ? ": " : "";
const localizedRisksRaw =
@@ -480,17 +433,6 @@ export function AiPinnedCityCard({
(isEn ? aiForecast.payload?.reason_en : aiForecast.payload?.reason_zh) ||
aiForecast.payload?.reason ||
"";
const marketLineText =
marketDecisionView.status === "ready"
? `${marketDecisionView.bucketLabel} · ${marketDecisionView.priceText}`
: marketDecisionView.status === "loading"
? isEn
? "syncing"
: "同步中"
: isEn
? "temporarily unavailable"
: "暂不可用";
const collapseId = `ai-city-body-${normalizeCityKey(item.cityName) || item.addedAt}`;
const loadingCopy = getCityLoadingCopy({ isEn, isHkoObservation });
const handleRefresh = (event: MouseEvent<HTMLButtonElement>) => {
@@ -522,7 +464,6 @@ export function AiPinnedCityCard({
tabIndex={-1}
data-ai-status={decisionState.aiStatus}
data-evidence-quality={decisionState.evidenceQuality}
data-market-status={decisionState.marketStatus}
data-recommendation={decisionState.recommendation}
data-urgency={decisionState.urgency}
>
@@ -550,8 +491,6 @@ export function AiPinnedCityCard({
isRefreshing={isRefreshing}
localModelSupportNote={localModelSupportNote}
localizedFinalJudgment={localizedFinalJudgment}
marketDecisionView={marketDecisionView}
marketLineText={marketLineText}
onRefresh={handleRefresh}
onRemove={handleRemove}
peakWindow={peakWindow}
@@ -587,8 +526,6 @@ export function AiPinnedCityCard({
decisionView={decisionView}
decisionWhyText={decisionState.primaryReason}
isEn={isEn}
marketDecisionView={marketDecisionView}
marketLineText={marketLineText}
paceDeltaText={paceView?.deltaText || "--"}
/>
@@ -11,9 +11,7 @@ import {
type StatusTone,
} from "@/components/dashboard/scan-terminal/CityStatusTags";
import { LoadingSignal } from "@/components/dashboard/scan-terminal/LoadingSignal";
import { MarketDecisionLine } from "@/components/dashboard/scan-terminal/MarketDecisionLine";
import { ModelEvidencePanel } from "@/components/dashboard/scan-terminal/ModelEvidencePanel";
import type { MarketDecisionView } from "@/components/dashboard/scan-terminal/city-card-decision-utils";
import type { CityDecisionState } from "@/components/dashboard/scan-terminal/city-decision-state";
import {
getCityLoadingCopy,
@@ -48,8 +46,6 @@ export function MobileDecisionCard({
isRefreshing,
localModelSupportNote,
localizedFinalJudgment,
marketDecisionView,
marketLineText,
onRefresh,
onRemove,
peakWindow,
@@ -78,8 +74,6 @@ export function MobileDecisionCard({
isRefreshing: boolean;
localModelSupportNote: string;
localizedFinalJudgment: string;
marketDecisionView: MarketDecisionView;
marketLineText: string;
onRefresh: (event: MouseEvent<HTMLButtonElement>) => void;
onRemove: (event: MouseEvent<HTMLButtonElement>) => void;
peakWindow: string;
@@ -140,11 +134,6 @@ export function MobileDecisionCard({
<p className="scan-mobile-decision-reason">{decisionState.primaryReason}</p>
<CityStatusTags tags={statusTags} />
<MarketDecisionLine
isEn={isEn}
marketDecisionView={marketDecisionView}
marketLineText={marketLineText}
/>
{!detail ? (
<div className="scan-ai-city-loading">
@@ -1,25 +1,17 @@
"use client";
import clsx from "clsx";
import type {
MarketDecisionView,
WeatherDecisionView,
} from "@/components/dashboard/scan-terminal/city-card-decision-utils";
import { MarketDecisionLine } from "@/components/dashboard/scan-terminal/MarketDecisionLine";
import type { WeatherDecisionView } from "@/components/dashboard/scan-terminal/city-card-decision-utils";
export function WeatherDecisionBand({
decisionView,
decisionWhyText,
isEn,
marketDecisionView,
marketLineText,
paceDeltaText,
}: {
decisionView: WeatherDecisionView;
decisionWhyText: string;
isEn: boolean;
marketDecisionView: MarketDecisionView;
marketLineText: string;
paceDeltaText: string;
}) {
return (
@@ -28,11 +20,6 @@ export function WeatherDecisionBand({
<span>{decisionView.kicker}</span>
<strong>{decisionView.action}</strong>
<p className="scan-ai-decision-why">{decisionWhyText}</p>
<MarketDecisionLine
isEn={isEn}
marketDecisionView={marketDecisionView}
marketLineText={marketLineText}
/>
</div>
<div className="scan-ai-decision-metrics">
<span>
@@ -42,10 +29,6 @@ export function WeatherDecisionBand({
<span>
{isEn ? "Path delta" : "路径偏差"} <b>{paceDeltaText}</b>
</span>
<span>
{isEn ? "Quote status" : "报价状态"}{" "}
<b>{marketDecisionView.status === "ready" ? (isEn ? "Ready" : "已同步") : marketDecisionView.status === "loading" ? (isEn ? "Loading" : "同步中") : (isEn ? "Unavailable" : "不可用")}</b>
</span>
</div>
</section>
);
@@ -29,7 +29,6 @@ export function runTests() {
aiRuleEvidenceMode: false,
isEn: false,
isHkoObservation: false,
marketDecisionView: market("ready"),
modelHighlyConsistent: true,
needsNextBulletin: false,
observationStale: false,
@@ -51,7 +50,6 @@ export function runTests() {
aiRuleEvidenceMode: false,
isEn: false,
isHkoObservation: false,
marketDecisionView: market("unavailable"),
modelHighlyConsistent: false,
needsNextBulletin: false,
observationStale: false,
@@ -61,9 +59,6 @@ export function runTests() {
peakHasPassed: false,
});
assert.equal(marketUnavailable.marketStatus, "unavailable");
assert.match(marketUnavailable.primaryReason, /暂无可交易价格/);
assert.doesNotMatch(marketUnavailable.primaryReason, /未接入|系统缺失|系统坏/);
const fallback = buildCityDecisionState({
aiCityForecast: null,
@@ -71,7 +66,6 @@ export function runTests() {
aiRuleEvidenceMode: true,
isEn: false,
isHkoObservation: false,
marketDecisionView: market("ready"),
modelHighlyConsistent: false,
needsNextBulletin: false,
observationStale: false,
@@ -91,7 +85,6 @@ export function runTests() {
aiRuleEvidenceMode: false,
isEn: false,
isHkoObservation: false,
marketDecisionView: market("ready"),
modelHighlyConsistent: false,
needsNextBulletin: true,
observationStale: false,
@@ -22,7 +22,6 @@ export function runTests() {
aiRuleEvidenceMode: false,
isEn: false,
isHkoObservation: false,
marketDecisionView: readyMarket,
modelHighlyConsistent: true,
needsNextBulletin: false,
observationStale: false,
@@ -47,7 +46,6 @@ export function runTests() {
aiRuleEvidenceMode: false,
isEn: false,
isHkoObservation: false,
marketDecisionView: readyMarket,
modelHighlyConsistent: false,
needsNextBulletin: false,
observationStale: true,
@@ -1,4 +1,3 @@
import type { MarketDecisionView } from "@/components/dashboard/scan-terminal/city-card-decision-utils";
import type {
AiCityForecastPayload,
AiCityForecastState,
@@ -12,7 +11,6 @@ export type CityDecisionAiStatus =
| "deepseek-loading"
| "complete"
| "fallback";
export type CityDecisionMarketStatus = "ready" | "loading" | "unavailable";
export type StatusBadgeTone = "green" | "blue" | "amber" | "red" | "muted";
export type StatusBadge = {
@@ -27,8 +25,6 @@ export type CityDecisionState = {
aiStatus: CityDecisionAiStatus;
aiStatusLabel: string;
aiStatusTone: StatusBadgeTone;
marketStatus: CityDecisionMarketStatus;
marketStatusTone: StatusBadgeTone;
badges: StatusBadge[];
primaryReason: string;
};
@@ -82,19 +78,12 @@ function resolveAiStatusView(aiStatus: CityDecisionAiStatus, isEn: boolean) {
};
}
function resolveMarketTone(status: CityDecisionMarketStatus): StatusBadgeTone {
if (status === "ready") return "green";
if (status === "loading") return "blue";
return "muted";
}
export function buildCityDecisionState({
aiCityForecast,
aiForecast,
aiRuleEvidenceMode,
isEn,
isHkoObservation,
marketDecisionView,
modelHighlyConsistent,
needsNextBulletin,
observationStale,
@@ -108,7 +97,6 @@ export function buildCityDecisionState({
aiRuleEvidenceMode: boolean;
isEn: boolean;
isHkoObservation: boolean;
marketDecisionView: MarketDecisionView;
modelHighlyConsistent: boolean;
needsNextBulletin: boolean;
observationStale: boolean;
@@ -119,11 +107,9 @@ export function buildCityDecisionState({
}): CityDecisionState {
const aiStatus = resolveAiStatus({ aiCityForecast, aiForecast, aiRuleEvidenceMode });
const aiStatusView = resolveAiStatusView(aiStatus, isEn);
const marketStatus = marketDecisionView.status;
const marketStatusTone = resolveMarketTone(marketStatus);
const evidenceQuality: CityDecisionEvidenceQuality = observationStale
? "stale"
: aiStatus === "fallback" || marketStatus === "unavailable"
: aiStatus === "fallback"
? "mixed"
: "fresh";
const urgency: CityDecisionUrgency = peakHasPassed
@@ -152,11 +138,7 @@ export function buildCityDecisionState({
? isEn
? "Observation is stale and needs the next report. Use only as background reference until fresh data arrives."
: "观测已过旧,需要下一报文确认。当前数据仅作背景参考,建议等待新报文后再做判断。"
: marketStatus === "unavailable"
? isEn
? "Weather evidence is usable, but no tradable quote is available yet. Weather judgment still applies."
: "天气证据可参考,但暂无可交易价格。天气判断仍可参考,无需急于操作。"
: modelHighlyConsistent
: modelHighlyConsistent
? isEn
? "Models are aligned; wait for observation confirmation. A clear direction should emerge after the next report."
: "模型高度一致,等待实测确认。下一报文后方向会更明确。"
@@ -205,12 +187,6 @@ export function buildCityDecisionState({
tone: aiStatusView.tone,
}
: null,
marketStatus === "unavailable"
? {
label: isEn ? "Market unavailable" : "市场价暂不可用",
tone: marketStatusTone,
}
: null,
modelHighlyConsistent
? {
label: isEn ? "Models agree" : "模型高度一致",
@@ -232,8 +208,6 @@ export function buildCityDecisionState({
aiStatus,
aiStatusLabel: aiStatusView.label,
aiStatusTone: aiStatusView.tone,
marketStatus,
marketStatusTone,
badges,
primaryReason,
};