-
Notifications
You must be signed in to change notification settings - Fork 0
SOV-5184: convert page UI #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
0f519f5
feat: convert page UI
norugpull 7d896a8
fix: missing files and build issue
norugpull 461ab64
feat: convert page chart
norugpull b8df16c
feat: convet chart ui
norugpull a75628f
Merge branch 'develop' into feat/convertPage
norugpull 351de31
Merge branch 'develop' into feat/convertPage
norugpull e3e4d96
fix: ui convert page issues
norugpull 08c3c63
fix: PR comments
norugpull 29c8cee
fix: convert page improvements
norugpull File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| import { Button } from '@/components/ui/button'; | ||
| import { Card, CardContent } from '@/components/ui/card'; | ||
| import { Checkbox } from '@/components/ui/checkbox'; | ||
| import { Label } from '@/components/ui/label'; | ||
| import { Switch } from '@/components/ui/switch'; | ||
| import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; | ||
| import { useNavigate, useSearch } from '@tanstack/react-router'; | ||
| import { ArrowDownUp, Info, Settings } from 'lucide-react'; | ||
| import { useState, type FC } from 'react'; | ||
| import { Hero } from '../Hero/Hero'; | ||
| import { AssetSelect } from './components/AssetSelect'; | ||
| import { CryptoChart } from './components/ConvertChart'; | ||
|
|
||
| export const Convert: FC = () => { | ||
| const search = useSearch({ from: '/convert' }); | ||
| const navigate = useNavigate({ from: '/convert' }); | ||
|
|
||
| const [fromAsset, setFromAsset] = useState('BTC'); | ||
| const [toAsset, setToAsset] = useState('BTC'); | ||
| const [amount, setAmount] = useState('1'); | ||
|
|
||
| const showChart = search.showChart === true; | ||
|
|
||
| const toggleChart = () => { | ||
| navigate({ | ||
| search: (prev) => ({ | ||
| ...prev, | ||
| showChart: prev.showChart ? undefined : true, | ||
| }), | ||
| replace: true, | ||
| }); | ||
| }; | ||
|
|
||
| const handleSwap = () => { | ||
| setFromAsset(toAsset); | ||
| setToAsset(fromAsset); | ||
| }; | ||
|
|
||
| return ( | ||
| <div> | ||
| <div className="flex flex-col lg:flex-row gap-6 justify-center"> | ||
| <Hero className="w-full lg:w-1/3" title="Convert"> | ||
| Lorem bitcoinae dollar situs ametus, consensusium adipiscing elitum, | ||
| sed do proofus-of-workium. | ||
| </Hero> | ||
| {showChart && <div className="w-full lg:w-1/2" />} | ||
| </div> | ||
|
|
||
| <div className="flex flex-col lg:flex-row items-center lg:items-start gap-6 justify-center"> | ||
| <div className="flex flex-col gap-6 w-full lg:w-1/3"> | ||
| <Card className="bg-neutral-900 border-none rounded-2xl shadow-lg py-3"> | ||
| <CardContent className="space-y-4 px-3"> | ||
| <div className="flex items-center justify-between"> | ||
| <Tabs defaultValue="convert" className="flex-1"> | ||
| <TabsList className="flex items-center bg-neutral-800 rounded-full p-1 w-full max-w-md mx-auto"> | ||
| <TabsTrigger value="convert">Convert</TabsTrigger> | ||
| <TabsTrigger value="cross">Cross-chain convert</TabsTrigger> | ||
| </TabsList> | ||
| </Tabs> | ||
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| className="ml-2 text-neutral-400 hover:text-white cursor-pointer" | ||
| > | ||
| <Settings className="size-6" /> | ||
| </Button> | ||
| </div> | ||
|
|
||
| <div className="space-y-3"> | ||
| <div className="p-4 bg-neutral-800 rounded-xl flex flex-col gap-4"> | ||
| <Label className="text-neutral-400 font-medium text-sm"> | ||
| From | ||
| </Label> | ||
| <div className="flex justify-between items-center"> | ||
| <AssetSelect value={fromAsset} onChange={setFromAsset} /> | ||
| <input | ||
| type="number" | ||
| value={amount} | ||
| onChange={(e) => setAmount(e.target.value)} | ||
| placeholder="0" | ||
| className="flex-1 bg-transparent border-none text-right text-2xl font-semibold focus-visible:ring-0 text-white outline-none | ||
| [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" | ||
| /> | ||
| </div> | ||
| <div className="flex justify-between items-center text-neutral-400 font-medium text-xs"> | ||
| <span>Balance -</span> | ||
| <span>$100.012</span> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="flex justify-center"> | ||
| <Button | ||
| className="bg-accent" | ||
| variant="ghost" | ||
| size="icon" | ||
| onClick={handleSwap} | ||
| > | ||
| <ArrowDownUp className="h-5 w-5 text-neutral-400" /> | ||
| </Button> | ||
| </div> | ||
|
|
||
| <div className="p-4 bg-neutral-800 rounded-xl flex flex-col gap-4"> | ||
| <Label className="text-neutral-400 font-medium text-sm"> | ||
| From | ||
| </Label> | ||
| <div className="flex justify-between items-center"> | ||
| <AssetSelect value={fromAsset} onChange={setFromAsset} /> | ||
| <input | ||
| type="number" | ||
| value={amount} | ||
| onChange={(e) => setAmount(e.target.value)} | ||
| placeholder="0" | ||
| className="flex-1 bg-transparent border-none text-right text-2xl font-semibold focus-visible:ring-0 text-white outline-none | ||
| [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" | ||
| /> | ||
| </div> | ||
| <div className="flex justify-between items-center text-neutral-400 font-medium text-xs"> | ||
| <span>Balance -</span> | ||
| <span>$100.012</span> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="flex justify-end items-center gap-2"> | ||
| <Label | ||
| htmlFor="chart" | ||
| className="text-sm cursor-pointer text-gray-50 font-medium" | ||
| > | ||
| Show chart | ||
| </Label> | ||
| <Switch | ||
| className="cursor-pointer" | ||
| checked={showChart} | ||
| id="chart" | ||
| onCheckedChange={toggleChart} | ||
| /> | ||
| </div> | ||
| </div> | ||
| </CardContent> | ||
| </Card> | ||
| <div className="rounded-xl border border-neutral-700 p-4 space-y-2 text-xs font-medium text-gray-400"> | ||
| <div className="flex justify-between"> | ||
| <span>You’ll receive</span> | ||
| <span>0.004 XYZ</span> | ||
| </div> | ||
| <div className="flex justify-between items-center"> | ||
| <span className="flex items-center gap-1"> | ||
| Estimated network fee <Info className="w-3 h-3" /> | ||
| </span> | ||
| <span>0.004 XYZ</span> | ||
| </div> | ||
| <div className="flex justify-between"> | ||
| <span>Transaction fee</span> | ||
| <span>0.004 XYZ</span> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="space-y-1 mx-auto"> | ||
| <div className="flex items-center gap-2"> | ||
| <Checkbox id="terms" /> | ||
| <Label | ||
| htmlFor="terms" | ||
| className="text-sm font-medium text-gray-50" | ||
| > | ||
| Accept terms and condition | ||
| </Label> | ||
| </div> | ||
| <p className="text-sm text-gray-50 pl-6"> | ||
| You agree to our Terms of Service and Privacy Policy. | ||
| </p> | ||
| </div> | ||
|
|
||
| <Button size="lg" className="w-40 mx-auto"> | ||
| Convert | ||
| </Button> | ||
| </div> | ||
|
|
||
| {showChart && ( | ||
| <div className="w-full lg:w-1/2"> | ||
| <CryptoChart /> | ||
| </div> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
38 changes: 38 additions & 0 deletions
38
apps/web-app/src/components/Convert/components/AssetSelect.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import { | ||
| Select, | ||
| SelectContent, | ||
| SelectItem, | ||
| SelectTrigger, | ||
| SelectValue, | ||
| } from '@/components/ui/select'; | ||
| import { BOB, BTC, USDT } from '@/constants/tokens'; | ||
| import type { FC } from 'react'; | ||
|
|
||
| interface AssetSelectProps { | ||
| value?: string; | ||
norugpull marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| onChange?(value: string): void; | ||
| } | ||
|
|
||
| export const AssetSelect: FC<AssetSelectProps> = ({ value, onChange }) => { | ||
| return ( | ||
| <Select value={value} onValueChange={onChange}> | ||
| <SelectTrigger className="shadow-none min-w-36 border-none text-lg cursor-pointer h-5 pl-0"> | ||
| <SelectValue placeholder="Token" /> | ||
| </SelectTrigger> | ||
| <SelectContent> | ||
| <SelectItem className="text-lg" value={BTC}> | ||
| <img className="size-8 rounded-full" src="/images/btc.svg" /> | ||
| {BTC} | ||
| </SelectItem> | ||
| <SelectItem className="text-lg" value={BOB}> | ||
| <img className="size-8 rounded-full" src="/images/bob.svg" /> | ||
| {BOB} | ||
| </SelectItem> | ||
| <SelectItem className="text-lg" value={USDT}> | ||
| <img className="size-8 rounded-full" src="/images/btc.svg" /> | ||
| {USDT} | ||
| </SelectItem> | ||
| </SelectContent> | ||
| </Select> | ||
| ); | ||
| }; | ||
140 changes: 140 additions & 0 deletions
140
apps/web-app/src/components/Convert/components/ConvertChart.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| 'use client'; | ||
|
|
||
| import { Button } from '@/components/ui/button'; | ||
| import { Card, CardContent } from '@/components/ui/card'; | ||
| import { cn } from '@/lib/utils'; | ||
| import { useState, type FC } from 'react'; | ||
| import { | ||
| Area, | ||
| AreaChart, | ||
| ResponsiveContainer, | ||
| Tooltip, | ||
| XAxis, | ||
| YAxis, | ||
| } from 'recharts'; | ||
|
|
||
| const data = [ | ||
| { date: 'Sep 16', value: 620000 }, | ||
| { date: 'Sep 17', value: 580000 }, | ||
| { date: 'Sep 18', value: 610000 }, | ||
| { date: 'Sep 19', value: 480000 }, | ||
| { date: 'Sep 20', value: 450000 }, | ||
| { date: 'Sep 21', value: 200000 }, | ||
| { date: 'Sep 22', value: 300000 }, | ||
| { date: 'Sep 23', value: 465123 }, | ||
| ]; | ||
|
|
||
| export const CryptoChart: FC = () => { | ||
| const [activeRange, setActiveRange] = useState('1W'); | ||
| const ranges = ['24H', '1W', '1M', '1Y']; | ||
|
|
||
| return ( | ||
| <Card className="bg-neutral-900 border-0 rounded-2xl pt-4 pb-7"> | ||
| <CardContent className="px-7"> | ||
| <div className="flex justify-between items-start mb-2"> | ||
| <div className="flex flex-col gap-2"> | ||
| <div className="flex items-center gap-2"> | ||
| <div className="flex items-center"> | ||
| <img className="size-8 rounded-full" src="/images/btc.svg" /> | ||
| <img | ||
| className="size-8 rounded-full -ml-2" | ||
| src="/images/bob.svg" | ||
| /> | ||
| </div> | ||
| <span className="font-medium text-lg text-gray-50"> | ||
| BTC / BOS | ||
| </span> | ||
| </div> | ||
| <div className="text-lg font-semibold text-gray-50 pl-1"> | ||
| 1,234,567.12 | ||
| </div> | ||
| <div className="text-sm font-medium text-gray-500 pl-1"> | ||
| Sept 23, 2025 | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="flex gap-1"> | ||
| {ranges.map((range) => ( | ||
| <Button | ||
| key={range} | ||
| size="sm" | ||
| variant="ghost" | ||
| className={cn( | ||
| 'cursor-pointer px-3 py-1.5 text-xs transition-colors text-white rounded-sm', | ||
| { | ||
| 'bg-neutral-700': activeRange === range, | ||
| }, | ||
| )} | ||
| onClick={() => setActiveRange(range)} | ||
| > | ||
| {range} | ||
| </Button> | ||
| ))} | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="h-[320px] w-full"> | ||
| <ResponsiveContainer width="100%" height="100%"> | ||
| <AreaChart data={data}> | ||
| <defs> | ||
| <linearGradient id="colorValue" x1="0" y1="0" x2="0" y2="1"> | ||
| <stop offset="0%" stopColor="#FF4500" stopOpacity={0.8} /> | ||
| <stop offset="100%" stopColor="#FF4500" stopOpacity={0} /> | ||
| </linearGradient> | ||
| </defs> | ||
|
|
||
| <XAxis | ||
| dataKey="date" | ||
| stroke="none" | ||
| tick={{ fill: '#6B7280', fontSize: 12, textAnchor: 'start' }} | ||
| tickLine={false} | ||
| axisLine={false} | ||
| interval={0} | ||
| padding={{ left: 10, right: 10 }} | ||
| /> | ||
|
|
||
| <YAxis | ||
| orientation="right" | ||
| stroke="none" | ||
| tick={{ fill: '#6B7280', fontSize: 12 }} | ||
| tickLine={false} | ||
| axisLine={false} | ||
| width={60} | ||
| tickFormatter={(v) => | ||
| v === 0 | ||
| ? '' | ||
| : v.toLocaleString('en-US', { | ||
| minimumFractionDigits: 0, | ||
| maximumFractionDigits: 0, | ||
| }) | ||
| } | ||
| padding={{ top: 10, bottom: 10 }} | ||
| /> | ||
|
|
||
| <Area | ||
| type="monotone" | ||
| dataKey="value" | ||
| stroke="#FF4500" | ||
| strokeWidth={2} | ||
| fill="url(#colorValue)" | ||
| dot={false} | ||
| /> | ||
|
|
||
| <Tooltip | ||
| contentStyle={{ | ||
| backgroundColor: '#1a1a1a', | ||
| border: 'none', | ||
| borderRadius: '4px', | ||
| }} | ||
| labelStyle={{ color: '#999' }} | ||
| formatter={(value) => | ||
| value.toLocaleString('en-US', { minimumFractionDigits: 2 }) | ||
| } | ||
| /> | ||
| </AreaChart> | ||
| </ResponsiveContainer> | ||
| </div> | ||
| </CardContent> | ||
| </Card> | ||
| ); | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create a constant for
BTC.