11import { getTranslations } from "next-intl/server" ;
2- import { ArrowDownLeft , ExternalLink , Gavel } from "lucide-react" ;
3- import { AddressDisplay } from "@/components/ui/address-display " ;
2+ import { ArrowDownLeft } from "lucide-react" ;
3+ import { TreasuryInflowsList } from "@/components/treasury/TreasuryInflowsList " ;
44import { Card , CardContent , CardHeader , CardTitle } from "@/components/ui/card" ;
5- import {
6- loadTreasuryInflows ,
7- type InflowAsset ,
8- type InflowSource ,
9- } from "@/services/treasury-inflows" ;
10-
11- /**
12- * Source accents. Not semantic tokens on purpose — like the asset colours below,
13- * these identify a kind of income, and the point is telling them apart at a
14- * glance rather than following the theme's foreground.
15- */
16- const SOURCE_TONE : Record < InflowSource , string > = {
17- auction : "border-amber-500/25 bg-amber-500/10 text-amber-600 dark:text-amber-400" ,
18- subnet : "border-emerald-500/25 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400" ,
19- splits : "border-emerald-500/25 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400" ,
20- transfer : "border-border bg-muted text-muted-foreground" ,
21- } ;
22-
23- /** Per-asset accent. Deliberately not the semantic tokens — these identify a currency. */
24- const ASSET_TONE : Record < InflowAsset , string > = {
25- ETH : "text-[#627eea]" ,
26- WETH : "text-[#627eea]" ,
27- USDC : "text-[#2775ca]" ,
28- } ;
29-
30- /**
31- * ETH is worth ~4 decimals; USDC is a dollar figure and reads wrong with more
32- * than two. `maximumFractionDigits` alone would print `0` for auction dust, so
33- * very small ETH amounts keep enough significant digits to stay non-zero.
34- */
35- function formatAmount ( amount : number , asset : InflowAsset , locale : string ) : string {
36- if ( asset === "USDC" ) {
37- return amount . toLocaleString ( locale , { minimumFractionDigits : 2 , maximumFractionDigits : 2 } ) ;
38- }
39- if ( amount > 0 && amount < 0.0001 ) {
40- return amount . toLocaleString ( locale , { maximumSignificantDigits : 2 } ) ;
41- }
42- return amount . toLocaleString ( locale , { maximumFractionDigits : 4 } ) ;
43- }
44-
45- function ageLabel ( at : string , now : number ) : string {
46- const ms = now - new Date ( at ) . getTime ( ) ;
47- if ( ! Number . isFinite ( ms ) || ms < 0 ) return "" ;
48- const mins = Math . floor ( ms / 60_000 ) ;
49- if ( mins < 60 ) return `${ Math . max ( 1 , mins ) } m` ;
50- const hours = Math . floor ( mins / 60 ) ;
51- if ( hours < 24 ) return `${ hours } h` ;
52- const days = Math . floor ( hours / 24 ) ;
53- if ( days < 30 ) return `${ days } d` ;
54- return `${ Math . floor ( days / 30 ) } mo` ;
55- }
5+ import { loadTreasuryInflows } from "@/services/treasury-inflows" ;
566
577/**
588 * The treasury's most recent income, newest first.
599 *
60- * Auction settlements arrive as internal transfers and are marked as such — it
61- * is the difference between "someone sent us money" and "the DAO earned it".
10+ * This component is now only the frame and the fetch — the rows moved to
11+ * `TreasuryInflowsList`, a client component, because "show more" needs state.
12+ *
13+ * The whole window is fetched here and paged in the browser rather than sliced
14+ * on the server, and the ordering is why: inflows are newest-first, and the
15+ * auction credits are older than the newest handful. Fetching eight rows meant
16+ * the Auction badge existed in code, fired correctly, and was never once on
17+ * screen. Paging client-side costs a slightly larger payload and shows the
18+ * history that makes the badges worth having.
6219 */
20+ const WINDOW = 100 ;
21+
6322export async function TreasuryInflows ( { locale } : { locale : string } ) {
6423 const t = await getTranslations ( "treasury.inflows" ) ;
65- const inflows = await loadTreasuryInflows ( 8 ) ;
66- // Rendered once per request on the server; the timestamp IS the snapshot.
24+ const inflows = await loadTreasuryInflows ( WINDOW ) ;
25+ // Rendered once per request on the server; the timestamp IS the snapshot, so
26+ // the relative ages cannot shift between server render and hydration.
6727 // eslint-disable-next-line react-hooks/purity -- server component, render-time clock read for relative ages
6828 const now = Date . now ( ) ;
6929
@@ -79,50 +39,7 @@ export async function TreasuryInflows({ locale }: { locale: string }) {
7939 { inflows . length === 0 ? (
8040 < p className = "py-6 text-center text-sm text-muted-foreground" > { t ( "empty" ) } </ p >
8141 ) : (
82- < ul className = "divide-y divide-border" >
83- { inflows . map ( ( flow ) => (
84- < li key = { flow . hash } className = "flex items-center gap-3 py-2.5" >
85- < div className = "flex min-w-0 flex-1 items-center gap-2" >
86- < AddressDisplay
87- address = { flow . from }
88- variant = "compact"
89- showCopy = { false }
90- showExplorer = { false }
91- truncateLength = { 4 }
92- />
93- { /* Every row carries its origin now, not just auctions. The
94- binary "internal = auction" badge could not say where the
95- other three quarters of the income came from. */ }
96- < span
97- className = { `inline-flex shrink-0 items-center gap-1 rounded-full border px-2 py-0.5 text-[10px] font-medium ${ SOURCE_TONE [ flow . source ] } ` }
98- title = { flow . source === "auction" ? t ( "auctionHint" ) : undefined }
99- >
100- { flow . source === "auction" ? < Gavel className = "size-3" /> : null }
101- { t ( flow . source ) }
102- </ span >
103- </ div >
104-
105- < span className = "shrink-0 whitespace-nowrap font-mono text-sm font-semibold tabular-nums" >
106- +{ formatAmount ( flow . amount , flow . asset , locale ) } { " " }
107- < span className = { ASSET_TONE [ flow . asset ] } > { flow . asset } </ span >
108- </ span >
109-
110- < span className = "w-9 shrink-0 text-right font-mono text-[11px] text-muted-foreground" >
111- { ageLabel ( flow . at , now ) }
112- </ span >
113-
114- < a
115- href = { `https://basescan.org/tx/${ flow . hash } ` }
116- target = "_blank"
117- rel = "noopener noreferrer"
118- aria-label = { t ( "viewTx" ) }
119- className = "shrink-0 text-muted-foreground hover:text-foreground"
120- >
121- < ExternalLink className = "size-3.5" />
122- </ a >
123- </ li >
124- ) ) }
125- </ ul >
42+ < TreasuryInflowsList inflows = { inflows } locale = { locale } now = { now } />
12643 ) }
12744 </ CardContent >
12845 </ Card >
0 commit comments