feat: add live temperature threshold chart logic and components for runway monitoring
This commit is contained in:
@@ -444,7 +444,17 @@ export function LiveTemperatureThresholdChart({
|
|||||||
}, [row, chartHourly, tzOffset, chartLocalDate]);
|
}, [row, chartHourly, tzOffset, chartLocalDate]);
|
||||||
|
|
||||||
const runwayPlates = useMemo(() => buildRunwayPlates(chartHourly?.amos, row, settlementObs), [chartHourly?.amos, row, settlementObs]);
|
const runwayPlates = useMemo(() => buildRunwayPlates(chartHourly?.amos, row, settlementObs), [chartHourly?.amos, row, settlementObs]);
|
||||||
const hasRunwayData = runwayPlates.length > 0;
|
const hasRunwaySeries = useMemo(
|
||||||
|
() =>
|
||||||
|
series.some(
|
||||||
|
(item) =>
|
||||||
|
item.key.startsWith("runway_") &&
|
||||||
|
item.key !== "runway_max" &&
|
||||||
|
item.values.some((value) => validNumber(value) !== null),
|
||||||
|
),
|
||||||
|
[series],
|
||||||
|
);
|
||||||
|
const hasRunwayData = runwayPlates.length > 0 || hasRunwaySeries;
|
||||||
const settlementPlate = useMemo(() => runwayPlates.find((p) => p.isSettlement), [runwayPlates]);
|
const settlementPlate = useMemo(() => runwayPlates.find((p) => p.isSettlement), [runwayPlates]);
|
||||||
|
|
||||||
const chartSeries = useMemo(() => {
|
const chartSeries = useMemo(() => {
|
||||||
|
|||||||
+48
@@ -10,6 +10,7 @@ import {
|
|||||||
__mergePatchIntoHourlyForTest,
|
__mergePatchIntoHourlyForTest,
|
||||||
__selectDisplayRunwayTempForTest,
|
__selectDisplayRunwayTempForTest,
|
||||||
} from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart";
|
} from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart";
|
||||||
|
import { __buildTemperatureTooltipRowsForTest } from "@/components/dashboard/scan-terminal/TemperatureTooltipContent";
|
||||||
|
|
||||||
function assert(condition: unknown, message: string): asserts condition {
|
function assert(condition: unknown, message: string): asserts condition {
|
||||||
if (!condition) throw new Error(message);
|
if (!condition) throw new Error(message);
|
||||||
@@ -369,6 +370,23 @@ export function runTests() {
|
|||||||
!englishSettlement?.label.includes("结算跑道"),
|
!englishSettlement?.label.includes("结算跑道"),
|
||||||
`${city} English settlement runway label should not leak Chinese`,
|
`${city} English settlement runway label should not leak Chinese`,
|
||||||
);
|
);
|
||||||
|
const englishRunwayPoint = englishChart.data.find(
|
||||||
|
(point: any) => point[runwayKey(settlementRwy)] !== null && point[runwayKey(settlementRwy)] !== undefined,
|
||||||
|
);
|
||||||
|
const tooltipRows = __buildTemperatureTooltipRowsForTest(
|
||||||
|
englishRunwayPoint as any,
|
||||||
|
englishChart.data,
|
||||||
|
englishChart.series,
|
||||||
|
);
|
||||||
|
const tooltipRunway = tooltipRows.find((item) => item.key === runwayKey(settlementRwy));
|
||||||
|
assert(
|
||||||
|
tooltipRunway?.label.includes("Settlement Runway"),
|
||||||
|
`${city} English tooltip runway label should be translated`,
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
!tooltipRunway?.label.includes("结算跑道"),
|
||||||
|
`${city} English tooltip runway label should not leak Chinese`,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const shenzhen = __buildTemperatureChartDataForTest(
|
const shenzhen = __buildTemperatureChartDataForTest(
|
||||||
@@ -785,6 +803,36 @@ export function runTests() {
|
|||||||
"live aggregate temp should not override runway-history current temp when runway data is rendered",
|
"live aggregate temp should not override runway-history current temp when runway data is rendered",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const wuhanRunwayMetrics = __getObservationDisplayMetricsForTest(
|
||||||
|
{
|
||||||
|
city: "wuhan",
|
||||||
|
local_date: "2026-05-27",
|
||||||
|
local_time: "13:54",
|
||||||
|
tz_offset_seconds: 8 * 60 * 60,
|
||||||
|
current_temp: 25.0,
|
||||||
|
temp_symbol: "°C",
|
||||||
|
} as any,
|
||||||
|
{
|
||||||
|
localTime: "13:54",
|
||||||
|
times: ["00:00", "12:00", "18:00", "23:00"],
|
||||||
|
temps: [22.0, 30.0, 29.0, 25.0],
|
||||||
|
runwayPlateHistory: {
|
||||||
|
"04/22": [
|
||||||
|
{ time: "13:52", temp: 28.6 },
|
||||||
|
{ time: "13:54", temp: 28.8 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
wuhanRunwayMetrics.currentRunwayTemp === 28.8,
|
||||||
|
"runway header metrics should use the latest settlement runway history point",
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
__selectDisplayRunwayTempForTest(25.0, wuhanRunwayMetrics.currentRunwayTemp, false) === 28.8,
|
||||||
|
"runway header should prefer runway-history current temp even when the latest detail payload lacks runway_obs snapshot",
|
||||||
|
);
|
||||||
|
|
||||||
const newYorkMetrics = __getObservationDisplayMetricsForTest(
|
const newYorkMetrics = __getObservationDisplayMetricsForTest(
|
||||||
{
|
{
|
||||||
city: "new york",
|
city: "new york",
|
||||||
|
|||||||
@@ -713,12 +713,10 @@ function getObservationDisplayMetrics(
|
|||||||
function selectDisplayRunwayTemp(
|
function selectDisplayRunwayTemp(
|
||||||
liveTemp: number | null,
|
liveTemp: number | null,
|
||||||
currentRunwayTemp: number | null,
|
currentRunwayTemp: number | null,
|
||||||
hasRunwayData: boolean,
|
_hasRunwayData: boolean,
|
||||||
) {
|
) {
|
||||||
if (hasRunwayData && currentRunwayTemp !== null) {
|
if (currentRunwayTemp !== null) return currentRunwayTemp;
|
||||||
return currentRunwayTemp;
|
return liveTemp;
|
||||||
}
|
|
||||||
return liveTemp ?? currentRunwayTemp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSettlementRunway(row: ScanOpportunityRow | null, rwy: string) {
|
function isSettlementRunway(row: ScanOpportunityRow | null, rwy: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user