Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8cfc1c4
feat: added filters for categories in market section v0
AGMASO Aug 20, 2025
a67bf56
feat: version 1.0.0 filter for markets finished
AGMASO Aug 21, 2025
bd4769c
feat: version 1.0.0 filter for markets finished
AGMASO Aug 22, 2025
737c9f7
fix: fix hover effect
AGMASO Aug 22, 2025
09067bb
feat: added dynamic filters using Coingecko api
AGMASO Aug 25, 2025
8d3f15a
feat: add dynamic assets categories using coingecko api & fix mobile …
AGMASO Aug 25, 2025
1a217d2
fix: standardize mobile layout padding for consistent spacing
AGMASO Aug 25, 2025
35216e8
fix: include env variable
AGMASO Aug 25, 2025
35cef78
feat: implement Figma design for asset category filter buttons
AGMASO Aug 26, 2025
5891f19
chore(i18n): regenerate catalogs after merge
AGMASO Aug 26, 2025
48ec234
feat: added category filters to supply and borrow inside dashboard
AGMASO Aug 26, 2025
2bd45f4
refactor: unify CoinGecko category hooks and connect to proxy backend
AGMASO Aug 27, 2025
a03613f
fix: resolve locale message merge conflicts
AGMASO Aug 27, 2025
1b241b9
Merge branch 'feat/add-filters' into feat/add-dashboard-filters
AGMASO Aug 27, 2025
c842f01
refactor: create useAssetCategoryFilters hook to reduce code duplication
AGMASO Aug 27, 2025
f03827a
Merge branch 'feat/add-filters' into feat/add-dashboard-filters
AGMASO Aug 27, 2025
73647e4
refactor: integrate asset category filters in dashboard components wi…
AGMASO Aug 27, 2025
4728fb0
chore: fetch of categories in parallel & stale for a week
AGMASO Sep 1, 2025
ed6062a
Merge remote-tracking branch 'origin/main' into feat/add-filters
AGMASO Sep 1, 2025
cd946aa
fix: simplified code deleting fallback for categories
AGMASO Sep 8, 2025
4f4af9f
Merge remote-tracking branch 'origin/main' into feat/add-filters
AGMASO Sep 8, 2025
f2a81ab
Merge remote-tracking branch 'origin/main' into feat/add-filters
AGMASO Sep 9, 2025
9a207a4
Merge remote-tracking branch 'origin/main' into feat/add-filters
AGMASO Sep 10, 2025
6232cc0
feat: merge dashboard asset category filters
AGMASO Sep 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/components/MarketAssetCategoryFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Trans } from '@lingui/macro';
import { Box, Button, ButtonGroup } from '@mui/material';
import { AssetCategory } from 'src/modules/markets/utils/assetCategories';

interface MarketAssetCategoryFiltersProps {
selectedCategory: AssetCategory;
onCategoryChange: (category: AssetCategory) => void;
}

export const MarketAssetCategoryFilter = ({
selectedCategory,
onCategoryChange,
}: MarketAssetCategoryFiltersProps) => {
return (
<Box sx={{ mt: 2, mb: 2 }}>
<ButtonGroup
sx={{
'& .MuiButton-root': {
textTransform: 'none',
whiteSpace: 'nowrap',
minWidth: 'auto',
p: 2,
fontSize: '0.875rem',
},
}}
variant="outlined"
size="small"
>
<Button
variant={selectedCategory === AssetCategory.ALL ? 'contained' : 'outlined'}
onClick={() => onCategoryChange(AssetCategory.ALL)}
>
<Trans>All</Trans>
</Button>
<Button
variant={selectedCategory === AssetCategory.STABLECOINS ? 'contained' : 'outlined'}
onClick={() => onCategoryChange(AssetCategory.STABLECOINS)}
>
<Trans>Stablecoins</Trans>
</Button>
<Button
variant={selectedCategory === AssetCategory.ETH_CORRELATED ? 'contained' : 'outlined'}
onClick={() => onCategoryChange(AssetCategory.ETH_CORRELATED)}
>
<Trans>ETH Correlated</Trans>
</Button>
</ButtonGroup>
</Box>
);
};
110 changes: 110 additions & 0 deletions src/components/TitleWithFiltersAndSearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { SearchIcon } from '@heroicons/react/solid';
import { Trans } from '@lingui/macro';
import {
Box,
Button,
IconButton,
SvgIcon,
Typography,
TypographyProps,
useMediaQuery,
useTheme,
} from '@mui/material';
import { ReactNode, useState } from 'react';
import { AssetCategory } from 'src/modules/markets/utils/assetCategories';

import { MarketAssetCategoryFilter } from './MarketAssetCategoryFilter';
import { SearchInput } from './SearchInput';

interface TitleWithFiltersAndSearchBarProps<C extends React.ElementType> {
onSearchTermChange: (value: string) => void;
searchPlaceholder: string;
titleProps?: TypographyProps<C, { component?: C }>;
title: ReactNode;
selectedCategory: AssetCategory;
onCategoryChange: (category: AssetCategory) => void;
}

export const TitleWithFiltersAndSearchBar = <T extends React.ElementType>({
onSearchTermChange,
searchPlaceholder,
titleProps,
title,
selectedCategory,
onCategoryChange,
}: TitleWithFiltersAndSearchBarProps<T>) => {
const [showSearchBar, setShowSearchBar] = useState(false);

const { breakpoints } = useTheme();
const sm = useMediaQuery(breakpoints.down('sm'));

const showSearchIcon = sm && !showSearchBar;
const showMarketTitle = !sm || !showSearchBar;

const handleCancelClick = () => {
setShowSearchBar(false);
onSearchTermChange('');
};

return (
<Box
sx={{
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
{showMarketTitle && (
<Typography component="div" variant="h2" sx={{ mr: 4 }} {...titleProps}>
{title}
</Typography>
)}

<Box
sx={{
height: '40px',
width: showSearchBar && sm ? '100%' : 'unset',
position: 'relative',
display: 'flex',
alignItems: 'center',
gap: 4,
justifyContent: 'space-between',
}}
>
<MarketAssetCategoryFilter
selectedCategory={selectedCategory}
onCategoryChange={onCategoryChange}
/>
{showSearchIcon && (
<IconButton onClick={() => setShowSearchBar(true)}>
<SvgIcon>
<SearchIcon />
</SvgIcon>
</IconButton>
)}
{(showSearchBar || !sm) && (
<Box sx={{ width: '100%', display: 'flex', justifyContent: 'space-between' }}>
<SearchInput
wrapperSx={{
width: {
xs: '100%',
sm: '340px',
},
}}
placeholder={searchPlaceholder}
onSearchTermChange={onSearchTermChange}
/>
{sm && (
<Button sx={{ ml: 2 }} onClick={() => handleCancelClick()}>
<Typography variant="buttonM">
<Trans>Cancel</Trans>
</Typography>
</Button>
)}
</Box>
)}
</Box>
</Box>
);
};
2 changes: 1 addition & 1 deletion src/locales/el/messages.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/locales/en/messages.js

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions src/locales/en/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ msgstr "Invalid amount to mint"
msgid "Disconnect Wallet"
msgstr "Disconnect Wallet"

#: src/modules/markets/MarketAssetsListContainer.tsx
#: src/modules/markets/MarketAssetsListContainer.tsx
msgid "assets"
msgstr "assets"
Expand Down Expand Up @@ -1212,6 +1213,10 @@ msgstr "{0}"
msgid "Manage analytics"
msgstr "Manage analytics"

#: src/components/MarketAssetCategoryFilter.tsx
msgid "Stablecoins"
msgstr "Stablecoins"

#: src/components/transactions/UnStake/UnStakeActions.tsx
#: src/modules/umbrella/UnstakeModalActions.tsx
msgid "Unstaking {symbol}"
Expand All @@ -1223,6 +1228,10 @@ msgstr "Unstaking {symbol}"
msgid "Swapping"
msgstr "Swapping"

#: src/components/MarketAssetCategoryFilter.tsx
msgid "ETH Correlated"
msgstr "ETH Correlated"

#: src/components/transactions/CollateralChange/CollateralChangeModalContent.tsx
msgid "You can not use this currency as collateral"
msgstr "You can not use this currency as collateral"
Expand Down Expand Up @@ -1466,6 +1475,10 @@ msgstr "Invalid return value of the flashloan executor function"
msgid "Learn more about the Kernel points distribution"
msgstr "Learn more about the Kernel points distribution"

#: src/components/MarketAssetCategoryFilter.tsx
msgid "All"
msgstr "All"

#: src/components/isolationMode/IsolatedBadge.tsx
msgid "Asset can be only used as collateral in isolation mode with limited borrowing power. To enter isolation mode, disable all other collateral."
msgstr "Asset can be only used as collateral in isolation mode with limited borrowing power. To enter isolation mode, disable all other collateral."
Expand Down Expand Up @@ -2343,6 +2356,7 @@ msgstr "You did not participate in this proposal"
msgid "Governance"
msgstr "Governance"

#: src/components/TitleWithFiltersAndSearchBar.tsx
#: src/components/TitleWithSearchBar.tsx
#: src/modules/history/HistoryWrapperMobile.tsx
msgid "Cancel"
Expand Down
2 changes: 1 addition & 1 deletion src/locales/es/messages.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/locales/fr/messages.js

Large diffs are not rendered by default.

54 changes: 45 additions & 9 deletions src/modules/markets/MarketAssetsListContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { Trans } from '@lingui/macro';
import { Box, Switch, Typography, useMediaQuery, useTheme } from '@mui/material';
import { useState } from 'react';
import { ListWrapper } from 'src/components/lists/ListWrapper';
import { MarketAssetCategoryFilter } from 'src/components/MarketAssetCategoryFilter';
import { NoSearchResults } from 'src/components/NoSearchResults';
import { Link } from 'src/components/primitives/Link';
import { Warning } from 'src/components/primitives/Warning';
import { TitleWithFiltersAndSearchBar } from 'src/components/TitleWithFiltersAndSearchBar';
import { TitleWithSearchBar } from 'src/components/TitleWithSearchBar';
import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider';
import MarketAssetsList from 'src/modules/markets/MarketAssetsList';
Expand All @@ -16,6 +18,7 @@ import { useShallow } from 'zustand/shallow';

import { GENERAL } from '../../utils/events';
import { SavingsGhoBanner } from './Gho/GhoBanner';
import { AssetCategory, isAssetInCategory } from './utils/assetCategories';

function shouldDisplayGhoBanner(marketTitle: string, searchTerm: string): boolean {
// GHO banner is only displayed on markets where new GHO is mintable (i.e. Ethereum)
Expand Down Expand Up @@ -45,6 +48,7 @@ export const MarketAssetsListContainer = () => {
])
);
const [searchTerm, setSearchTerm] = useState('');
const [selectedCategory, setSelectedCategory] = useState<AssetCategory>(AssetCategory.ALL);
const { breakpoints } = useTheme();
const sm = useMediaQuery(breakpoints.down('sm'));

Expand All @@ -63,6 +67,8 @@ export const MarketAssetsListContainer = () => {
res.underlyingAsset.toLowerCase().includes(term)
);
})
// Filter by category
.filter((res) => isAssetInCategory(res.symbol, selectedCategory))
// Transform the object for list to consume it
.map((reserve) => ({
...reserve,
Expand All @@ -88,15 +94,45 @@ export const MarketAssetsListContainer = () => {
return (
<ListWrapper
titleComponent={
<TitleWithSearchBar
onSearchTermChange={setSearchTerm}
title={
<>
{currentMarketData.marketTitle} <Trans>assets</Trans>
</>
}
searchPlaceholder={sm ? 'Search asset' : 'Search asset name, symbol, or address'}
/>
sm ? (
<Box sx={{ width: '100%' }}>
<TitleWithSearchBar
onSearchTermChange={setSearchTerm}
title={
<>
{currentMarketData.marketTitle} <Trans>assets</Trans>
</>
}
searchPlaceholder={sm ? 'Search asset' : 'Search asset name, symbol, or address'}
/>

<Box
sx={{
display: 'flex',
justifyContent: 'center',
width: '100%',
pt: 1,
}}
>
<MarketAssetCategoryFilter
selectedCategory={selectedCategory}
onCategoryChange={setSelectedCategory}
/>
</Box>
</Box>
) : (
<TitleWithFiltersAndSearchBar
onSearchTermChange={setSearchTerm}
title={
<>
{currentMarketData.marketTitle} <Trans>assets</Trans>
</>
}
searchPlaceholder={sm ? 'Search asset' : 'Search asset name, symbol, or address'}
selectedCategory={selectedCategory}
onCategoryChange={setSelectedCategory}
/>
)
}
>
{displayGhoBanner && (
Expand Down
60 changes: 60 additions & 0 deletions src/modules/markets/utils/assetCategories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export enum AssetCategory {
ALL = 'all',
STABLECOINS = 'stablecoins',
ETH_CORRELATED = 'eth_correlated',
}

export const STABLECOINS_SYMBOLS = [
//proto_mainnet_v3
'USDT',
'USDC',
'PT SUSDE SEPTEMBER',
'PT USDE SEPTEMBER 25TH 2025',
'USDE',
'RLUSD',
'GHO',
'DAI',
'USDtb',
'PT EUSDE AUGUST',
'USDS',
'PYUSD',
'LUSD',
'EUSDE',
'FRAX',
'crvUSD',
'PT SUSDE JULY',
'PT EUSDE MAY',
'PT USDE JULY',
];
export const ETH_CORRELATED_SYMBOLS = [
//proto_mainnet_v3
'ETH',
'WEETH',
'WSTETH',
'RSETH',
'OSETH',
'RETH',
'ETHX',
'CBETH',
];
export const categorizeAsset = (symbol: string): AssetCategory[] => {
const categories: AssetCategory[] = [AssetCategory.ALL];

const normalizedSymbol = symbol.toUpperCase();
if (STABLECOINS_SYMBOLS.includes(normalizedSymbol)) {
categories.push(AssetCategory.STABLECOINS);
}

if (ETH_CORRELATED_SYMBOLS.includes(normalizedSymbol)) {
categories.push(AssetCategory.ETH_CORRELATED);
}

return categories;
};

export const isAssetInCategory = (symbol: string, category: AssetCategory): boolean => {
if (category === AssetCategory.ALL) {
return true;
}
return categorizeAsset(symbol).includes(category);
};
Loading