From dc2f26e8c213ef10cfb04021184aa980dab6598e Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Wed, 1 Apr 2026 15:59:13 +0100 Subject: [PATCH 1/2] =?UTF-8?q?[#725]=20Portfolio=20tab=20v4=20=E2=80=94?= =?UTF-8?q?=20structured=20label-value=20grid=20layout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Portfolio summary: grid-cols-[auto_1fr] for Value/Tokens/Best 24h Holdings: bordered cards with grid-cols-[auto_1fr] for all stats (Value, Change, Balance, Price, Entry, Traded) Donations: structured grid for Received/Given totals, grid rows for per-story breakdown Trades: two-line per trade (Buy/Sell + title, then tokens + amount + date in grid) No inline flex-wrap — all data in predictable grid positions Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/profile/[address]/page.tsx | 335 +++++++++++++---------------- 1 file changed, 155 insertions(+), 180 deletions(-) diff --git a/src/app/profile/[address]/page.tsx b/src/app/profile/[address]/page.tsx index e5c9d676..b351eda1 100644 --- a/src/app/profile/[address]/page.tsx +++ b/src/app/profile/[address]/page.tsx @@ -1370,30 +1370,26 @@ function PortfolioTab({ address, isOwnProfile }: { address: string; isOwnProfile return (
- {/* Portfolio summary — compact horizontal row */} + {/* Portfolio summary — structured grid */} {hasHoldings && ( -
-
- Portfolio - +
+

Portfolio

+
+ Value + {formatPrice(formatUnits(totalValue, reserveDecimals))} {RESERVE_LABEL} + {plotUsd && ({formatUsdValue(Number(formatUnits(totalValue, reserveDecimals)) * plotUsd)})} - {plotUsd && ( - ≈ {formatUsdValue(Number(formatUnits(totalValue, reserveDecimals)) * plotUsd)} - )} - · - {holdings!.length} - {holdings!.length === 1 ? "token" : "tokens"} + Tokens + {holdings!.length} {bestPick && bestPick.priceChange !== null && ( <> - · - best 24h: - - {bestPick.storyline.title.slice(0, 20)} - {bestPick.storyline.title.length > 20 ? "..." : ""} - - = 0 ? "text-accent" : "text-error"}> - {bestPick.priceChange >= 0 ? "+" : ""}{bestPick.priceChange.toFixed(1)}% + Best 24h + + {bestPick.storyline.title.slice(0, 20)}{bestPick.storyline.title.length > 20 ? "..." : ""} + = 0 ? "text-accent" : "text-error"}`}> + {bestPick.priceChange >= 0 ? "+" : ""}{bestPick.priceChange.toFixed(1)}% + )} @@ -1401,140 +1397,132 @@ function PortfolioTab({ address, isOwnProfile }: { address: string; isOwnProfile
)} - {/* Token holdings */} - {hasHoldings && ( -
- {holdings!.map((h) => ( -
- {/* Primary: title + genre + value + change */} -
- - {h.storyline.title} - - {h.storyline.genre && ( - - {h.storyline.genre} - - )} - - {formatPrice(formatUnits(h.value, h.reserveDecimals))} {RESERVE_LABEL} + {/* Token holdings — structured cards */} + {hasHoldings && holdings!.map((h) => ( +
+ {/* Title + genre */} +
+ + {h.storyline.title} + + {h.storyline.genre && ( + {h.storyline.genre} + )} +
+ {/* Stats grid */} +
+ Value + + {formatPrice(formatUnits(h.value, h.reserveDecimals))} {RESERVE_LABEL} + {plotUsd && ({formatUsdValue(Number(formatUnits(h.value, h.reserveDecimals)) * plotUsd)})} + + {h.priceChange !== null && ( + <> + Change + = 0 ? "text-accent" : "text-error"}`}> + {h.priceChange >= 0 ? "+" : ""}{h.priceChange.toFixed(1)}% - {plotUsd && ( - - ({formatUsdValue(Number(formatUnits(h.value, h.reserveDecimals)) * plotUsd)}) - - )} - {h.priceChange !== null && ( - = 0 ? "text-accent" : "text-error"}`}> - {h.priceChange >= 0 ? "+" : ""}{h.priceChange.toFixed(1)}% - - )} -
- {/* Secondary: compact stats */} -
- {formatPrice(formatUnits(h.balance, 18))} tokens - · - price {formatPrice(formatUnits(h.price, 18))} {RESERVE_LABEL} - {plotUsd != null && (≈ {formatUsdValue(Number(formatUnits(h.price, 18)) * plotUsd)})} - {h.entryPrice !== null && h.entryPrice > 0 && ( - <> - · - entry {formatPrice(h.entryPrice)} {RESERVE_LABEL} - {plotUsd != null && (≈ {formatUsdValue(h.entryPrice * plotUsd)})} - - )} - {h.lastTraded && ( - <> - · - {new Date(h.lastTraded).toLocaleDateString("en-US", { month: "short", day: "numeric" })} - - )} -
-
- ))} + + )} + Balance + {formatPrice(formatUnits(h.balance, 18))} tokens + Price + + {formatPrice(formatUnits(h.price, 18))} {RESERVE_LABEL} + {plotUsd != null && ({formatUsdValue(Number(formatUnits(h.price, 18)) * plotUsd)})} + + {h.entryPrice !== null && h.entryPrice > 0 && ( + <> + Entry + + {formatPrice(h.entryPrice)} {RESERVE_LABEL} + {plotUsd != null && ({formatUsdValue(h.entryPrice * plotUsd)})} + + + )} + {h.lastTraded && ( + <> + Traded + + {new Date(h.lastTraded).toLocaleDateString("en-US", { month: "short", day: "numeric" })} + + + )} +
- )} + ))} - {/* Donations — consistent card styling for both received and given */} + {/* Donations — structured card */} {(hasDonationsReceived || (isOwnProfile && hasDonationsGiven)) && ( -
- {hasDonationsReceived && ( -
- Received - - {formatPrice(formatUnits(donationsReceived!.total, 18))} {RESERVE_LABEL} - - {plotUsd != null && ( - (≈ {formatUsdValue(Number(formatUnits(donationsReceived!.total, 18)) * plotUsd)}) - )} - from {donationsReceived!.count} {donationsReceived!.count === 1 ? "donation" : "donations"} -
- )} - - {isOwnProfile && hasDonationsGiven && ( -
-
- Given - +
+

Donations

+
+ {hasDonationsReceived && ( + <> + Received + + {formatPrice(formatUnits(donationsReceived!.total, 18))} {RESERVE_LABEL} + {plotUsd != null && ({formatUsdValue(Number(formatUnits(donationsReceived!.total, 18)) * plotUsd)})} + + + from {donationsReceived!.count} {donationsReceived!.count === 1 ? "donation" : "donations"} + + )} + {isOwnProfile && hasDonationsGiven && ( + <> + Given + {formatPrice(formatUnits(totalDonated, 18))} {RESERVE_LABEL} + {plotUsd != null && totalDonated > BigInt(0) && ({formatUsdValue(Number(formatUnits(totalDonated, 18)) * plotUsd)})} - {plotUsd != null && totalDonated > BigInt(0) && ( - (≈ {formatUsdValue(Number(formatUnits(totalDonated, 18)) * plotUsd)}) - )} - {donationTotalCount} {donationTotalCount === 1 ? "donation" : "donations"} -
-
- {donationsGiven.map((d) => ( -
- - Story #{d.storyline_id} - - - {formatPrice(formatUnits(BigInt(d.amount), 18))} {RESERVE_LABEL} - - {plotUsd != null && ( - (≈ {formatUsdValue(Number(formatUnits(BigInt(d.amount), 18)) * plotUsd)}) - )} + + {donationTotalCount} {donationTotalCount === 1 ? "donation" : "donations"} + + )} +
+ {isOwnProfile && hasDonationsGiven && ( +
+ {donationsGiven.map((d) => ( +
+ + Story #{d.storyline_id} + + + {formatPrice(formatUnits(BigInt(d.amount), 18))} {RESERVE_LABEL} + + {d.block_timestamp && ( -
- ))} - {donHasNext && ( - - )} -
+ +
+ ))} + {donHasNext && ( + + )}
)}
)} - {/* Trading History — public data, shown for all profiles */} + {/* Trading History */}
); @@ -1598,60 +1586,47 @@ function PortfolioTradingHistory({ address, plotUsd }: { address: string; plotUs if (trades.length === 0) return null; return ( -
-
+
+
Trades - {totalCount} - {totalCount === 1 ? "trade" : "trades"} + {totalCount} {totalCount === 1 ? "trade" : "trades"}
-
+
{trades.map((t) => { const isBuy = t.event_type === "mint"; const title = storylineTitles?.[t.storyline_id]; const tokenCount = t.price_per_token > 0 ? t.reserve_amount / t.price_per_token : 0; return ( -
- - {isBuy ? "Buy" : "Sell"} - - - {title || `Story #${t.storyline_id}`} - - - {formatPrice(t.reserve_amount)} {RESERVE_LABEL} - - {plotUsd && ( - - (≈ {formatUsdValue(t.reserve_amount * plotUsd)}) +
+
+ + {isBuy ? "Buy" : "Sell"} - )} - {tokenCount > 0 && ( - {formatSupply(tokenCount)} tokens - )} - {t.block_timestamp && ( - - )} - {t.tx_hash && ( - - ↗ - - )} + {title || `Story #${t.storyline_id}`} + +
+
+ {tokenCount > 0 ? `${formatSupply(tokenCount)} tokens` : ""} + + {formatPrice(t.reserve_amount)} {RESERVE_LABEL} + {plotUsd && ({formatUsdValue(t.reserve_amount * plotUsd)})} + + + {t.block_timestamp && ( + + )} + {t.tx_hash && ( + + )} + +
); })} From 9bce7c087be9cdd1b5a98c1b2bbf6d3365ff0e6e Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Wed, 1 Apr 2026 16:07:00 +0100 Subject: [PATCH 2/2] [#725] Fix T2a review: trade rows split into 3 lines per wireframe Row 1: Buy/Sell + title Row 2: token count + amount (grid-cols-[auto_1fr]) Row 3: date + tx link Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/profile/[address]/page.tsx | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/app/profile/[address]/page.tsx b/src/app/profile/[address]/page.tsx index b351eda1..1366e610 100644 --- a/src/app/profile/[address]/page.tsx +++ b/src/app/profile/[address]/page.tsx @@ -1599,6 +1599,7 @@ function PortfolioTradingHistory({ address, plotUsd }: { address: string; plotUs const tokenCount = t.price_per_token > 0 ? t.reserve_amount / t.price_per_token : 0; return (
+ {/* Row 1: action + title */}
{isBuy ? "Buy" : "Sell"} @@ -1610,22 +1611,24 @@ function PortfolioTradingHistory({ address, plotUsd }: { address: string; plotUs {title || `Story #${t.storyline_id}`}
-
+ {/* Row 2: tokens + amount */} +
{tokenCount > 0 ? `${formatSupply(tokenCount)} tokens` : ""} {formatPrice(t.reserve_amount)} {RESERVE_LABEL} {plotUsd && ({formatUsdValue(t.reserve_amount * plotUsd)})} - - {t.block_timestamp && ( - - )} - {t.tx_hash && ( - - )} - +
+ {/* Row 3: date + tx link */} +
+ {t.block_timestamp && ( + + )} + {t.tx_hash && ( + + )}
);