Skip to content

feat(web): rebuild the home page around one number - #1004

Open
tobySolutions wants to merge 20 commits into
mainfrom
feat/home-metrics
Open

feat(web): rebuild the home page around one number#1004
tobySolutions wants to merge 20 commits into
mainfrom
feat/home-metrics

Conversation

@tobySolutions

@tobySolutions tobySolutions commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

The home page showed two currency tiles and one table, leaving most of the viewport empty and answering no question first.

It now leads with total balance at full weight, with the day's volume, wallet count and tokens held as context beneath it and the primary actions in the same block. Token allocation folds into the same card, so the money picture is one object rather than three.

Details worth a look:

  • Priced holdings compose a single segmented bar whose shares sum to 100. Unpriced holdings — an organization's own issued tokens have no price feed — are listed separately with no bar, because ranking a raw token count against a dollar amount compares quantities with no shared unit.
  • Both lists are capped, the priced side into "Other" and the unpriced side into a count. Uncapped, an organization issuing a dozen tokens turned the card into a ledger.
  • Token marks come from the existing TokenMark, so bundled logos render and everything else gets a monogram. Hovering a segment or a row lifts that holding and drops the others; segments are buttons, so keyboard focus does the same.
  • Every value comes from the wallet aggregate the page already fetched and previously discarded after summing, so this adds no request.
  • The empty state is one instruction and a button instead of four zeroes each with a hint.
  • /dashboard stops painting a 36px "Home" title, which said nothing the sidebar and the page did not already say. The heading is now sr-only; other routes are unchanged.
  • The route skeleton mirrors the new layout so nothing reflows on load.

Rendered and checked in dark and light. Allocation logic is a pure module with unit tests.

Screenshots

Screenshot 2026-07-30 at 19 00 26 Screenshot 2026-07-30 at 19 00 21

… page

The home page showed two currency tiles and one table, leaving most of the
viewport empty and saying little about the organization.

It now shows four tiles — total balance, today's volume, wallets and tokens held
— plus a per-token balance breakdown ordered largest first. All of it comes from
the wallet aggregate the page already fetched and previously discarded after
summing, so this adds no request and no latency.

Bars scale against the largest holding rather than the total, so a small position
next to a dominant one stays visible instead of rounding to nothing. Token
symbols go through the shared label resolver with the symbols the balances carry,
so org-issued tokens are named rather than shown as shortened mints. The route
skeleton now draws four tiles so the grid does not reflow on load.
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sdp-docs Ready Ready Preview Jul 31, 2026 10:31am
sdp-web Ready Ready Preview Jul 31, 2026 10:31am

Request Review

The first version drew a share bar under every holding. An organization's own
issued tokens have no price feed, so their bars were empty full-width tracks that
read as dividers — and ranking 132.5 nwSOL against $149.11 on one scale compared
quantities that share no unit.

Priced holdings now compose a single segmented allocation bar whose shares sum to
100, with a legend carrying the percentage and the value. Unpriced holdings are
listed below a No price feed rule with their amount and no bar, because there is
no share to show. Holdings past the fourth fold into one Other segment so the bar
stays readable.

Segment fills step down the neutral emphasis ramp. The design system ships no
categorical chart palette, and an allocation bar encodes magnitude rather than
identity, so a single ramp is the right encoding — and it re-steps itself per
theme instead of needing a hand-picked dark variant.
Four equal tiles gave the balance, the day's volume, a wallet count and a token
count the same visual weight, so the page answered no question first — the reader
had to pick one. Total balance is what someone opening a custody dashboard came
for, so it now leads at roughly twice the type size, with the other three as
context beneath it and the primary actions in the same block rather than floating
above the page. The token allocation folds into the same card, so the whole money
picture is one object instead of three.

An organization with no wallets no longer gets four zeroes each with its own
apologetic hint, which reads as a broken dashboard rather than a new one. It gets
one instruction and the button beside it.
Extracting the hero took the workspace under the cognitive-complexity threshold, so
its biome-ignore no longer suppresses anything, and the tile grid took the day's
volume hint with it.
Three changes to the same card.

