Skip to content

Commit ff55333

Browse files
evavirsedamsarcev
andauthored
feat: update style for auction & purchase dialogs (#411)
* feat: update purchase and auction popups styles * remove styles * changes requested * cleanup --------- Co-authored-by: msarcev <mario.sarcevic@iota.org>
1 parent 8d07ebc commit ff55333

File tree

7 files changed

+197
-104
lines changed

7 files changed

+197
-104
lines changed

dapp/src/auctions/components/dialogs/AuctionBidDialog.tsx

Lines changed: 112 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,25 @@
33

44
'use client';
55

6+
import { Warning } from '@iota/apps-ui-icons';
67
import {
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';
1826
import { useCurrentAccount, useIotaClient, useSignAndExecuteTransaction } from '@iota/dapp-kit';
1927
import { Transaction } from '@iota/iota-sdk/transactions';
@@ -22,17 +30,22 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
2230
import { useState } from 'react';
2331

2432
import { useAuctionBid } from '@/auctions/hooks/useAuctionBid';
33+
import { useCountdown } from '@/auctions/hooks/useCountdown';
2534
import { useGetAuctionMetadata } from '@/auctions/hooks/useGetAuctionMetadata';
35+
import { formatTimeRemaining, getTimeRemaining, getUserAuctionStatus } from '@/auctions/lib/utils';
2636
import { queryKey } from '@/hooks';
2737
import { formatNanosToIota } from '@/lib/utils';
2838
import { toNanos } from '@/lib/utils/amount';
39+
import { formatExpirationDate } from '@/lib/utils/format/formatExpirationDate';
40+
import { normalizeNameInput } from '@/lib/utils/format/formatNames';
2941

3042
interface 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
);

dapp/src/components/AvailabilityCheck.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,11 @@ import { AuctionBidDialog } from '@/auctions/components/dialogs/AuctionBidDialog
1313
import { useGetAuctionMetadata } from '@/auctions/hooks/useGetAuctionMetadata';
1414
import { useNameRecord, usePriceList } from '@/hooks';
1515
import { formatNanosToIota } from '@/lib/utils';
16+
import { normalizeNameInput } from '@/lib/utils/format/formatNames';
1617

1718
import { PurchaseNameDialog } from './dialogs/PurchaseNameDialog';
1819
import { NamePurchaseCard } from './NamePurchaseCard';
1920

20-
function normalizeNameInput(name: string) {
21-
return name.toLowerCase().replace(/\.iota$/i, '');
22-
}
23-
2421
function getValidationError(
2522
name: string,
2623
minLength: number = 3,
@@ -43,8 +40,9 @@ function getValidationError(
4340

4441
interface AvailabilityCheckProps {
4542
autoFocusInput?: boolean;
43+
onCompleted?: () => void;
4644
}
47-
export function AvailabilityCheck({ autoFocusInput }: AvailabilityCheckProps) {
45+
export function AvailabilityCheck({ autoFocusInput, onCompleted }: AvailabilityCheckProps) {
4846
const { isConnected } = useCurrentWallet();
4947
const [searchValue, setSearchValue] = useState<string>('');
5048
const [name, setName] = useState<string>('');
@@ -86,10 +84,18 @@ export function AvailabilityCheck({ autoFocusInput }: AvailabilityCheckProps) {
8684
}
8785
}
8886

87+
function handleBid() {
88+
setAuctionDialogOpen(false);
89+
setSearchValue('');
90+
setName('');
91+
onCompleted?.();
92+
}
93+
8994
function handlePurchase() {
9095
setPurchaseDialogOpen(false);
9196
setSearchValue('');
9297
setName('');
98+
onCompleted?.();
9399
}
94100
const statusMessage =
95101
isUnavailable && !isAuctionInProgress
@@ -203,7 +209,11 @@ export function AvailabilityCheck({ autoFocusInput }: AvailabilityCheckProps) {
203209
)}
204210

205211
{isAuctionBidDialogOpen && (
206-
<AuctionBidDialog name={name} closeDialog={() => setAuctionDialogOpen(false)} />
212+
<AuctionBidDialog
213+
name={name}
214+
closeDialog={() => setAuctionDialogOpen(false)}
215+
onCompleted={handleBid}
216+
/>
207217
)}
208218
</div>
209219
</div>

dapp/src/components/LandingHero.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ export function LandingHero() {
4444
customWidth="w-[60vw] h-[clamp(400px,80vh,600px)]"
4545
>
4646
<div className="flex flex-col gap-md px-48 py-20 flex-1">
47-
<AvailabilityCheck autoFocusInput />
47+
<AvailabilityCheck
48+
autoFocusInput
49+
onCompleted={() => setDialogOpen(false)}
50+
/>
4851
</div>
4952
</DialogContent>
5053
</Dialog>

0 commit comments

Comments
 (0)