diff --git a/frontend/components/dashboard/Dashboard.module.css b/frontend/components/dashboard/Dashboard.module.css index c8df9c23..5a62ebb5 100644 --- a/frontend/components/dashboard/Dashboard.module.css +++ b/frontend/components/dashboard/Dashboard.module.css @@ -1314,6 +1314,49 @@ stroke-width: 1.6; } +.root :global(.home-intraday-hotspot) { + position: absolute; + width: 18px; + height: 18px; + transform: translate(-50%, -50%); + border: 0; + border-radius: 999px; + background: transparent; + cursor: pointer; +} + +.root :global(.home-intraday-hotspot:focus-visible) { + outline: 1px solid rgba(34, 211, 238, 0.6); + outline-offset: 1px; +} + +.root :global(.home-intraday-tooltip) { + position: absolute; + transform: translate(-50%, -100%); + pointer-events: none; + display: inline-flex; + flex-direction: column; + gap: 2px; + padding: 6px 8px; + border: 1px solid rgba(34, 211, 238, 0.22); + border-radius: 10px; + background: rgba(7, 15, 28, 0.96); + box-shadow: 0 10px 26px rgba(2, 6, 23, 0.34); + white-space: nowrap; +} + +.root :global(.home-intraday-tooltip strong) { + color: #f8fafc; + font-size: 12px; + font-weight: 900; +} + +.root :global(.home-intraday-tooltip span) { + color: rgba(148, 163, 184, 0.88); + font-size: 10px; + font-weight: 700; +} + .root :global(.home-intraday-meta) { color: rgba(148, 163, 184, 0.9); font-size: 11px; diff --git a/frontend/components/dashboard/PolyWeatherDashboard.tsx b/frontend/components/dashboard/PolyWeatherDashboard.tsx index a1d4561a..1fd8570a 100644 --- a/frontend/components/dashboard/PolyWeatherDashboard.tsx +++ b/frontend/components/dashboard/PolyWeatherDashboard.tsx @@ -2,7 +2,7 @@ import clsx from "clsx"; import dynamic from "next/dynamic"; import Link from "next/link"; -import { useEffect, useMemo, useRef } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import styles from "./Dashboard.module.css"; import detailChromeStyles from "./DetailPanelChrome.module.css"; import modalChromeStyles from "./ModalChrome.module.css"; @@ -222,6 +222,13 @@ type HomeTrendChart = { forecastPath: string; legendText: string; observationDots: Array<{ cx: number; cy: number; key: string }>; + hoverPoints: Array<{ + cx: number; + cy: number; + key: string; + label: string; + temperatureText: string; + }>; }; type HomeForecastDay = { @@ -230,6 +237,8 @@ type HomeForecastDay = { maxTemp: number; }; +const RECENT_OPENED_CITIES_STORAGE_KEY = "polyWeather_recent_opened_cities_v1"; + function projectHomeTrendPoint( x: number, y: number, @@ -319,11 +328,30 @@ function buildHomeTrendChart( key: `${point.labelTime}-${index}`, }; }); + const hoverSource = observationSeries.length > 0 ? observationSeries : forecastSeries; + const hoverPoints = hoverSource.map((point, index) => { + const projected = projectHomeTrendPoint( + point.x, + point.y, + chartData.xMin, + chartData.xMax, + chartData.min, + chartData.max, + ); + return { + cx: projected.cx, + cy: projected.cy, + key: `hover-${point.labelTime}-${index}`, + label: point.labelTime, + temperatureText: formatTemperature(point.y, detail.temp_symbol || "°C"), + }; + }); return { forecastPath, legendText: chartData.legendText, observationDots, + hoverPoints, }; } @@ -441,6 +469,13 @@ function isTradableMarketOpportunity(detail?: CityDetail | null) { function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) { const store = useDashboardStore(); const { locale } = useI18n(); + const [hoveredTrendPoint, setHoveredTrendPoint] = useState<{ + cx: number; + cy: number; + key: string; + label: string; + temperatureText: string; + } | null>(null); const selectedSnapshot = store.selectedCity ? snapshots.find((snapshot) => snapshot.city.name === store.selectedCity) : null; @@ -672,6 +707,39 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) { ))} + {trendChart.hoverPoints.map((point) => ( +