Token marks replace the colour swatches. The repo already ships brand SVGs under
public/token-logos and a TokenMark component that falls back to a tinted monogram,
so an organization's own issued tokens get a mark too rather than a bare square.

The secondary figures were 15px against a 46px balance, which read as fine print
rather than as the three numbers someone actually scans. They are now 22px.

The bar and the list are one object: hovering either a segment or its row lifts
that holding and drops the others to 35 percent. Segments are buttons so the same
highlight follows keyboard focus, and each carries its share in an accessible
label.
Mouse handlers on a plain element are a keyboard trap and biome flags them. The
segment button already owns the interaction and drives the dim-others linkage;
the row now mirrors it with a CSS hover instead of its own handlers.
Three follow-ups on the balance card.

The unpriced list was uncapped. An organization issuing a dozen of its own tokens
turned the card into a ledger, so it now lists the largest few and counts the
rest, the same way the priced side already folded into Other.

Home no longer paints a 36px page title. The sidebar marks Home active and the
page opens on a balance, so the heading said nothing the reader could not see; it
is now rendered sr-only, which keeps it for assistive tech. Only the home route
changed — a wallet detail or a new API key still needs its title for orientation.

The route skeleton still drew the retired four-tile grid, so the layout jumped on
load. It now mirrors the hero: balance, three context figures, the allocation bar
and its rows.
@tobySolutions tobySolutions changed the title feat(web): add wallet, token and per-token balance detail to the home page feat(web): rebuild the home page around one number Jul 30, 2026
The activity table squeezed Token and Amount into fixed 8rem and 10rem columns
while Address took every remaining pixel, so both truncated with space to spare.
Amount also repeated the token beside a column that already named it, which is how
a row read "132.5 Ackroo…tPc9" twice over.

Amount is now numeric, right aligned and tabular, Token carries its mark, and the
freed width goes to Address. The mobile card keeps the symbol on the amount,
because it has no separate token column to lean on.

Page titles are hidden on the top-level list routes. The sidebar already names the
active section, so a 36px heading repeating it spent vertical space saying nothing.
Detail and action routes keep theirs — the centred title beside a back action is
the only orientation those have. Hiding is now visual only: the top bar renders an
sr-only h1 in place of the visible one, so every page still has exactly one
heading for assistive tech and for the tests that look one up.

@resourcefulmind resourcefulmind left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Home page looks great and the breakdown module is well tested. Three things.

Token column trades one naming bug for another. resolveTransferTokenLabel always returns something for non-blank input, so ?? row.token on 539 only fires when tokenMint is null. The two symbol sets don't nest: yours names tokens you hold that the catalogue doesn't know, the server's names tokens you issued and hold none of, and those now render as a shortened mint where main showed the symbol. Chaining both catches everything: symbolsByMint[row.tokenMint ?? ""] ?? row.token.

Seven routes lose their visible title and the description says other routes are unchanged, but all eight hideTitle lines are in this PR, not inherited from #1006. Policies, wallets, custody and issuance end up with no page heading at all, and wallets and issuance become indistinguishable since they share the Overview/API Playground tabs with payments, which keeps its title. A11y is fine, the sr-only h1 is the right call, it's the consistency I'd want a second opinion on. Was that a design call? Feels like its own PR either way.

countHeldTokens doesn't filter on amount, so emptied accounts count toward the Tokens tile and get 0% segments in the bar. You already filter this in wallet-asset-breakdown.tsx:19-21 on the same data. One caveat, I couldn't tell from code whether Helius actually returns emptied ATAs for these wallets, which is the difference between cosmetic and a wrong number on the front page, so maybe worth an eyeball on a staging response.

Two nits: "Today's Volume" lost the hint that separated loading from a real zero, so they're now identical. And noWalletsYet plus the three hint keys are unused in both catalogs.

