Frame homepage map and auto-focus top opportunity
This commit is contained in:
@@ -67,12 +67,19 @@
|
||||
|
||||
/* ── Map ── */
|
||||
.root :global(.map) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
top: calc(var(--header-height) + 16px);
|
||||
right: 418px;
|
||||
bottom: 128px;
|
||||
left: calc(var(--sidebar-width) + 30px);
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(34, 211, 238, 0.12);
|
||||
border-radius: 22px;
|
||||
background: #020617;
|
||||
box-shadow:
|
||||
0 28px 80px rgba(0, 0, 0, 0.38),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.weatherAura {
|
||||
@@ -890,6 +897,31 @@
|
||||
background: rgba(148, 163, 184, 0.1);
|
||||
}
|
||||
|
||||
.root :global(.home-top-opportunity-label) {
|
||||
display: inline-flex;
|
||||
align-self: flex-start;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 5px 8px;
|
||||
border: 1px solid rgba(34, 211, 238, 0.18);
|
||||
border-radius: 999px;
|
||||
background: rgba(8, 145, 178, 0.1);
|
||||
color: #67e8f9;
|
||||
font-size: 10px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.root :global(.home-top-opportunity-label::before) {
|
||||
content: "";
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background: #22c55e;
|
||||
box-shadow: 0 0 12px rgba(34, 197, 94, 0.72);
|
||||
}
|
||||
|
||||
.root :global(.home-card-titlebar) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -1468,6 +1500,10 @@
|
||||
}
|
||||
|
||||
@media (max-width: 1360px) {
|
||||
.root :global(.map) {
|
||||
right: 18px;
|
||||
}
|
||||
|
||||
.root :global(.home-intelligence-panel) {
|
||||
display: none;
|
||||
}
|
||||
@@ -1478,6 +1514,11 @@
|
||||
}
|
||||
|
||||
@media (max-width: 1020px) {
|
||||
.root :global(.map) {
|
||||
left: 18px;
|
||||
bottom: 188px;
|
||||
}
|
||||
|
||||
.root :global(.home-opportunity-strip) {
|
||||
left: 18px;
|
||||
grid-template-columns: 1fr;
|
||||
@@ -1489,6 +1530,13 @@
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.root :global(.map) {
|
||||
inset: var(--header-height) 0 0;
|
||||
border-radius: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.root :global(.home-opportunity-strip) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import clsx from "clsx";
|
||||
import dynamic from "next/dynamic";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import styles from "./Dashboard.module.css";
|
||||
import detailChromeStyles from "./DetailPanelChrome.module.css";
|
||||
import modalChromeStyles from "./ModalChrome.module.css";
|
||||
@@ -122,7 +122,13 @@ function buildSnapshot(
|
||||
detail?.risk?.level;
|
||||
const hitRate = Number(city.deb_recent_hit_rate ?? 0);
|
||||
const sampleCount = Number(city.deb_recent_sample_count ?? 0);
|
||||
const edgePercent = Number(detail?.market_scan?.edge_percent);
|
||||
const normalizedEdge =
|
||||
Number.isFinite(edgePercent) && Math.abs(edgePercent) <= 1
|
||||
? edgePercent * 100
|
||||
: edgePercent;
|
||||
const score =
|
||||
(Number.isFinite(normalizedEdge) ? 1000 + normalizedEdge * 10 : 0) +
|
||||
(RISK_SCORE[String(tier || "")] || 0) * 100 +
|
||||
hitRate * 100 +
|
||||
Math.min(sampleCount, 60) / 10;
|
||||
@@ -288,6 +294,10 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
|
||||
×
|
||||
</button>
|
||||
|
||||
<div className="home-top-opportunity-label">
|
||||
{locale === "en-US" ? "Today’s Top Opportunity" : "今日最佳机会"}
|
||||
</div>
|
||||
|
||||
<div className="home-card-titlebar">
|
||||
<div>
|
||||
<h2>{summary?.display_name || detail?.display_name || city.display_name}</h2>
|
||||
@@ -520,6 +530,7 @@ function OpportunityStrip({ snapshots }: { snapshots: CitySnapshot[] }) {
|
||||
function DashboardScreen() {
|
||||
const store = useDashboardStore();
|
||||
const { t } = useI18n();
|
||||
const didAutoFocusRef = useRef(false);
|
||||
const activeSummary = store.selectedCity
|
||||
? store.citySummariesByName[store.selectedCity] || null
|
||||
: null;
|
||||
@@ -581,6 +592,17 @@ function DashboardScreen() {
|
||||
const showHomepageChrome =
|
||||
!store.isPanelOpen && !store.historyState.isOpen && !store.futureModalDate;
|
||||
|
||||
useEffect(() => {
|
||||
if (didAutoFocusRef.current) return;
|
||||
if (!showHomepageChrome) return;
|
||||
if (store.selectedCity) return;
|
||||
const topOpportunity = homepageSnapshots[0]?.city.name;
|
||||
if (!topOpportunity) return;
|
||||
|
||||
didAutoFocusRef.current = true;
|
||||
void store.focusCity(topOpportunity);
|
||||
}, [homepageSnapshots, showHomepageChrome, store]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
|
||||
Reference in New Issue
Block a user