"use client"; import type { CityDetail } from "@/lib/dashboard-types"; import { getDisplayAirportPrimary } from "@/lib/airport-observation-display"; import { formatTemperatureValue } from "@/lib/temperature-utils"; const FOCUS_RUNWAY_PAIRS: Record> = { chongqing: [["02L", "20R"]], shanghai: [["17L", "35R"]], wuhan: [["04", "22"]], beijing: [["01", "19"]], guangzhou: [["02L", "20R"]], chengdu: [["02L", "20R"]], seoul: [["15R", "33L"]], }; function normalizeRunwayLabel(value?: string | null) { return String(value || "").trim().toUpperCase().replace(/\s+/g, ""); } function normalizeCityKey(value?: string | null) { return String(value || "").trim().toLowerCase().replace(/[\s_-]+/g, ""); } function pairKey(pair: [string, string]) { return pair.map(normalizeRunwayLabel).sort().join("/"); } function toFiniteNumber(value: unknown) { const numeric = Number(value); return Number.isFinite(numeric) ? numeric : null; } function formatObsTime(value: unknown) { const raw = String(value || "").trim(); if (!raw) return ""; if (raw.includes("T")) { const parsed = new Date(raw); if (!Number.isNaN(parsed.getTime())) { return `${String(parsed.getUTCHours()).padStart(2, "0")}:${String( parsed.getUTCMinutes(), ).padStart(2, "0")}Z`; } } return raw.length >= 16 && raw[10] === " " ? raw.slice(11, 16) : raw; } function buildFocusedRunwayEvidence(detail: CityDetail | null) { if (!detail) return null; const cityKey = normalizeCityKey(detail.name) || normalizeCityKey(detail.display_name); const focusPairs = FOCUS_RUNWAY_PAIRS[cityKey]; if (!focusPairs?.length) return null; const focusKeys = new Set(focusPairs.map(pairKey)); const runwayObs = detail.amos?.runway_obs || {}; const runwayPairs = runwayObs.runway_pairs || []; const runwayTemps = runwayObs.temperatures || []; const pointTemps = runwayObs.point_temperatures || []; const rows: Array<{ label: string; maxTemp: number; values: number[]; }> = []; runwayPairs.forEach((rawPair, index) => { const pair = rawPair as [string, string]; if (!Array.isArray(pair) || pair.length < 2) return; if (!focusKeys.has(pairKey(pair))) return; const values = [ ...(Array.isArray(runwayTemps[index]) ? runwayTemps[index] : []), toFiniteNumber(pointTemps[index]?.tdz_temp), toFiniteNumber(pointTemps[index]?.mid_temp), toFiniteNumber(pointTemps[index]?.end_temp), ].filter((value): value is number => Number.isFinite(value)); if (!values.length) return; rows.push({ label: `${normalizeRunwayLabel(pair[0])}/${normalizeRunwayLabel(pair[1])}`, maxTemp: Math.max(...values), values, }); }); if (!rows.length) return null; return { observedAt: formatObsTime(detail.amos?.observation_time_local) || formatObsTime(detail.amos?.observation_time), rows, sourceLabel: detail.amos?.source_label || detail.amos?.source || "AMOS", }; } export function AirportEvidencePanel({ detail, isEn, }: { detail: CityDetail | null; isEn: boolean; }) { const airportPrimary = getDisplayAirportPrimary(detail); const airportCurrent = detail?.airport_current; const station = airportPrimary || airportCurrent || null; const runwayEvidence = buildFocusedRunwayEvidence(detail); const tempSymbol = detail?.temp_symbol || "°C"; if (!station && !runwayEvidence) return null; return (
{isEn ? "Airport live evidence" : "机场实时证据"}

{isEn ? "Airport / focused runway" : "机场主站 / 重点跑道"}

{station ? (
{isEn ? "Airport station" : "机场主站"} {station.temp != null && Number.isFinite(Number(station.temp)) ? formatTemperatureValue(Number(station.temp), tempSymbol, { digits: 1 }) : "--"} {[station.station_label || station.station_code, station.source_label || "METAR", formatObsTime(station.obs_time || station.report_time)] .filter(Boolean) .join(" · ")}
) : null} {runwayEvidence?.rows.map((row) => (
{isEn ? "Focused runway" : "重点跑道"} {formatTemperatureValue(row.maxTemp, tempSymbol, { digits: 1 })} {[row.label, runwayEvidence.sourceLabel, runwayEvidence.observedAt] .filter(Boolean) .join(" · ")}
))}
); }