fix: align hong kong compact hko stat
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
|||||||
normObs,
|
normObs,
|
||||||
prefersHighFrequencyRunwayResolution,
|
prefersHighFrequencyRunwayResolution,
|
||||||
readSessionCache,
|
readSessionCache,
|
||||||
|
selectCompactSecondaryTemp,
|
||||||
selectDisplayRunwayTemp,
|
selectDisplayRunwayTemp,
|
||||||
seedHourlyForecastFromRow,
|
seedHourlyForecastFromRow,
|
||||||
shouldPollLiveChart,
|
shouldPollLiveChart,
|
||||||
@@ -635,6 +636,7 @@ export function LiveTemperatureThresholdChart({
|
|||||||
}, [chartSeries, userToggledKeys, city, showRunwayDetails]);
|
}, [chartSeries, userToggledKeys, city, showRunwayDetails]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
isHKO,
|
||||||
isShenzhen,
|
isShenzhen,
|
||||||
metarHeaderLabel,
|
metarHeaderLabel,
|
||||||
metarHighLabel,
|
metarHighLabel,
|
||||||
@@ -642,11 +644,17 @@ export function LiveTemperatureThresholdChart({
|
|||||||
runwayHighLabel,
|
runwayHighLabel,
|
||||||
} = useMemo(() => getLiveObservationLabels(row, chartHourly), [row, chartHourly]);
|
} = useMemo(() => getLiveObservationLabels(row, chartHourly), [row, chartHourly]);
|
||||||
|
|
||||||
const { currentRunwayTemp, observedHighMetar, observedHighRunway } = useMemo(
|
const { currentMetarTemp, currentRunwayTemp, observedHighMetar, observedHighRunway } = useMemo(
|
||||||
() => getObservationDisplayMetrics(row, chartHourly, settlementPlate),
|
() => getObservationDisplayMetrics(row, chartHourly, settlementPlate),
|
||||||
[row, chartHourly, settlementPlate],
|
[row, chartHourly, settlementPlate],
|
||||||
);
|
);
|
||||||
const displayRunwayTemp = selectDisplayRunwayTemp(liveTemp, currentRunwayTemp, hasRunwayData);
|
const displayRunwayTemp = selectDisplayRunwayTemp(liveTemp, currentRunwayTemp, hasRunwayData);
|
||||||
|
const displayMetarTemp = selectCompactSecondaryTemp({
|
||||||
|
isHKO,
|
||||||
|
isShenzhen,
|
||||||
|
displayMetarTemp: currentMetarTemp,
|
||||||
|
observedHighMetar,
|
||||||
|
});
|
||||||
const wundergroundDailyHigh = getWundergroundDailyHigh(chartHourly);
|
const wundergroundDailyHigh = getWundergroundDailyHigh(chartHourly);
|
||||||
|
|
||||||
const localDateStr = chartLocalDate || new Date().toISOString().slice(0, 10);
|
const localDateStr = chartLocalDate || new Date().toISOString().slice(0, 10);
|
||||||
@@ -805,6 +813,7 @@ export function LiveTemperatureThresholdChart({
|
|||||||
visible_series: activeSeries.map((item) => item.key),
|
visible_series: activeSeries.map((item) => item.key),
|
||||||
live_temp: liveTemp,
|
live_temp: liveTemp,
|
||||||
current_runway_temp: currentRunwayTemp,
|
current_runway_temp: currentRunwayTemp,
|
||||||
|
current_metar_temp: currentMetarTemp,
|
||||||
observed_high_metar: observedHighMetar,
|
observed_high_metar: observedHighMetar,
|
||||||
observed_high_runway: observedHighRunway,
|
observed_high_runway: observedHighRunway,
|
||||||
});
|
});
|
||||||
@@ -814,6 +823,7 @@ export function LiveTemperatureThresholdChart({
|
|||||||
chartSeries,
|
chartSeries,
|
||||||
city,
|
city,
|
||||||
compact,
|
compact,
|
||||||
|
currentMetarTemp,
|
||||||
currentRunwayTemp,
|
currentRunwayTemp,
|
||||||
detailError,
|
detailError,
|
||||||
hasRunwayData,
|
hasRunwayData,
|
||||||
@@ -960,6 +970,7 @@ export function LiveTemperatureThresholdChart({
|
|||||||
metarHighLabel={metarHighLabel}
|
metarHighLabel={metarHighLabel}
|
||||||
isShenzhen={isShenzhen}
|
isShenzhen={isShenzhen}
|
||||||
displayRunwayTemp={displayRunwayTemp}
|
displayRunwayTemp={displayRunwayTemp}
|
||||||
|
displayMetarTemp={displayMetarTemp}
|
||||||
observedHighMetar={observedHighMetar}
|
observedHighMetar={observedHighMetar}
|
||||||
observedHighRunway={observedHighRunway}
|
observedHighRunway={observedHighRunway}
|
||||||
wundergroundDailyHigh={wundergroundDailyHigh}
|
wundergroundDailyHigh={wundergroundDailyHigh}
|
||||||
@@ -1034,4 +1045,5 @@ export const __getInitialDetailLoadDelayMsForTest = getInitialDetailLoadDelayMs;
|
|||||||
export const __shouldFetchCityDetailForChartForTest = shouldFetchCityDetailForChart;
|
export const __shouldFetchCityDetailForChartForTest = shouldFetchCityDetailForChart;
|
||||||
export const __shouldPollLiveChartForTest = shouldPollLiveChart;
|
export const __shouldPollLiveChartForTest = shouldPollLiveChart;
|
||||||
export const __mergePatchIntoHourlyForTest = mergePatchIntoHourly;
|
export const __mergePatchIntoHourlyForTest = mergePatchIntoHourly;
|
||||||
|
export const __selectCompactSecondaryTempForTest = selectCompactSecondaryTemp;
|
||||||
export const __selectDisplayRunwayTempForTest = selectDisplayRunwayTemp;
|
export const __selectDisplayRunwayTempForTest = selectDisplayRunwayTemp;
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ export function TemperatureStatsBars({
|
|||||||
metarHighLabel,
|
metarHighLabel,
|
||||||
isShenzhen,
|
isShenzhen,
|
||||||
displayRunwayTemp,
|
displayRunwayTemp,
|
||||||
|
displayMetarTemp,
|
||||||
observedHighMetar,
|
observedHighMetar,
|
||||||
observedHighRunway,
|
observedHighRunway,
|
||||||
wundergroundDailyHigh,
|
wundergroundDailyHigh,
|
||||||
@@ -97,6 +98,7 @@ export function TemperatureStatsBars({
|
|||||||
metarHighLabel: string;
|
metarHighLabel: string;
|
||||||
isShenzhen: boolean;
|
isShenzhen: boolean;
|
||||||
displayRunwayTemp: number | null;
|
displayRunwayTemp: number | null;
|
||||||
|
displayMetarTemp: number | null;
|
||||||
observedHighMetar: number | null;
|
observedHighMetar: number | null;
|
||||||
observedHighRunway: number | null;
|
observedHighRunway: number | null;
|
||||||
wundergroundDailyHigh: number | null;
|
wundergroundDailyHigh: number | null;
|
||||||
@@ -129,7 +131,7 @@ export function TemperatureStatsBars({
|
|||||||
<span className="text-slate-300">|</span>
|
<span className="text-slate-300">|</span>
|
||||||
<span className="font-semibold text-slate-500">
|
<span className="font-semibold text-slate-500">
|
||||||
{labels.compactSecondary}:{" "}
|
{labels.compactSecondary}:{" "}
|
||||||
<strong className="text-blue-600 font-mono">{temp(observedHighMetar, tempSymbol)}</strong>
|
<strong className="text-blue-600 font-mono">{temp(displayMetarTemp, tempSymbol)}</strong>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
+63
@@ -9,6 +9,7 @@ import {
|
|||||||
__getVisibleTemperatureSeriesForTest,
|
__getVisibleTemperatureSeriesForTest,
|
||||||
__isTemperatureSeriesVisibleByDefaultForTest,
|
__isTemperatureSeriesVisibleByDefaultForTest,
|
||||||
__mergePatchIntoHourlyForTest,
|
__mergePatchIntoHourlyForTest,
|
||||||
|
__selectCompactSecondaryTempForTest,
|
||||||
__selectDisplayRunwayTempForTest,
|
__selectDisplayRunwayTempForTest,
|
||||||
} from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart";
|
} from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart";
|
||||||
import { __buildTemperatureTooltipRowsForTest } from "@/components/dashboard/scan-terminal/TemperatureTooltipContent";
|
import { __buildTemperatureTooltipRowsForTest } from "@/components/dashboard/scan-terminal/TemperatureTooltipContent";
|
||||||
@@ -976,6 +977,68 @@ export function runTests() {
|
|||||||
"runway header should prefer runway-history current temp even when the latest detail payload lacks runway_obs snapshot",
|
"runway header should prefer runway-history current temp even when the latest detail payload lacks runway_obs snapshot",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hongKongMetrics = __getObservationDisplayMetricsForTest(
|
||||||
|
{
|
||||||
|
city: "hong kong",
|
||||||
|
local_date: "2026-06-06",
|
||||||
|
local_time: "16:48",
|
||||||
|
tz_offset_seconds: 8 * 60 * 60,
|
||||||
|
current_temp: 30.4,
|
||||||
|
current_max_so_far: 30.4,
|
||||||
|
temp_symbol: "°C",
|
||||||
|
} as any,
|
||||||
|
{
|
||||||
|
localTime: "16:48",
|
||||||
|
times: ["00:00", "12:00", "18:00", "23:00"],
|
||||||
|
temps: [28.0, 31.0, 29.0, 28.0],
|
||||||
|
settlementTodayObs: [
|
||||||
|
{ time: "14:00", temp: 30.4 },
|
||||||
|
{ time: "16:40", temp: 28.1 },
|
||||||
|
],
|
||||||
|
airportPrimary: {
|
||||||
|
temp: 28.7,
|
||||||
|
obs_time: "2026-06-06T16:48:00+08:00",
|
||||||
|
source_code: "cowin_obs",
|
||||||
|
source_label: "CoWIN 6087",
|
||||||
|
},
|
||||||
|
airportPrimaryTodayObs: [
|
||||||
|
{ time: "16:47", temp: 28.6 },
|
||||||
|
{ time: "16:48", temp: 28.7 },
|
||||||
|
],
|
||||||
|
} as any,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
hongKongMetrics.currentRunwayTemp === 28.7,
|
||||||
|
"Hong Kong primary compact stat should stay on the latest CoWIN reference-station point",
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
(hongKongMetrics as any).currentMetarTemp === 28.1,
|
||||||
|
"Hong Kong secondary compact stat should have the latest HKO point separate from the HKO daily high",
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
hongKongMetrics.observedHighMetar === 30.4,
|
||||||
|
"Hong Kong HKO daily high should still be available for expanded daily-high summaries",
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
__selectCompactSecondaryTempForTest({
|
||||||
|
isHKO: true,
|
||||||
|
isShenzhen: false,
|
||||||
|
displayMetarTemp: (hongKongMetrics as any).currentMetarTemp,
|
||||||
|
observedHighMetar: hongKongMetrics.observedHighMetar,
|
||||||
|
}) === 28.1,
|
||||||
|
"Hong Kong compact HKO stat should render the latest HKO point, not the HKO daily high",
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
__selectCompactSecondaryTempForTest({
|
||||||
|
isHKO: false,
|
||||||
|
isShenzhen: false,
|
||||||
|
displayMetarTemp: 72.0,
|
||||||
|
observedHighMetar: 73.9,
|
||||||
|
}) === 73.9,
|
||||||
|
"non-HKO compact secondary stat should keep the existing daily-high behavior",
|
||||||
|
);
|
||||||
|
|
||||||
const wuhanRunwayChart = __buildTemperatureChartDataForTest(
|
const wuhanRunwayChart = __buildTemperatureChartDataForTest(
|
||||||
{
|
{
|
||||||
city: "wuhan",
|
city: "wuhan",
|
||||||
|
|||||||
@@ -845,9 +845,14 @@ function getObservationDisplayMetrics(
|
|||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentMetarTemp =
|
||||||
|
latestSettlement ??
|
||||||
|
latestMetar ??
|
||||||
|
airportCurrentTemp ??
|
||||||
|
null;
|
||||||
const observedHighMetar = airportHigh ?? highSettlement ?? highMetar ?? rowMetarHigh ?? null;
|
const observedHighMetar = airportHigh ?? highSettlement ?? highMetar ?? rowMetarHigh ?? null;
|
||||||
|
|
||||||
return { currentRunwayTemp, observedHighMetar, observedHighRunway };
|
return { currentMetarTemp, currentRunwayTemp, observedHighMetar, observedHighRunway };
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectDisplayRunwayTemp(
|
function selectDisplayRunwayTemp(
|
||||||
@@ -859,6 +864,23 @@ function selectDisplayRunwayTemp(
|
|||||||
return liveTemp;
|
return liveTemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectCompactSecondaryTemp({
|
||||||
|
isHKO,
|
||||||
|
isShenzhen,
|
||||||
|
displayMetarTemp,
|
||||||
|
observedHighMetar,
|
||||||
|
}: {
|
||||||
|
isHKO: boolean;
|
||||||
|
isShenzhen: boolean;
|
||||||
|
displayMetarTemp: number | null;
|
||||||
|
observedHighMetar: number | null;
|
||||||
|
}) {
|
||||||
|
if (isHKO && !isShenzhen && displayMetarTemp !== null) {
|
||||||
|
return displayMetarTemp;
|
||||||
|
}
|
||||||
|
return observedHighMetar;
|
||||||
|
}
|
||||||
|
|
||||||
function isSettlementRunway(row: ScanOpportunityRow | null, rwy: string) {
|
function isSettlementRunway(row: ScanOpportunityRow | null, rwy: string) {
|
||||||
const cityKey = normalizeCityKey(row?.city);
|
const cityKey = normalizeCityKey(row?.city);
|
||||||
const settlementPairs = SETTLEMENT_RUNWAY_PAIRS[cityKey] || [];
|
const settlementPairs = SETTLEMENT_RUNWAY_PAIRS[cityKey] || [];
|
||||||
@@ -2485,6 +2507,7 @@ export {
|
|||||||
normalizeCityKey,
|
normalizeCityKey,
|
||||||
prefersHighFrequencyRunwayResolution,
|
prefersHighFrequencyRunwayResolution,
|
||||||
readSessionCache,
|
readSessionCache,
|
||||||
|
selectCompactSecondaryTemp,
|
||||||
selectDisplayRunwayTemp,
|
selectDisplayRunwayTemp,
|
||||||
seedHourlyForecastFromRow,
|
seedHourlyForecastFromRow,
|
||||||
seriesStats,
|
seriesStats,
|
||||||
|
|||||||
Reference in New Issue
Block a user