diff --git a/public/locales/en/common.json b/public/locales/en/common.json index a6d7e0c..f7e7e2f 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -71,6 +71,9 @@ }, "backButton": "Go back" }, + "TOKENS": { + "title": "Tokens" + }, "FOOTER": { "docs": "Documentation", "github": "GitHub", diff --git a/public/locales/es/common.json b/public/locales/es/common.json index 7522d93..9041076 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -72,6 +72,9 @@ }, "backButton": "Volver" }, + "TOKENS": { + "title": "Tokens" + }, "FOOTER": { "docs": "Documentación", "github": "GitHub", diff --git a/src/components/Table.tsx b/src/components/ChainTable.tsx similarity index 98% rename from src/components/Table.tsx rename to src/components/ChainTable.tsx index ba59ada..c99fbfa 100644 --- a/src/components/Table.tsx +++ b/src/components/ChainTable.tsx @@ -21,7 +21,7 @@ interface TableProps { chains: EcosystemChainData[]; } -export const DataTable = ({ chains }: TableProps) => { +export const ChainTable = ({ chains }: TableProps) => { const { t } = useTranslation(); const router = useRouter(); diff --git a/src/components/TVL.tsx b/src/components/TVL.tsx index 6f1e8c6..235a705 100644 --- a/src/components/TVL.tsx +++ b/src/components/TVL.tsx @@ -30,6 +30,7 @@ export const TVL = () => { {t('CHAIN.TVL.tvl')} + {tvl.map((token, index) => ( @@ -39,6 +40,7 @@ export const TVL = () => { {token.name} ({token.symbol}) + ${token.price.toLocaleString()} ${((token.amountUsd * token.price) / 1e18).toLocaleString()} diff --git a/src/components/TotalValueLocked.tsx b/src/components/TotalValueLocked.tsx index 4d8a63d..33a3d69 100644 --- a/src/components/TotalValueLocked.tsx +++ b/src/components/TotalValueLocked.tsx @@ -1,13 +1,10 @@ import { Box, Typography, Grid, styled, useMediaQuery, useTheme } from '@mui/material'; import { useTranslation } from 'next-i18next'; -import { TvlData } from '~/types'; + +import { TvlData, TotalValueLockedProps } from '~/types'; import { TvlContentBox } from '~/components'; import { useCustomTheme } from '~/hooks'; -interface TotalValueLockedProps { - tvl: TvlData[]; -} - export const TotalValueLocked = ({ tvl }: TotalValueLockedProps) => { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('sm')); diff --git a/src/components/TvlContentBox.tsx b/src/components/TvlContentBox.tsx index db48d30..b6aefac 100644 --- a/src/components/TvlContentBox.tsx +++ b/src/components/TvlContentBox.tsx @@ -19,6 +19,7 @@ export const TvlContentBox = ({ avatar, token, total, tokenName, isLast }: TvlCo + {tokenName} {token} diff --git a/src/components/index.ts b/src/components/index.ts index 77249e1..b995248 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,6 +1,6 @@ export * from './Theme'; export * from './CustomHead'; -export * from './Table'; +export * from './ChainTable'; export * from './SearchBar'; export * from './TotalValueLocked'; export * from './Title'; diff --git a/src/containers/Dashboard/index.tsx b/src/containers/Dashboard/index.tsx index fd0415f..9fd0f7e 100644 --- a/src/containers/Dashboard/index.tsx +++ b/src/containers/Dashboard/index.tsx @@ -1,7 +1,7 @@ import { useTranslation } from 'next-i18next'; import { Typography, styled } from '@mui/material'; -import { DataTable } from '~/components'; +import { ChainTable } from '~/components'; import { useData, useSearchContext, useCustomTheme } from '~/hooks'; export const Dashboard = () => { @@ -38,7 +38,7 @@ export const Dashboard = () => { {enterSearchTerm && {t('HOME.DASHBOARD.enterSearchTerm')}} - {availableChains && } + {availableChains && } {!availableChains && {t('HOME.DASHBOARD.notFound')}} ); diff --git a/src/containers/Header/MobileHeader.tsx b/src/containers/Header/MobileHeader.tsx index 6e8208c..845ed6f 100644 --- a/src/containers/Header/MobileHeader.tsx +++ b/src/containers/Header/MobileHeader.tsx @@ -39,6 +39,7 @@ export const MobileHeader = ({ theme, goToHome, handleChangeLanguage, localesMap + {theme === 'dark' ? ( @@ -47,10 +48,12 @@ export const MobileHeader = ({ theme, goToHome, handleChangeLanguage, localesMap search-icon )} + {theme === 'dark' ? menu-icon : menu-icon} + + + + {theme === 'dark' ? t('HEADER.lightMode') : t('HEADER.darkMode')} diff --git a/src/containers/LockedAssets/index.tsx b/src/containers/LockedAssets/index.tsx index 8459a2f..d5ad6c4 100644 --- a/src/containers/LockedAssets/index.tsx +++ b/src/containers/LockedAssets/index.tsx @@ -1,5 +1,6 @@ import { useTranslation } from 'next-i18next'; -import { Box, Typography, styled } from '@mui/material'; +import { useRouter } from 'next/router'; +import { Box, Typography, styled, Button } from '@mui/material'; import { TotalValueLocked } from '~/components'; import { useData, useCustomTheme } from '~/hooks'; @@ -8,6 +9,11 @@ import { formatDataNumber } from '~/utils'; export const LockedAssets = () => { const { t } = useTranslation(); const { ecosystemData, totalL1TVL } = useData(); + const router = useRouter(); + + const goToTokensPage = () => { + router.push('/tokens'); + }; return ( @@ -18,9 +24,14 @@ export const LockedAssets = () => { {t('HOME.LOCKEDASSETS.lockedAssets')} {t('HOME.LOCKEDASSETS.lockedAssetsDescription')} + {formatDataNumber(totalL1TVL, 0, true, true)} + + + {t('HOME.LOCKEDASSETS.allTokens')} {' '} + )} @@ -58,3 +69,26 @@ const TitleAmount = styled(Typography)(() => ({ fontWeight: 700, lineHeight: '2.5rem', })); + +const AllTokensButton = styled(Button)(() => { + const { currentTheme } = useCustomTheme(); + return { + color: currentTheme.textPrimary, + backgroundColor: currentTheme.backgroundSecondary, + borderRadius: currentTheme.borderRadius, + padding: '0.5rem 1rem', + gap: currentTheme.gap, + width: '5.5rem', + textTransform: 'none', + fontSize: '0.75rem', + marginTop: '1.5rem', + alignItems: 'center', + }; +}); + +export const ButtonContainer = styled(Box)(() => { + return { + display: 'flex', + justifyContent: 'center', + }; +}); diff --git a/src/containers/Tokens/SkeletonTokens.tsx b/src/containers/Tokens/SkeletonTokens.tsx new file mode 100644 index 0000000..7f10428 --- /dev/null +++ b/src/containers/Tokens/SkeletonTokens.tsx @@ -0,0 +1,17 @@ +import { Box } from '@mui/material'; +import { Skeleton } from '@mui/material'; + +export const SkeletonTokens = () => { + return ( + + {/* Breadcrumb Skeleton */} + + + {/* Title Skeleton */} + + + {/* Main Container Skeleton for Locked Assets */} + + + ); +}; diff --git a/src/containers/Tokens/TokensTable.tsx b/src/containers/Tokens/TokensTable.tsx new file mode 100644 index 0000000..d9348e5 --- /dev/null +++ b/src/containers/Tokens/TokensTable.tsx @@ -0,0 +1,53 @@ +import { useTranslation } from 'next-i18next'; +import { Table, Typography } from '@mui/material'; + +import { TotalValueLockedProps } from '~/types'; +import { + STableContainer, + STableHead, + STableRow, + STableCellHead, + STableCell, + STableBody, + LogoCell, + TokenAvatar, + STitle, +} from '~/components'; + +export const TokensTable = ({ tvl }: TotalValueLockedProps) => { + const { t } = useTranslation(); + + return ( +
+ {t('TOKENS.title')} + + + + + {t('CHAIN.TVL.chain')} + {t('CHAIN.TVL.price')} + {t('CHAIN.TVL.tvl')} + + + + + {tvl.map((token, index) => ( + + + + + {token.tokenName} ({token.token}) + + + + ${token.price.toLocaleString()} + + ${((token.total * token.price) / 1e18).toLocaleString()} + + ))} + +
+
+
+ ); +}; diff --git a/src/containers/Tokens/index.tsx b/src/containers/Tokens/index.tsx new file mode 100644 index 0000000..e3bf26e --- /dev/null +++ b/src/containers/Tokens/index.tsx @@ -0,0 +1,35 @@ +import { styled } from '@mui/material'; + +import { useData } from '~/hooks'; +import { Breadcrumb } from '~/components'; +import { TokensTable } from './TokensTable'; +import { SkeletonTokens } from './SkeletonTokens'; + +export const Tokens = () => { + const { ecosystemData, isEcosystemLoading } = useData(); + const tvl = ecosystemData?.l1Tvl || []; + return ( + + {isEcosystemLoading && } + {!isEcosystemLoading && ( + <> + + + + )} + + ); +}; + +const TokensContainer = styled('main')(({ theme }) => ({ + padding: '0 7rem', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + gap: '4rem', + marginTop: '4rem', + marginBottom: '4rem', + [theme.breakpoints.down('sm')]: { + padding: '0 1rem', + }, +})); diff --git a/src/containers/index.ts b/src/containers/index.ts index 2d86631..bf93c22 100644 --- a/src/containers/index.ts +++ b/src/containers/index.ts @@ -7,3 +7,4 @@ export * from './LockedAssets'; export * from './ChainDetail'; export * from './ChainDetail/ChainDescription'; export * from './ChainDetail/ChainMetadata'; +export * from './Tokens'; diff --git a/src/data/ecosystemMockData.json b/src/data/ecosystemMockData.json index db23a4c..ef8d37f 100644 --- a/src/data/ecosystemMockData.json +++ b/src/data/ecosystemMockData.json @@ -4,48 +4,56 @@ "token": "ETH", "tokenName": "Ethereum", "total": 557596566000, + "price": 300, "imageUrl": "https://dynamic-assets.coinbase.com/dbb4b4983bde81309ddab83eb598358eb44375b930b94687ebe38bc22e52c3b2125258ffb8477a5ef22e33d6bd72e32a506c391caa13af64c00e46613c3e5806/asset_icons/4113b082d21cc5fab17fc8f2d19fb996165bcce635e6900f7fc2d57c4ef33ae9.png" }, { "token": "USDT", "tokenName": "Tether USD", "total": 114493849618, + "price": 300, "imageUrl": "https://www.cryptomkt.com/static/landing/img/crypto-pages/trending/usdt.svg" }, { "token": "USDC", "tokenName": "Bridged USD", "total": 34115209093, + "price": 300, "imageUrl": "https://assets.coingecko.com/coins/images/6319/standard/usdc.png?1696506694" }, { "token": "KOI", "tokenName": "Koi Finance", "total": 24115209093, + "price": 300, "imageUrl": "https://assets.coingecko.com/coins/images/35766/standard/Koi_logo.png?1709782399" }, { "token": "WBTC", "tokenName": "Wrapped BTC", "total": 12620248, + "price": 300, "imageUrl": "https://assets.coingecko.com/coins/images/7598/standard/wrapped_bitcoin_wbtc.png?1696507857" }, { "token": "wstETH", "tokenName": "Lido wstETH", "total": 3552439, + "price": 300, "imageUrl": "https://assets.coingecko.com/coins/images/18834/standard/wstETH.png?1696518295" }, { "token": "cbETH", "tokenName": "Curve cbETH", "total": 2552439, + "price": 300, "imageUrl": "https://assets.coingecko.com/coins/images/27008/standard/cbeth.png?1709186989" }, { "token": "BAL", "tokenName": "Balancer", "total": 1552439, + "price": 300, "imageUrl": "https://coin-images.coingecko.com/coins/images/671/large/balancer.png" } ], diff --git a/src/pages/tokens/index.tsx b/src/pages/tokens/index.tsx new file mode 100644 index 0000000..2f29843 --- /dev/null +++ b/src/pages/tokens/index.tsx @@ -0,0 +1,32 @@ +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +import { GetStaticProps } from 'next'; +import { useTranslation } from 'next-i18next'; + +import { CustomHead } from '~/components'; +import { Tokens } from '~/containers'; +import { getConfig } from '~/config'; + +const { DEFAULT_LANG, SUPPORTED_LANGUAGES } = getConfig(); + +const TokensPage = () => { + const { t } = useTranslation(); + + return ( + <> + + + + ); +}; + +export const getStaticProps: GetStaticProps = async ({ locale }) => { + const i18Config = await serverSideTranslations(locale || DEFAULT_LANG, ['common'], null, SUPPORTED_LANGUAGES); + + return { + props: { + ...i18Config, + }, + }; +}; + +export default TokensPage; diff --git a/src/types/Data.ts b/src/types/Data.ts index aa67be7..22aff46 100644 --- a/src/types/Data.ts +++ b/src/types/Data.ts @@ -62,6 +62,7 @@ export interface TvlData { symbol: string; name: string; amountUsd: number; + price: number; imageUrl: string; } @@ -72,3 +73,6 @@ export interface ChainTvl { imageUrl: string; price: number; } +export interface TotalValueLockedProps { + tvl: TvlData[]; +}