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.
+
+
+
+ Delegate veION
+
+
+ );
+}
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 (
+
+
+
+
+
+ {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')}
+
+
+
Extend Lock
+
+ );
+}
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
+
+
+
+ Increase Locked Amount
+
+
+ );
+}
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.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
-
-
-
- Increase Locked Amount
-
-
- )}
-
- {activeManageToggle === 'Extend' && (
-
-
-
-
-
-
- VOTING POWER{' '}
-
-
-
0.00 veIon
-
-
-
- LOCKED Until{' '}
-
-
-
28 Aug 2023 → 28 Aug 2024
-
-
- Extend Lock
-
-
- )}
-
- {activeManageToggle === 'Delegate' && (
-
-
Delegate Address
-
setDelegateAddress(e.target.value)}
- />
-
-
-
- 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.
-
-
-
- Delegate veION
-
-
- )}
+
+ {activeManageToggle === 'Increase' && }
+ {activeManageToggle === 'Extend' && }
+ {activeManageToggle === 'Delegate' && }
{activeManageToggle === 'Merge' && (
-
-
veION
-
#10990
-
Merge To
-
- )}
-
- {activeManageToggle === 'Split' && (
-
-
SPLIT TO
-
- {[2, 3, 4].map((value) => (
- setSplitTokenInto(value)}
- >
- {value} tokens
-
- ))}
-
- {Array.from({ length: splitTokenInto }).map((_, index) => (
-
-
{index + 1} veION
-
{
- if (!val) return;
- const updatedSplitValues = [...splitValuesArr];
- updatedSplitValues[index] = val;
- setSplitValuesArr(updatedSplitValues);
- }}
- />
-
- ))}
-
- Split
-
-
- )}
-
- {activeManageToggle === 'Transfer' && (
-
-
TRANSFER ADDRESS
-
setTransferAddress(e.target.value)}
- />
-
-
-
- Once you transfer the tokens, you lose access to them
- irrevocably.
-
-
-
- Transfer veION
-
-
+
)}
+ {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')}
+
+
+ Merge
+
+
+ );
+}
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!
+
+
+
+
Split veION
+
+ );
+}
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.
+
+
+
+ Transfer veION
+
+
+ );
+}
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 && (
-
- )}
-
{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"