11"use client" ;
22
3- import { useState , useEffect } from "react" ;
3+ import { useState , useEffect , useCallback } from "react" ;
44import Link from "next/link" ;
55import { useAuth } from "@/lib/auth-context" ;
66import { authHeaders } from "@/lib/auth-client" ;
77import { parseWalletPaste , formatWalletCopyText } from "@/lib/wallet-import" ;
88
9+ interface ReferralWallet {
10+ id : string ;
11+ cryptocurrency : string ;
12+ wallet_address : string ;
13+ label : string | null ;
14+ is_primary : boolean ;
15+ created_at : string ;
16+ }
17+
918export default function AccountContent ( ) {
1019 const { signedIn, profile, loading, signOut, refreshProfile } = useAuth ( ) ;
1120 const [ editing , setEditing ] = useState ( false ) ;
@@ -19,7 +28,35 @@ export default function AccountContent() {
1928 const [ copied , setCopied ] = useState ( false ) ;
2029 const [ walletPasteOpen , setWalletPasteOpen ] = useState ( false ) ;
2130 const [ walletPasteText , setWalletPasteText ] = useState ( "" ) ;
22- const [ walletImportResult , setWalletImportResult ] = useState < { imported : number ; errors : string [ ] } | null > ( null ) ;
31+ const [ walletImportResult , setWalletImportResult ] = useState < {
32+ imported : Array < { coin : string ; address : string ; action : string } > ;
33+ errors : string [ ] ;
34+ } | null > ( null ) ;
35+ const [ referralWallets , setReferralWallets ] = useState < ReferralWallet [ ] > ( [ ] ) ;
36+ const [ loadingWallets , setLoadingWallets ] = useState ( false ) ;
37+
38+ const fetchReferralWallets = useCallback ( async ( ) => {
39+ const token = localStorage . getItem ( "tc_access_token" ) ;
40+ if ( ! token ) return ;
41+ setLoadingWallets ( true ) ;
42+ try {
43+ const res = await fetch ( "/api/referrals/import-wallets" , {
44+ headers : { Authorization : `Bearer ${ token } ` } ,
45+ } ) ;
46+ if ( res . ok ) {
47+ const data = await res . json ( ) ;
48+ setReferralWallets ( data . wallets || [ ] ) ;
49+ }
50+ } catch {
51+ // ignore
52+ } finally {
53+ setLoadingWallets ( false ) ;
54+ }
55+ } , [ ] ) ;
56+
57+ useEffect ( ( ) => {
58+ fetchReferralWallets ( ) ;
59+ } , [ fetchReferralWallets ] ) ;
2360
2461 useEffect ( ( ) => {
2562 if ( profile ) {
@@ -98,25 +135,42 @@ export default function AccountContent() {
98135 const handleWalletImport = async ( ) => {
99136 const parsed = parseWalletPaste ( walletPasteText ) ;
100137 if ( parsed . wallets . length === 0 ) {
101- setWalletImportResult ( { imported : 0 , errors : [ "No valid wallet addresses found. Use CoinPay 'Copy All Addresses' format." ] } ) ;
138+ setWalletImportResult ( { imported : [ ] , errors : [ "No valid wallet addresses found. Use CoinPay 'Copy All Addresses' format." ] } ) ;
102139 return ;
103140 }
104141
105- // Auto-select: use the first parsed wallet
106- const wallet = parsed . wallets [ 0 ] ;
107- setWalletAddress ( wallet . address ) ;
142+ try {
143+ const token = localStorage . getItem ( "tc_access_token" ) ;
144+ const res = await fetch ( "/api/referrals/import-wallets" , {
145+ method : "POST" ,
146+ headers : {
147+ "Content-Type" : "application/json" ,
148+ Authorization : `Bearer ${ token } ` ,
149+ } ,
150+ body : JSON . stringify ( { paste_text : walletPasteText } ) ,
151+ } ) ;
108152
109- // Map coin to payout_crypto
110- const payoutMap : Record < string , string > = {
111- "BTC" : "BTC" , "ETH" : "ETH" , "SOL" : "SOL" , "USDT" : "USDT" , "USDC" : "USDC" ,
112- "USDC_ETH" : "USDC" , "USDC_SOL" : "USDC" , "USDC_POL" : "USDC" ,
113- "USDT_ETH" : "USDT" , "USDT_SOL" : "USDT" , "USDT_POL" : "USDT" ,
114- "BNB" : "BNB" , "XRP" : "XRP" , "ADA" : "ADA" , "DOGE" : "DOGE" , "POL" : "POL" , "BCH" : "BCH" ,
115- } ;
116- setPayoutCrypto ( payoutMap [ wallet . coin ] || wallet . coin ) ;
153+ const data = await res . json ( ) ;
117154
118- setWalletPasteText ( "" ) ;
119- setWalletImportResult ( { imported : parsed . wallets . length , errors : [ ] } ) ;
155+ if ( ! res . ok ) {
156+ setWalletImportResult ( {
157+ imported : [ ] ,
158+ errors : [ data . error || "Import failed" ] ,
159+ } ) ;
160+ return ;
161+ }
162+
163+ setWalletImportResult ( {
164+ imported : data . imported || [ ] ,
165+ errors : data . errors ?. map ( ( e : { coin : string ; error : string } ) => `${ e . coin } : ${ e . error } ` ) || [ ] ,
166+ } ) ;
167+
168+ setWalletPasteText ( "" ) ;
169+ setWalletPasteOpen ( false ) ;
170+ await fetchReferralWallets ( ) ;
171+ } catch {
172+ setWalletImportResult ( { imported : [ ] , errors : [ "Network error during import" ] } ) ;
173+ }
120174 } ;
121175
122176 return (
@@ -251,80 +305,89 @@ export default function AccountContent() {
251305 < p className = "text-white text-xl font-bold" > ${ profile ?. total_referral_earnings_usd ?. toFixed ( 2 ) || "0.00" } </ p >
252306 </ div >
253307 < div >
254- < span className = "text-sm text-tc-text-dim" > Payout Wallet</ span >
255- { editing ? (
256- < div className = "space-y-2 mt-1" >
257- < select
258- value = { payoutCrypto }
259- onChange = { ( e ) => setPayoutCrypto ( e . target . value ) }
260- className = "w-full bg-tc-darker border border-tc-border rounded-lg px-3 py-2 text-white focus:outline-none focus:border-tc-green/50"
261- >
262- < option value = "USDT" > USDT</ option >
263- < option value = "BTC" > BTC</ option >
264- < option value = "ETH" > ETH</ option >
265- < option value = "SOL" > SOL</ option >
266- < option value = "USDC" > USDC</ option >
267- </ select >
268- < div className = "flex gap-2" >
269- < input
270- value = { walletAddress }
271- onChange = { ( e ) => setWalletAddress ( e . target . value ) }
272- placeholder = "Wallet address"
273- className = "flex-1 bg-tc-darker border border-tc-border rounded-lg px-3 py-2 text-white focus:outline-none focus:border-tc-green/50"
274- />
275- < button
276- type = "button"
277- onClick = { ( ) => setWalletPasteOpen ( ! walletPasteOpen ) }
278- className = "shrink-0 bg-tc-green/10 border border-tc-green/30 text-tc-green px-3 py-2 rounded-lg text-sm hover:bg-tc-green/20 transition-colors"
279- title = "Paste from CoinPay"
280- >
281- 📋
282- </ button >
283- </ div >
284- { walletPasteOpen && (
285- < div className = "mt-2" >
286- < textarea
287- value = { walletPasteText }
288- onChange = { ( e ) => setWalletPasteText ( e . target . value ) }
289- placeholder = { `Paste from CoinPay "Copy All Addresses":\nBTC: bc1q...\nUSDC_SOL: FX8Q...` }
290- rows = { 3 }
291- className = "w-full rounded-lg border border-tc-border bg-tc-darker px-3 py-2 text-white placeholder:text-tc-text-dim/50 focus:outline-none focus:border-tc-green/50 font-mono text-xs"
292- />
293- < div className = "flex gap-2 mt-2" >
294- < button
295- type = "button"
296- onClick = { handleWalletImport }
297- disabled = { ! walletPasteText . trim ( ) }
298- className = "bg-tc-green text-black px-3 py-1.5 rounded-lg text-sm font-semibold hover:bg-tc-green-dim disabled:opacity-40 disabled:cursor-not-allowed"
299- >
300- Import Wallet
301- </ button >
302- < button
303- type = "button"
304- onClick = { ( ) => { setWalletPasteOpen ( false ) ; setWalletPasteText ( "" ) ; } }
305- className = "text-tc-text-dim text-sm hover:text-white"
306- >
307- Cancel
308- </ button >
309- </ div >
310- { walletImportResult && (
311- < p className = { `text-xs mt-2 ${ walletImportResult . errors . length > 0 ? "text-red-400" : "text-tc-green" } ` } >
312- { walletImportResult . errors . length > 0
313- ? walletImportResult . errors [ 0 ]
314- : `✅ Wallet imported: ${ walletAddress . slice ( 0 , 8 ) } ...${ walletAddress . slice ( - 6 ) } `
315- }
316- </ p >
317- ) }
318- </ div >
308+ < span className = "text-sm text-tc-text-dim" > Payout Wallets</ span >
309+ { loadingWallets ? (
310+ < p className = "text-tc-text-dim text-xs mt-1" > Loading...</ p >
311+ ) : referralWallets . length > 0 ? (
312+ < div className = "space-y-1 mt-1" >
313+ { referralWallets . slice ( 0 , 3 ) . map ( ( w ) => (
314+ < p key = { w . cryptocurrency } className = "text-white text-xs font-mono flex items-center gap-1" >
315+ < span className = "text-tc-green font-bold w-20 shrink-0" > { w . cryptocurrency } </ span >
316+ < span className = "truncate" > { w . wallet_address } </ span >
317+ { w . is_primary && < span className = "text-[10px] bg-tc-green/10 text-tc-green px-1 rounded" > ★</ span > }
318+ </ p >
319+ ) ) }
320+ { referralWallets . length > 3 && (
321+ < p className = "text-tc-text-dim text-xs" > +{ referralWallets . length - 3 } more</ p >
319322 ) }
320323 </ div >
321324 ) : (
322- < p className = "text-white text-sm truncate" >
323- { profile ?. wallet_address ? `${ profile . payout_crypto } : ${ profile . wallet_address } ` : "Not set — click Edit to add" }
324- </ p >
325+ < p className = "text-white text-xs mt-1" > None — import below</ p >
325326 ) }
326327 </ div >
327328 </ div >
329+
330+ { /* Bulk Wallet Import */ }
331+ < div className = "mt-4 border-t border-tc-border pt-4" >
332+ < div className = "flex items-center justify-between mb-2" >
333+ < h3 className = "text-sm font-semibold text-white" > Wallet Addresses</ h3 >
334+ < button
335+ type = "button"
336+ onClick = { ( ) => setWalletPasteOpen ( ! walletPasteOpen ) }
337+ className = "text-xs text-tc-green hover:underline"
338+ >
339+ { walletPasteOpen ? "▲ Close" : "📋 Import from CoinPay" }
340+ </ button >
341+ </ div >
342+
343+ { walletPasteOpen && (
344+ < div >
345+ < textarea
346+ value = { walletPasteText }
347+ onChange = { ( e ) => setWalletPasteText ( e . target . value ) }
348+ placeholder = { `Paste from CoinPay "Copy All Addresses":\nBTC: bc1q...\nETH: 0x...\nUSDC_SOL: FX8Q...\nUSDC_POL: 0x...` }
349+ rows = { 5 }
350+ className = "w-full rounded-lg border border-tc-border bg-tc-darker px-3 py-2 text-white placeholder:text-tc-text-dim/50 focus:outline-none focus:border-tc-green/50 font-mono text-xs"
351+ />
352+ < div className = "flex gap-2 mt-2" >
353+ < button
354+ type = "button"
355+ onClick = { handleWalletImport }
356+ disabled = { ! walletPasteText . trim ( ) }
357+ className = "bg-tc-green text-black px-4 py-1.5 rounded-lg text-sm font-semibold hover:bg-tc-green-dim disabled:opacity-40 disabled:cursor-not-allowed"
358+ >
359+ Import All Wallets
360+ </ button >
361+ < button
362+ type = "button"
363+ onClick = { ( ) => { setWalletPasteOpen ( false ) ; setWalletPasteText ( "" ) ; setWalletImportResult ( null ) ; } }
364+ className = "text-tc-text-dim text-sm hover:text-white"
365+ >
366+ Cancel
367+ </ button >
368+ </ div >
369+ { walletImportResult && (
370+ < div className = "mt-2 text-xs" >
371+ { walletImportResult . imported . length > 0 && (
372+ < div className = "text-tc-green" >
373+ ✅ Imported { walletImportResult . imported . length } wallet(s):
374+ { walletImportResult . imported . map ( ( w , i ) => (
375+ < span key = { i } className = "inline-block mr-2 mt-1 px-2 py-0.5 bg-tc-green/10 rounded font-mono" >
376+ { w . coin } : { w . address . slice ( 0 , 8 ) } …{ w . address . slice ( - 6 ) }
377+ </ span >
378+ ) ) }
379+ </ div >
380+ ) }
381+ { walletImportResult . errors . length > 0 && (
382+ < div className = "text-red-400 mt-1" >
383+ ❌ { walletImportResult . errors . join ( ", " ) }
384+ </ div >
385+ ) }
386+ </ div >
387+ ) }
388+ </ div >
389+ ) }
390+ </ div >
328391 </ div >
329392 </ div >
330393
0 commit comments