From d7ba5a0b02e7937a3ab498c3c8e15602bafead4f Mon Sep 17 00:00:00 2001 From: vidvidvid Date: Mon, 28 Oct 2024 13:35:29 +0100 Subject: [PATCH] manage dialog --- .../dashboards/CollateralSwapPopup.tsx | 2 +- .../ui/app/_components/veion/DelegateView.tsx | 37 +++ .../ui/app/_components/veion/ExtendView.tsx | 155 ++++++++++++ .../ui/app/_components/veion/IncreaseView.tsx | 89 +++++++ .../ui/app/_components/veion/InfoBlock.tsx | 32 +++ .../ui/app/_components/veion/LockDuration.tsx | 3 +- .../ui/app/_components/veion/ManagePopup.tsx | 239 ++---------------- .../ui/app/_components/veion/ManageTabs.tsx | 35 +++ .../ui/app/_components/veion/MergeView.tsx | 48 ++++ .../ui/app/_components/veion/SplitView.tsx | 98 +++++++ .../ui/app/_components/veion/TransferView.tsx | 36 +++ packages/ui/app/_components/veion/index.tsx | 2 + packages/ui/app/layout.tsx | 21 +- packages/ui/app/veion/governance/page.tsx | 3 +- .../ui/app/veion/governance/vote/page.tsx | 64 ++--- packages/ui/components/ui/button.tsx | 6 +- packages/ui/components/ui/calendar.tsx | 12 +- packages/ui/components/ui/dialog.tsx | 15 +- packages/ui/components/ui/tabs.tsx | 56 ++++ packages/ui/constants/mock.ts | 28 ++ packages/ui/package.json | 1 + packages/ui/utils/NetworkChecker.ts | 18 +- yarn.lock | 27 ++ 23 files changed, 717 insertions(+), 310 deletions(-) create mode 100644 packages/ui/app/_components/veion/DelegateView.tsx create mode 100644 packages/ui/app/_components/veion/ExtendView.tsx create mode 100644 packages/ui/app/_components/veion/IncreaseView.tsx create mode 100644 packages/ui/app/_components/veion/InfoBlock.tsx create mode 100644 packages/ui/app/_components/veion/ManageTabs.tsx create mode 100644 packages/ui/app/_components/veion/MergeView.tsx create mode 100644 packages/ui/app/_components/veion/SplitView.tsx create mode 100644 packages/ui/app/_components/veion/TransferView.tsx create mode 100644 packages/ui/components/ui/tabs.tsx diff --git a/packages/ui/app/_components/dashboards/CollateralSwapPopup.tsx b/packages/ui/app/_components/dashboards/CollateralSwapPopup.tsx index 8ad9dd119..dce68e9dc 100644 --- a/packages/ui/app/_components/dashboards/CollateralSwapPopup.tsx +++ b/packages/ui/app/_components/dashboards/CollateralSwapPopup.tsx @@ -47,7 +47,7 @@ import MaxDeposit from './MaxDeposit'; import SwapTo from './SwapTo'; import { SlippageDropdown } from '../SlippageDropdown'; -import { collateralSwapAbi } from '@ionicprotocol/sdk'; +import { collateralSwapAbi } from '@ionicprotocol/sdk/src'; createConfig({ integrator: 'ionic', diff --git a/packages/ui/app/_components/veion/DelegateView.tsx b/packages/ui/app/_components/veion/DelegateView.tsx new file mode 100644 index 000000000..231178518 --- /dev/null +++ b/packages/ui/app/_components/veion/DelegateView.tsx @@ -0,0 +1,37 @@ +import { useState } from 'react'; + +import { InfoIcon } from 'lucide-react'; +import { isAddress } from 'viem'; + +import { Button } from '@ui/components/ui/button'; +import { Input } from '@ui/components/ui/input'; + +export function DelegateView() { + const [delegateAddress, setDelegateAddress] = useState(''); + const isValidAddress = delegateAddress ? isAddress(delegateAddress) : false; + + return ( +
+

Delegate Address

+ setDelegateAddress(e.target.value)} + className={!isValidAddress && delegateAddress ? 'border-red-500' : ''} + /> +
+ + + You may delegate your voting power to any user, without transferring + the tokens. You may revoke it, but the user will still be able to vote + until the end of the current voting period. + +
+ +
+ ); +} diff --git a/packages/ui/app/_components/veion/ExtendView.tsx b/packages/ui/app/_components/veion/ExtendView.tsx new file mode 100644 index 000000000..0ac461262 --- /dev/null +++ b/packages/ui/app/_components/veion/ExtendView.tsx @@ -0,0 +1,155 @@ +import { useState, useEffect, useMemo } from 'react'; + +import { format, addDays } from 'date-fns'; +import { Calendar as CalendarIcon } from 'lucide-react'; + +import { Button } from '@ui/components/ui/button'; +import { Calendar } from '@ui/components/ui/calendar'; +import { + Popover, + PopoverContent, + PopoverTrigger +} from '@ui/components/ui/popover'; +import { Separator } from '@ui/components/ui/separator'; +import { Slider } from '@ui/components/ui/slider'; + +import AutoLock from './AutoLock'; +import CustomTooltip from '../CustomTooltip'; + +export function ExtendView() { + const [autoLock, setAutoLock] = useState(false); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [lockDuration, setLockDuration] = useState('180'); + const [lockDate, setLockDate] = useState(() => + addDays(new Date(), 180) + ); + const [selectedDuration, setSelectedDuration] = useState(180); + const [isCalendarOpen, setIsCalendarOpen] = useState(false); + + // Calculate the valid date range for the calendar + const dateRange = useMemo(() => { + const today = new Date(); + return { + minDate: addDays(today, 180), // Minimum 180 days from today + maxDate: addDays(today, 730) // Maximum 730 days from today + }; + }, []); + + const durationLabels = { + 180: '180d', + 365: '1y', + 547: '1.5y', + 730: '2y' + }; + + useEffect(() => { + const newDate = addDays(new Date(), selectedDuration); + setLockDate(newDate); + setLockDuration(selectedDuration.toString()); + }, [selectedDuration]); + + const handleDurationChange = (val: number[]) => { + const duration = val[0]; + setSelectedDuration(duration); + }; + + const handleDateSelect = (date: Date | undefined) => { + if (date) { + setLockDate(date); + // Calculate duration in days + const durationInDays = Math.round( + (date.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24) + ); + + // Clamp the duration between 180 and 730 days + const clampedDuration = Math.max(180, Math.min(730, durationInDays)); + setSelectedDuration(clampedDuration); + setLockDuration(clampedDuration.toString()); + setIsCalendarOpen(false); + } + }; + + return ( +
+
+
+

LOCK UNTIL

+ +
+
+
+ {format(lockDate, 'dd. MM. yyyy')} +
+ + + + + + + + +
+ +
+ {Object.entries(durationLabels).map(([days, label]) => ( + = Number(days) ? 'text-accent' : ''} + > + {label} + + ))} +
+
+ + + + + +
+
+ VOTING POWER + +
+

0.00 veIon

+
+ +
+ LOCKED Until +

28 Aug 2023 → {format(lockDate, 'dd MMM yyyy')}

+
+ + +
+ ); +} diff --git a/packages/ui/app/_components/veion/IncreaseView.tsx b/packages/ui/app/_components/veion/IncreaseView.tsx new file mode 100644 index 000000000..134c0b61c --- /dev/null +++ b/packages/ui/app/_components/veion/IncreaseView.tsx @@ -0,0 +1,89 @@ +import { useState, useEffect } from 'react'; + +import { Button } from '@ui/components/ui/button'; +import { Separator } from '@ui/components/ui/separator'; +import { Slider } from '@ui/components/ui/slider'; +import { getToken } from '@ui/utils/getStakingTokens'; + +import CustomTooltip from '../CustomTooltip'; +import MaxDeposit from '../stake/MaxDeposit'; + +type IncreaseViewProps = { + chain: string; +}; + +export function IncreaseView({ chain }: IncreaseViewProps) { + const utilizationMarks = [0, 25, 50, 75, 100]; + const [veionAmount, setVeIonAmount] = useState(0); + const [sliderValue, setSliderValue] = useState(0); + const maxtoken = '1000'; + + useEffect(() => { + const newSliderValue = (veionAmount / Number(maxtoken)) * 100; + setSliderValue(newSliderValue); + }, [veionAmount, maxtoken]); + + const handleInputChange = (val?: string) => { + if (val !== undefined) { + setVeIonAmount(Number(val)); + } + }; + + const handleSliderChange = (val: number[]) => { + const newVal = val[0]; + setSliderValue(newVal); + const veionval = (newVal / 100) * Number(maxtoken); + setVeIonAmount(veionval); + }; + + return ( +
+ +
+
+ {utilizationMarks.map((mark) => ( + = mark ? 'text-accent' : ''} + > + {mark}% + + ))} +
+ +
+ + +
+
+ VOTING POWER + +
+

0.00 veIon

+
+
+
+ LP +
+

67.90 veIon

+
+ +
+ ); +} diff --git a/packages/ui/app/_components/veion/InfoBlock.tsx b/packages/ui/app/_components/veion/InfoBlock.tsx new file mode 100644 index 000000000..a4af4575b --- /dev/null +++ b/packages/ui/app/_components/veion/InfoBlock.tsx @@ -0,0 +1,32 @@ +import Image from 'next/image'; + +import type { InfoBlock } from '@ui/constants/mock'; + +import CustomTooltip from '../CustomTooltip'; + +interface InfoBlockProps { + block: InfoBlock; +} + +const InfoBlockComponent: React.FC = ({ block }) => ( +
+
+ {block.label} + +
+
+ {block.icon && ( + {`${block.label} + )} + {block.value} +
+
+); + +export default InfoBlockComponent; diff --git a/packages/ui/app/_components/veion/LockDuration.tsx b/packages/ui/app/_components/veion/LockDuration.tsx index b81e730ca..733c15f50 100644 --- a/packages/ui/app/_components/veion/LockDuration.tsx +++ b/packages/ui/app/_components/veion/LockDuration.tsx @@ -1,8 +1,7 @@ 'use client'; import { useState } from 'react'; -import { format } from 'date-fns'; // to format dates -import formatDistanceToNowStrict from 'date-fns/formatDistanceToNowStrict'; +import { format, formatDistanceToNowStrict } from 'date-fns'; interface Iprops { setLockDuration: React.Dispatch>; diff --git a/packages/ui/app/_components/veion/ManagePopup.tsx b/packages/ui/app/_components/veion/ManagePopup.tsx index 7ee99bf3b..8c80890eb 100644 --- a/packages/ui/app/_components/veion/ManagePopup.tsx +++ b/packages/ui/app/_components/veion/ManagePopup.tsx @@ -1,28 +1,26 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { useSearchParams } from 'next/navigation'; +import { format } from 'date-fns'; import { useChainId } from 'wagmi'; -import { Button } from '@ui/components/ui/button'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@ui/components/ui/dialog'; -import { Input } from '@ui/components/ui/input'; -import { Separator } from '@ui/components/ui/separator'; -import { getToken } from '@ui/utils/getStakingTokens'; -import AutoLock from './AutoLock'; -import InfoPopover from './InfoPopover'; -import LockDuration from './LockDuration'; -import SliderComponent from '../popup/Slider'; -import MaxDeposit from '../stake/MaxDeposit'; -import Toggle from '../Toggle'; +import { DelegateView } from './DelegateView'; +import { ExtendView } from './ExtendView'; +import { IncreaseView } from './IncreaseView'; +import { ManageTabs } from './ManageTabs'; +import { MergeView } from './MergeView'; +import { SplitView } from './SplitView'; +import { TransferView } from './TransferView'; interface ManagePopupProps { isOpen: boolean; @@ -41,7 +39,6 @@ export default function ManagePopup({ 'Split', 'Transfer' ]; - const maxtoken = '100'; // This will change in future const chainId = useChainId(); const searchParams = useSearchParams(); @@ -51,37 +48,7 @@ export default function ManagePopup({ toggleArr[0] ); - const [increaseVeionAmount, setIncreaseVeionAmount] = useState(''); - const [extendDuration, setExtendDuration] = useState(''); - const [delegateAddress, setDelegateAddress] = useState(''); - const [splitTokenInto, setSplitTokenInto] = useState(2); - - const [autoLock, setAutoLock] = useState(false); - const [utilization, setUtilization] = useState(0); - const [transferAddress, setTransferAddress] = useState(''); - - const [splitValuesArr, setSplitValuesArr] = useState( - Array(splitTokenInto).fill(0) - ); - - // eslint-disable-next-line no-console - console.log(extendDuration, delegateAddress, transferAddress); - - useEffect(() => { - setUtilization( - Number(((+increaseVeionAmount / Number(maxtoken)) * 100).toFixed(0)) || 0 - ); - }, [increaseVeionAmount]); - - const handleSubmit = () => { - const result = splitValuesArr.map((value, index) => ({ - veionTokenNumber: index + 1, - splitAmount: value - })); - - // eslint-disable-next-line no-console - console.log(result); - }; + const lockedUntil = new Date('2023-08-28'); return ( Voting Power: 20.00 veION - Locked Until: 28 Aug 2023 - - -
- setActiveManageToggle(val)} - arrText={toggleArr} - /> + + Locked Until: {format(lockedUntil, 'dd MMM yyyy')} +
- {activeManageToggle === 'Increase' && ( -
- { - if (val !== undefined) { - setIncreaseVeionAmount(val); - } - }} - chain={+chain} - /> - { - if (!val) return; - const veionval = (Number(val) / 100) * Number(maxtoken); - setIncreaseVeionAmount(veionval.toString()); - }} - /> -
-
- VOTING POWER{' '} - -
-

0.00 veIon

-
-
-
- LP -
-

67.90 veIon

-
- -
- )} - - {activeManageToggle === 'Extend' && ( -
- - - -
-
- VOTING POWER{' '} - -
-

0.00 veIon

-
-
-
- LOCKED Until{' '} - -
-

28 Aug 2023 → 28 Aug 2024

-
- -
- )} - - {activeManageToggle === 'Delegate' && ( -
-

Delegate Address

- setDelegateAddress(e.target.value)} - /> -
- warn - - You may delegate your voting power to any user, without - transferring the tokens. You may revoke it, but the user will - still be able to vote until the end of the current voting - period. - -
- -
- )} + + {activeManageToggle === 'Increase' && } + {activeManageToggle === 'Extend' && } + {activeManageToggle === 'Delegate' && } {activeManageToggle === 'Merge' && ( -
-

veION

-

#10990

-

Merge To

-
- )} - - {activeManageToggle === 'Split' && ( -
-

SPLIT TO

-
- {[2, 3, 4].map((value) => ( - - ))} -
- {Array.from({ length: splitTokenInto }).map((_, index) => ( -
-

{index + 1} veION

- { - if (!val) return; - const updatedSplitValues = [...splitValuesArr]; - updatedSplitValues[index] = val; - setSplitValuesArr(updatedSplitValues); - }} - /> -
- ))} - -
- )} - - {activeManageToggle === 'Transfer' && ( -
-

TRANSFER ADDRESS

- setTransferAddress(e.target.value)} - /> -
- warn - - Once you transfer the tokens, you lose access to them - irrevocably. - -
- -
+ )} + {activeManageToggle === 'Split' && } + {activeManageToggle === 'Transfer' && }
); diff --git a/packages/ui/app/_components/veion/ManageTabs.tsx b/packages/ui/app/_components/veion/ManageTabs.tsx new file mode 100644 index 000000000..4b1d6cab9 --- /dev/null +++ b/packages/ui/app/_components/veion/ManageTabs.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; + +import { Tabs, TabsList, TabsTrigger } from '@ui/components/ui/tabs'; + +interface ManageTabsProps { + arrText: string[]; + setActiveToggle: (value: string) => void; + defaultValue?: string; +} + +export function ManageTabs({ + arrText, + setActiveToggle, + defaultValue = 'Increase' +}: ManageTabsProps) { + return ( + + + {arrText.map((text) => ( + + {text} + + ))} + + + ); +} diff --git a/packages/ui/app/_components/veion/MergeView.tsx b/packages/ui/app/_components/veion/MergeView.tsx new file mode 100644 index 000000000..2c4a0acc1 --- /dev/null +++ b/packages/ui/app/_components/veion/MergeView.tsx @@ -0,0 +1,48 @@ +import { useState } from 'react'; + +import { format } from 'date-fns'; +import { isAddress } from 'viem'; + +import { Button } from '@ui/components/ui/button'; +import { Input } from '@ui/components/ui/input'; +import { Separator } from '@ui/components/ui/separator'; + +import CustomTooltip from '../CustomTooltip'; + +export function MergeView({ lockedUntil }: { lockedUntil: Date }) { + const [delegateAddress, setDelegateAddress] = useState(''); + const isValidAddress = delegateAddress ? isAddress(delegateAddress) : false; + + return ( +
+

veION

+

#10990

+

Merge To

+ setDelegateAddress(e.target.value)} + className={!isValidAddress && delegateAddress ? 'border-red-500' : ''} + /> + + +
+
+ VOTING POWER + +
+

0.00 veIon

+
+ +
+ LOCKED Until +

{format(lockedUntil, 'dd MMM yyyy')}

+
+ +
+ ); +} diff --git a/packages/ui/app/_components/veion/SplitView.tsx b/packages/ui/app/_components/veion/SplitView.tsx new file mode 100644 index 000000000..8028d7695 --- /dev/null +++ b/packages/ui/app/_components/veion/SplitView.tsx @@ -0,0 +1,98 @@ +import { useState } from 'react'; + +import { InfoIcon } from 'lucide-react'; + +import { Button } from '@ui/components/ui/button'; +import { Separator } from '@ui/components/ui/separator'; +import { Slider } from '@ui/components/ui/slider'; + +export function SplitView() { + const [splitValues, setSplitValues] = useState<[number, number]>([50, 50]); + const maxAmount = 1000; // Default max amount + const utilizationMarks = [0, 25, 50, 75, 100]; + + // Update both values from either slider + const handleFirstSliderChange = (newValue: number) => { + setSplitValues([newValue, 100 - newValue]); + }; + + const handleSecondSliderChange = (newValue: number) => { + setSplitValues([100 - newValue, newValue]); + }; + + // Calculate actual token amounts based on percentages + const firstAmount = (splitValues[0] / 100) * maxAmount; + const secondAmount = (splitValues[1] / 100) * maxAmount; + + return ( +
+ + +
+
+

+ First Split: {splitValues[0]}% +

+
+ {utilizationMarks.map((mark) => ( + = mark ? 'text-accent' : ''} + > + {mark}% + + ))} +
+ handleFirstSliderChange(val[0])} + max={100} + step={1} + className="[&_[role=slider]]:bg-accent [&_[role=slider]]:border-0" + /> +

+ Amount: {firstAmount.toLocaleString()} veION +

+
+ + + +
+

+ Second Split: {splitValues[1]}% +

+
+ {utilizationMarks.map((mark) => ( + = mark ? 'text-accent' : ''} + > + {mark}% + + ))} +
+ handleSecondSliderChange(val[0])} + max={100} + step={1} + className="[&_[role=slider]]:bg-accent [&_[role=slider]]:border-0" + /> +

+ Amount: {secondAmount.toLocaleString()} veION +

+
+
+ +
+ + + Splitting will cause a loss of unclaimed and pending rewards. Make + sure to claim everything before you split! + +
+ + +
+ ); +} diff --git a/packages/ui/app/_components/veion/TransferView.tsx b/packages/ui/app/_components/veion/TransferView.tsx new file mode 100644 index 000000000..d3918badb --- /dev/null +++ b/packages/ui/app/_components/veion/TransferView.tsx @@ -0,0 +1,36 @@ +import { useState } from 'react'; + +import { InfoIcon } from 'lucide-react'; +import { isAddress } from 'viem'; + +import { Button } from '@ui/components/ui/button'; +import { Input } from '@ui/components/ui/input'; + +// TransferView.tsx +export function TransferView() { + const [transferAddress, setTransferAddress] = useState(''); + const isValidAddress = transferAddress ? isAddress(transferAddress) : false; + + return ( +
+

TRANSFER ADDRESS

+ setTransferAddress(e.target.value)} + className={!isValidAddress && transferAddress ? 'border-red-500' : ''} + /> +
+ + + Once you transfer the tokens, you lose access to them irrevocably. + +
+ +
+ ); +} diff --git a/packages/ui/app/_components/veion/index.tsx b/packages/ui/app/_components/veion/index.tsx index f7a947d18..6a82dfe7a 100644 --- a/packages/ui/app/_components/veion/index.tsx +++ b/packages/ui/app/_components/veion/index.tsx @@ -16,3 +16,5 @@ export { default as MarketSelector } from './MarketSelector'; export { default as MigrateIonDialog } from './MigrateIonDialog'; export { default as TimeRemaining } from './TimeRemaining'; export { default as VoteInput } from './VoteInput'; +export { default as InfoBlock } from './InfoBlock'; +export { default as EmissionsManagementTable } from './EmissionsManagementTable'; diff --git a/packages/ui/app/layout.tsx b/packages/ui/app/layout.tsx index 894ea7d29..bf692ed72 100644 --- a/packages/ui/app/layout.tsx +++ b/packages/ui/app/layout.tsx @@ -8,21 +8,14 @@ import Link from 'next/link'; import Script from 'next/script'; import { createAppKit } from '@reown/appkit'; -import { - base, - optimism, - mode, - bob, - fraxtal, - lisk -} from '@reown/appkit/networks'; -import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'; +import { mode, bob, fraxtal } from '@reown/appkit/networks'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { AppProgressBar as ProgressBar } from 'next-nprogress-bar'; import { Toaster } from 'react-hot-toast'; import { WagmiProvider } from 'wagmi'; import { MultiIonicProvider } from '@ui/context/MultiIonicContext'; +import { networks, projectId, wagmiAdapter } from '@ui/utils/NetworkChecker'; import Navbar from './_components/Navbar'; import './globals.css'; @@ -34,16 +27,6 @@ const metadata = { url: 'https://app.ionic.money' }; -export const networks = [base, mode, optimism, bob, fraxtal, lisk]; - -export const projectId = '923645e96d6f05f650d266a32ea7295f'; - -export const wagmiAdapter = new WagmiAdapter({ - networks, - projectId, - ssr: true -}); - // Create the new web3 modal createAppKit({ projectId, diff --git a/packages/ui/app/veion/governance/page.tsx b/packages/ui/app/veion/governance/page.tsx index aca889bb2..575d5916d 100644 --- a/packages/ui/app/veion/governance/page.tsx +++ b/packages/ui/app/veion/governance/page.tsx @@ -8,8 +8,7 @@ import CustomTooltip from '@ui/app/_components/CustomTooltip'; import NetworkSelector from '@ui/app/_components/markets/NetworkSelector'; import FlatMap from '@ui/app/_components/points_comp/FlatMap'; import ToggleLinks from '@ui/app/_components/ToggleLink'; -import { MyVeionTable } from '@ui/app/_components/veion'; -import DelegateVeIonTable from '@ui/app/_components/veion/DelegateVeIonTable'; +import { MyVeionTable, DelegateVeIonTable } from '@ui/app/_components/veion'; import { Card, CardHeader, diff --git a/packages/ui/app/veion/governance/vote/page.tsx b/packages/ui/app/veion/governance/vote/page.tsx index 31f6a5f5c..4aad9b267 100644 --- a/packages/ui/app/veion/governance/vote/page.tsx +++ b/packages/ui/app/veion/governance/vote/page.tsx @@ -1,15 +1,14 @@ 'use client'; -import { useState } from 'react'; +import React, { useState } from 'react'; import { useSearchParams } from 'next/navigation'; import { useChainId } from 'wagmi'; -import CustomTooltip from '@ui/app/_components/CustomTooltip'; import NetworkSelector from '@ui/app/_components/markets/NetworkSelector'; import FlatMap from '@ui/app/_components/points_comp/FlatMap'; -import EmissionsManagementTable from '@ui/app/_components/veion/EmissionsManagementTable'; +import { InfoBlock, EmissionsManagementTable } from '@ui/app/_components/veion'; import { Card, CardHeader, @@ -17,36 +16,15 @@ import { CardContent } from '@ui/components/ui/card'; import { Switch } from '@ui/components/ui/switch'; -import { votingData } from '@ui/constants/mock'; +import { infoBlocks, votingData } from '@ui/constants/mock'; -export default function Vote() { - const [showPendingOnly, setShowPendingOnly] = useState(false); +const Vote: React.FC = () => { + const [showPendingOnly, setShowPendingOnly] = useState(false); const searchParams = useSearchParams(); const chainId = useChainId(); const querychain = searchParams.get('chain'); const chain = querychain ?? String(chainId); - const infoBlocks = [ - { - label: 'Locked Value', - value: '$7894', - infoContent: 'This is the amount of ION you have locked.', - icon: null - }, - { - label: 'Locked Until', - value: '11 Jan 2026', - infoContent: 'This is the date until your ION is locked.', - icon: null - }, - { - label: 'My Voting Power', - value: '5674 veION', - infoContent: 'This is your current voting power.', - icon: '/img/symbols/32/color/ion.png' - } - ]; - return (
@@ -54,27 +32,12 @@ export default function Vote() { Vote -
+
{infoBlocks.map((block) => ( -
-
- {block.label} - -
-
- {block.icon && ( - icon - )} - {block.value} -
-
+ block={block} + /> ))}
@@ -89,9 +52,9 @@ export default function Vote() { className="w-full" style={{ backgroundColor: '#212126ff' }} > - + Emissions Management -
+
@@ -115,4 +79,6 @@ export default function Vote() {
); -} +}; + +export default Vote; diff --git a/packages/ui/components/ui/button.tsx b/packages/ui/components/ui/button.tsx index eeb8151ee..7b59cd0cc 100644 --- a/packages/ui/components/ui/button.tsx +++ b/packages/ui/components/ui/button.tsx @@ -6,7 +6,7 @@ import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@ui/lib/utils'; const buttonVariants = cva( - 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', + 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', { variants: { variant: { @@ -22,8 +22,8 @@ const buttonVariants = cva( }, size: { default: 'h-10 px-4 py-2', - sm: 'h-9 rounded-md px-3', - lg: 'h-11 rounded-md px-8', + sm: 'h-9 rounded-xl px-3', + lg: 'h-11 rounded-xl px-8', icon: 'h-10 w-10' } }, diff --git a/packages/ui/components/ui/calendar.tsx b/packages/ui/components/ui/calendar.tsx index be726aac5..a09d62c5c 100644 --- a/packages/ui/components/ui/calendar.tsx +++ b/packages/ui/components/ui/calendar.tsx @@ -37,20 +37,16 @@ function Calendar({ head_cell: 'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]', row: 'flex w-full mt-2', - cell: 'h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20', + cell: 'h-9 w-9 text-center text-sm p-0 relative', day: cn( buttonVariants({ variant: 'ghost' }), 'h-9 w-9 p-0 font-normal aria-selected:opacity-100' ), - day_range_end: 'day-range-end', day_selected: - 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground', - day_today: 'bg-accent text-accent-foreground', - day_outside: - 'day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30', + 'bg-accent text-black hover:bg-accent hover:text-black focus:bg-accent focus:text-black', + day_today: 'bg-accent/20 text-accent', + day_outside: 'text-muted-foreground opacity-50', day_disabled: 'text-muted-foreground opacity-50', - day_range_middle: - 'aria-selected:bg-accent aria-selected:text-accent-foreground', day_hidden: 'invisible', ...classNames }} diff --git a/packages/ui/components/ui/dialog.tsx b/packages/ui/components/ui/dialog.tsx index d5e184eaf..708907edd 100644 --- a/packages/ui/components/ui/dialog.tsx +++ b/packages/ui/components/ui/dialog.tsx @@ -1,5 +1,4 @@ -'use client'; - +// Dialog.tsx import * as React from 'react'; import * as DialogPrimitive from '@radix-ui/react-dialog'; @@ -19,7 +18,7 @@ const DialogOverlay = React.forwardRef< { hideCloseButton?: boolean; @@ -42,7 +40,7 @@ const DialogContent = React.forwardRef< - + Close )} @@ -93,10 +91,7 @@ const DialogTitle = React.forwardRef< >(({ className, ...props }, ref) => ( )); diff --git a/packages/ui/components/ui/tabs.tsx b/packages/ui/components/ui/tabs.tsx new file mode 100644 index 000000000..97a291a48 --- /dev/null +++ b/packages/ui/components/ui/tabs.tsx @@ -0,0 +1,56 @@ +'use client'; + +import * as React from 'react'; + +import * as TabsPrimitive from '@radix-ui/react-tabs'; + +import { cn } from '@ui/lib/utils'; + +const Tabs = TabsPrimitive.Root; + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsList.displayName = TabsPrimitive.List.displayName; + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsContent.displayName = TabsPrimitive.Content.displayName; + +export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/packages/ui/constants/mock.ts b/packages/ui/constants/mock.ts index 6cbafc4e8..1e0188f5f 100644 --- a/packages/ui/constants/mock.ts +++ b/packages/ui/constants/mock.ts @@ -230,3 +230,31 @@ export const votingData: VotingData[] = [ } } ]; + +export interface InfoBlock { + label: string; + value: string; + infoContent: string; + icon: string | null; +} + +export const infoBlocks: InfoBlock[] = [ + { + label: 'Locked Value', + value: '$7894', + infoContent: 'This is the amount of ION you have locked.', + icon: null + }, + { + label: 'Locked Until', + value: '11 Jan 2026', + infoContent: 'This is the date until your ION is locked.', + icon: null + }, + { + label: 'My Voting Power', + value: '5674 veION', + infoContent: 'This is your current voting power.', + icon: '/img/symbols/32/color/ion.png' + } +]; diff --git a/packages/ui/package.json b/packages/ui/package.json index e5ce2863a..7b054e47d 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -26,6 +26,7 @@ "@radix-ui/react-slider": "^1.2.1", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.1", + "@radix-ui/react-tabs": "^1.1.1", "@radix-ui/react-tooltip": "^1.1.3", "@reown/appkit": "^1.1.7", "@reown/appkit-adapter-wagmi": "^1.1.7", diff --git a/packages/ui/utils/NetworkChecker.ts b/packages/ui/utils/NetworkChecker.ts index 38abcdae0..f32e6f686 100644 --- a/packages/ui/utils/NetworkChecker.ts +++ b/packages/ui/utils/NetworkChecker.ts @@ -1,6 +1,22 @@ +import { + base, + optimism, + mode, + bob, + fraxtal, + lisk +} from '@reown/appkit/networks'; +import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'; import { switchChain } from '@wagmi/core'; -import { wagmiAdapter } from '@ui/app/layout'; +export const projectId = '923645e96d6f05f650d266a32ea7295f'; +export const networks = [base, mode, optimism, bob, fraxtal, lisk]; + +export const wagmiAdapter = new WagmiAdapter({ + networks, + projectId, + ssr: true +}); export const handleSwitchOriginChain = async ( selectedDropdownChain: number, diff --git a/yarn.lock b/yarn.lock index 752c2a14d..90f046ed9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2221,6 +2221,7 @@ __metadata: "@radix-ui/react-slider": "npm:^1.2.1" "@radix-ui/react-slot": "npm:^1.1.0" "@radix-ui/react-switch": "npm:^1.1.1" + "@radix-ui/react-tabs": "npm:^1.1.1" "@radix-ui/react-tooltip": "npm:^1.1.3" "@reown/appkit": "npm:^1.1.7" "@reown/appkit-adapter-wagmi": "npm:^1.1.7" @@ -5926,6 +5927,32 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-tabs@npm:^1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-tabs@npm:1.1.1" + dependencies: + "@radix-ui/primitive": "npm:1.1.0" + "@radix-ui/react-context": "npm:1.1.1" + "@radix-ui/react-direction": "npm:1.1.0" + "@radix-ui/react-id": "npm:1.1.0" + "@radix-ui/react-presence": "npm:1.1.1" + "@radix-ui/react-primitive": "npm:2.0.0" + "@radix-ui/react-roving-focus": "npm:1.1.0" + "@radix-ui/react-use-controllable-state": "npm:1.1.0" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10/9ec6f6749360c5d77a6ab785995f4e7851ac36debcbcf6aef787aedf565d4e3f19bfd3122b43d71e086cdfe7745ca5d683a79b1744af4787ac0830fdf390d421 + languageName: node + linkType: hard + "@radix-ui/react-tooltip@npm:^1.0.7": version: 1.1.2 resolution: "@radix-ui/react-tooltip@npm:1.1.2"