mirror of
https://github.com/Zproger/Aurum.git
synced 2026-10-13 02:56:39 +07:00
fix: allocation view as a real table (color · ticker, %, amount)
Replaces the flex-based list (coin logo + full name + ticker suffix, prone to truncation and misaligned columns) with an actual <table>: a color swatch + ticker, percent, and amount as three real columns that align natively instead of fighting flexbox for it. Full coin name is still available via a title tooltip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01855PiW5Wd8n4aRW4n9kByY
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { Bitcoin } from "lucide-react";
|
||||
import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from "recharts";
|
||||
import { formatCurrency, maskAmount } from "@/lib/format";
|
||||
import { useTranslation } from "@/lib/i18n";
|
||||
@@ -31,7 +30,6 @@ interface Slice {
|
||||
key: string;
|
||||
name: string;
|
||||
symbol: string | null;
|
||||
thumbUrl: string | null;
|
||||
amount: number;
|
||||
color: string;
|
||||
}
|
||||
@@ -39,7 +37,7 @@ interface Slice {
|
||||
function buildSlices(holdings: CryptoHolding[]): Slice[] {
|
||||
const priced = holdings
|
||||
.filter((h) => h.value !== null && Number(h.value) > 0)
|
||||
.map((h) => ({ key: String(h.asset_id), name: h.name, symbol: h.symbol, thumbUrl: h.thumb_url, amount: Number(h.value) }))
|
||||
.map((h) => ({ key: String(h.asset_id), name: h.name, symbol: h.symbol, amount: Number(h.value) }))
|
||||
.sort((a, b) => b.amount - a.amount);
|
||||
|
||||
const top = priced.slice(0, MAX_SLICES).map((item, index) => ({ ...item, color: SERIES_COLORS[index] }));
|
||||
@@ -47,7 +45,7 @@ function buildSlices(holdings: CryptoHolding[]): Slice[] {
|
||||
if (rest.length === 0) return top;
|
||||
|
||||
const otherTotal = rest.reduce((sum, item) => sum + item.amount, 0);
|
||||
return [...top, { key: "other", name: "Other", symbol: null, thumbUrl: null, amount: otherTotal, color: OTHER_COLOR }];
|
||||
return [...top, { key: "other", name: "Other", symbol: null, amount: otherTotal, color: OTHER_COLOR }];
|
||||
}
|
||||
|
||||
function DonutTooltip({
|
||||
@@ -73,11 +71,13 @@ function DonutTooltip({
|
||||
);
|
||||
}
|
||||
|
||||
/** The "Allocation" tab's content inside CryptoOverviewCard — a donut (part-
|
||||
* to-whole across few categories is exactly the case a donut is fine for)
|
||||
* plus a list that also carries the $ amount, which the donut/legend alone
|
||||
* can't — the user explicitly wants both percent and sum per coin, not just
|
||||
* percent the way CoinMarketCap's own version shows it. */
|
||||
/** The "Allocation" tab's content inside CryptoOverviewCard — a donut
|
||||
* (part-to-whole across few categories is exactly the case a donut is fine
|
||||
* for) plus a compact table: a color swatch identifying the ticker, its
|
||||
* share, and the amount. A real <table>, not a flex row list — table
|
||||
* columns size to their own content and align natively, so this can't
|
||||
* stretch across whatever space is left next to the donut the way a
|
||||
* flex-1 row did. */
|
||||
export function CryptoAllocationBody({ holdings, isLoading, hidden }: CryptoAllocationBodyProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -93,7 +93,7 @@ export function CryptoAllocationBody({ holdings, isLoading, hidden }: CryptoAllo
|
||||
const donutData = slices.map((slice) => ({ ...slice, percent: (slice.amount / total) * 100 }));
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-4 sm:flex-row sm:items-center sm:justify-center sm:gap-8">
|
||||
<div className="flex flex-col items-center justify-center gap-6 sm:flex-row">
|
||||
<div className="h-48 w-48 shrink-0 sm:h-56 sm:w-56">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<PieChart>
|
||||
@@ -117,35 +117,26 @@ export function CryptoAllocationBody({ holdings, isLoading, hidden }: CryptoAllo
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Capped width, not flex-1 — otherwise with few/short coin names the
|
||||
row stretches across whatever's left of the card and the percent/
|
||||
amount columns end up stranded far from the coin they describe. */}
|
||||
<ul className="w-full min-w-0 divide-y divide-gridline sm:w-72">
|
||||
{donutData.map((slice) => (
|
||||
<li key={slice.key} className="flex items-center gap-3 py-2 first:pt-0">
|
||||
{slice.thumbUrl ? (
|
||||
<img src={slice.thumbUrl} alt="" className="h-6 w-6 shrink-0 rounded-full" />
|
||||
) : (
|
||||
<span
|
||||
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full"
|
||||
style={{ backgroundColor: `${slice.color}26` }}
|
||||
>
|
||||
<Bitcoin size={12} style={{ color: slice.color }} />
|
||||
</span>
|
||||
)}
|
||||
<span className="min-w-0 flex-1 truncate text-sm text-text-primary">
|
||||
{slice.key === "other" ? t("crypto.allocation.other") : slice.name}
|
||||
{slice.symbol && <span className="text-text-muted"> · {slice.symbol}</span>}
|
||||
</span>
|
||||
<span className="shrink-0 text-right text-sm font-medium tabular-nums text-text-primary">
|
||||
{slice.percent.toFixed(1)}%
|
||||
</span>
|
||||
<span className="w-20 shrink-0 text-right text-xs tabular-nums text-text-muted">
|
||||
{maskAmount(formatCurrency(slice.amount), hidden)}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<table className="text-sm">
|
||||
<tbody>
|
||||
{donutData.map((slice) => (
|
||||
<tr key={slice.key} title={slice.key === "other" ? undefined : slice.name}>
|
||||
<td className="py-1.5 pr-3">
|
||||
<span className="flex items-center gap-2">
|
||||
<span className="h-2.5 w-2.5 shrink-0 rounded-full" style={{ backgroundColor: slice.color }} />
|
||||
<span className="font-medium text-text-primary">
|
||||
{slice.key === "other" ? t("crypto.allocation.other") : slice.symbol}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-1.5 pr-3 text-right font-medium tabular-nums text-text-primary">
|
||||
{slice.percent.toFixed(1)}%
|
||||
</td>
|
||||
<td className="py-1.5 text-right tabular-nums text-text-muted">{maskAmount(formatCurrency(slice.amount), hidden)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user