From 9511ba94b1058c00f15d045dab2f9e28d5c6f6f5 Mon Sep 17 00:00:00 2001 From: zvspany Date: Mon, 9 Mar 2026 14:41:52 +0100 Subject: [PATCH] feat(ui): add dynamic amount currency prefix, normalize fiat symbols, and simplify data source badge --- components/converter/converter-card.tsx | 97 ++++++++++++++++-------- components/converter/currency-select.tsx | 93 +++++++++++++---------- lib/currency-display.ts | 38 ++++++++++ 3 files changed, 155 insertions(+), 73 deletions(-) create mode 100644 lib/currency-display.ts diff --git a/components/converter/converter-card.tsx b/components/converter/converter-card.tsx index b5f0767..87d45f5 100644 --- a/components/converter/converter-card.tsx +++ b/components/converter/converter-card.tsx @@ -18,6 +18,7 @@ import { Input } from "@/components/ui/input"; import { Separator } from "@/components/ui/separator"; import { Skeleton } from "@/components/ui/skeleton"; import { useDebouncedValue } from "@/hooks/use-debounced-value"; +import { getDisplaySymbol } from "@/lib/currency-display"; import { useMarketRates } from "@/hooks/use-market-rates"; import { formatAmount, @@ -26,6 +27,7 @@ import { formatTimestamp, } from "@/lib/format"; import { buildRateMap, convertAmount } from "@/lib/rates"; +import { cn } from "@/lib/utils"; import { validateAmount } from "@/lib/validation"; const DEFAULT_FROM = "USD"; @@ -210,6 +212,7 @@ export function ConverterCard({ : "--"; const amountError = inputValidation.ok ? null : inputValidation.error; + const amountPrefix = getDisplaySymbol(fromAsset); return ( @@ -220,27 +223,49 @@ export function ConverterCard({ - Fiat rates by - - {data.sources.fiat} - - - Price data by - - {data.sources.crypto} - + + Data sources: + + {data.sources.fiat} + + + + {data.sources.crypto} + + + + + Data sources: + + {data.sources.fiat} + + + + {data.sources.crypto} + + @@ -265,17 +290,27 @@ export function ConverterCard({ > Amount - setAmountInput(event.target.value)} - placeholder="Enter amount" - className="h-14 rounded-xl bg-background/70 px-4 text-lg" - aria-invalid={Boolean(amountError)} - aria-describedby={amountError ? "amount-error" : undefined} - /> +
+ {amountPrefix ? ( + + {amountPrefix} + + ) : null} + setAmountInput(event.target.value)} + placeholder="Enter amount" + className={cn( + "h-14 rounded-xl bg-background/70 px-4 text-lg", + amountPrefix ? "pl-10" : "", + )} + aria-invalid={Boolean(amountError)} + aria-describedby={amountError ? "amount-error" : undefined} + /> +
{amountError ? (

{amountError} diff --git a/components/converter/currency-select.tsx b/components/converter/currency-select.tsx index 4d7f5e3..08c60b0 100644 --- a/components/converter/currency-select.tsx +++ b/components/converter/currency-select.tsx @@ -4,6 +4,7 @@ import { useMemo, useState } from "react"; import { Check, ChevronDown } from "lucide-react"; import { POPULAR_CODES } from "@/lib/assets"; +import { getDisplaySymbol } from "@/lib/currency-display"; import { RateAsset } from "@/lib/rates"; import { cn } from "@/lib/utils"; import { CurrencyIcon } from "@/components/converter/currency-icon"; @@ -34,6 +35,8 @@ function AssetLabel({ asset }: { asset?: RateAsset }) { ); } + const displaySymbol = getDisplaySymbol(asset); + return ( @@ -43,7 +46,7 @@ function AssetLabel({ asset }: { asset?: RateAsset }) { {asset.type === "fiat" ? "Fiat" : "Crypto"} - {asset.symbol ? ` | ${asset.symbol}` : ""} + {displaySymbol ? ` | ${displaySymbol}` : ""} @@ -111,7 +114,50 @@ export function CurrencySelect({ No assets found. {popularAssets.length > 0 ? ( - {popularAssets.map((asset) => ( + {popularAssets.map((asset) => { + const displaySymbol = getDisplaySymbol(asset); + return ( + { + onChange(asset.code); + setOpen(false); + }} + > +

+ + + + {asset.code} - {asset.name} + + + {asset.type === "fiat" ? "Fiat" : "Crypto"} + {displaySymbol ? ` | ${displaySymbol}` : ""} + + +
+ + + ); + })} + + ) : null} + {allAssets.length > 0 ? : null} + + {allAssets.map((asset) => { + const displaySymbol = getDisplaySymbol(asset); + return ( {asset.type === "fiat" ? "Fiat" : "Crypto"} - {asset.symbol ? ` | ${asset.symbol}` : ""} + {displaySymbol ? ` | ${displaySymbol}` : ""} @@ -144,45 +190,8 @@ export function CurrencySelect({ )} /> - ))} - - ) : null} - {allAssets.length > 0 ? : null} - - {allAssets.map((asset) => ( - { - onChange(asset.code); - setOpen(false); - }} - > -
- - - - {asset.code} - {asset.name} - - - {asset.type === "fiat" ? "Fiat" : "Crypto"} - {asset.symbol ? ` | ${asset.symbol}` : ""} - - -
- -
- ))} + ); + })}
diff --git a/lib/currency-display.ts b/lib/currency-display.ts new file mode 100644 index 0000000..0850824 --- /dev/null +++ b/lib/currency-display.ts @@ -0,0 +1,38 @@ +import { AssetType } from "@/lib/assets"; + +interface DisplayAsset { + code: string; + type: AssetType; + symbol?: string; +} + +const FIAT_DISPLAY_SYMBOLS: Record = { + USD: "$", + EUR: "€", + GBP: "£", + JPY: "¥", + PLN: "zł", + CHF: "CHF", + CAD: "C$", + AUD: "A$", + NZD: "NZ$", + CNY: "¥", + INR: "₹", + KRW: "₩", + TRY: "₺", + BRL: "R$", + MXN: "MX$", + THB: "฿" +}; + +export function getDisplaySymbol(asset?: DisplayAsset): string | undefined { + if (!asset) { + return undefined; + } + + if (asset.type === "fiat") { + return FIAT_DISPLAY_SYMBOLS[asset.code] ?? asset.symbol ?? asset.code; + } + + return asset.symbol ?? asset.code; +}