Files
PolyWeather/frontend/components/dashboard/DetailPanel.tsx
T

173 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import clsx from "clsx";
import { useDashboardStore } from "@/hooks/useDashboardStore";
import { getCityScenery } from "@/lib/dashboard-scenery";
import {
getCityProfileStats,
getClimateDrivers,
getRiskBadgeLabel,
getSettlementRiskNarrative,
} from "@/lib/dashboard-utils";
import { ForecastTable } from "@/components/dashboard/PanelSections";
export function DetailPanel() {
const store = useDashboardStore();
const detail = store.selectedDetail;
const isOverlayOpen =
Boolean(store.futureModalDate) ||
store.historyState.isOpen ||
store.isGuideOpen;
const isVisible =
store.isPanelOpen &&
Boolean(store.selectedCity) &&
Boolean(detail) &&
!store.loadingState.cityDetail &&
!isOverlayOpen;
const profileStats = detail ? getCityProfileStats(detail) : [];
const riskLines = detail ? getSettlementRiskNarrative(detail) : [];
const climateDrivers = detail ? getClimateDrivers(detail) : [];
const scenery = getCityScenery(detail?.name);
return (
<aside
className={clsx("detail-panel", isVisible && "visible")}
aria-hidden={!isVisible}
>
<div className="panel-header">
<button
type="button"
className="panel-close"
aria-label="关闭城市详情面板"
onClick={store.closePanel}
>
×
</button>
<div className="panel-title-area">
<h2>{detail?.display_name?.toUpperCase() || "—"}</h2>
<div className="panel-meta">
<span className={clsx("risk-badge", detail?.risk?.level || "low")}>
{getRiskBadgeLabel(detail?.risk?.level)}
</span>
<span className="local-time">
{detail
? `${detail.local_date} ${detail.local_time}`
: "等待选择城市"}
</span>
<button
type="button"
className="history-btn"
title="查看今日日内分析"
onClick={store.openTodayModal}
disabled={!detail}
>
今日日内分析
</button>
<button
type="button"
className="history-btn"
title="查看历史对账"
onClick={() => void store.openHistory()}
disabled={!detail}
>
历史对账
</button>
</div>
</div>
</div>
<div className="panel-body">
{!detail ? (
<section>
<div style={{ color: "var(--text-muted)", fontSize: "13px" }}>
{store.loadingState.cityDetail
? "正在加载城市详情..."
: "从左侧城市列表选择一个城市查看详情。"}
</div>
</section>
) : (
<>
<section className="detail-scenery-card">
{scenery ? (
<>
<img
className="detail-scenery-image"
src={scenery.imageUrl}
alt={`${detail.display_name} 风景照`}
/>
<div className="detail-scenery-overlay">
<div className="detail-scenery-copy">
<span className="detail-scenery-kicker">
{detail.display_name}
</span>
</div>
<a
className="detail-scenery-credit"
href={scenery.creditUrl}
target="_blank"
rel="noreferrer"
>
{scenery.creditLabel}
</a>
</div>
</>
) : (
<div className="detail-scenery-fallback">
<span className="detail-scenery-kicker">
{detail.display_name}
</span>
<strong className="detail-scenery-title">
城市风景与微气候
</strong>
<span className="detail-scenery-subtitle">
当前没有匹配到风景图,仍可从下方档案与风险说明查看城市特征。
</span>
</div>
)}
</section>
<section className="detail-section">
<h3>城市档案</h3>
<div className="detail-grid">
{profileStats.map((item) => (
<div key={item.label} className="detail-card">
<span className="detail-label">{item.label}</span>
<span className="detail-value">{item.value}</span>
</div>
))}
</div>
</section>
<section className="detail-section">
<h3>结算与偏差风险</h3>
<div className="risk-info">
{riskLines.map((line) => (
<div key={line} className="risk-row">
<span style={{ color: "var(--accent-cyan)", opacity: 0.6 }}>
</span>
<span>{line}</span>
</div>
))}
</div>
</section>
<section className="detail-section">
<h3>当地气候主要受什么影响</h3>
<div className="insight-list">
{climateDrivers.map((driver) => (
<div key={driver.label} className="insight-item">
<div className="insight-title">{driver.label}</div>
<div className="insight-text">{driver.text}</div>
</div>
))}
</div>
</section>
<ForecastTable />
</>
)}
</div>
</aside>
);
}