1+ import { ChangeEvent , useRef } from "react" ;
2+ import Papa from "papaparse" ;
3+ import { useDropzone } from "react-dropzone" ;
4+ import { useWalletsStore } from "@/lib/zustand/wallets" ;
5+ import { toast } from "@/hooks/use-toast" ;
6+
7+ type RecipientCsvProps = {
8+ setRecipientAddresses : ( value : string [ ] ) => void ;
9+ setAmounts : ( value : string [ ] ) => void ;
10+ setAssets : ( value : string [ ] ) => void ;
11+ recipientAddresses : string [ ] ;
12+ amounts : string [ ] ;
13+ assets : string [ ] ;
14+ } ;
15+
16+ export default function RecipientCsv ( {
17+ setRecipientAddresses,
18+ setAmounts,
19+ setAssets,
20+ recipientAddresses,
21+ amounts,
22+ assets,
23+ } : RecipientCsvProps ) {
24+ const walletAssets = useWalletsStore ( ( state ) => state . walletAssets ) ;
25+ const onDrop = ( acceptedFiles : File [ ] ) => {
26+ const file = acceptedFiles [ 0 ] ;
27+ if ( ! file ) return ;
28+
29+ Papa . parse < string [ ] > ( file , {
30+ complete : ( results : Papa . ParseResult < string [ ] > ) => {
31+ const rows = results . data as string [ ] [ ] ;
32+ const newAddresses : string [ ] = [ ] ;
33+ const newAmounts : string [ ] = [ ] ;
34+ const newAssets : string [ ] = [ ] ;
35+
36+ rows . forEach ( ( row , index ) => {
37+ if ( index === 0 ) return ; // skip header row
38+ const [ address , unit , amount ] = row ;
39+ if ( address && amount ) {
40+ const resolvedUnit = ( ! unit || unit === "ADA" ) ? "ADA" : unit ;
41+ const walletUnitList = walletAssets . map ( a => a . unit ) . concat ( "ADA" ) ;
42+
43+ if ( ! walletUnitList . includes ( resolvedUnit ) ) {
44+ toast ( {
45+ title : "Unknown Asset" ,
46+ description : `Unit "${ resolvedUnit } " not found in your wallet.` ,
47+ variant : "destructive" ,
48+ } ) ;
49+ return ;
50+ }
51+
52+ newAddresses . push ( address ) ;
53+ newAmounts . push ( amount ) ;
54+ newAssets . push ( resolvedUnit ) ;
55+ }
56+ } ) ;
57+
58+ setRecipientAddresses ( [ ...recipientAddresses , ...newAddresses ] ) ;
59+ setAmounts ( [ ...amounts , ...newAmounts ] ) ;
60+ setAssets ( [ ...assets , ...newAssets ] ) ;
61+ } ,
62+ skipEmptyLines : true ,
63+ } ) ;
64+ } ;
65+
66+ const { getRootProps, getInputProps, isDragActive } = useDropzone ( { onDrop, accept : { "text/csv" : [ ".csv" ] } } ) ;
67+
68+ return (
69+ < >
70+ < div
71+ { ...getRootProps ( ) }
72+ className = "flex items-center justify-center border-2 border-dashed border-gray-400 p-4 rounded-md cursor-pointer hover:bg-gray-100"
73+ >
74+ < input { ...getInputProps ( ) } />
75+ { isDragActive ? (
76+ < p > Drop the CSV file here ...</ p >
77+ ) : (
78+ < p > Drag & drop a CSV file here , or click to select one < / p >
79+ ) }
80+ </ div >
81+ < div className = "mt-2" >
82+ < button
83+ className = "text-blue-500 hover:underline text-sm"
84+ onClick = { ( ) => {
85+ const headers = "address,unit,amount\n" ;
86+ const rows = recipientAddresses . map ( ( address , index ) => {
87+ const unit = assets [ index ] === "ADA" ? "" : assets [ index ] ;
88+ const amount = amounts [ index ] || "" ;
89+ return `${ address } ,${ unit } ,${ amount } ` ;
90+ } ) ;
91+ const csvContent = headers + rows . join ( "\n" ) ;
92+
93+ const blob = new Blob ( [ csvContent ] , { type : "text/csv;charset=utf-8;" } ) ;
94+ const url = URL . createObjectURL ( blob ) ;
95+
96+ const link = document . createElement ( "a" ) ;
97+ link . setAttribute ( "href" , url ) ;
98+ link . setAttribute ( "download" , "recipients.csv" ) ;
99+ document . body . appendChild ( link ) ;
100+ link . click ( ) ;
101+ document . body . removeChild ( link ) ;
102+ } }
103+ >
104+ Download current recipients as CSV
105+ </ button >
106+ </ div >
107+ </ >
108+ ) ;
109+ }
0 commit comments