清理时区逻辑:TRADING_REGIONS→REGIONS,移除 detectLocalRegion/TZ fallback,用 CITY_REGION 硬编码替代
This commit is contained in:
@@ -27,9 +27,9 @@ import {
|
|||||||
getGapColor,
|
getGapColor,
|
||||||
getSignalLabel,
|
getSignalLabel,
|
||||||
getSignalState,
|
getSignalState,
|
||||||
resolveTradingRegionKey,
|
getCityRegion,
|
||||||
TRADING_REGIONS,
|
REGIONS,
|
||||||
detectLocalRegion,
|
getDefaultRegion,
|
||||||
} from "@/components/dashboard/scan-terminal/continent-grouping";
|
} from "@/components/dashboard/scan-terminal/continent-grouping";
|
||||||
import { MobileCityCard } from "@/components/dashboard/scan-terminal/MobileCityCard";
|
import { MobileCityCard } from "@/components/dashboard/scan-terminal/MobileCityCard";
|
||||||
import { MobileRegionTabs } from "@/components/dashboard/scan-terminal/MobileRegionTabs";
|
import { MobileRegionTabs } from "@/components/dashboard/scan-terminal/MobileRegionTabs";
|
||||||
@@ -290,7 +290,7 @@ function PolyWeatherTerminal({
|
|||||||
|
|
||||||
const filteredRegionRows = useMemo(() => {
|
const filteredRegionRows = useMemo(() => {
|
||||||
return rows.filter(
|
return rows.filter(
|
||||||
(row) => resolveTradingRegionKey(row) === selectedRegionKey,
|
(row) => getCityRegion(row) === selectedRegionKey,
|
||||||
);
|
);
|
||||||
}, [rows, selectedRegionKey]);
|
}, [rows, selectedRegionKey]);
|
||||||
|
|
||||||
@@ -504,7 +504,7 @@ function PolyWeatherTerminal({
|
|||||||
<>
|
<>
|
||||||
{/* Region tabs */}
|
{/* Region tabs */}
|
||||||
<div className="flex shrink-0 items-center gap-1 overflow-x-auto rounded-[4px] border border-[#cfd6df] bg-white p-1 mb-2 scrollbar-none">
|
<div className="flex shrink-0 items-center gap-1 overflow-x-auto rounded-[4px] border border-[#cfd6df] bg-white p-1 mb-2 scrollbar-none">
|
||||||
{TRADING_REGIONS.filter((r) => visibleRegions.has(r.key)).map((r) => ({
|
{REGIONS.filter((r) => visibleRegions.has(r.key)).map((r) => ({
|
||||||
key: r.key,
|
key: r.key,
|
||||||
labelEn: r.labelEn.toUpperCase(),
|
labelEn: r.labelEn.toUpperCase(),
|
||||||
labelZh: r.labelZh,
|
labelZh: r.labelZh,
|
||||||
@@ -540,7 +540,7 @@ function PolyWeatherTerminal({
|
|||||||
id="region-selector-popover"
|
id="region-selector-popover"
|
||||||
className="hidden absolute right-0 top-full mt-1 z-50 bg-white border border-slate-200 rounded-lg shadow-lg p-2 min-w-[180px]"
|
className="hidden absolute right-0 top-full mt-1 z-50 bg-white border border-slate-200 rounded-lg shadow-lg p-2 min-w-[180px]"
|
||||||
>
|
>
|
||||||
{TRADING_REGIONS.map((r) => {
|
{REGIONS.map((r) => {
|
||||||
const checked = visibleRegions.has(r.key);
|
const checked = visibleRegions.has(r.key);
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
@@ -649,7 +649,7 @@ function ScanTerminalScreen() {
|
|||||||
const stored = localStorage.getItem("polyweather_visible_regions");
|
const stored = localStorage.getItem("polyweather_visible_regions");
|
||||||
if (stored) return new Set(JSON.parse(stored));
|
if (stored) return new Set(JSON.parse(stored));
|
||||||
} catch {}
|
} catch {}
|
||||||
return new Set(TRADING_REGIONS.map((r) => r.key));
|
return new Set(REGIONS.map((r) => r.key));
|
||||||
});
|
});
|
||||||
const toggleRegion = useCallback((key: string) => {
|
const toggleRegion = useCallback((key: string) => {
|
||||||
setVisibleRegions((prev) => {
|
setVisibleRegions((prev) => {
|
||||||
@@ -734,7 +734,7 @@ function ScanTerminalScreen() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedRegionKey(detectLocalRegion());
|
setSelectedRegionKey(getDefaultRegion());
|
||||||
setLocalTimezoneOffsetSeconds(-new Date().getTimezoneOffset() * 60);
|
setLocalTimezoneOffsetSeconds(-new Date().getTimezoneOffset() * 60);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,14 @@ import {
|
|||||||
XAxis,
|
XAxis,
|
||||||
YAxis,
|
YAxis,
|
||||||
} from "recharts";
|
} from "recharts";
|
||||||
import type { AmosData, AirportCurrentConditions, CityDetail, ScanOpportunityRow } from "@/lib/dashboard-types";
|
import type {
|
||||||
|
AmosData,
|
||||||
|
AirportCurrentConditions,
|
||||||
|
CityDetail,
|
||||||
|
ScanOpportunityRow,
|
||||||
|
ForecastDay,
|
||||||
|
DailyModelForecast,
|
||||||
|
} from "@/lib/dashboard-types";
|
||||||
import { buildDebBaselinePath } from "@/lib/temperature-chart-paths";
|
import { buildDebBaselinePath } from "@/lib/temperature-chart-paths";
|
||||||
import { Panel } from "@/components/dashboard/scan-terminal/Panel";
|
import { Panel } from "@/components/dashboard/scan-terminal/Panel";
|
||||||
import { rowName, temp } from "@/components/dashboard/scan-terminal/utils";
|
import { rowName, temp } from "@/components/dashboard/scan-terminal/utils";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { ScanOpportunityRow } from "@/lib/dashboard-types";
|
import type { ScanOpportunityRow } from "@/lib/dashboard-types";
|
||||||
|
|
||||||
export const TRADING_REGIONS = [
|
export const REGIONS = [
|
||||||
{ key: "east_asia", labelEn: "East Asia", labelZh: "东亚", sort: 1 },
|
{ key: "east_asia", labelEn: "East Asia", labelZh: "东亚", sort: 1 },
|
||||||
{ key: "southeast_asia", labelEn: "Southeast Asia", labelZh: "东南亚", sort: 2 },
|
{ key: "southeast_asia", labelEn: "Southeast Asia", labelZh: "东南亚", sort: 2 },
|
||||||
{ key: "central_asia", labelEn: "Central / South Asia", labelZh: "中亚 / 南亚", sort: 3 },
|
{ key: "central_asia", labelEn: "Central / South Asia", labelZh: "中亚 / 南亚", sort: 3 },
|
||||||
@@ -10,23 +10,11 @@ export const TRADING_REGIONS = [
|
|||||||
{ key: "north_america", labelEn: "North America", labelZh: "北美", sort: 7 },
|
{ key: "north_america", labelEn: "North America", labelZh: "北美", sort: 7 },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type TradingRegionKey = (typeof TRADING_REGIONS)[number]["key"];
|
export type RegionKey = (typeof REGIONS)[number]["key"];
|
||||||
|
|
||||||
/** Map browser timezone offset (hours) to the closest trading region. */
|
const REGION_KEYS = new Set<string>(REGIONS.map((r) => r.key));
|
||||||
export function detectLocalRegion(): TradingRegionKey {
|
|
||||||
const offset = -new Date().getTimezoneOffset() / 60; // UTC offset in hours
|
|
||||||
if (offset >= 8) return "east_asia";
|
|
||||||
if (offset >= 7) return "southeast_asia";
|
|
||||||
if (offset >= 4.5) return "central_asia";
|
|
||||||
if (offset >= 2) return "west_asia";
|
|
||||||
if (offset >= -2) return "europe_africa";
|
|
||||||
if (offset >= -4) return "south_america";
|
|
||||||
return "north_america";
|
|
||||||
}
|
|
||||||
|
|
||||||
const TRADING_REGION_KEYS = new Set<string>(TRADING_REGIONS.map((region) => region.key));
|
const CITY_REGION: Record<string, RegionKey> = {
|
||||||
|
|
||||||
const CITY_REGION_FALLBACK: Record<string, TradingRegionKey> = {
|
|
||||||
beijing: "east_asia",
|
beijing: "east_asia",
|
||||||
busan: "east_asia",
|
busan: "east_asia",
|
||||||
chengdu: "east_asia",
|
chengdu: "east_asia",
|
||||||
@@ -79,51 +67,23 @@ const CITY_REGION_FALLBACK: Record<string, TradingRegionKey> = {
|
|||||||
wellington: "east_asia",
|
wellington: "east_asia",
|
||||||
};
|
};
|
||||||
|
|
||||||
function normalizeRegionValue(value?: string | null) {
|
function normalizeCity(value?: string | null) {
|
||||||
return String(value || "")
|
return String(value || "").trim().toLowerCase()
|
||||||
.trim()
|
.replace(/[_-]+/g, " ").replace(/\s+/g, " ");
|
||||||
.toLowerCase()
|
|
||||||
.replace(/[-\s]+/g, "_");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeCityValue(value?: string | null) {
|
export function getCityRegion(row: ScanOpportunityRow): RegionKey | null {
|
||||||
return String(value || "")
|
const key = normalizeCity(row.city || row.city_display_name || row.display_name);
|
||||||
.trim()
|
return CITY_REGION[key] || null;
|
||||||
.toLowerCase()
|
|
||||||
.replace(/[_-]+/g, " ")
|
|
||||||
.replace(/\s+/g, " ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function finiteNumber(value: unknown) {
|
/** Default region on first load — East Asia. */
|
||||||
const numeric = Number(value);
|
export function getDefaultRegion(): RegionKey {
|
||||||
return Number.isFinite(numeric) ? numeric : null;
|
return "east_asia";
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveTradingRegionKey(row: ScanOpportunityRow): TradingRegionKey | null {
|
|
||||||
const direct = normalizeRegionValue(row.trading_region);
|
|
||||||
if (TRADING_REGION_KEYS.has(direct)) return direct as TradingRegionKey;
|
|
||||||
|
|
||||||
const cityKey = normalizeCityValue(row.city || row.city_display_name || row.display_name);
|
|
||||||
const cityRegion = CITY_REGION_FALLBACK[cityKey];
|
|
||||||
if (cityRegion) return cityRegion;
|
|
||||||
|
|
||||||
const offset = finiteNumber(row.tz_offset_seconds);
|
|
||||||
if (offset !== null) {
|
|
||||||
const hours = offset / 3600;
|
|
||||||
if (hours >= 8) return "east_asia";
|
|
||||||
if (hours >= 7) return "southeast_asia";
|
|
||||||
if (hours >= 4.5) return "central_asia";
|
|
||||||
if (hours >= 2) return "west_asia";
|
|
||||||
if (hours >= -2) return "europe_africa";
|
|
||||||
if (hours >= -4) return "south_america";
|
|
||||||
return "north_america";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContinentGroup {
|
export interface ContinentGroup {
|
||||||
key: TradingRegionKey | "active_signals";
|
key: RegionKey | "active_signals";
|
||||||
labelEn: string;
|
labelEn: string;
|
||||||
labelZh: string;
|
labelZh: string;
|
||||||
sort: number;
|
sort: number;
|
||||||
@@ -203,7 +163,7 @@ export function buildContinentGroups(rows: ScanOpportunityRow[], isEn: boolean):
|
|||||||
const regionMap = new Map<string, ScanOpportunityRow[]>();
|
const regionMap = new Map<string, ScanOpportunityRow[]>();
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const region = resolveTradingRegionKey(row) || "unknown";
|
const region = getCityRegion(row) || "unknown";
|
||||||
if (!regionMap.has(region)) regionMap.set(region, []);
|
if (!regionMap.has(region)) regionMap.set(region, []);
|
||||||
regionMap.get(region)!.push(row);
|
regionMap.get(region)!.push(row);
|
||||||
}
|
}
|
||||||
@@ -229,7 +189,7 @@ export function buildContinentGroups(rows: ScanOpportunityRow[], isEn: boolean):
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const region of TRADING_REGIONS) {
|
for (const region of REGIONS) {
|
||||||
const regionRows = regionMap.get(region.key) || [];
|
const regionRows = regionMap.get(region.key) || [];
|
||||||
if (regionRows.length === 0) continue;
|
if (regionRows.length === 0) continue;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user