import { memo, useEffect, useMemo, useState } from "react"; import { getWindowPhaseMeta } from "@/components/dashboard/opportunity-window-phase"; import type { ScanOpportunityRow } from "@/lib/dashboard-types"; import { getLocalizedCityName } from "@/lib/dashboard-home-copy"; import { formatTemperatureValue } from "@/lib/temperature-utils"; import { buildCalendarCoreReason, buildCalendarMeta, dedupeCalendarRows, formatCalendarCardShortDate, getCalendarActionGroup, isCalendarActionable, type CalendarActionItem, } from "@/components/dashboard/scan-terminal/calendar-action-utils"; const CalendarActionCard = memo(function CalendarActionCard({ item, locale, selected, onSelectRow, }: { item: CalendarActionItem; locale: string; selected: boolean; onSelectRow: (row: ScanOpportunityRow) => void; }) { const { row, meta, reason } = item; const tempSymbol = row.temp_symbol || "°C"; const phaseMeta = getWindowPhaseMeta(row, locale); return ( ); }); export const CalendarView = memo(function CalendarView({ rows, locale, selectedRowId, onSelectRow, }: { rows: ScanOpportunityRow[]; locale: string; selectedRowId: string | null; onSelectRow: (row: ScanOpportunityRow) => void; }) { const [snapshotMs, setSnapshotMs] = useState(() => Date.now()); const [nowMs, setNowMs] = useState(() => Date.now()); useEffect(() => { setSnapshotMs(Date.now()); setNowMs(Date.now()); }, [rows]); useEffect(() => { const intervalId = window.setInterval(() => { setNowMs(Date.now()); }, 60_000); return () => window.clearInterval(intervalId); }, []); const groups = useMemo(() => { const byPhase = new Map< string, { label: string; subtitle: string; sort: number; items: CalendarActionItem[]; } >(); dedupeCalendarRows(rows).forEach((row) => { const meta = buildCalendarMeta(row, locale, snapshotMs, nowMs); if (!isCalendarActionable(row, meta, nowMs)) return; const actionGroup = getCalendarActionGroup(row, meta, nowMs, locale); const current = byPhase.get(actionGroup.key) || { label: actionGroup.label, subtitle: actionGroup.subtitle, sort: actionGroup.sort, items: [], }; current.items.push({ row, meta, reason: buildCalendarCoreReason(row, actionGroup, locale), }); byPhase.set(actionGroup.key, current); }); return Array.from(byPhase.entries()) .sort(([, left], [, right]) => left.sort - right.sort) .map(([key, group]) => ({ key, label: group.label, subtitle: group.subtitle, items: group.items.sort((left, right) => { if (left.meta.sort !== right.meta.sort) return left.meta.sort - right.meta.sort; return Number(right.row.edge_percent || 0) - Number(left.row.edge_percent || 0); }), })); }, [locale, nowMs, rows, snapshotMs]); if (!groups.length) { return (
{locale === "en-US" ? "No actionable calendar windows in the next 12 hours" : "未来 12 小时内没有可行动日历窗口"}
); } return (
{groups.map((group) => (
{group.label}
{group.subtitle}
{locale === "en-US" ? `${group.items.length} rows` : `${group.items.length} 条`}
{group.items.map((item) => ( ))}
))}
); });