feat: implement dashboard layout with glassmorphism styling and modular panel sections
This commit is contained in:
@@ -1075,6 +1075,37 @@
|
|||||||
border: 1px solid rgba(251, 113, 133, 0.26);
|
border: 1px solid rgba(251, 113, 133, 0.26);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.root :global(.prob-distribution-panel) {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(15, 23, 42, 0.24);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.prob-distribution-head) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.prob-distribution-head span) {
|
||||||
|
color: var(--cyan);
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 900;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.prob-distribution-head em) {
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 11px;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
.root :global(.prob-price-card) {
|
.root :global(.prob-price-card) {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|||||||
@@ -1125,9 +1125,70 @@ export function ProbabilityDistribution({
|
|||||||
: `市场仅作参考:最高交易温度桶 ${topMarketBucketText}`
|
: `市场仅作参考:最高交易温度桶 ${topMarketBucketText}`
|
||||||
: locale === "en-US"
|
: locale === "en-US"
|
||||||
? `Market reference only: this bucket ${marketYesText}`
|
? `Market reference only: this bucket ${marketYesText}`
|
||||||
: `市场仅作参考:该温度桶 ${marketYesText}`}
|
: `市场仅作参考:该温度桶 ${marketYesText}`}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="prob-distribution-panel">
|
||||||
|
<div className="prob-distribution-head">
|
||||||
|
<span>
|
||||||
|
{locale === "en-US"
|
||||||
|
? "EMOS probability distribution"
|
||||||
|
: "EMOS 概率分布"}
|
||||||
|
</span>
|
||||||
|
<em>
|
||||||
|
{marketContractRows.length > 0
|
||||||
|
? locale === "en-US"
|
||||||
|
? "aligned to market contract buckets"
|
||||||
|
: "已按市场合约桶聚合"
|
||||||
|
: locale === "en-US"
|
||||||
|
? "calibrated temperature buckets"
|
||||||
|
: "校准后的温度桶"}
|
||||||
|
</em>
|
||||||
|
</div>
|
||||||
|
{probabilityRows.length === 0 ? (
|
||||||
|
<EmptyState text={t("section.noProb")} />
|
||||||
|
) : (
|
||||||
|
probabilityRows.map((row, index) => {
|
||||||
|
const probability = Math.round(Number(row.probability || 0) * 100);
|
||||||
|
const rowMarketBucket = row.marketBucket;
|
||||||
|
const rowMarketPrice =
|
||||||
|
rowMarketBucket?.market_price ?? rowMarketBucket?.yes_buy ?? null;
|
||||||
|
const yesPriceText = toPriceCents(rowMarketPrice);
|
||||||
|
const marketTagFinal = rowMarketBucket
|
||||||
|
? locale === "en-US"
|
||||||
|
? `Market ref: ${yesPriceText || "--"}`
|
||||||
|
: `市场参考: ${yesPriceText || "--"}`
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`${row.key || index}`}
|
||||||
|
className="prob-row"
|
||||||
|
>
|
||||||
|
<div className="prob-label">{row.label}</div>
|
||||||
|
<div className="prob-bar-track">
|
||||||
|
<div
|
||||||
|
className={clsx("prob-bar-fill", `rank-${index}`)}
|
||||||
|
style={{ width: `${Math.max(probability, 8)}%` }}
|
||||||
|
>
|
||||||
|
{probability}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{marketTagFinal && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"prob-market-inline",
|
||||||
|
rowMarketBucket ? "yes" : "no",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{marketTagFinal}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{hasPriceAnalysis && (
|
{hasPriceAnalysis && (
|
||||||
<div className="prob-price-card">
|
<div className="prob-price-card">
|
||||||
<div className="prob-price-head">
|
<div className="prob-price-head">
|
||||||
@@ -1206,49 +1267,6 @@ export function ProbabilityDistribution({
|
|||||||
</em>
|
</em>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{probabilityRows.length === 0 ? (
|
|
||||||
<EmptyState text={t("section.noProb")} />
|
|
||||||
) : (
|
|
||||||
probabilityRows.map((row, index) => {
|
|
||||||
const probability = Math.round(Number(row.probability || 0) * 100);
|
|
||||||
const rowMarketBucket = row.marketBucket;
|
|
||||||
const rowMarketPrice =
|
|
||||||
rowMarketBucket?.market_price ?? rowMarketBucket?.yes_buy ?? null;
|
|
||||||
const yesPriceText = toPriceCents(rowMarketPrice);
|
|
||||||
const marketTagFinal = rowMarketBucket
|
|
||||||
? locale === "en-US"
|
|
||||||
? `Market ref: ${yesPriceText || "--"}`
|
|
||||||
: `市场参考: ${yesPriceText || "--"}`
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={`${row.key || index}`}
|
|
||||||
className="prob-row"
|
|
||||||
>
|
|
||||||
<div className="prob-label">{row.label}</div>
|
|
||||||
<div className="prob-bar-track">
|
|
||||||
<div
|
|
||||||
className={clsx("prob-bar-fill", `rank-${index}`)}
|
|
||||||
style={{ width: `${Math.max(probability, 8)}%` }}
|
|
||||||
>
|
|
||||||
{probability}%
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{marketTagFinal && (
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
"prob-market-inline",
|
|
||||||
rowMarketBucket ? "yes" : "no",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{marketTagFinal}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user