{reason}
import { useMemo } from "react";
import clsx from "clsx";
import type { ScanOpportunityRow, ScanTerminalResponse } from "@/lib/dashboard-types";
import { formatTemperatureValue } from "@/lib/dashboard-utils";
import { LoadingSignal } from "@/components/dashboard/scan-terminal/LoadingSignal";
import {
formatRowPrice,
formatRowProbability,
formatRowSignedPercent,
getPeakCountdownMeta,
getRowDecisionMeta,
getRowTemperatureBucket,
pickOpportunitySections,
} from "@/components/dashboard/scan-terminal/decision-utils";
function OpportunityDecisionCard({
row,
locale,
selected,
onOpenDecision,
onSelectRow,
}: {
row: ScanOpportunityRow;
locale: string;
selected: boolean;
onOpenDecision: (row: ScanOpportunityRow) => void;
onSelectRow: (row: ScanOpportunityRow) => void;
}) {
const isEn = locale === "en-US";
const displayName = row.city_display_name || row.display_name || row.city;
const unit = row.temp_symbol || row.target_unit || "°C";
const decision = getRowDecisionMeta(row, locale);
const phase = getPeakCountdownMeta(row, locale);
const modelProb = row.model_event_probability ?? row.model_probability ?? row.peak_probability ?? null;
const marketProb = row.market_event_probability ?? row.market_probability ?? null;
const price = row.yes_ask ?? row.ask ?? row.yes_bid ?? row.bid ?? row.midpoint ?? null;
const confidence =
row.ai_confidence ||
row.ai_city_confidence ||
row.ai_forecast_confidence ||
(row.signal_confidence != null ? formatRowProbability(row.signal_confidence) : "--");
const predicted =
row.ai_predicted_max ??
row.deb_prediction ??
row.cluster_center ??
row.current_max_so_far ??
null;
const reason = decision.reason.length > 128 ? `${decision.reason.slice(0, 125)}…` : decision.reason;
return (
{reason}
{error || (isEn ? "Use the map to add cities, or refresh after the scan backend is ready." : "可以先用地图添加城市;扫描后端就绪后会显示今日机会榜。")}
{isEn ? "Cards translate weather, METAR and Polymarket pricing into action states." : "把天气、METAR 与 Polymarket 报价先翻译成行动状态,再让你展开验证。"}
{section.subtitle}