33
44'use client' ;
55
6+ import { Warning } from '@iota/apps-ui-icons' ;
67import {
78 Button ,
8- ButtonSize ,
9+ ButtonPill ,
910 ButtonType ,
1011 Dialog ,
1112 DialogBody ,
1213 DialogContent ,
14+ DialogPosition ,
15+ DisplayStats ,
1316 Header ,
17+ InfoBox ,
18+ InfoBoxStyle ,
19+ InfoBoxType ,
1420 Input ,
1521 InputType ,
1622 LoadingIndicator ,
23+ Panel ,
24+ TooltipPosition ,
1725} from '@iota/apps-ui-kit' ;
1826import { useCurrentAccount , useIotaClient , useSignAndExecuteTransaction } from '@iota/dapp-kit' ;
1927import { Transaction } from '@iota/iota-sdk/transactions' ;
@@ -22,17 +30,22 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
2230import { useState } from 'react' ;
2331
2432import { useAuctionBid } from '@/auctions/hooks/useAuctionBid' ;
33+ import { useCountdown } from '@/auctions/hooks/useCountdown' ;
2534import { useGetAuctionMetadata } from '@/auctions/hooks/useGetAuctionMetadata' ;
35+ import { formatTimeRemaining , getTimeRemaining , getUserAuctionStatus } from '@/auctions/lib/utils' ;
2636import { queryKey } from '@/hooks' ;
2737import { formatNanosToIota } from '@/lib/utils' ;
2838import { toNanos } from '@/lib/utils/amount' ;
39+ import { formatExpirationDate } from '@/lib/utils/format/formatExpirationDate' ;
40+ import { normalizeNameInput } from '@/lib/utils/format/formatNames' ;
2941
3042interface AuctionBidDialogDialogProps {
3143 name : string ;
3244 closeDialog : ( ) => void ;
45+ onCompleted ?: ( ) => void ;
3346}
3447
35- export function AuctionBidDialog ( { name, closeDialog } : AuctionBidDialogDialogProps ) {
48+ export function AuctionBidDialog ( { name, closeDialog, onCompleted } : AuctionBidDialogDialogProps ) {
3649 const iotaClient = useIotaClient ( ) ;
3750 const account = useCurrentAccount ( ) ;
3851 const queryClient = useQueryClient ( ) ;
@@ -75,13 +88,19 @@ export function AuctionBidDialog({ name, closeDialog }: AuctionBidDialogDialogPr
7588 } ) ;
7689 queryClient . invalidateQueries ( { queryKey : queryKey . auctionMetadata ( name ) } ) ;
7790 closeDialog ( ) ;
91+ onCompleted ?.( ) ;
7892 } ,
7993 } ) ;
8094
8195 const minBidLabel = formatNanosToIota ( minBidNanos , {
8296 formatRounded : false ,
8397 showIotaSymbol : true ,
8498 } ) ;
99+
100+ const minBidWithoutLabel = formatNanosToIota ( minBidNanos , {
101+ formatRounded : false ,
102+ showIotaSymbol : false ,
103+ } ) ;
85104 const isBidAboveDecimals = bidNanos === null ;
86105 const isBidBelowMinimum = ( bidNanos || BigInt ( 0 ) ) < minBidNanos ;
87106
@@ -98,55 +117,109 @@ export function AuctionBidDialog({ name, closeDialog }: AuctionBidDialogDialogPr
98117 return error . message ;
99118 }
100119 } ) ( ) ;
101-
120+ const cleanName = normalizeNameInput ( name ) ;
121+
122+ const status = auctionMetadata && getUserAuctionStatus ( auctionMetadata , account ?. address || '' ) ;
123+ const timeRemainingMs = auctionMetadata && getTimeRemaining ( auctionMetadata ) ;
124+ const { milliseconds } = useCountdown ( timeRemainingMs || 0 ) ;
125+
126+ const formattedTimeRemaining = formatTimeRemaining ( milliseconds ) ;
127+ const currentBid = auctionMetadata
128+ ? formatNanosToIota ( auctionMetadata . currentBidNanos , {
129+ formatRounded : false ,
130+ showIotaSymbol : true ,
131+ } )
132+ : '--' ;
133+ const expirationDate = auctionMetadata
134+ ? formatExpirationDate ( auctionMetadata . nftExpiration )
135+ : '--' ;
102136 return (
103137 < Dialog open onOpenChange = { closeDialog } >
104- < DialogContent showCloseOnOverlay >
138+ < DialogContent containerId = "overlay-portal-container" position = { DialogPosition . Right } >
105139 < Header
106- title = { auctionMetadata ? `Bid for ${ name } ` : `Start Auction for ${ name } ` }
140+ title = " Auction"
107141 titleCentered
108142 onClose = { ( ) => closeDialog ( ) }
109143 onBack = { ( ) => closeDialog ( ) }
110144 />
111145
112146 < DialogBody >
113- < div className = "flex flex-col gap-md" >
114- < Input
115- type = { InputType . Number }
116- label = "Your bid (IOTA)"
117- min = { Number ( minBidNanos ) }
118- value = { bidAmountValue }
119- onChange = { ( { target : { value } } ) => setBidAmountValue ( value ) }
120- errorMessage = { errorMessage }
121- />
122-
123- < div className = "flex items-center justify-between" >
124- < span className = "text-body-md text-neutral-60" > Minimum bid:</ span >
125- < span className = "text-body-md" > { minBidLabel } </ span >
147+ < div className = "flex flex-col justify-between h-full items-center" >
148+ < div className = "flex flex-col w-full gap-y-md" >
149+ { status === 'top_bidder' && (
150+ < InfoBox
151+ title = "Top Bidder"
152+ supportingText = "Your are the top bidder already"
153+ icon = { < Warning /> }
154+ type = { InfoBoxType . Warning }
155+ style = { InfoBoxStyle . Default }
156+ />
157+ ) }
158+
159+ < Panel bgColor = "bg-names-neutral-12" >
160+ < div className = "px-md py-lg" >
161+ < span className = "text-names-neutral-100 text-headline-sm" >
162+ @{ cleanName }
163+ </ span >
164+ </ div >
165+ </ Panel >
166+ { auctionMetadata && (
167+ < div className = "flex flex-row gap-x-sm w-full" >
168+ < DisplayStats
169+ label = "Current Bid"
170+ value = { currentBid }
171+ tooltipText = "The current highest bid for this auction."
172+ tooltipPosition = { TooltipPosition . Right }
173+ />
174+ < DisplayStats
175+ label = "Time Left"
176+ value = { formattedTimeRemaining }
177+ />
178+ </ div >
179+ ) }
180+ < Input
181+ type = { InputType . Number }
182+ label = "Your Bid"
183+ min = { Number ( minBidNanos ) }
184+ value = { bidAmountValue }
185+ onChange = { ( { target : { value } } ) => setBidAmountValue ( value ) }
186+ errorMessage = { errorMessage }
187+ trailingElement = {
188+ < ButtonPill
189+ onClick = { ( ) => setBidAmountValue ( minBidWithoutLabel ) }
190+ >
191+ Min
192+ </ ButtonPill >
193+ }
194+ />
195+ </ div >
196+ < div className = "flex w-full flex-col gap-y-md" >
197+ { auctionMetadata && (
198+ < DisplayStats label = "Registration Expires" value = { expirationDate } />
199+ ) }
200+ < div className = "flex w-full flex-row gap-x-xs mt-xs" >
201+ < Button
202+ type = { ButtonType . Secondary }
203+ text = "Cancel"
204+ onClick = { ( ) => closeDialog ( ) }
205+ fullWidth
206+ />
207+ < Button
208+ type = { ButtonType . Primary }
209+ disabled = { disablePlaceBid }
210+ icon = { isLoading ? < LoadingIndicator /> : null }
211+ text = { auctionMetadata ? 'Bid' : 'Start auction' }
212+ onClick = { ( ) => {
213+ if ( auctionBidTransaction ) {
214+ handleConfirm ( auctionBidTransaction ) ;
215+ }
216+ } }
217+ fullWidth
218+ />
219+ </ div >
126220 </ div >
127221 </ div >
128222 </ DialogBody >
129-
130- < div className = "flex w-full justify-center gap-2 px-md--rs pb-md--rs pt-sm--rs" >
131- < Button
132- size = { ButtonSize . Small }
133- type = { ButtonType . Outlined }
134- text = "Cancel"
135- onClick = { ( ) => closeDialog ( ) }
136- />
137- < Button
138- size = { ButtonSize . Small }
139- type = { ButtonType . Primary }
140- disabled = { disablePlaceBid }
141- icon = { isLoading ? < LoadingIndicator /> : null }
142- text = { auctionMetadata ? 'Place bid' : 'Start auction' }
143- onClick = { ( ) => {
144- if ( auctionBidTransaction ) {
145- handleConfirm ( auctionBidTransaction ) ;
146- }
147- } }
148- />
149- </ div >
150223 </ DialogContent >
151224 </ Dialog >
152225 ) ;
0 commit comments