"use client"; import { Fragment, useState } from "react"; import clsx from "clsx"; import { ChevronDown, ChevronRight } from "lucide-react"; import type { ScanOpportunityRow } from "@/lib/dashboard-types"; import { type ContinentGroup, formatPrice, formatSpreadLiquidity, GAP_COLOR_MAP, getGapColor, getSignalLabel, getSignalState, getDefaultExpanded, } from "@/components/dashboard/scan-terminal/continent-grouping"; import { rowName, temp, pct, edgeClass } from "./utils"; export function GroupedMarketTable({ groups, isEn, onSelect, selectedId, }: { groups: ContinentGroup[]; isEn: boolean; onSelect: (row: ScanOpportunityRow) => void; selectedId?: string | null; }) { const [collapsed, setCollapsed] = useState>(() => { const c = new Set(); const defaultExpanded = getDefaultExpanded(groups); for (const g of groups) { if (!defaultExpanded.has(g.key)) c.add(g.key); } return c; }); const toggleGroup = (key: string) => { setCollapsed((prev) => { const next = new Set(prev); if (next.has(key)) next.delete(key); else next.add(key); return next; }); }; const labelActive = isEn ? "Active" : "活跃"; const labelWatch = isEn ? "Watch" : "观察"; const showHeaders = groups.length > 1; return (
{groups.flatMap((group) => { const isExpanded = !collapsed.has(group.key); const rows = showHeaders && !isExpanded ? [] : group.rows; return ( {showHeaders && ( )} {rows.map((row) => { const signal = getSignalState(row); const gapColor = GAP_COLOR_MAP[getGapColor(row)]; return ( onSelect(row)} > ); })} ); })}
City Obs High DEB Gap Market Edge Spr/Liq Signal
{rowName(row)}
{row.airport || ""}{row.local_time ? ` · ${row.local_time}` : ""}
{temp(row.current_temp, row.temp_symbol)} {temp(row.current_max_so_far, row.temp_symbol)} {temp(row.deb_prediction, row.temp_symbol)} {temp(row.signed_gap ?? row.gap_to_target, row.temp_symbol)} {formatPrice(row.midpoint, row.ask, row.bid)} {pct(row.edge_percent)} {formatSpreadLiquidity(row.spread, row.book_liquidity ?? row.market_liquidity)} {getSignalLabel(signal, isEn)}
); }