diff --git a/frontend/components/dashboard/DetailPanel.tsx b/frontend/components/dashboard/DetailPanel.tsx index 660208d1..9aec2eb1 100644 --- a/frontend/components/dashboard/DetailPanel.tsx +++ b/frontend/components/dashboard/DetailPanel.tsx @@ -2,6 +2,7 @@ import type { ChartConfiguration } from "chart.js"; import clsx from "clsx"; +import { useRouter } from "next/navigation"; import { useEffect, useMemo, useRef, useState } from "react"; import { ForecastTable } from "@/components/dashboard/PanelSections"; import { useChart } from "@/hooks/useChart"; @@ -130,6 +131,7 @@ function DetailMiniTemperatureChart({ detail }: { detail: CityDetail }) { export function DetailPanel() { const store = useDashboardStore(); const { locale, t } = useI18n(); + const router = useRouter(); const detail = store.selectedDetail; const selectedCityItem = useMemo( () => @@ -138,11 +140,8 @@ export function DetailPanel() { : null, [store.cities, store.selectedCity], ); - const selectedSummary = store.selectedCity - ? store.citySummariesByName[store.selectedCity] || null - : null; - const isBasicGuestPanel = !detail && Boolean(store.selectedCity && selectedCityItem && selectedSummary); const isPro = store.proAccess.subscriptionActive; + const isAuthenticated = store.proAccess.authenticated; const panelRef = useRef(null); const [heavyContentReady, setHeavyContentReady] = useState(false); const isOverlayOpen = @@ -151,7 +150,7 @@ export function DetailPanel() { const isVisible = store.isPanelOpen && Boolean(store.selectedCity) && - (Boolean(detail) || isBasicGuestPanel) && + Boolean(detail) && !store.loadingState.cityDetail && !isOverlayOpen; const panelDisplayName = @@ -176,6 +175,29 @@ export function DetailPanel() { active.blur(); } }; + const handleFeatureAccess = (feature: "today" | "history") => { + blurActiveElement(); + + if (isPro) { + if (feature === "today") { + void store.openTodayModal(); + return; + } + void store.openHistory(); + return; + } + + if (isAuthenticated) { + router.push("/account"); + return; + } + + if (feature === "today") { + void store.openTodayModal(); + return; + } + void store.openHistory(); + }; useEffect(() => { const panel = panelRef.current; @@ -266,11 +288,8 @@ export function DetailPanel() { ? t("detail.todayAnalysis") : `${t("detail.todayAnalysis")} (Pro)` } - onClick={() => { - blurActiveElement(); - void store.openTodayModal(); - }} - disabled={!detail} + onClick={() => handleFeatureAccess("today")} + disabled={!store.selectedCity} > {isPro ? t("detail.todayAnalysis") @@ -282,11 +301,8 @@ export function DetailPanel() { title={ isPro ? t("detail.history") : `${t("detail.history")} (Pro)` } - onClick={() => { - blurActiveElement(); - void store.openHistory(); - }} - disabled={!detail} + onClick={() => handleFeatureAccess("history")} + disabled={!store.selectedCity} > {isPro ? t("detail.history") : `${t("detail.history")} · Pro`} @@ -296,7 +312,7 @@ export function DetailPanel() {
- {!detail && !isBasicGuestPanel ? ( + {!detail ? (
{store.loadingState.cityDetail @@ -306,66 +322,6 @@ export function DetailPanel() {
) : ( <> - {isBasicGuestPanel && selectedCityItem && selectedSummary ? ( - <> -
-

{t("detail.profile")}

-
-
- {locale === "en-US" ? "City" : "城市"} - {selectedCityItem.display_name} -
-
- {locale === "en-US" ? "Current" : "当前温度"} - - {selectedSummary.current?.temp != null - ? `${selectedSummary.current.temp}${selectedSummary.temp_symbol || "°C"}` - : t("common.na")} - -
-
- {locale === "en-US" ? "Observation" : "观测时间"} - {selectedSummary.current?.obs_time || t("common.na")} -
-
- {locale === "en-US" ? "DEB" : "DEB 预测"} - - {selectedSummary.deb?.prediction != null - ? `${selectedSummary.deb.prediction}${selectedSummary.temp_symbol || "°C"}` - : t("common.na")} - -
-
- {locale === "en-US" ? "Settlement" : "结算口径"} - - {selectedSummary.current?.settlement_source_label || - selectedCityItem.settlement_source_label || - t("common.na")} - -
-
- {t("section.airport")} - {selectedCityItem.airport || t("common.na")} -
-
-
-
-
- - {locale === "en-US" ? "Pro required for intraday analysis and history" : "今日日内分析与历史对账需开通 Pro"} - - - {locale === "en-US" - ? "Guests can browse the city overview here. Sign in and subscribe to unlock the full intraday model, history reconciliation, and market-linked weather analysis." - : "游客可先浏览城市概览。登录并开通 Pro 后,可查看完整的今日日内分析、历史对账和市场联动天气解读。"} - -
-
- - ) : null} - - {!isBasicGuestPanel ? ( - <>
{scenery ? ( <> @@ -454,8 +410,6 @@ export function DetailPanel() {
{heavyContentReady ? : null} - - ) : null} )}
diff --git a/frontend/hooks/useDashboardStore.tsx b/frontend/hooks/useDashboardStore.tsx index 509b8e62..73863317 100644 --- a/frontend/hooks/useDashboardStore.tsx +++ b/frontend/hooks/useDashboardStore.tsx @@ -491,17 +491,19 @@ export function DashboardStoreProvider({ await refreshProAccess(); } const access = proAccessRef.current; - if (!access.authenticated || !access.subscriptionActive) { - return; - } setLoadingState((current) => ({ ...current, cityDetail: true })); try { const detail = await ensureCityDetail(cityName); setSelectedForecastDate(detail.local_date); - // 预热市场数据,不做 await 阻塞,后台静默拉取 - void ensureCityMarketScan(cityName, false, null, detail.local_date).catch( - () => {}, - ); + if (access.authenticated && access.subscriptionActive) { + // 预热市场数据,不做 await 阻塞,后台静默拉取 + void ensureCityMarketScan( + cityName, + false, + null, + detail.local_date, + ).catch(() => {}); + } } finally { setLoadingState((current) => ({ ...current, cityDetail: false })); }