Simplify scan opportunity list and speed quotes
This commit is contained in:
@@ -9572,7 +9572,7 @@
|
||||
|
||||
.root :global(.scan-table-header) {
|
||||
display: grid;
|
||||
grid-template-columns: 46px minmax(180px, 1.08fr) minmax(132px, 0.72fr) minmax(220px, 1.08fr) minmax(150px, 0.82fr) 92px 82px;
|
||||
grid-template-columns: minmax(210px, 1.1fr) minmax(132px, 0.72fr) minmax(220px, 1.12fr) minmax(150px, 0.82fr) 96px;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 14px 18px;
|
||||
@@ -9786,7 +9786,7 @@
|
||||
|
||||
.root :global(.scan-table-row) {
|
||||
display: grid;
|
||||
grid-template-columns: 46px minmax(180px, 1.08fr) minmax(132px, 0.72fr) minmax(220px, 1.08fr) minmax(150px, 0.82fr) 92px 82px;
|
||||
grid-template-columns: minmax(210px, 1.1fr) minmax(132px, 0.72fr) minmax(220px, 1.12fr) minmax(150px, 0.82fr) 96px;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
min-height: 132px;
|
||||
@@ -9813,38 +9813,12 @@
|
||||
box-shadow: inset 0 0 0 1px rgba(23, 217, 139, 0.48);
|
||||
}
|
||||
|
||||
.root :global(.scan-rank-cell),
|
||||
.root :global(.scan-score-cell),
|
||||
.root :global(.scan-edge-cell) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.root :global(.scan-rank-circle) {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
color: #06150d;
|
||||
background: linear-gradient(180deg, #24e495, #11b86f);
|
||||
}
|
||||
|
||||
.root :global(.scan-rank-circle.yellow) {
|
||||
color: #281a00;
|
||||
background: linear-gradient(180deg, #ffd95f, #ffb61c);
|
||||
}
|
||||
|
||||
.root :global(.scan-rank-circle.gray) {
|
||||
color: #08131f;
|
||||
background: linear-gradient(180deg, #d6e3f7, #94a7c4);
|
||||
}
|
||||
|
||||
.root :global(.scan-rank-circle.red) {
|
||||
color: #fff3f3;
|
||||
background: linear-gradient(180deg, #ff7373, #df4747);
|
||||
}
|
||||
|
||||
.root :global(.scan-city-cell) {
|
||||
display: block;
|
||||
}
|
||||
@@ -9870,11 +9844,6 @@
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.root :global(.scan-city-volume) {
|
||||
font-size: 14px;
|
||||
color: #dbe7f8;
|
||||
}
|
||||
|
||||
.root :global(.scan-time-cell) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -10036,38 +10005,6 @@
|
||||
color: #ff6868;
|
||||
}
|
||||
|
||||
.root :global(.scan-score-ring) {
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
border-radius: 50%;
|
||||
background: conic-gradient(#20df91 0 85%, rgba(255, 255, 255, 0.06) 85% 100%);
|
||||
}
|
||||
|
||||
.root :global(.scan-score-ring.tone-yellow) {
|
||||
background: conic-gradient(#ffbe26 0 72%, rgba(255, 255, 255, 0.06) 72% 100%);
|
||||
}
|
||||
|
||||
.root :global(.scan-score-ring.tone-red) {
|
||||
background: conic-gradient(#ff5d5d 0 58%, rgba(255, 255, 255, 0.06) 58% 100%);
|
||||
}
|
||||
|
||||
.root :global(.scan-score-ring::after) {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 5px;
|
||||
border-radius: 50%;
|
||||
background: #0b1728;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.root :global(.scan-score-ring span) {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
color: #ecf4ff;
|
||||
}
|
||||
|
||||
.root :global(.scan-row-fav) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -24,14 +24,6 @@ function formatPercent(value?: number | null, signed = false) {
|
||||
return `${signed && numeric >= 0 ? "+" : ""}${numeric.toFixed(1)}%`;
|
||||
}
|
||||
|
||||
function formatVolume(value?: number | null) {
|
||||
const numeric = Number(value || 0);
|
||||
if (!Number.isFinite(numeric) || numeric <= 0) return "--";
|
||||
if (numeric >= 1_000_000) return `$${(numeric / 1_000_000).toFixed(1)}M`;
|
||||
if (numeric >= 1_000) return `$${(numeric / 1_000).toFixed(0)}K`;
|
||||
return `$${numeric.toFixed(0)}`;
|
||||
}
|
||||
|
||||
function formatWindowMinutes(value: number | null | undefined, locale: string) {
|
||||
if (value == null || !Number.isFinite(Number(value))) return "--";
|
||||
const minutes = Math.max(0, Math.round(Number(value)));
|
||||
@@ -118,13 +110,6 @@ export function getWindowPhaseMeta(
|
||||
};
|
||||
}
|
||||
|
||||
function scoreTone(score?: number | null) {
|
||||
const numeric = Number(score || 0);
|
||||
if (numeric >= 85) return "green";
|
||||
if (numeric >= 70) return "yellow";
|
||||
return "gray";
|
||||
}
|
||||
|
||||
function ProbabilityPreview({
|
||||
row,
|
||||
locale,
|
||||
@@ -197,15 +182,6 @@ function ProbabilityPreview({
|
||||
);
|
||||
}
|
||||
|
||||
function ScoreRing({ score }: { score?: number | null }) {
|
||||
const displayScore = Math.max(0, Math.min(100, Number(score || 0)));
|
||||
return (
|
||||
<div className={`scan-score-ring tone-${scoreTone(displayScore)}`}>
|
||||
<span>{displayScore.toFixed(0)}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const OpportunityTable = React.memo(function OpportunityTable({
|
||||
rows,
|
||||
status,
|
||||
@@ -271,17 +247,15 @@ export const OpportunityTable = React.memo(function OpportunityTable({
|
||||
</div>
|
||||
) : null}
|
||||
<div className="scan-table-header">
|
||||
<span />
|
||||
<span>{isEn ? "City / Market" : "城市 / 市场"}</span>
|
||||
<span>{isEn ? "Local Time / Phase" : "当前时间 / 阶段"}</span>
|
||||
<span>{isEn ? "EMOS / Market" : "EMOS / 市场"}</span>
|
||||
<span>{isEn ? "Quote / Model" : "买价 / 模型"}</span>
|
||||
<span>{isEn ? "Edge" : "边际优势"}</span>
|
||||
<span>{isEn ? "Score" : "综合得分"}</span>
|
||||
</div>
|
||||
|
||||
<div className="scan-table-body">
|
||||
{rows.map((row, index) => {
|
||||
{rows.map((row) => {
|
||||
const phaseMeta = getWindowPhaseMeta(row, locale);
|
||||
const tempSymbol = normalizeTemperatureSymbol(row.target_unit || row.temp_symbol);
|
||||
const localizedCityName = getLocalizedCityName(
|
||||
@@ -290,7 +264,6 @@ export const OpportunityTable = React.memo(function OpportunityTable({
|
||||
locale,
|
||||
);
|
||||
const selected = selectedRowId === row.id;
|
||||
const scoreClass = scoreTone(row.final_score);
|
||||
return (
|
||||
<button
|
||||
key={row.id}
|
||||
@@ -298,19 +271,12 @@ export const OpportunityTable = React.memo(function OpportunityTable({
|
||||
className={`scan-table-row ${selected ? "selected" : ""}`}
|
||||
onClick={() => onSelectRow?.(row)}
|
||||
>
|
||||
<div className="scan-rank-cell">
|
||||
<div className={`scan-rank-circle ${scoreClass}`}>
|
||||
{index + 1}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="scan-city-cell">
|
||||
<div className="scan-city-copy">
|
||||
<div className="scan-city-name">{localizedCityName}</div>
|
||||
<div className="scan-city-sub">
|
||||
{row.market_question || row.target_label || "--"}
|
||||
</div>
|
||||
<div className="scan-city-volume">{formatVolume(row.volume)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -344,10 +310,6 @@ export const OpportunityTable = React.memo(function OpportunityTable({
|
||||
<div className={`scan-edge-cell ${Number(row.edge_percent || 0) >= 0 ? "positive" : "negative"}`}>
|
||||
{formatPercent(row.edge_percent, true)}
|
||||
</div>
|
||||
|
||||
<div className="scan-score-cell">
|
||||
<ScoreRing score={row.final_score} />
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user