diff --git a/src/components/ui/icon-switch.tsx b/src/components/ui/icon-switch.tsx index 800d94c4..a70d1d39 100644 --- a/src/components/ui/icon-switch.tsx +++ b/src/components/ui/icon-switch.tsx @@ -9,7 +9,7 @@ export type IconSwitchProps = { defaultSelected?: boolean; selected?: boolean; size?: 'xs' | 'sm' | 'md' | 'lg'; - color?: 'primary' | 'secondary' | 'accent' | 'destructive'; + color?: 'primary' | 'secondary' | 'accent' | 'destructive' | 'success'; onChange?: (selected: boolean) => void; thumbIcon?: React.ComponentType<{ className?: string }> | null; thumbIconOn?: React.ComponentType<{ className?: string }>; @@ -128,6 +128,7 @@ const TRACK_COLOR: Record, string> = { secondary: 'bg-[var(--color-background-secondary)]', accent: 'bg-[var(--palette-bg-hovered)]', destructive: 'bg-[#d92d20]', + success: 'bg-[#16a34a]', }; export function IconSwitch({ diff --git a/src/features/markets/components/table/markets-table.tsx b/src/features/markets/components/table/markets-table.tsx index 5cd2b346..4dd5f430 100644 --- a/src/features/markets/components/table/markets-table.tsx +++ b/src/features/markets/components/table/markets-table.tsx @@ -9,6 +9,7 @@ import { useMarketsQuery } from '@/hooks/queries/useMarketsQuery'; import { useFilteredMarkets } from '@/hooks/useFilteredMarkets'; import { useRateLabel } from '@/hooks/useRateLabel'; import { useMarketPreferences } from '@/stores/useMarketPreferences'; +import { useMarketsFilters } from '@/stores/useMarketsFilters'; import { useTrustedVaults } from '@/stores/useTrustedVaults'; import { buildTrustedVaultMap } from '@/utils/vaults'; import { SortColumn } from '../constants'; @@ -52,8 +53,11 @@ function MarketsTable({ currentPage, setCurrentPage, className, tableClassName, minSupplyEnabled, minBorrowEnabled, minLiquidityEnabled, + starredMarkets, } = useMarketPreferences(); + const { starredOnly } = useMarketsFilters(); + // Handle column header clicks for sorting const titleOnclick = useCallback( (column: number) => { @@ -88,11 +92,17 @@ function MarketsTable({ currentPage, setCurrentPage, className, tableClassName, // Determine empty state hint based on active filters const getEmptyStateHint = () => { + if (starredOnly && starredMarkets.length === 0) { + return 'You have no starred markets. Star some markets first, then use this filter.'; + } + if (starredOnly) { + return 'Disable the Starred Only filter to see all markets.'; + } if (!includeUnknownTokens) { - return "Try enabling 'Show Unknown Tokens' in settings, or adjust your current filters."; + return "Try disabling 'Hide Unknown Tokens' guard in filters."; } if (!showUnknownOracle) { - return "Try enabling 'Show Unknown Oracles' in settings, or adjust your oracle filters."; + return "Try disabling 'Hide Unknown Oracles' guard in filters."; } if (trustedVaultsOnly) { return 'Disable the Trusted Vaults filter or update your trusted list in Settings.'; diff --git a/src/features/positions/components/markets-filter-compact.tsx b/src/features/positions/components/markets-filter-compact.tsx index f7a28719..cee6c9de 100644 --- a/src/features/positions/components/markets-filter-compact.tsx +++ b/src/features/positions/components/markets-filter-compact.tsx @@ -1,6 +1,6 @@ 'use client'; -import { GoFilter, GoGear } from 'react-icons/go'; +import { GoFilter, GoGear, GoShield, GoShieldCheck, GoStar } from 'react-icons/go'; import { Button } from '@/components/ui/button'; import { Divider } from '@/components/ui/divider'; import { FilterRow, FilterSection } from '@/components/ui/filter-components'; @@ -51,12 +51,14 @@ export function MarketFilter({ className, variant = 'ghost', zIndex = 'settings' usdMinLiquidity, showOfficialTrending, customTagConfig, + starredMarkets, } = useMarketPreferences(); - const { trendingMode, toggleTrendingMode, customTagMode, toggleCustomTagMode } = useMarketsFilters(); + const { trendingMode, toggleTrendingMode, customTagMode, toggleCustomTagMode, starredOnly, toggleStarredOnly } = useMarketsFilters(); const { showUnwhitelistedMarkets, setShowUnwhitelistedMarkets } = useAppSettings(); const { vaults: trustedVaults } = useTrustedVaults(); const trustedVaultCount = trustedVaults.length; + const starredCount = starredMarkets.length; // Navigate to a specific detail view, then reopen filter when settings closes const handleOpenDetailView = (detailView: DetailViewType) => { @@ -67,10 +69,11 @@ export function MarketFilter({ className, variant = 'ghost', zIndex = 'settings' }); }; - const basicGuardianAllAllowed = includeUnknownTokens && showUnknownOracle && showUnwhitelistedMarkets && showLockedMarkets; + // Guards are active when the "show" flags are FALSE (meaning risky content is hidden) + const anyGuardActive = !includeUnknownTokens || !showUnknownOracle || !showUnwhitelistedMarkets || !showLockedMarkets; const advancedFilterActive = - trustedVaultsOnly || minSupplyEnabled || minBorrowEnabled || minLiquidityEnabled || trendingMode || customTagMode; - const hasActiveFilters = advancedFilterActive || !basicGuardianAllAllowed; + trustedVaultsOnly || minSupplyEnabled || minBorrowEnabled || minLiquidityEnabled || trendingMode || customTagMode || starredOnly; + const hasActiveFilters = advancedFilterActive || anyGuardActive; const isButtonVariant = variant === 'button'; @@ -123,47 +126,58 @@ export function MarketFilter({ className, variant = 'ghost', zIndex = 'settings' > setIncludeUnknownTokens(!checked)} size="xs" + color="success" + thumbIconOn={GoShieldCheck} + thumbIconOff={GoShield} /> setShowUnknownOracle(!checked)} size="xs" + color="success" + thumbIconOn={GoShieldCheck} + thumbIconOff={GoShield} /> setShowUnwhitelistedMarkets(!checked)} size="xs" - color="destructive" + color="success" + thumbIconOn={GoShieldCheck} + thumbIconOff={GoShield} /> setShowLockedMarkets(!checked)} size="xs" + color="success" + thumbIconOn={GoShieldCheck} + thumbIconOff={GoShield} /> @@ -226,6 +240,20 @@ export function MarketFilter({ className, variant = 'ghost', zIndex = 'settings' title="My Preferences" helper="Filters based on your configured preferences." > + + + { }); } + // Starred markets filter + if (filters.starredOnly && preferences.starredMarkets.length > 0) { + const starredSet = new Set(preferences.starredMarkets); + markets = markets.filter((market) => starredSet.has(market.uniqueKey)); + } + if (preferences.sortColumn === SortColumn.Starred) { return sortMarkets(markets, createStarredSort(preferences.starredMarkets), 1); } diff --git a/src/modals/settings/monarch-settings/SettingItem.tsx b/src/modals/settings/monarch-settings/SettingItem.tsx index 770cde15..d7c2d072 100644 --- a/src/modals/settings/monarch-settings/SettingItem.tsx +++ b/src/modals/settings/monarch-settings/SettingItem.tsx @@ -15,6 +15,7 @@ type SettingToggleItemProps = { thumbIconOff?: IconType; badge?: ReactNode; disabled?: boolean; + color?: 'primary' | 'secondary' | 'accent' | 'destructive' | 'success'; }; export function SettingToggleItem({ @@ -27,6 +28,7 @@ export function SettingToggleItem({ thumbIconOff, badge, disabled, + color = 'primary', }: SettingToggleItemProps) { return (
@@ -41,7 +43,7 @@ export function SettingToggleItem({ selected={selected} onChange={onChange} size="xs" - color="primary" + color={color} thumbIconOn={thumbIconOn} thumbIconOff={thumbIconOff} aria-label={ariaLabel} diff --git a/src/modals/settings/monarch-settings/panels/FiltersPanel.tsx b/src/modals/settings/monarch-settings/panels/FiltersPanel.tsx index b32db979..f514835b 100644 --- a/src/modals/settings/monarch-settings/panels/FiltersPanel.tsx +++ b/src/modals/settings/monarch-settings/panels/FiltersPanel.tsx @@ -36,44 +36,49 @@ export function FiltersPanel() { {/* Risk Guards Section */}

Risk Guards

+

Enable guards to filter out unverified or risky markets.

setIncludeUnknownTokens(!checked)} + ariaLabel="Toggle hide unknown tokens" + color="success" + thumbIconOn={GoShieldCheck} + thumbIconOff={GoShield} /> setShowUnknownOracle(!checked)} + ariaLabel="Toggle hide unknown oracles" + color="success" + thumbIconOn={GoShieldCheck} + thumbIconOff={GoShield} /> setShowUnwhitelistedMarkets(!checked)} + ariaLabel="Toggle hide unwhitelisted markets" + color="success" + thumbIconOn={GoShieldCheck} + thumbIconOff={GoShield} /> setShowLockedMarkets(!checked)} + ariaLabel="Toggle hide locked markets" + color="success" + thumbIconOn={GoShieldCheck} + thumbIconOff={GoShield} />
diff --git a/src/stores/useMarketsFilters.ts b/src/stores/useMarketsFilters.ts index 44520c5a..dfb23fa6 100644 --- a/src/stores/useMarketsFilters.ts +++ b/src/stores/useMarketsFilters.ts @@ -17,6 +17,7 @@ type MarketsFiltersState = { searchQuery: string; trendingMode: boolean; // Official trending filter (backend-computed) customTagMode: boolean; // User's custom tag filter + starredOnly: boolean; // Show only starred markets }; type MarketsFiltersActions = { @@ -27,6 +28,7 @@ type MarketsFiltersActions = { setSearchQuery: (query: string) => void; toggleTrendingMode: () => void; toggleCustomTagMode: () => void; + toggleStarredOnly: () => void; resetFilters: () => void; }; @@ -40,6 +42,7 @@ const DEFAULT_STATE: MarketsFiltersState = { searchQuery: '', trendingMode: false, customTagMode: false, + starredOnly: false, }; /** @@ -61,6 +64,7 @@ export const useMarketsFilters = create()((set) => ({ setSearchQuery: (query) => set({ searchQuery: query }), toggleTrendingMode: () => set((state) => ({ trendingMode: !state.trendingMode })), toggleCustomTagMode: () => set((state) => ({ customTagMode: !state.customTagMode })), + toggleStarredOnly: () => set((state) => ({ starredOnly: !state.starredOnly })), resetFilters: () => set(DEFAULT_STATE), }));