…onger holds

hideTitle was only ever argued for home; the seven other routes had no
replacement heading, so they went from a 36px h1 to sr-only for no stated
reason. Home keeps it, the rest are back.

The activity Token column re-resolved from the mint against balances alone,
which fell through to a shortened mint — truthy, so it beat the authoritative
issued symbol the row already carried. Rows now seed the map, balances still win.

Tokens held counted every aggregate row, including spent accounts sitting at
zero; the list and the tile now share one definition of held.
Comment thread apps/sdp-web/src/components/dashboard-header.tsx
Tokens held now uses the same finite-and-positive rule as
wallet-asset-breakdown rather than a second definition. The breakdown list
keeps the looser one on purpose — a row whose amount will not parse is still
worth showing as unpriced, just not worth claiming as a holding.
@tobySolutions

Copy link
Copy Markdown
Collaborator Author

Home page looks great and the breakdown module is well tested. Three things.

Token column trades one naming bug for another. resolveTransferTokenLabel always returns something for non-blank input, so ?? row.token on 539 only fires when tokenMint is null. The two symbol sets don't nest: yours names tokens you hold that the catalogue doesn't know, the server's names tokens you issued and hold none of, and those now render as a shortened mint where main showed the symbol. Chaining both catches everything: symbolsByMint[row.tokenMint ?? ""] ?? row.token.

Seven routes lose their visible title and the description says other routes are unchanged, but all eight hideTitle lines are in this PR, not inherited from #1006. Policies, wallets, custody and issuance end up with no page heading at all, and wallets and issuance become indistinguishable since they share the Overview/API Playground tabs with payments, which keeps its title. A11y is fine, the sr-only h1 is the right call, it's the consistency I'd want a second opinion on. Was that a design call? Feels like its own PR either way.

countHeldTokens doesn't filter on amount, so emptied accounts count toward the Tokens tile and get 0% segments in the bar. You already filter this in wallet-asset-breakdown.tsx:19-21 on the same data. One caveat, I couldn't tell from code whether Helius actually returns emptied ATAs for these wallets, which is the difference between cosmetic and a wrong number on the front page, so maybe worth an eyeball on a staging response.

Two nits: "Today's Volume" lost the hint that separated loading from a real zero, so they're now identical. And noWalletsYet plus the three hint keys are unused in both catalogs.

Help check again to see if the updates satisfy the requested changes chief.

@GuiBibeau GuiBibeau left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do a review of the pull french with the LLM ensuring we kee english context for some words french does not translate in regular talk. Ex: Token, nobody says jetons

Comment thread apps/sdp-web/messages/fr/shared.json Outdated
Comment thread apps/sdp-web/messages/fr/shared.json Outdated
The French home copy translated Token to jeton and Wallet to portefeuille, which nobody says in daily use and which the rest of homeWorkspace already avoids. Also drops four home keys nothing renders.
Product PRs change only the English source catalog now; Eve fills locale keys on the release branch and a missing key falls back to English at runtime.
translate only substitutes {name}, so the plural forms reached the page verbatim. Splits them into singular and count keys the way the rest of the catalog does, and guards the catalogs against ICU syntax in a test.
Comment thread apps/sdp-web/src/components/dashboard-header.tsx
@tobySolutions

Copy link
Copy Markdown
Collaborator Author

all nine keys had it, not just those two.

  • french is in fix(web): add French home copy for the rebuilt dashboard #1034 now, the catalog policy fails a PR that touches en and fr together
  • kept Token and Wallet capitalised to match the rest of the catalog
  • notPriced reads "Aucun flux de prix", reusing the wording already in the custody copy
  • separate find: moreTokens/otherTokens were ICU plurals, and translate() only substitutes {name}, so they were reaching the page as raw {count, plural, ...} text. split into singular/count keys the way selectedWalletCount does it
  • added a test so ICU syntax cannot get back into a catalog

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants