Fix homepage opportunity and focus panel layout
This commit is contained in:
@@ -1146,7 +1146,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-pro-card) {
|
.root :global(.home-pro-card) {
|
||||||
margin-top: auto;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -1665,12 +1664,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-card-section.intraday) {
|
.root :global(.home-card-section.intraday) {
|
||||||
border-color: rgba(34, 211, 238, 0.18);
|
border-color: rgba(34, 211, 238, 0.2);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 0% 35%, rgba(34, 211, 238, 0.12), transparent 44%),
|
||||||
|
linear-gradient(180deg, rgba(7, 14, 26, 0.94), rgba(9, 17, 30, 0.82));
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-intraday-chart) {
|
.root :global(.home-intraday-chart) {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 10px 0 0;
|
padding: 6px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-intraday-chart svg) {
|
.root :global(.home-intraday-chart svg) {
|
||||||
@@ -1742,6 +1744,37 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.root :global(.home-intraday-reports) {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 7px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.home-intraday-reports span) {
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 7px 8px;
|
||||||
|
border: 1px solid rgba(34, 211, 238, 0.12);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgba(2, 8, 18, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.home-intraday-reports b) {
|
||||||
|
color: rgba(148, 163, 184, 0.9);
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.home-intraday-reports strong) {
|
||||||
|
color: #e0f2fe;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
.root :global(.home-card-section.forecast) {
|
.root :global(.home-card-section.forecast) {
|
||||||
border-color: rgba(99, 102, 241, 0.24);
|
border-color: rgba(99, 102, 241, 0.24);
|
||||||
}
|
}
|
||||||
@@ -1806,33 +1839,40 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-card-section.probability) {
|
.root :global(.home-card-section.probability) {
|
||||||
border-color: rgba(115, 137, 161, 0.16);
|
border-color: rgba(251, 113, 133, 0.18);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 100% 0%, rgba(251, 113, 133, 0.08), transparent 42%),
|
||||||
|
linear-gradient(180deg, rgba(7, 14, 26, 0.94), rgba(9, 17, 30, 0.82));
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-probability-list) {
|
.root :global(.home-probability-list),
|
||||||
|
.root :global(.home-probability-ladder) {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-probability-row) {
|
.root :global(.home-probability-row),
|
||||||
|
.root :global(.home-probability-ladder-row) {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 58px 1fr;
|
grid-template-columns: 58px minmax(0, 1fr) 42px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 9px;
|
||||||
color: rgba(226, 232, 240, 0.95);
|
color: rgba(226, 232, 240, 0.95);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 850;
|
font-weight: 850;
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-probability-row div) {
|
.root :global(.home-probability-row div),
|
||||||
|
.root :global(.home-probability-track) {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 18px;
|
height: 14px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: rgba(115, 137, 161, 0.12);
|
background: rgba(115, 137, 161, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-probability-row i) {
|
.root :global(.home-probability-row i),
|
||||||
|
.root :global(.home-probability-track i) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0 auto 0 0;
|
inset: 0 auto 0 0;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
@@ -1840,22 +1880,31 @@
|
|||||||
box-shadow: 0 0 10px rgba(34, 211, 238, 0.14);
|
box-shadow: 0 0 10px rgba(34, 211, 238, 0.14);
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-probability-row:nth-child(3) i) {
|
.root :global(.home-probability-row:nth-child(3) i),
|
||||||
|
.root :global(.home-probability-ladder-row:nth-child(3) i) {
|
||||||
background: linear-gradient(90deg, #f59e0b, #fbbf24);
|
background: linear-gradient(90deg, #f59e0b, #fbbf24);
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-probability-row:nth-child(n + 4) i) {
|
.root :global(.home-probability-row:nth-child(n + 4) i),
|
||||||
|
.root :global(.home-probability-ladder-row:nth-child(n + 4) i) {
|
||||||
background: linear-gradient(90deg, #ef4444, #fb7185);
|
background: linear-gradient(90deg, #ef4444, #fb7185);
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-probability-row strong) {
|
.root :global(.home-probability-row strong),
|
||||||
|
.root :global(.home-probability-ladder-row strong) {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
display: block;
|
display: block;
|
||||||
padding-left: 12px;
|
|
||||||
color: #f8fafc;
|
color: #f8fafc;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 18px;
|
line-height: 1;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.home-probability-threshold) {
|
||||||
|
color: rgba(226, 232, 240, 0.95);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-card-empty) {
|
.root :global(.home-card-empty) {
|
||||||
@@ -2033,7 +2082,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.home-summary-card) {
|
.root :global(.home-summary-card) {
|
||||||
min-height: 118px;
|
min-height: 106px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -2145,8 +2194,8 @@
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 9px;
|
||||||
min-height: 122px;
|
min-height: 132px;
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
@@ -2247,6 +2296,7 @@
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
min-height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.opportunity-yes) {
|
.root :global(.opportunity-yes) {
|
||||||
@@ -2267,6 +2317,21 @@
|
|||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.root :global(.opportunity-sparkline) {
|
||||||
|
width: 100%;
|
||||||
|
height: 28px;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.opportunity-sparkline polyline) {
|
||||||
|
fill: none;
|
||||||
|
stroke: #18e6d4;
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
filter: drop-shadow(0 0 8px rgba(34, 211, 238, 0.22));
|
||||||
|
}
|
||||||
|
|
||||||
/* Market-closed opportunity card — dimmed state */
|
/* Market-closed opportunity card — dimmed state */
|
||||||
.root :global(.opportunity-card.market-closed) {
|
.root :global(.opportunity-card.market-closed) {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import {
|
|||||||
getWeatherSummary,
|
getWeatherSummary,
|
||||||
} from "@/lib/dashboard-utils";
|
} from "@/lib/dashboard-utils";
|
||||||
import { normalizeObservationSourceLabel } from "@/lib/source-labels";
|
import { normalizeObservationSourceLabel } from "@/lib/source-labels";
|
||||||
import { Expand, Thermometer, CloudRain, Wind } from "lucide-react";
|
|
||||||
|
|
||||||
const loadHistoryModal = () =>
|
const loadHistoryModal = () =>
|
||||||
import("@/components/dashboard/HistoryModal").then(
|
import("@/components/dashboard/HistoryModal").then(
|
||||||
@@ -227,6 +226,11 @@ type HomeTrendChart = {
|
|||||||
forecastPath: string;
|
forecastPath: string;
|
||||||
legendText: string;
|
legendText: string;
|
||||||
observationDots: Array<{ cx: number; cy: number; key: string }>;
|
observationDots: Array<{ cx: number; cy: number; key: string }>;
|
||||||
|
hourlyReports: Array<{
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
temperatureText: string;
|
||||||
|
}>;
|
||||||
hoverPoints: Array<{
|
hoverPoints: Array<{
|
||||||
cx: number;
|
cx: number;
|
||||||
cy: number;
|
cy: number;
|
||||||
@@ -399,32 +403,6 @@ function HomeMapToolbar() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
className="home-map-toolbar"
|
|
||||||
aria-label={locale === "en-US" ? "Map layer controls" : "地图图层控件"}
|
|
||||||
>
|
|
||||||
<div className="home-map-modes">
|
|
||||||
<button type="button" className="active">
|
|
||||||
<Thermometer size={14} strokeWidth={2} />
|
|
||||||
<span>{locale === "en-US" ? "Temperature" : "温度"}</span>
|
|
||||||
</button>
|
|
||||||
<button type="button">
|
|
||||||
<CloudRain size={14} strokeWidth={2} />
|
|
||||||
<span>{locale === "en-US" ? "Precipitation" : "降水"}</span>
|
|
||||||
</button>
|
|
||||||
<button type="button">
|
|
||||||
<Wind size={14} strokeWidth={2} />
|
|
||||||
<span>{locale === "en-US" ? "Wind" : "风场"}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="home-map-expand"
|
|
||||||
aria-label={locale === "en-US" ? "Expand map" : "放大地图"}
|
|
||||||
>
|
|
||||||
<Expand size={15} strokeWidth={2} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="home-map-legend" aria-hidden="true">
|
<div className="home-map-legend" aria-hidden="true">
|
||||||
<span>{locale === "en-US" ? "Temperature (°F)" : "温度 (°F)"}</span>
|
<span>{locale === "en-US" ? "Temperature (°F)" : "温度 (°F)"}</span>
|
||||||
<div className="home-map-legend-bar" />
|
<div className="home-map-legend-bar" />
|
||||||
@@ -565,11 +543,23 @@ function buildHomeTrendChart(
|
|||||||
temperatureText: formatTemperature(point.y, detail.temp_symbol || "°C"),
|
temperatureText: formatTemperature(point.y, detail.temp_symbol || "°C"),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
const hourlyReports = hoverSource
|
||||||
|
.filter((point) => {
|
||||||
|
const label = String(point.labelTime || "");
|
||||||
|
return /(^|\D)\d{1,2}:00($|\D)/.test(label) || hoverSource.length <= 8;
|
||||||
|
})
|
||||||
|
.slice(-6)
|
||||||
|
.map((point, index) => ({
|
||||||
|
key: `hourly-${point.labelTime}-${index}`,
|
||||||
|
label: point.labelTime,
|
||||||
|
temperatureText: formatTemperature(point.y, detail.temp_symbol || "°C"),
|
||||||
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
forecastPath,
|
forecastPath,
|
||||||
legendText: chartData.legendText,
|
legendText: chartData.legendText,
|
||||||
observationDots,
|
observationDots,
|
||||||
|
hourlyReports,
|
||||||
hoverPoints,
|
hoverPoints,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -747,7 +737,7 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
? detail?.multi_model_daily?.[detail.local_date]?.probabilities
|
? detail?.multi_model_daily?.[detail.local_date]?.probabilities
|
||||||
: undefined) ||
|
: undefined) ||
|
||||||
[];
|
[];
|
||||||
const displayedProbabilities = probabilityBuckets.slice(0, 5);
|
const displayedProbabilities = probabilityBuckets.slice(0, 4);
|
||||||
const modelLabels = getModelLabels(detail);
|
const modelLabels = getModelLabels(detail);
|
||||||
const marketScan = detail?.market_scan;
|
const marketScan = detail?.market_scan;
|
||||||
const marketBucket =
|
const marketBucket =
|
||||||
@@ -910,6 +900,29 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
keySignals,
|
keySignals,
|
||||||
} = spotlightView;
|
} = spotlightView;
|
||||||
const subtitle = `${cityCode} · ${localizedAirportName}`;
|
const subtitle = `${cityCode} · ${localizedAirportName}`;
|
||||||
|
const proCard = (
|
||||||
|
<div className={clsx("home-pro-card", isPro && "active")}>
|
||||||
|
<div>
|
||||||
|
<span>{proLabel}</span>
|
||||||
|
<strong>
|
||||||
|
{isPro
|
||||||
|
? locale === "en-US"
|
||||||
|
? "Today intraday analysis is the primary paid workflow."
|
||||||
|
: "今日日内分析是当前主要付费工作流。"
|
||||||
|
: locale === "en-US"
|
||||||
|
? "History review and future dates stay paid."
|
||||||
|
: "历史复盘和未来日期保持付费。"}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
{isPro ? (
|
||||||
|
<button type="button" onClick={() => void store.openTodayModal()}>
|
||||||
|
{locale === "en-US" ? "Today intraday" : "今日日内分析"}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<Link href="/account">{locale === "en-US" ? "Upgrade" : "升级"}</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside
|
<aside
|
||||||
@@ -928,12 +941,9 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
: "最佳机会"
|
: "最佳机会"
|
||||||
: locale === "en-US"
|
: locale === "en-US"
|
||||||
? "FOCUS CITY"
|
? "FOCUS CITY"
|
||||||
: "焦点城市"}
|
: "焦点城市"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" className="home-why-link">
|
|
||||||
{locale === "en-US" ? "Why this city?" : "为什么是它?"}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="home-card-titlebar">
|
<div className="home-card-titlebar">
|
||||||
@@ -962,6 +972,8 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{proCard}
|
||||||
|
|
||||||
<div className="home-weather-hero">
|
<div className="home-weather-hero">
|
||||||
<div>
|
<div>
|
||||||
<strong>{formatTemperature(currentTemp, symbol)}</strong>
|
<strong>{formatTemperature(currentTemp, symbol)}</strong>
|
||||||
@@ -1007,8 +1019,8 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
{trendChart ? (
|
{trendChart ? (
|
||||||
<div className="home-card-section intraday">
|
<div className="home-card-section intraday">
|
||||||
<h3>
|
<h3>
|
||||||
{locale === "en-US" ? "Intraday trend" : "今日日内走势"}{" "}
|
{locale === "en-US" ? "Hourly reports" : "小时准点报"}{" "}
|
||||||
<small>{locale === "en-US" ? "compact" : "简版"}</small>
|
<small>{locale === "en-US" ? "1h cadence" : "1h 级别"}</small>
|
||||||
</h3>
|
</h3>
|
||||||
<div className="home-intraday-chart">
|
<div className="home-intraday-chart">
|
||||||
<svg viewBox="0 0 296 78" aria-hidden="true">
|
<svg viewBox="0 0 296 78" aria-hidden="true">
|
||||||
@@ -1056,6 +1068,16 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
{trendChart.hourlyReports.length ? (
|
||||||
|
<div className="home-intraday-reports">
|
||||||
|
{trendChart.hourlyReports.map((report) => (
|
||||||
|
<span key={report.key}>
|
||||||
|
<b>{report.label}</b>
|
||||||
|
<strong>{report.temperatureText}</strong>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
@@ -1087,23 +1109,25 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
|
|
||||||
<div className="home-card-section probability">
|
<div className="home-card-section probability">
|
||||||
<h3>
|
<h3>
|
||||||
{probabilityTitle} <small>({dayMaxLabel})</small>
|
{probabilityTitle} <small>{dayMaxLabel}</small>
|
||||||
</h3>
|
</h3>
|
||||||
{displayedProbabilities.length ? (
|
{displayedProbabilities.length ? (
|
||||||
<div className="home-probability-list">
|
<div className="home-probability-ladder">
|
||||||
{displayedProbabilities.map((bucket, index) => {
|
{displayedProbabilities.map((bucket, index) => {
|
||||||
const probability = Number(bucket.probability ?? 0);
|
const probability = Number(bucket.probability ?? 0);
|
||||||
const width = Math.max(6, Math.min(100, probability * 100));
|
const width = Math.max(6, Math.min(100, probability * 100));
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`${getProbabilityLabel(bucket, symbol)}-${index}`}
|
key={`${getProbabilityLabel(bucket, symbol)}-${index}`}
|
||||||
className="home-probability-row"
|
className="home-probability-ladder-row"
|
||||||
>
|
>
|
||||||
<span>{getProbabilityLabel(bucket, symbol)}</span>
|
<span className="home-probability-threshold">
|
||||||
<div>
|
{getProbabilityLabel(bucket, symbol)}
|
||||||
|
</span>
|
||||||
|
<div className="home-probability-track">
|
||||||
<i style={{ width: `${width}%` }} />
|
<i style={{ width: `${width}%` }} />
|
||||||
<strong>{formatProbability(probability)}</strong>
|
|
||||||
</div>
|
</div>
|
||||||
|
<strong>{formatProbability(probability)}</strong>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -1182,27 +1206,6 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
<p>{observationSource}</p>
|
<p>{observationSource}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={clsx("home-pro-card", isPro && "active")}>
|
|
||||||
<div>
|
|
||||||
<span>{proLabel}</span>
|
|
||||||
<strong>
|
|
||||||
{isPro
|
|
||||||
? locale === "en-US"
|
|
||||||
? "Open today intraday analysis first. History review and future-day workflow stay available after that."
|
|
||||||
: "先打开今日日内分析,历史复盘和未来日工作流仍可继续查看。"
|
|
||||||
: locale === "en-US"
|
|
||||||
? "History review and future dates stay paid."
|
|
||||||
: "历史复盘和未来日期保持付费。"}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
{isPro ? (
|
|
||||||
<button type="button" onClick={() => void store.openTodayModal()}>
|
|
||||||
{locale === "en-US" ? "Today intraday" : "今日日内分析"}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<Link href="/account">{locale === "en-US" ? "Upgrade" : "升级"}</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1265,8 +1268,8 @@ function OpportunityStrip({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
</span>
|
</span>
|
||||||
<strong>
|
<strong>
|
||||||
{locale === "en-US"
|
{locale === "en-US"
|
||||||
? "Sorted by score and risk"
|
? "Market question · YES/NO · Edge · Trend"
|
||||||
: "按综合评分排序"}
|
: "市场问题 · YES/NO 价格 · Edge · 小趋势线"}
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
<Link href="/docs/intraday-signal" className="opportunity-view-all">
|
<Link href="/docs/intraday-signal" className="opportunity-view-all">
|
||||||
@@ -1291,6 +1294,23 @@ function OpportunityStrip({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
summary?.risk?.level ||
|
summary?.risk?.level ||
|
||||||
detail?.risk?.level;
|
detail?.risk?.level;
|
||||||
const marketClosed = !tradableOpportunity;
|
const marketClosed = !tradableOpportunity;
|
||||||
|
const marketBucket = detail?.market_scan?.temperature_bucket;
|
||||||
|
const marketQuestion =
|
||||||
|
detail?.market_scan?.primary_market?.question ||
|
||||||
|
`${getProbabilityLabel(marketBucket || {}, symbol)} ${
|
||||||
|
detail?.market_scan?.selected_date ||
|
||||||
|
detail?.local_date ||
|
||||||
|
""
|
||||||
|
}`;
|
||||||
|
const opportunitySparkline = buildSparklinePoints(
|
||||||
|
detail?.market_scan?.sparkline?.length
|
||||||
|
? detail.market_scan.sparkline
|
||||||
|
: [
|
||||||
|
Number(summary?.current?.temp ?? detail?.current?.temp ?? 0),
|
||||||
|
Number(debPrediction ?? 0),
|
||||||
|
Number(detail?.forecast?.today_high ?? debPrediction ?? 0),
|
||||||
|
],
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={city.name}
|
key={city.name}
|
||||||
@@ -1316,8 +1336,7 @@ function OpportunityStrip({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="opportunity-meta">
|
<span className="opportunity-meta">
|
||||||
> {formatTemperature(debPrediction, symbol)}{" "}
|
{marketQuestion}
|
||||||
{locale === "en-US" ? "target" : "目标"}
|
|
||||||
{marketClosed && (
|
{marketClosed && (
|
||||||
<span className="opportunity-market-status">
|
<span className="opportunity-market-status">
|
||||||
{" · "}
|
{" · "}
|
||||||
@@ -1338,6 +1357,11 @@ function OpportunityStrip({ snapshots }: { snapshots: CitySnapshot[] }) {
|
|||||||
{formatEdge(getMarketEdgeValue(detail))}
|
{formatEdge(getMarketEdgeValue(detail))}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<svg className="opportunity-sparkline" viewBox="0 0 92 28" aria-hidden="true">
|
||||||
|
{opportunitySparkline ? (
|
||||||
|
<polyline points={opportunitySparkline} />
|
||||||
|
) : null}
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user