From 767f7ed6bf48aa383c9e86f784119684efa38c4c Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Thu, 16 Apr 2026 15:19:31 +0800 Subject: [PATCH] Show observed temp and format observation updates --- frontend/lib/dashboard-utils.ts | 153 ++++++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 5 deletions(-) diff --git a/frontend/lib/dashboard-utils.ts b/frontend/lib/dashboard-utils.ts index b823578c..8f484b72 100644 --- a/frontend/lib/dashboard-utils.ts +++ b/frontend/lib/dashboard-utils.ts @@ -2837,9 +2837,151 @@ export function getHistorySummary( }; } +function toFiniteNumber(value: unknown): number | null { + const numeric = Number(value); + return Number.isFinite(numeric) ? numeric : null; +} + +function asRecord(value: unknown): Record | null { + return value && typeof value === "object" + ? (value as Record) + : null; +} + +function firstFiniteNumber(values: unknown[]) { + for (const value of values) { + const numeric = toFiniteNumber(value); + if (numeric != null) return numeric; + } + return null; +} + +function firstNonEmptyString(values: unknown[]) { + for (const value of values) { + const text = String(value ?? "").trim(); + if (text) return text; + } + return null; +} + +function formatObservationUpdate(value: unknown, locale: Locale) { + const raw = String(value ?? "").trim(); + if (!raw) return null; + + const looksDated = + /^\d{4}-\d{2}-\d{2}/.test(raw) || + raw.includes("T") || + /[+-]\d{2}:?\d{2}$/.test(raw); + if (looksDated) { + const parsed = new Date(raw); + if (!Number.isNaN(parsed.getTime())) { + return new Intl.DateTimeFormat(isEnglish(locale) ? "en-US" : "zh-CN", { + day: "2-digit", + hour: "2-digit", + hour12: false, + minute: "2-digit", + month: "2-digit", + }) + .format(parsed) + .replace(",", ""); + } + } + + return normalizeHm(raw) || raw; +} + +function getOfficialObservationCandidates(detail: CityDetail) { + const officialNearby = Array.isArray(detail.official_nearby) + ? detail.official_nearby + : []; + const mgmNearby = Array.isArray(detail.mgm_nearby) ? detail.mgm_nearby : []; + const officialAnchor = + officialNearby.find( + (station) => station.is_settlement_anchor || station.is_airport_station, + ) || officialNearby[0]; + + return { + centerStation: detail.center_station_candidate, + mgmNearby, + officialAnchor, + officialNearby, + }; +} + +function getObservedTemperatureProfile(detail: CityDetail, locale: Locale) { + const { centerStation, officialAnchor, officialNearby, mgmNearby } = + getOfficialObservationCandidates(detail); + const observedTemp = firstFiniteNumber([ + detail.current?.temp, + detail.airport_primary?.temp, + detail.airport_current?.temp, + centerStation?.temp, + officialAnchor?.temp, + officialNearby[0]?.temp, + mgmNearby[0]?.temp, + ]); + + if (observedTemp == null) { + return isEnglish(locale) ? "Unavailable" : "未提供"; + } + + const sourceLabel = firstNonEmptyString([ + detail.current?.settlement_source_label, + detail.airport_primary?.source_label, + detail.airport_current?.source_label, + centerStation?.source_label, + officialAnchor?.source_label, + officialNearby[0]?.source_label, + mgmNearby[0]?.source_label, + getObservationSourceTag(detail), + ]); + const rounded = + Math.abs(observedTemp - Math.round(observedTemp)) < 0.05 + ? String(Math.round(observedTemp)) + : observedTemp.toFixed(1); + + return `${rounded}${detail.temp_symbol || "°C"}${ + sourceLabel ? ` · ${sourceLabel}` : "" + }`; +} + +function getObservationUpdateProfile(detail: CityDetail, locale: Locale) { + const { centerStation, officialAnchor, officialNearby, mgmNearby } = + getOfficialObservationCandidates(detail); + const centerRecord = asRecord(centerStation); + const officialAnchorRecord = asRecord(officialAnchor); + const officialFirstRecord = asRecord(officialNearby[0]); + const mgmFirstRecord = asRecord(mgmNearby[0]); + const rawValue = firstNonEmptyString([ + detail.current?.obs_time, + detail.current?.report_time, + detail.airport_primary?.obs_time, + detail.airport_primary?.report_time, + detail.airport_current?.obs_time, + detail.airport_current?.report_time, + centerRecord?.obs_time, + centerRecord?.report_time, + centerRecord?.time, + officialAnchorRecord?.obs_time, + officialAnchorRecord?.report_time, + officialAnchorRecord?.time, + officialFirstRecord?.obs_time, + officialFirstRecord?.report_time, + officialFirstRecord?.time, + mgmFirstRecord?.obs_time, + mgmFirstRecord?.report_time, + mgmFirstRecord?.time, + detail.updated_at, + ]); + + return ( + formatObservationUpdate(rawValue, locale) || + (isEnglish(locale) ? "Unavailable" : "未提供") + ); +} + export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN") { const risk = detail.risk || {}; - const current = detail.current || {}; const nearbyCount = Array.isArray(detail.mgm_nearby) ? detail.mgm_nearby.length : 0; const nearbySource = String(detail.nearby_source || "").trim().toLowerCase(); const sourceCode = getObservationSourceCode(detail); @@ -2911,12 +3053,13 @@ export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN" ? "Not marked" : "未标注", }, + { + label: isEnglish(locale) ? "Observed temp" : "实测温度", + value: getObservedTemperatureProfile(detail, locale), + }, { label: isEnglish(locale) ? "Observation update" : "观测更新", - value: - current.obs_time || - detail.updated_at || - (isEnglish(locale) ? "Unavailable" : "未提供"), + value: getObservationUpdateProfile(detail, locale), }, { label: isEnglish(locale) ? "Nearby stations" : "周边站点",