@@ -12,7 +12,7 @@ import confetti from 'canvas-confetti';
1212export default function DatasetDetailPage ( ) {
1313 const params = useParams ( ) ;
1414 const id = params . id as string ;
15-
15+
1616 const account = useCurrentAccount ( ) ;
1717 const { data : listing , isLoading } = useListing ( id ) ;
1818 const { data : balance } = useAccountBalance ( ) ;
@@ -24,6 +24,7 @@ export default function DatasetDetailPage() {
2424 const [ isPurchasedState , setIsPurchasedState ] = useState ( false ) ;
2525 const [ purchaseTxDigest , setPurchaseTxDigest ] = useState ( '' ) ;
2626 const [ purchaseError , setPurchaseError ] = useState < string | null > ( null ) ;
27+ const [ isDownloading , setIsDownloading ] = useState ( false ) ;
2728
2829 // Check if already purchased from blockchain data
2930 const existingPurchase = useMemo ( ( ) => {
@@ -37,37 +38,13 @@ export default function DatasetDetailPage() {
3738 const isOwner = listing && account ?. address === listing . seller ;
3839
3940 const fireConfetti = ( ) => {
40- // First burst
41- confetti ( {
42- particleCount : 100 ,
43- spread : 70 ,
44- origin : { y : 0.6 }
45- } ) ;
46-
47- // Side bursts
41+ confetti ( { particleCount : 100 , spread : 70 , origin : { y : 0.6 } } ) ;
4842 setTimeout ( ( ) => {
49- confetti ( {
50- particleCount : 50 ,
51- angle : 60 ,
52- spread : 55 ,
53- origin : { x : 0 }
54- } ) ;
55- confetti ( {
56- particleCount : 50 ,
57- angle : 120 ,
58- spread : 55 ,
59- origin : { x : 1 }
60- } ) ;
43+ confetti ( { particleCount : 50 , angle : 60 , spread : 55 , origin : { x : 0 } } ) ;
44+ confetti ( { particleCount : 50 , angle : 120 , spread : 55 , origin : { x : 1 } } ) ;
6145 } , 150 ) ;
62-
63- // Final celebration
6446 setTimeout ( ( ) => {
65- confetti ( {
66- particleCount : 100 ,
67- spread : 100 ,
68- origin : { y : 0.6 } ,
69- colors : [ '#ccff00' , '#3B82F6' , '#FF5C00' , '#9747FF' ]
70- } ) ;
47+ confetti ( { particleCount : 100 , spread : 100 , origin : { y : 0.6 } , colors : [ '#ccff00' , '#3B82F6' , '#FF5C00' , '#9747FF' ] } ) ;
7148 } , 300 ) ;
7249 } ;
7350
@@ -78,7 +55,7 @@ export default function DatasetDetailPage() {
7855 }
7956 setPurchaseError ( null ) ;
8057 purchase (
81- listing ,
58+ listing ,
8259 ( result ) => {
8360 setIsPurchasedState ( true ) ;
8461 setPurchaseTxDigest ( result . digest ) ;
@@ -96,13 +73,9 @@ export default function DatasetDetailPage() {
9673 ) ;
9774 } ;
9875
99- const [ isDownloading , setIsDownloading ] = useState ( false ) ;
100-
10176 const handleDownloadClick = async ( ) => {
10277 if ( ! account || ! listing || ! isPurchased ) return ;
103-
10478 setIsDownloading ( true ) ;
105-
10679 try {
10780 const payload = {
10881 dataset_id : listing . id ,
@@ -112,25 +85,15 @@ export default function DatasetDetailPage() {
11285 mime_type : listing . mimeType || 'application/octet-stream' ,
11386 file_name : listing . fileName || 'data.bin' ,
11487 } ;
115-
116- console . log ( '=== DOWNLOAD REQUEST ===' ) ;
117- console . log ( 'Payload:' , JSON . stringify ( payload , null , 2 ) ) ;
118-
11988 const response = await fetch ( '/api/nautilus/download' , {
12089 method : 'POST' ,
121- headers : {
122- 'Content-Type' : 'application/json' ,
123- } ,
90+ headers : { 'Content-Type' : 'application/json' } ,
12491 body : JSON . stringify ( payload ) ,
12592 } ) ;
126-
12793 if ( ! response . ok ) {
12894 const errorData = await response . json ( ) ;
129- console . error ( 'Error response:' , errorData ) ;
13095 throw new Error ( errorData . error || `Download failed: ${ response . status } ` ) ;
13196 }
132-
133- // Get the blob and trigger download
13497 const blob = await response . blob ( ) ;
13598 const url = window . URL . createObjectURL ( blob ) ;
13699 const a = document . createElement ( 'a' ) ;
@@ -140,10 +103,8 @@ export default function DatasetDetailPage() {
140103 a . click ( ) ;
141104 window . URL . revokeObjectURL ( url ) ;
142105 document . body . removeChild ( a ) ;
143-
144- console . log ( '=== DOWNLOAD SUCCESS ===' ) ;
145106 } catch ( error ) {
146- console . error ( '=== DOWNLOAD ERROR === ' , error ) ;
107+ console . error ( 'Download error: ' , error ) ;
147108 alert ( 'Failed to download. Please try again.' ) ;
148109 } finally {
149110 setIsDownloading ( false ) ;
@@ -186,8 +147,8 @@ export default function DatasetDetailPage() {
186147 { version : 'v1.0' , date : 'Sep 15, 2024' , isCurrent : false } ,
187148 ] ,
188149 reviews : [
189- { id : 'r1' , user : 'CryptoWhale_99' , initials : 'CW' , rating : 5 , date : '2 days ago' , comment : 'Extremely high quality data. The JSON formatting is clean and required zero cleanup before ingestion into our analytics pipeline. Worth every SUI.' , bgColor : 'bg-blue-500' } ,
190- { id : 'r2' , user : 'AlphaLab Research' , initials : 'AL' , rating : 4 , date : '5 days ago' , comment : 'Solid dataset. The consumer behavior section is particularly detailed. Would recommend for DeFi analysis.' , bgColor : 'bg-primary' } ,
150+ { id : 'r1' , user : 'CryptoWhale_99' , initials : 'CW' , rating : 5 , date : '2 days ago' , comment : 'Extremely high quality data. Worth every SUI.' , bgColor : 'bg-blue-500' } ,
151+ { id : 'r2' , user : 'AlphaLab Research' , initials : 'AL' , rating : 4 , date : '5 days ago' , comment : 'Solid dataset. Would recommend for DeFi analysis.' , bgColor : 'bg-primary' } ,
191152 ] ,
192153 features : [ 'Verified on-chain data' , 'Encrypted with Seal Protocol' , 'Stored on Walrus' ] ,
193154 } ;
@@ -200,7 +161,7 @@ export default function DatasetDetailPage() {
200161 < span className = "material-symbols-outlined" > arrow_back</ span >
201162 Back to Marketplace
202163 </ Link >
203-
164+
204165 < div className = "flex flex-col gap-2 mt-4" >
205166 < div className = "flex gap-2 flex-wrap" >
206167 < span className = "inline-flex items-center gap-1 rounded-full border-2 border-ink bg-white px-3 py-1 text-xs font-black uppercase tracking-wider shadow-sm text-ink" >
@@ -216,13 +177,19 @@ export default function DatasetDetailPage() {
216177 Your Listing
217178 </ span >
218179 ) }
180+ { isPurchased && ! isOwner && (
181+ < span className = "inline-flex items-center gap-1 rounded-full border-2 border-green-500 bg-green-500/10 px-3 py-1 text-xs font-black uppercase tracking-wider text-green-600" >
182+ < span className = "material-symbols-outlined text-[14px]" > check_circle</ span >
183+ Purchased
184+ </ span >
185+ ) }
219186 </ div >
220187 < h2 className = "text-3xl font-black leading-tight text-ink" > { listing . name } </ h2 >
221188 < p className = "text-sm font-bold text-ink opacity-80" > Updated: { dummyData . updatedAt } </ p >
222189 </ div >
223-
190+
224191 < hr className = "border-t-2 border-ink border-dashed opacity-50" />
225-
192+
226193 < div className = "grid grid-cols-1 gap-4" >
227194 < div className = "flex items-center justify-between p-4 bg-white border-2 border-ink rounded-xl shadow-hard-sm" >
228195 < span className = "text-sm font-bold text-gray-500 uppercase" > Storage Size</ span >
@@ -244,14 +211,14 @@ export default function DatasetDetailPage() {
244211 < span className = "text-xl font-black text-ink" > { listing . mimeType || dummyData . formats . join ( ' / ' ) } </ span >
245212 </ div >
246213 </ div >
247-
214+
248215 < div className = "mt-auto flex flex-col gap-4" >
249216 < div className = "flex flex-col" >
250217 < span className = "text-sm font-bold uppercase tracking-widest text-ink opacity-60" > Current Price</ span >
251218 < span className = "text-5xl font-black tracking-tighter text-ink" > { formatPrice ( listing . price ) } </ span >
252219 < span className = "text-sm font-bold text-ink opacity-60" > ≈ ${ ( Number ( listing . price ) / 1e9 * 1.25 ) . toFixed ( 2 ) } USD</ span >
253220 </ div >
254-
221+
255222 { isOwner ? (
256223 < div className = "bg-white/50 border-2 border-ink rounded-xl p-4" >
257224 < p className = "font-bold text-ink flex items-center gap-2" >
@@ -260,8 +227,8 @@ export default function DatasetDetailPage() {
260227 </ p >
261228 </ div >
262229 ) : isPurchased ? (
263- < button
264- onClick = { handleDownloadClick }
230+ < button
231+ onClick = { handleDownloadClick }
265232 disabled = { isDownloading }
266233 className = "group w-full rounded-xl border-2 border-ink bg-ink py-4 text-white shadow-hard transition-all hover:-translate-y-1 hover:bg-primary hover:text-ink disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:translate-y-0"
267234 >
@@ -283,9 +250,15 @@ export default function DatasetDetailPage() {
283250 < button onClick = { ( ) => setIsBuyModalOpen ( true ) } disabled = { isPurchasing || ! account } className = "group w-full rounded-xl border-2 border-ink bg-ink py-4 text-white shadow-hard transition-all hover:-translate-y-1 hover:bg-primary hover:text-ink disabled:opacity-50 disabled:cursor-not-allowed" >
284251 < div className = "flex items-center justify-center gap-3" >
285252 { isPurchasing ? (
286- < > < span className = "material-symbols-outlined text-2xl animate-spin" > sync</ span > < span className = "text-xl font-black uppercase tracking-wide" > Processing...</ span > </ >
253+ < >
254+ < span className = "material-symbols-outlined text-2xl animate-spin" > sync</ span >
255+ < span className = "text-xl font-black uppercase tracking-wide" > Processing...</ span >
256+ </ >
287257 ) : (
288- < > < span className = "material-symbols-outlined text-2xl group-hover:animate-bounce" > shopping_cart_checkout</ span > < span className = "text-xl font-black uppercase tracking-wide" > Buy Now</ span > </ >
258+ < >
259+ < span className = "material-symbols-outlined text-2xl group-hover:animate-bounce" > shopping_cart_checkout</ span >
260+ < span className = "text-xl font-black uppercase tracking-wide" > Buy Now</ span >
261+ </ >
289262 ) }
290263 </ div >
291264 </ button >
@@ -297,28 +270,16 @@ export default function DatasetDetailPage() {
297270
298271 { /* RIGHT PANEL */ }
299272 < div className = "flex-1 bg-white overflow-y-auto relative" >
300- < div className = "absolute right-0 top-0 h-64 w-64 bg-gray-50 rounded-bl-[100px] - z-0" > </ div >
273+ < div className = "absolute right-0 top-0 h-64 w-64 bg-gray-50 rounded-bl-[100px] z-0" > </ div >
301274 < div className = "max-w-5xl mx-auto p-6 lg:p-12 flex flex-col gap-12 relative z-10" >
302-
303275 { /* Hero Image */ }
304276 < section >
305- < div className = "relative w-full aspect-[ 2/1] rounded-2xl border-2 border-ink overflow-hidden shadow-hard-lg group" >
277+ < div className = "relative w-full aspect-2/1 rounded-2xl border-2 border-ink overflow-hidden shadow-hard-lg group" >
306278 { listing . imageUrl ? (
307- < img
308- src = { listing . imageUrl }
309- alt = { listing . name }
310- className = "w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
311- />
279+ < img src = { listing . imageUrl } alt = { listing . name } className = "w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" />
312280 ) : (
313281 < >
314- < div className = "absolute inset-0 bg-gradient-to-br from-gray-800 to-gray-900" />
315- < div className = "absolute inset-0 opacity-20" >
316- < svg className = "w-full h-full" viewBox = "0 0 100 100" preserveAspectRatio = "none" >
317- < path d = "M0,50 Q25,30 50,50 T100,50" stroke = "currentColor" strokeWidth = "0.5" fill = "none" className = "text-accent-lime" />
318- < path d = "M0,60 Q25,40 50,60 T100,60" stroke = "currentColor" strokeWidth = "0.5" fill = "none" className = "text-accent-lime" />
319- < path d = "M0,40 Q25,20 50,40 T100,40" stroke = "currentColor" strokeWidth = "0.5" fill = "none" className = "text-accent-lime" />
320- </ svg >
321- </ div >
282+ < div className = "absolute inset-0 bg-linear-to-br from-gray-800 to-gray-900" />
322283 < div className = "absolute inset-0 flex items-center justify-center" >
323284 < span className = "material-symbols-outlined text-[120px] text-gray-600" > dataset</ span >
324285 </ div >
@@ -332,8 +293,8 @@ export default function DatasetDetailPage() {
332293 < button className = "h-10 w-10 bg-white border-2 border-ink rounded-lg flex items-center justify-center hover:bg-accent-lime transition-colors shadow-hard-sm text-ink" >
333294 < span className = "material-symbols-outlined" > fullscreen</ span >
334295 </ button >
335- < button onClick = { handleDownloadClick } className = "h-10 w-10 bg-white border-2 border-ink rounded-lg flex items-center justify-center hover:bg-accent-lime transition-colors shadow-hard-sm text-ink" >
336- < span className = " material-symbols-outlined" > download</ span >
296+ < button onClick = { handleDownloadClick } disabled = { ! isPurchased || isDownloading } className = "h-10 w-10 bg-white border-2 border-ink rounded-lg flex items-center justify-center hover:bg-accent-lime transition-colors shadow-hard-sm text-ink disabled:opacity-50 " >
297+ < span className = { ` material-symbols-outlined ${ isDownloading ? 'animate-spin' : '' } ` } > { isDownloading ? 'sync' : ' download' } </ span >
337298 </ button >
338299 </ div >
339300 </ div >
@@ -352,7 +313,7 @@ export default function DatasetDetailPage() {
352313 { dummyData . features . map ( ( f , i ) => < li key = { i } > { f } </ li > ) }
353314 </ ul >
354315 </ div >
355-
316+
356317 { /* IDs Section */ }
357318 < div className = "flex flex-col gap-3 p-4 bg-gray-50 rounded-xl border border-gray-200" >
358319 < div className = "flex items-center gap-2 flex-wrap" >
@@ -361,9 +322,6 @@ export default function DatasetDetailPage() {
361322 < a href = { `https://suiscan.xyz/testnet/object/${ listing . id } ` } target = "_blank" rel = "noopener noreferrer" className = "h-6 w-6 rounded bg-white hover:bg-gray-100 flex items-center justify-center border" title = "SuiScan" >
362323 < svg className = "w-3.5 h-3.5" viewBox = "0 0 234 234" fill = "none" > < path d = "M0 100C0 65 0 47.5 6.8 33C12.7 21.3 22.3 11.8 34 5.8C47.3 0 64.7 0 99.5 0H133.8C168.6 0 186 0 199.3 6.8C211 12.8 220.6 22.3 226.5 34C233.3 47.4 233.3 64.9 233.3 99.8V134.2C233.3 169.1 233.3 186.6 226.5 199.9C220.6 211.6 211 221.2 199.3 227.2C186 234 168.6 234 133.8 234H99.5C64.7 234 47.3 234 34 227.2C22.3 221.2 12.7 211.7 6.8 200C0 186.6 0 169.1 0 134.2V100Z" fill = "#4C72FF" /> < path d = "M177 87C178.7 85.9 180.8 85.6 182.4 86.3C183.2 86.6 183.9 87.1 184.3 87.8C184.7 88.5 185 89.4 184.9 90.2L181.4 148.2C181 155.7 178.2 163.4 173.6 170.4C160.4 190.4 133.2 200 112.8 191.8C107.1 189.5 102.5 186 99.2 181.7C100 181.8 100.8 181.7 101.5 181.7C122.4 181.7 143.5 170.3 155.1 152.7C160.7 144.1 164 134.7 164.6 125.6L166.5 93.3L177 87Z" fill = "white" /> < path d = "M150 63.6C151.7 62.5 153.8 62.3 155.5 62.9C156.3 63.3 156.9 63.8 157.4 64.5C157.9 65.2 158.1 66.1 158 66.9L154.5 125C154 132.5 151.3 140.1 146.7 147.1C133.5 167.2 106.3 176.7 85.9 168.5C80.1 166.2 75.6 162.7 72.3 158.4C73.1 158.4 73.9 158.4 74.6 158.4C95.6 158.4 116.6 147.1 128.2 129.4C133.9 120.8 137.1 111.4 137.7 102.3L139.6 70L150 63.6Z" fill = "white" /> < path d = "M123 40.3C124.7 39.2 126.8 39 128.5 39.6C129.2 39.9 129.9 40.5 130.3 41.2C130.8 41.9 131 42.7 130.9 43.5L127.4 101.6C127 109.1 124.2 116.7 119.6 123.7C106.4 143.8 79.2 153.3 58.8 145.1C38.5 136.9 32.7 114 45.9 93.9C50.5 86.9 57.1 80.7 64.6 76.1L123 40.3Z" fill = "white" /> </ svg >
363324 </ a >
364- < a href = { `https://testnet.suivision.xyz/object/${ listing . id } ` } target = "_blank" rel = "noopener noreferrer" className = "h-6 w-6 rounded bg-white hover:bg-gray-100 flex items-center justify-center border" title = "SuiVision" >
365- < svg className = "w-3.5 h-3.5" viewBox = "0 0 24 24" fill = "none" > < path d = "M0 6C0 2.68629 2.68629 0 6 0H18C21.3137 0 24 2.68629 24 6V18C24 21.3137 21.3137 24 18 24H6C2.68629 24 0 21.3137 0 18V6Z" fill = "#4DA2FF" /> < path d = "M6.99748 5.28362L6.99748 11.0148L8.71768 12.0008L6.99731 12.987L6.99731 18.7182L11.9972 21.584L16.9971 18.7182L16.9971 12.9866L15.2769 12.0007L16.9973 11.0147L16.9973 5.28308L11.997 2.41732L6.99748 5.28362ZM11.6464 3.42366L11.6464 7.94789L7.69961 10.2105L7.69961 5.68623L11.6464 3.42366ZM12.3482 20.5781L12.3482 16.0535L16.2954 13.7912L16.2954 18.3159L12.3482 20.5781ZM15.9441 13.1879L11.9973 15.4501L8.05048 13.1879L9.41994 12.4031L11.9973 13.8803L14.575 12.4031L15.9441 13.1879ZM11.9973 10.1208L9.41964 11.5982L8.05048 10.8134L11.9973 8.55113L15.9445 10.8134L14.575 11.5982L11.9973 10.1208Z" fill = "white" /> </ svg >
366- </ a >
367325 </ div >
368326 < div className = "flex items-center gap-2 flex-wrap" >
369327 < span className = "text-xs font-bold text-gray-500 uppercase w-20" > Blob ID:</ span >
@@ -372,7 +330,6 @@ export default function DatasetDetailPage() {
372330 < svg className = "w-3.5 h-3.5" viewBox = "0 0 24 24" fill = "none" > < circle cx = "12" cy = "10" r = "8" fill = "#36B5A8" /> < ellipse cx = "12" cy = "22" rx = "6" ry = "2" fill = "#36B5A8" opacity = "0.5" /> < circle cx = "9" cy = "8" r = "1.5" fill = "white" /> < circle cx = "15" cy = "8" r = "1.5" fill = "white" /> < path d = "M9 13C9 13 10.5 15 12 15C13.5 15 15 13 15 13" stroke = "white" strokeWidth = "1.5" strokeLinecap = "round" /> </ svg >
373331 </ a >
374332 </ div >
375- { /* Purchase TX - only show if purchased */ }
376333 { isPurchased && displayTxDigest && (
377334 < div className = "flex items-center gap-2 flex-wrap pt-2 border-t border-gray-200 mt-1" >
378335 < span className = "text-xs font-bold text-green-600 uppercase w-20 flex items-center gap-1" >
@@ -428,9 +385,7 @@ export default function DatasetDetailPage() {
428385 < div key = { review . id } className = "rounded-xl border-2 border-ink bg-white p-6 shadow-hard-sm hover:shadow-hard transition-shadow" >
429386 < div className = "flex justify-between items-start mb-4" >
430387 < div className = "flex items-center gap-3" >
431- < div className = { `h-10 w-10 rounded-full border-2 border-ink flex items-center justify-center font-bold text-white shadow-sm ${ review . bgColor } ` } >
432- { review . initials }
433- </ div >
388+ < div className = { `h-10 w-10 rounded-full border-2 border-ink flex items-center justify-center font-bold text-white shadow-sm ${ review . bgColor } ` } > { review . initials } </ div >
434389 < div className = "flex flex-col" >
435390 < span className = "font-bold text-sm text-ink" > { review . user } </ span >
436391 < div className = "flex text-accent-orange" >
@@ -462,7 +417,7 @@ export default function DatasetDetailPage() {
462417 { isBuyModalOpen && (
463418 < div className = "fixed inset-0 z-50 flex items-center justify-center bg-ink/50 backdrop-blur-sm p-4" >
464419 < div className = "w-full max-w-md bg-white rounded-2xl border-2 border-ink shadow-hard-lg p-6 relative" >
465- < button onClick = { ( ) => setIsBuyModalOpen ( false ) } className = "absolute top-4 right-4 h-8 w-8 flex items-center justify-center rounded-lg hover:bg-gray-100" >
420+ < button onClick = { ( ) => { setIsBuyModalOpen ( false ) ; setPurchaseError ( null ) ; } } className = "absolute top-4 right-4 h-8 w-8 flex items-center justify-center rounded-lg hover:bg-gray-100" >
466421 < span className = "material-symbols-outlined text-ink" > close</ span >
467422 </ button >
468423 < div className = "flex flex-col gap-4" >
@@ -501,7 +456,7 @@ export default function DatasetDetailPage() {
501456 </ div >
502457 ) }
503458
504- { /* Preview Modal with Decryption Animation */ }
459+ { /* Preview Modal */ }
505460 < DecryptionPreviewModal
506461 isOpen = { isPreviewModalOpen }
507462 onClose = { ( ) => setIsPreviewModalOpen ( false ) }
@@ -511,8 +466,7 @@ export default function DatasetDetailPage() {
511466 previewBytes = { Number ( listing . previewSize ) }
512467 requesterAddress = { account ?. address || '' }
513468 mimeType = { listing . mimeType || 'application/octet-stream' }
514- fileName = { listing . fileName || 'download' }
515469 />
516470 </ div >
517471 ) ;
518- }
472+ }
0 commit comments