From 1afca8ecbb666dd90be3d135e783a23d31e3f79f Mon Sep 17 00:00:00 2001
From: "2569718930@qq.com" <2569718930@qq.com>
Date: Tue, 10 Mar 2026 10:17:59 +0800
Subject: [PATCH] feat: Implement new dashboard panel sections, utilities, and
a future forecast modal.
---
.../dashboard/FutureForecastModal.tsx | 63 -------------------
.../components/dashboard/PanelSections.tsx | 8 +--
frontend/lib/dashboard-utils.ts | 33 ++++++----
3 files changed, 24 insertions(+), 80 deletions(-)
diff --git a/frontend/components/dashboard/FutureForecastModal.tsx b/frontend/components/dashboard/FutureForecastModal.tsx
index 26f9485d..249b6a34 100644
--- a/frontend/components/dashboard/FutureForecastModal.tsx
+++ b/frontend/components/dashboard/FutureForecastModal.tsx
@@ -715,69 +715,6 @@ export function FutureForecastModal() {
{settlementBucketLabel}
-
-
-
-
- 📊 {locale === "en-US" ? "Core Edge" : "核心测算"}
-
- 0
- ? "positive"
- : "negative",
- )}
- >
- ({marketEdge})
-
-
-
-
-
- {locale === "en-US" ? "Model Prob:" : "模型概率:"}
-
-
- {modelProbability}
-
-
-
-
- {locale === "en-US"
- ? "Market Prob:"
- : "市场概率:"}
-
-
- {marketMidpoint}
-
-
-
-
-
-
- {/* Layer 2: Order Book */}
-
-
- 📈 {locale === "en-US" ? "Order Book" : "订单簿报价"}
-
-
- YES:
-
- {marketYesBuy} /{" "}
- {marketYesSell}
-
-
- ({locale === "en-US" ? "Spread" : "价差"}{" "}
- {marketSpread})
-
-
-
- NO:
-
- {marketNoBuy} /{" "}
- {marketNoSell}
-
-
{/* Layer 3: Context */}
diff --git a/frontend/components/dashboard/PanelSections.tsx b/frontend/components/dashboard/PanelSections.tsx
index c7c55200..8034e398 100644
--- a/frontend/components/dashboard/PanelSections.tsx
+++ b/frontend/components/dashboard/PanelSections.tsx
@@ -552,8 +552,8 @@ export function ModelForecast({
}) {
const { locale, t } = useI18n();
const view = getModelView(detail, targetDate);
- const modelEntries = Object.entries(view.models).filter(([, value]) =>
- Number.isFinite(Number(value)),
+ const modelEntries = Object.entries(view.models).filter(
+ ([, value]) => value !== null && value !== undefined && Number.isFinite(Number(value)),
);
const numericValues = modelEntries.map(([, value]) => Number(value));
const comparisonValues =
@@ -567,9 +567,7 @@ export function ModelForecast({
const range = Math.max(maxValue - minValue, 1);
const getModelDisplayName = (name: string) => {
if (String(name).trim().toLowerCase() === "meteoblue") {
- return locale === "en-US"
- ? "Meteoblue (Daily Max)"
- : "Meteoblue (日最高温)";
+ return "Meteoblue";
}
return name;
};
diff --git a/frontend/lib/dashboard-utils.ts b/frontend/lib/dashboard-utils.ts
index ee71f40d..3583090b 100644
--- a/frontend/lib/dashboard-utils.ts
+++ b/frontend/lib/dashboard-utils.ts
@@ -330,28 +330,34 @@ export function getProbabilityView(detail: CityDetail, targetDate?: string | nul
}
export function getModelView(detail: CityDetail, targetDate?: string | null) {
+ const toFiniteNumber = (value: unknown): number | null => {
+ if (value === null || value === undefined || value === "") return null;
+ const numeric = Number(value);
+ return Number.isFinite(numeric) ? numeric : null;
+ };
+
const pickMeteoblueForDate = (dateStr: string) => {
const meteoblue = detail.source_forecasts?.meteoblue;
if (!meteoblue) return null;
const dates = detail.forecast?.daily?.map((item) => item.date) || [];
const index = dates.findIndex((date) => date === dateStr);
+ const todayHigh = toFiniteNumber(meteoblue.today_high);
+
+ // Today must always use Meteoblue daily max (today_high) first.
+ if (dateStr === detail.local_date && todayHigh != null) {
+ return todayHigh;
+ }
const dailyHighs = Array.isArray(meteoblue.daily_highs)
? meteoblue.daily_highs
: [];
if (index >= 0) {
- const byDailyHigh = Number(dailyHighs[index]);
- if (Number.isFinite(byDailyHigh)) return byDailyHigh;
+ const byDailyHigh = toFiniteNumber(dailyHighs[index]);
+ if (byDailyHigh != null) return byDailyHigh;
}
- const todayHigh = Number(meteoblue.today_high);
- if (
- Number.isFinite(todayHigh) &&
- (dateStr === detail.local_date || index === 0)
- ) {
- return todayHigh;
- }
+ if (index === 0 && todayHigh != null) return todayHigh;
return null;
};
@@ -361,11 +367,14 @@ export function getModelView(detail: CityDetail, targetDate?: string | null) {
dateStr: string,
) => {
const nextModels = { ...models };
- if (!Number.isFinite(Number(nextModels.Meteoblue))) {
+ const existing = toFiniteNumber(nextModels.Meteoblue);
+ if (existing == null) {
const meteoblueVal = pickMeteoblueForDate(dateStr);
- if (Number.isFinite(Number(meteoblueVal))) {
- nextModels.Meteoblue = Number(meteoblueVal);
+ if (meteoblueVal != null) {
+ nextModels.Meteoblue = meteoblueVal;
}
+ } else {
+ nextModels.Meteoblue = existing;
}
return nextModels;
};