From 8c28d370b267f761fb7540c7533805f0e5062468 Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Thu, 12 Dec 2024 12:37:00 +0800 Subject: [PATCH 1/9] chore: rename and update flow --- .../components/onboarding/RiskSelection.tsx | 196 ++++++---------- .../report/components/ReportTable.tsx | 6 +- src/abis/monarch-agent-v1.ts | 210 ++++++++++++++++++ src/components/SupplyProcessModal.tsx | 4 +- src/components/common/MarketInfoBlock.tsx | 4 +- src/components/supplyModal.tsx | 4 +- src/components/withdrawModal.tsx | 4 +- src/utils/monarch-agent.ts | 1 + 8 files changed, 292 insertions(+), 137 deletions(-) create mode 100644 src/abis/monarch-agent-v1.ts create mode 100644 src/utils/monarch-agent.ts diff --git a/app/positions/components/onboarding/RiskSelection.tsx b/app/positions/components/onboarding/RiskSelection.tsx index 873ce684..22b0e92c 100644 --- a/app/positions/components/onboarding/RiskSelection.tsx +++ b/app/positions/components/onboarding/RiskSelection.tsx @@ -1,5 +1,4 @@ import { useMemo, useState } from 'react'; -import { Button } from '@nextui-org/react'; import Image from 'next/image'; import { useRouter, useSearchParams } from 'next/navigation'; import { formatUnits } from 'viem'; @@ -16,6 +15,8 @@ import { MarketAssetIndicator, MarketOracleIndicator, } from 'app/markets/components/RiskIndicator'; +import { MarketInfoBlock } from '@/components/common/MarketInfoBlock'; +import { Button } from '@/components/common'; import { useOnboarding } from './OnboardingContext'; export function RiskSelection() { @@ -111,7 +112,7 @@ export function RiskSelection() { {/* Input Section */} -
+

Choose markets you want to trust

+

+ selected markets: {selectedMarkets.size} +

- {/* Markets Table */} -
-
- - - - - - - - - - - - - - - {filteredMarkets.map((market) => { - const collateralToken = findToken( - market.collateralAsset.address, - market.morphoBlue.chain.id, - ); - if (!collateralToken) return null; - - const isSelected = selectedMarkets.has(market.uniqueKey); - const { vendors } = parseOracleVendors(market.oracle.data); - - return ( - toggleMarketSelection(market)} - className={`cursor-pointer transition-all duration-200 ease-in-out hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-800 ${ - isSelected ? 'bg-primary-50 dark:bg-primary-900/20' : '' - }`} - > - - - - - - - - - - ); - })} - -
MarketOracleWarningsLLTVSupply APYTotal SupplyUtilizationActions
-
- {collateralToken?.img && ( -
- {market.collateralAsset.symbol} -
- )} -
-
- e.stopPropagation()} - className="flex items-center gap-1 no-underline hover:underline" - > - {market.collateralAsset.symbol} - - -
- as collateral -
-
-
-
- {vendors.map((vendor) => ( - - ))} -
-
-
+ {/* Markets List - Scrollable Section */} +
+
+
+ {filteredMarkets.map((market, index) => { + const collateralToken = findToken( + market.collateralAsset.address, + market.morphoBlue.chain.id, + ); + if (!collateralToken) return null; + + const isSelected = selectedMarkets.has(market.uniqueKey); + + return ( +
toggleMarketSelection(market)} + className={`relative cursor-pointer group mx-1 transition-all duration-200 ease-in-out ${ + isSelected ? 'ring-2 ring-primary ring-offset-2 dark:ring-offset-gray-900' : '' + } ${index === 0 ? 'mt-2' : ''}`} + > +
+
+ +
+ +
+ {/* Risk Indicators */} +
-
- {formatUnits(BigInt(market.lltv), 16)}% - - {formatReadable(market.state.supplyApy * 100)}% - - {formatReadable( - Number( - formatUnits(BigInt(market.state.supplyAssets), market.loanAsset.decimals), - ), - )}{' '} - {market.loanAsset.symbol} - - {formatReadable(market.state.utilization * 100)}% - - -
+ +
+
+
+ ); + })} +
{/* Navigation */} -
+
From 8b8dd5c8ebce4833554f3fff0e48f3dd337b2945 Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 15 Dec 2024 11:15:53 +0800 Subject: [PATCH 3/9] chore: modal view --- app/positions/components/PositionsContent.tsx | 6 - app/positions/components/SmartOnboarding.tsx | 14 --- .../components/onboarding/AssetSelection.tsx | 17 +-- app/positions/components/onboarding/Modal.tsx | 105 ++++++++++++++++++ .../onboarding/OnboardingContent.tsx | 40 ------- .../onboarding/OnboardingContext.tsx | 66 +++++++---- .../components/onboarding/SmartOnboarding.tsx | 22 ---- .../components/onboarding/content.tsx | 11 ++ app/positions/onboarding/page.tsx | 5 +- docs/Styling.md | 8 +- src/components/providers/ClientProviders.tsx | 3 + 11 files changed, 176 insertions(+), 121 deletions(-) delete mode 100644 app/positions/components/SmartOnboarding.tsx create mode 100644 app/positions/components/onboarding/Modal.tsx delete mode 100644 app/positions/components/onboarding/OnboardingContent.tsx delete mode 100644 app/positions/components/onboarding/SmartOnboarding.tsx create mode 100644 app/positions/components/onboarding/content.tsx diff --git a/app/positions/components/PositionsContent.tsx b/app/positions/components/PositionsContent.tsx index a551fce4..ddf24cdc 100644 --- a/app/positions/components/PositionsContent.tsx +++ b/app/positions/components/PositionsContent.tsx @@ -126,12 +126,6 @@ export default function Positions() { ) : !hasSuppliedMarkets ? (
- - -
) : (
diff --git a/app/positions/components/SmartOnboarding.tsx b/app/positions/components/SmartOnboarding.tsx deleted file mode 100644 index 64226b9c..00000000 --- a/app/positions/components/SmartOnboarding.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { AssetSelection } from './onboarding/AssetSelection'; -import { OnboardingProvider } from './onboarding/OnboardingContext'; -import { RiskSelection } from './onboarding/RiskSelection'; - -export function SmartOnboarding() { - return ( -
- - - - -
- ); -} diff --git a/app/positions/components/onboarding/AssetSelection.tsx b/app/positions/components/onboarding/AssetSelection.tsx index 16908e88..d1d495b7 100644 --- a/app/positions/components/onboarding/AssetSelection.tsx +++ b/app/positions/components/onboarding/AssetSelection.tsx @@ -77,23 +77,15 @@ export function AssetSelection() { if (balancesLoading || marketsLoading) { return ( -
-
-

Select an Asset

-

Choose which asset you want to supply

-
+
Loading...
); } return ( -
-
-

Select an Asset

-

Choose which asset you want to supply

-
- +
+ {tokensWithMarkets.length === 0 ? (

No assets available

@@ -107,7 +99,7 @@ export function AssetSelection() {
) : ( -
+
{tokensWithMarkets.map((token) => ( handleTokenSelect(token)} className="group relative flex items-start gap-4 rounded border border-gray-200 bg-white p-4 text-left transition-all duration-300 hover:border-primary hover:shadow-lg dark:border-gray-700 dark:bg-gray-800/50 dark:hover:bg-gray-800" - whileHover={{ scale: 1.02 }} transition={{ type: 'spring', stiffness: 300, damping: 20 }} >
diff --git a/app/positions/components/onboarding/Modal.tsx b/app/positions/components/onboarding/Modal.tsx new file mode 100644 index 00000000..433851f2 --- /dev/null +++ b/app/positions/components/onboarding/Modal.tsx @@ -0,0 +1,105 @@ +import { + Modal, + ModalContent, + ModalHeader, + ModalBody, + ModalFooter, +} from '@nextui-org/modal'; +import { CrossCircledIcon } from '@radix-ui/react-icons'; +import { Button } from '@/components/common/Button'; +import { useOnboarding, ONBOARDING_STEPS } from './OnboardingContext'; +import { AssetSelection } from './AssetSelection'; +import { RiskSelection } from './RiskSelection'; +import { SetupPositions } from './SetupPositions'; + +type Props = { + isOpen: boolean; + onClose: () => void; +}; + +const StepComponents = { + 'asset-selection': AssetSelection, + 'risk-selection': RiskSelection, + 'setup': SetupPositions, + 'success': () =>
Success!
, +} as const; + +export function OnboardingModal({ isOpen, onClose }: Props) { + const { step, canGoNext, goToNextStep, goToPrevStep } = useOnboarding(); + const currentStepIndex = ONBOARDING_STEPS.findIndex((s) => s.id === step); + const CurrentStepComponent = StepComponents[step]; + + return ( + + + {/* Header */} + +
+

+ {ONBOARDING_STEPS[currentStepIndex].title} +

+

+ {ONBOARDING_STEPS[currentStepIndex].description} +

+
+ +
+ + {/* Content */} +
+
+ +
+
+ + {/* Footer */} + +
+ {currentStepIndex > 0 && ( + + )} +
+
+ {currentStepIndex < ONBOARDING_STEPS.length - 1 ? ( + + ) : ( + + )} +
+
+
+
+ ); +} diff --git a/app/positions/components/onboarding/OnboardingContent.tsx b/app/positions/components/onboarding/OnboardingContent.tsx deleted file mode 100644 index 15fbc870..00000000 --- a/app/positions/components/onboarding/OnboardingContent.tsx +++ /dev/null @@ -1,40 +0,0 @@ -'use client'; - -import { useSearchParams } from 'next/navigation'; -import Header from '@/components/layout/header/Header'; -import { AssetSelection } from './AssetSelection'; -import { OnboardingProvider } from './OnboardingContext'; -import { RiskSelection } from './RiskSelection'; -import { SetupPositions } from './SetupPositions'; -import { SuccessPage } from './SuccessPage'; - -export function OnboardingContent() { - const searchParams = useSearchParams(); - const step = searchParams.get('step') ?? 'asset-selection'; - - const renderStep = () => { - switch (step) { - case 'asset-selection': - return ; - case 'risk-selection': - return ; - case 'setup': - return ; - case 'success': - return ; - default: - return ; - } - }; - - return ( -
-
-
- -
{renderStep()}
-
-
-
- ); -} diff --git a/app/positions/components/onboarding/OnboardingContext.tsx b/app/positions/components/onboarding/OnboardingContext.tsx index 750d3605..1c45af4a 100644 --- a/app/positions/components/onboarding/OnboardingContext.tsx +++ b/app/positions/components/onboarding/OnboardingContext.tsx @@ -3,7 +3,14 @@ import { useRouter, useSearchParams } from 'next/navigation'; import { Market } from '@/utils/types'; import { TokenWithMarkets } from './types'; -type OnboardingStep = 'asset-selection' | 'risk-selection' | 'setup' | 'success'; +export const ONBOARDING_STEPS = [ + { id: 'asset-selection', title: 'Select Asset', description: 'Choose the asset you want to supply' }, + { id: 'risk-selection', title: 'Risk Parameters', description: 'Set your risk preferences' }, + { id: 'setup', title: 'Position Setup', description: 'Configure your position' }, + { id: 'success', title: 'Complete', description: 'Position created successfully' }, +] as const; + +export type OnboardingStep = typeof ONBOARDING_STEPS[number]['id']; type OnboardingContextType = { selectedToken: TokenWithMarkets | null; @@ -12,6 +19,9 @@ type OnboardingContextType = { setSelectedMarkets: (markets: Market[]) => void; step: OnboardingStep; setStep: (step: OnboardingStep) => void; + canGoNext: boolean; + goToNextStep: () => void; + goToPrevStep: () => void; }; const OnboardingContext = createContext(null); @@ -30,34 +40,52 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) router.push(`/positions/onboarding?${params.toString()}`); }; + const currentStepIndex = ONBOARDING_STEPS.findIndex((s) => s.id === currentStep); + + const canGoNext = useMemo(() => { + switch (currentStep) { + case 'asset-selection': + return !!selectedToken; + case 'risk-selection': + return selectedMarkets.length > 0; + case 'setup': + return true; + default: + return false; + } + }, [currentStep, selectedToken, selectedMarkets]); + + const goToNextStep = () => { + if (!canGoNext) return; + const nextStep = ONBOARDING_STEPS[currentStepIndex + 1]; + if (nextStep) { + setStep(nextStep.id); + } + }; + + const goToPrevStep = () => { + const prevStep = ONBOARDING_STEPS[currentStepIndex - 1]; + if (prevStep) { + setStep(prevStep.id); + } + }; + const contextValue = useMemo( () => ({ selectedToken, setSelectedToken: (token: TokenWithMarkets | null) => { setSelectedToken(token); - // Reset markets when token changes setSelectedMarkets([]); }, selectedMarkets, - setSelectedMarkets: (markets: Market[]) => { - setSelectedMarkets(markets); - }, + setSelectedMarkets, step: currentStep, - setStep: (newStep: OnboardingStep) => { - // Validate step transitions - if (newStep !== 'asset-selection' && !selectedToken) { - throw new Error('Token must be selected before proceeding'); - } - if (newStep === 'setup' && selectedMarkets.length === 0) { - throw new Error('Markets must be selected before setup'); - } - if (newStep === 'success' && !selectedToken) { - throw new Error('Token must be selected before showing success'); - } - setStep(newStep); - }, + setStep, + canGoNext, + goToNextStep, + goToPrevStep, }), - [selectedToken, selectedMarkets, currentStep], + [selectedToken, selectedMarkets, currentStep, canGoNext], ); return {children}; diff --git a/app/positions/components/onboarding/SmartOnboarding.tsx b/app/positions/components/onboarding/SmartOnboarding.tsx deleted file mode 100644 index 3085b4de..00000000 --- a/app/positions/components/onboarding/SmartOnboarding.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { AssetSelection } from './AssetSelection'; -import { OnboardingProvider, useOnboarding } from './OnboardingContext'; -import { RiskSelection } from './RiskSelection'; - -function OnboardingContent() { - const { step } = useOnboarding(); - - return ( -
- {step === 'asset-selection' && } - {step === 'risk-selection' && } -
- ); -} - -export function SmartOnboarding() { - return ( - - - - ); -} diff --git a/app/positions/components/onboarding/content.tsx b/app/positions/components/onboarding/content.tsx new file mode 100644 index 00000000..82abedbf --- /dev/null +++ b/app/positions/components/onboarding/content.tsx @@ -0,0 +1,11 @@ +'use client' + +import React from 'react' +import { OnboardingModal } from './Modal'; + +export default function OnboardingContent() { + return (
+ {}} /> +
) + +} diff --git a/app/positions/onboarding/page.tsx b/app/positions/onboarding/page.tsx index 70b6437d..51392362 100644 --- a/app/positions/onboarding/page.tsx +++ b/app/positions/onboarding/page.tsx @@ -1,5 +1,5 @@ import { Metadata } from 'next'; -import { OnboardingContent } from '../components/onboarding/OnboardingContent'; +import OnboardingContent from '../components/onboarding/content'; export const metadata: Metadata = { title: 'New Position | Monarch', @@ -7,5 +7,6 @@ export const metadata: Metadata = { }; export default function OnboardingPage() { - return ; + return () + } diff --git a/docs/Styling.md b/docs/Styling.md index 1eb91e76..525c8dd0 100644 --- a/docs/Styling.md +++ b/docs/Styling.md @@ -11,6 +11,9 @@ Use these shared components instead of raw HTML elements: ## Component Guidelines +- Use `rounded` for tables, cards or bigger components +- Use `rounded-sm` for buttons, inputs + ### Button Component ```typescript @@ -43,11 +46,6 @@ import { Button } from '@/components/common/Button'; - Use for: Tertiary actions, subtle navigation - Example: "Refresh" buttons, utility actions -#### Common Classes - -- Font: `font-zen` for consistent typography -- Icons: Use `mr-2` for icon spacing when icons are present -- Transitions: Built into variants, no need to add transition classes #### Examples diff --git a/src/components/providers/ClientProviders.tsx b/src/components/providers/ClientProviders.tsx index c0b7aef1..fdc7b726 100644 --- a/src/components/providers/ClientProviders.tsx +++ b/src/components/providers/ClientProviders.tsx @@ -3,6 +3,7 @@ import { ReactNode } from 'react'; import { ToastContainer } from 'react-toastify'; import { MarketsProvider } from '@/contexts/MarketsContext'; +import { OnboardingProvider } from 'app/positions/components/onboarding/OnboardingContext'; type ClientProvidersProps = { children: ReactNode; @@ -11,8 +12,10 @@ type ClientProvidersProps = { export function ClientProviders({ children }: ClientProvidersProps) { return ( + {children} + ); } From 33a44794d54407928d27d7b8feec0eabf138aa70 Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 15 Dec 2024 11:35:27 +0800 Subject: [PATCH 4/9] chore: modal half done --- app/positions/components/onboarding/Modal.tsx | 4 +- .../onboarding/OnboardingContext.tsx | 6 +- .../components/onboarding/RiskSelection.tsx | 56 +++---------------- .../components/onboarding/SetupPositions.tsx | 8 --- 4 files changed, 15 insertions(+), 59 deletions(-) diff --git a/app/positions/components/onboarding/Modal.tsx b/app/positions/components/onboarding/Modal.tsx index 433851f2..7ddc2984 100644 --- a/app/positions/components/onboarding/Modal.tsx +++ b/app/positions/components/onboarding/Modal.tsx @@ -45,7 +45,7 @@ export function OnboardingModal({ isOpen, onClose }: Props) { {/* Header */}
-

+

{ONBOARDING_STEPS[currentStepIndex].title}

@@ -85,7 +85,7 @@ export function OnboardingModal({ isOpen, onClose }: Props) { diff --git a/app/positions/components/onboarding/OnboardingContext.tsx b/app/positions/components/onboarding/OnboardingContext.tsx index 1c45af4a..42a0bdda 100644 --- a/app/positions/components/onboarding/OnboardingContext.tsx +++ b/app/positions/components/onboarding/OnboardingContext.tsx @@ -5,7 +5,7 @@ import { TokenWithMarkets } from './types'; export const ONBOARDING_STEPS = [ { id: 'asset-selection', title: 'Select Asset', description: 'Choose the asset you want to supply' }, - { id: 'risk-selection', title: 'Risk Parameters', description: 'Set your risk preferences' }, + { id: 'risk-selection', title: 'Select Markets', description: 'Set your risk preferences' }, { id: 'setup', title: 'Position Setup', description: 'Configure your position' }, { id: 'success', title: 'Complete', description: 'Position created successfully' }, ] as const; @@ -33,7 +33,7 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) const [selectedToken, setSelectedToken] = useState(null); const [selectedMarkets, setSelectedMarkets] = useState([]); - + const setStep = (newStep: OnboardingStep) => { const params = new URLSearchParams(searchParams.toString()); params.set('step', newStep); @@ -70,6 +70,8 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) } }; + console.log('can go next', currentStep, canGoNext); + const contextValue = useMemo( () => ({ selectedToken, diff --git a/app/positions/components/onboarding/RiskSelection.tsx b/app/positions/components/onboarding/RiskSelection.tsx index 70ca0425..93c04574 100644 --- a/app/positions/components/onboarding/RiskSelection.tsx +++ b/app/positions/components/onboarding/RiskSelection.tsx @@ -1,10 +1,7 @@ import { useMemo, useState } from 'react'; -import Image from 'next/image'; import { useRouter, useSearchParams } from 'next/navigation'; import { formatUnits } from 'viem'; -import OracleVendorBadge from '@/components/OracleVendorBadge'; import { formatReadable } from '@/utils/balance'; -import { getAssetURL } from '@/utils/external'; import { OracleVendors, parseOracleVendors } from '@/utils/oracle'; import { findToken, getUniqueTokens } from '@/utils/tokens'; import { Market } from '@/utils/types'; @@ -22,10 +19,9 @@ import { useOnboarding } from './OnboardingContext'; export function RiskSelection() { const router = useRouter(); const searchParams = useSearchParams(); - const { selectedToken, setSelectedMarkets } = useOnboarding(); + const { selectedToken, selectedMarkets, setSelectedMarkets } = useOnboarding(); const [selectedCollaterals, setSelectedCollaterals] = useState([]); const [selectedOracles, setSelectedOracles] = useState([]); - const [selectedMarkets, setSelectedMarketsLocal] = useState>(new Set()); const collateralTokens = useMemo(() => { if (!selectedToken?.markets) return []; @@ -73,17 +69,6 @@ export function RiskSelection() { }); }, [selectedToken, selectedCollaterals, selectedOracles]); - const handleNext = () => { - if (selectedMarkets.size > 0) { - const selectedMarketsArray = Array.from(selectedMarkets) - .map((key) => filteredMarkets.find((m) => m.uniqueKey === key)) - .filter((m): m is Market => m !== undefined); - - setSelectedMarkets(selectedMarketsArray); - router.push('/positions/onboarding?step=setup'); - } - }; - const handleMarketDetails = (market: Market, e: React.MouseEvent) => { e.stopPropagation(); const currentParams = searchParams.toString(); @@ -95,22 +80,18 @@ export function RiskSelection() { }; const toggleMarketSelection = (market: Market) => { - const newSelection = new Set(selectedMarkets); - if (selectedMarkets.has(market.uniqueKey)) { - newSelection.delete(market.uniqueKey); + const ids = selectedMarkets.map((m) => m.uniqueKey); + + if (ids.includes(market.uniqueKey)) { + setSelectedMarkets(selectedMarkets.filter((m) => m.uniqueKey !== market.uniqueKey)); } else { - newSelection.add(market.uniqueKey); + setSelectedMarkets([...selectedMarkets, market]); } - setSelectedMarketsLocal(newSelection); }; return (

-
-

Select Your Risk Preference

-

Choose which assets and oracles you want to trust

-
- + {/* Input Section */}
@@ -130,7 +111,7 @@ export function RiskSelection() {

Choose markets you want to trust

-

selected markets: {selectedMarkets.size}

+

selected markets: {selectedMarkets.length}

{/* Markets List - Scrollable Section */} @@ -144,7 +125,7 @@ export function RiskSelection() { ); if (!collateralToken) return null; - const isSelected = selectedMarkets.has(market.uniqueKey); + const isSelected = selectedMarkets.some((m) => m.uniqueKey === market.uniqueKey); return (
- - {/* Navigation */} -
- - -
); } diff --git a/app/positions/components/onboarding/SetupPositions.tsx b/app/positions/components/onboarding/SetupPositions.tsx index f59b3237..7ebb40f0 100644 --- a/app/positions/components/onboarding/SetupPositions.tsx +++ b/app/positions/components/onboarding/SetupPositions.tsx @@ -269,14 +269,6 @@ export function SetupPositions() { return (
-
-

Setup Your Positions

-

- Choose how much {selectedToken.symbol} you want to supply in total and distribute it - across markets -

-
- {/* Total Amount Section */}
From 282204aa82947a9c9ed3daae00f19bba88ae2b33 Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 15 Dec 2024 12:08:32 +0800 Subject: [PATCH 5/9] chore: styling, risk selection --- .../components/onboarding/RiskSelection.tsx | 18 +++++++----------- src/components/OracleVendorBadge.tsx | 2 +- src/components/common/MarketInfoBlock.tsx | 8 +++----- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/app/positions/components/onboarding/RiskSelection.tsx b/app/positions/components/onboarding/RiskSelection.tsx index 93c04574..2c58cff1 100644 --- a/app/positions/components/onboarding/RiskSelection.tsx +++ b/app/positions/components/onboarding/RiskSelection.tsx @@ -17,7 +17,6 @@ import { Button } from '@/components/common'; import { useOnboarding } from './OnboardingContext'; export function RiskSelection() { - const router = useRouter(); const searchParams = useSearchParams(); const { selectedToken, selectedMarkets, setSelectedMarkets } = useOnboarding(); const [selectedCollaterals, setSelectedCollaterals] = useState([]); @@ -91,9 +90,8 @@ export function RiskSelection() { return (
- {/* Input Section */} -
+
toggleMarketSelection(market)} - className={`group relative mx-1 cursor-pointer transition-all duration-200 ease-in-out ${ - isSelected ? 'ring-2 ring-primary ring-offset-2 dark:ring-offset-gray-900' : '' - } ${index === 0 ? 'mt-2' : ''}`} + className={`rounded relative cursor-pointer transition-all duration-200 ease-in-out p-1 ${index === 0 ? 'mt-2' : ''} ${isSelected ? 'border-2 bg-hovered' : ''}`} > -
-
- +
+
+
@@ -149,7 +145,7 @@ export function RiskSelection() {
{/* Total Supply */} -
+
Total Supply:
{formatReadable( @@ -165,7 +161,7 @@ export function RiskSelection() {
{/* Utilization Rate */} -
+
Utilization:
{formatReadable(market.state.utilization * 100)}% diff --git a/src/components/OracleVendorBadge.tsx b/src/components/OracleVendorBadge.tsx index 4c317b2c..896ac050 100644 --- a/src/components/OracleVendorBadge.tsx +++ b/src/components/OracleVendorBadge.tsx @@ -28,7 +28,7 @@ function OracleVendorBadge({ const noFeeds = vendors.length === 0; const content = ( -
+
{showText && ( {noFeeds ? 'No Oracle' : vendors.join(', ')} diff --git a/src/components/common/MarketInfoBlock.tsx b/src/components/common/MarketInfoBlock.tsx index 0ffc006e..010ef846 100644 --- a/src/components/common/MarketInfoBlock.tsx +++ b/src/components/common/MarketInfoBlock.tsx @@ -9,18 +9,16 @@ import OracleVendorBadge from '../OracleVendorBadge'; type MarketInfoBlockProps = { market: Market; amount?: bigint; - lltv?: string; - apy?: string; className?: string; }; -export function MarketInfoBlock({ market, amount }: MarketInfoBlockProps): JSX.Element { +export function MarketInfoBlock({ market, amount, className }: MarketInfoBlockProps): JSX.Element { const collateralToken = findToken(market.collateralAsset.address, market.morphoBlue.chain.id); return (
{collateralToken?.img && ( @@ -46,7 +44,7 @@ export function MarketInfoBlock({ market, amount }: MarketInfoBlockProps): JSX.E {formatBalance(amount, market.loanAsset.decimals)} {market.loanAsset.symbol} ) : ( - + )}
From ba04c5d96af37e8eee9a89e3dc5e63f6009d5e0d Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 15 Dec 2024 19:52:07 +0800 Subject: [PATCH 6/9] chore: refactor to modal done --- .../components/onboarding/AssetSelection.tsx | 16 +- app/positions/components/onboarding/Modal.tsx | 90 ++++----- .../onboarding/OnboardingContext.tsx | 19 +- .../components/onboarding/RiskSelection.tsx | 186 +++++++++++------- .../components/onboarding/SetupPositions.tsx | 70 +------ 5 files changed, 187 insertions(+), 194 deletions(-) diff --git a/app/positions/components/onboarding/AssetSelection.tsx b/app/positions/components/onboarding/AssetSelection.tsx index d1d495b7..808f8c76 100644 --- a/app/positions/components/onboarding/AssetSelection.tsx +++ b/app/positions/components/onboarding/AssetSelection.tsx @@ -1,8 +1,9 @@ import { useMemo } from 'react'; -import { Button, Tooltip } from '@nextui-org/react'; +import { Tooltip } from '@nextui-org/react'; import { motion } from 'framer-motion'; import Image from 'next/image'; import Link from 'next/link'; +import { Button } from '@/components/common/Button'; import { useRouter } from 'next/navigation'; import { useMarkets } from '@/hooks/useMarkets'; import { useUserBalances } from '@/hooks/useUserBalances'; @@ -10,6 +11,7 @@ import { formatBalance } from '@/utils/balance'; import { getNetworkImg, getNetworkName, SupportedNetworks } from '@/utils/networks'; import { useOnboarding } from './OnboardingContext'; import { TokenWithMarkets } from './types'; +import { Spinner } from '@/components/common/Spinner'; function NetworkIcon({ networkId }: { networkId: number }) { const url = getNetworkImg(networkId); @@ -27,8 +29,7 @@ function NetworkIcon({ networkId }: { networkId: number }) { export function AssetSelection() { const { balances, loading: balancesLoading } = useUserBalances(); const { markets, loading: marketsLoading } = useMarkets(); - const { setSelectedToken, setSelectedMarkets } = useOnboarding(); - const router = useRouter(); + const { setSelectedToken, setSelectedMarkets, goToNextStep } = useOnboarding(); const tokensWithMarkets = useMemo(() => { if (!balances || !markets) return []; @@ -72,19 +73,20 @@ export function AssetSelection() { const handleTokenSelect = (token: TokenWithMarkets) => { setSelectedToken(token); setSelectedMarkets([]); // Reset selected markets when changing token - router.push('/positions/onboarding?step=risk-selection'); + goToNextStep(); + }; if (balancesLoading || marketsLoading) { return ( -
-
Loading...
+
+
); } return ( -
+
{tokensWithMarkets.length === 0 ? (
diff --git a/app/positions/components/onboarding/Modal.tsx b/app/positions/components/onboarding/Modal.tsx index 7ddc2984..bfdc7bd5 100644 --- a/app/positions/components/onboarding/Modal.tsx +++ b/app/positions/components/onboarding/Modal.tsx @@ -1,31 +1,44 @@ -import { - Modal, - ModalContent, - ModalHeader, - ModalBody, - ModalFooter, -} from '@nextui-org/modal'; +import { Modal, ModalContent, ModalHeader, Button } from '@nextui-org/react'; import { CrossCircledIcon } from '@radix-ui/react-icons'; -import { Button } from '@/components/common/Button'; -import { useOnboarding, ONBOARDING_STEPS } from './OnboardingContext'; +import { useOnboarding } from './OnboardingContext'; import { AssetSelection } from './AssetSelection'; import { RiskSelection } from './RiskSelection'; import { SetupPositions } from './SetupPositions'; - -type Props = { - isOpen: boolean; - onClose: () => void; -}; +import { ONBOARDING_STEPS } from './OnboardingContext'; +import { SuccessPage } from './SuccessPage'; const StepComponents = { 'asset-selection': AssetSelection, 'risk-selection': RiskSelection, 'setup': SetupPositions, - 'success': () =>
Success!
, + 'success': () => SuccessPage } as const; -export function OnboardingModal({ isOpen, onClose }: Props) { - const { step, canGoNext, goToNextStep, goToPrevStep } = useOnboarding(); +function StepIndicator({ currentStep }: { currentStep: string }) { + const currentIndex = ONBOARDING_STEPS.findIndex(s => s.id === currentStep); + + return ( +
+ {ONBOARDING_STEPS.map((step, index) => { + const isPast = index < currentIndex; + const isCurrent = index === currentIndex; + + return ( +
+
+
+ ); + })} +
+ ); +} + +export function OnboardingModal({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) { + const { step } = useOnboarding(); const currentStepIndex = ONBOARDING_STEPS.findIndex((s) => s.id === step); const CurrentStepComponent = StepComponents[step]; @@ -41,14 +54,14 @@ export function OnboardingModal({ isOpen, onClose }: Props) { closeButton: "hidden", }} > - + {/* Header */}
-

+

{ONBOARDING_STEPS[currentStepIndex].title}

-

+

{ONBOARDING_STEPS[currentStepIndex].description}

@@ -63,42 +76,15 @@ export function OnboardingModal({ isOpen, onClose }: Props) { {/* Content */}
-
+
- {/* Footer */} - -
- {currentStepIndex > 0 && ( - - )} -
-
- {currentStepIndex < ONBOARDING_STEPS.length - 1 ? ( - - ) : ( - - )} -
-
+ {/* Footer with Step Indicator */} +
+ +
); diff --git a/app/positions/components/onboarding/OnboardingContext.tsx b/app/positions/components/onboarding/OnboardingContext.tsx index 42a0bdda..d2f461c0 100644 --- a/app/positions/components/onboarding/OnboardingContext.tsx +++ b/app/positions/components/onboarding/OnboardingContext.tsx @@ -2,11 +2,12 @@ import { createContext, useContext, useState, useMemo } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { Market } from '@/utils/types'; import { TokenWithMarkets } from './types'; +import { RiskSelection } from './RiskSelection'; export const ONBOARDING_STEPS = [ { id: 'asset-selection', title: 'Select Asset', description: 'Choose the asset you want to supply' }, { id: 'risk-selection', title: 'Select Markets', description: 'Set your risk preferences' }, - { id: 'setup', title: 'Position Setup', description: 'Configure your position' }, + { id: 'setup', title: 'Position Setup', description: 'Configure your initial position' }, { id: 'success', title: 'Complete', description: 'Position created successfully' }, ] as const; @@ -28,17 +29,17 @@ const OnboardingContext = createContext(null); export function OnboardingProvider({ children }: { children: React.ReactNode }) { const router = useRouter(); - const searchParams = useSearchParams(); - const currentStep = (searchParams.get('step') as OnboardingStep) || 'asset-selection'; - + const [selectedToken, setSelectedToken] = useState(null); const [selectedMarkets, setSelectedMarkets] = useState([]); - const setStep = (newStep: OnboardingStep) => { - const params = new URLSearchParams(searchParams.toString()); - params.set('step', newStep); - router.push(`/positions/onboarding?${params.toString()}`); - }; + const defaultStep = useMemo(() => { + if (!selectedToken) return 'asset-selection'; + if (selectedMarkets.length === 0) return 'risk-selection'; + return 'setup'; + }, [selectedToken, selectedMarkets]); + + const [currentStep, setStep] = useState(defaultStep); const currentStepIndex = ONBOARDING_STEPS.findIndex((s) => s.id === currentStep); diff --git a/app/positions/components/onboarding/RiskSelection.tsx b/app/positions/components/onboarding/RiskSelection.tsx index 2c58cff1..0698dfe4 100644 --- a/app/positions/components/onboarding/RiskSelection.tsx +++ b/app/positions/components/onboarding/RiskSelection.tsx @@ -1,5 +1,6 @@ import { useMemo, useState } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; +import { motion } from 'framer-motion'; import { formatUnits } from 'viem'; import { formatReadable } from '@/utils/balance'; import { OracleVendors, parseOracleVendors } from '@/utils/oracle'; @@ -13,12 +14,12 @@ import { MarketOracleIndicator, } from 'app/markets/components/RiskIndicator'; import { MarketInfoBlock } from '@/components/common/MarketInfoBlock'; -import { Button } from '@/components/common'; +import { Button } from '@/components/common/Button'; import { useOnboarding } from './OnboardingContext'; export function RiskSelection() { const searchParams = useSearchParams(); - const { selectedToken, selectedMarkets, setSelectedMarkets } = useOnboarding(); + const { selectedToken, selectedMarkets, setSelectedMarkets, canGoNext, goToNextStep, goToPrevStep } = useOnboarding(); const [selectedCollaterals, setSelectedCollaterals] = useState([]); const [selectedOracles, setSelectedOracles] = useState([]); @@ -68,6 +69,9 @@ export function RiskSelection() { }); }, [selectedToken, selectedCollaterals, selectedOracles]); + // Check if criteria is met to show markets + const shouldShowMarkets = selectedCollaterals.length > 0 && selectedOracles.length > 0; + const handleMarketDetails = (market: Market, e: React.MouseEvent) => { e.stopPropagation(); const currentParams = searchParams.toString(); @@ -91,6 +95,11 @@ export function RiskSelection() { return (
{/* Input Section */} +
+

Choose collateral you want to trust

+

+
+
-

Choose markets you want to trust

-

selected markets: {selectedMarkets.length}

+

Choose markets you want to trust

+

+ {shouldShowMarkets + ? `selected markets: ${selectedMarkets.length}` + : 'Select collateral and oracles to view available markets'} +

{/* Markets List - Scrollable Section */} -
-
-
- {filteredMarkets.map((market, index) => { - const collateralToken = findToken( - market.collateralAsset.address, - market.morphoBlue.chain.id, - ); - if (!collateralToken) return null; - - const isSelected = selectedMarkets.some((m) => m.uniqueKey === market.uniqueKey); - - return ( -
toggleMarketSelection(market)} - className={`rounded relative cursor-pointer transition-all duration-200 ease-in-out p-1 ${index === 0 ? 'mt-2' : ''} ${isSelected ? 'border-2 bg-hovered' : ''}`} - > -
-
- -
- -
- {/* Risk Indicators */} -
- - - +
+
+ {!shouldShowMarkets ? ( +
+
+

Select your preferences

+

+ {selectedCollaterals.length === 0 && 'Choose at least one collateral asset'} + {selectedCollaterals.length > 0 && selectedOracles.length === 0 && 'Now select oracle vendors'} +

+
+
+ ) : ( + + {filteredMarkets.map((market, index) => { + const collateralToken = findToken( + market.collateralAsset.address, + market.morphoBlue.chain.id, + ); + if (!collateralToken) return null; + + const isSelected = selectedMarkets.some((m) => m.uniqueKey === market.uniqueKey); + + return ( + toggleMarketSelection(market)} + className={`rounded relative cursor-pointer transition-all duration-200 ease-in-out p-1 ${ + index === 0 ? 'mt-2' : '' + } ${isSelected ? 'border-2 bg-hovered' : ''}`} + > +
+
+
- {/* Total Supply */} -
- Total Supply: -
- {formatReadable( - Number( - formatUnits( - BigInt(market.state.supplyAssets), - market.loanAsset.decimals, +
+ {/* Risk Indicators */} +
+ + + +
+ + {/* Total Supply */} +
+ Total Supply: +
+ {formatReadable( + Number( + formatUnits( + BigInt(market.state.supplyAssets), + market.loanAsset.decimals, + ), ), - ), - )}{' '} - {market.loanAsset.symbol} + )}{' '} + {market.loanAsset.symbol} +
-
- {/* Utilization Rate */} -
- Utilization: -
- {formatReadable(market.state.utilization * 100)}% + {/* Utilization Rate */} +
+ Utilization: +
+ {formatReadable(market.state.utilization * 100)}% +
-
- {/* Details Button */} - + {/* Details Button */} + +
-
-
- ); - })} -
+ + ); + })} + + )}
+ + {/* Navigation */} +
+ + +
); } diff --git a/app/positions/components/onboarding/SetupPositions.tsx b/app/positions/components/onboarding/SetupPositions.tsx index 7ebb40f0..74935970 100644 --- a/app/positions/components/onboarding/SetupPositions.tsx +++ b/app/positions/components/onboarding/SetupPositions.tsx @@ -6,20 +6,19 @@ import { useRouter } from 'next/navigation'; import { toast } from 'react-toastify'; import { formatUnits, parseUnits } from 'viem'; import { useChainId, useSwitchChain } from 'wagmi'; -import OracleVendorBadge from '@/components/OracleVendorBadge'; import { SupplyProcessModal } from '@/components/SupplyProcessModal'; import { useLocalStorage } from '@/hooks/useLocalStorage'; import { useMultiMarketSupply } from '@/hooks/useMultiMarketSupply'; import { useUserBalances } from '@/hooks/useUserBalances'; import { formatBalance, formatReadable } from '@/utils/balance'; -import { parseOracleVendors } from '@/utils/oracle'; import { findToken } from '@/utils/tokens'; import { useOnboarding } from './OnboardingContext'; +import { MarketInfoBlock } from '@/components/common/MarketInfoBlock'; export function SetupPositions() { const router = useRouter(); const chainId = useChainId(); - const { selectedToken, selectedMarkets } = useOnboarding(); + const { selectedToken, selectedMarkets, goToNextStep } = useOnboarding(); const { balances } = useUserBalances(); const [useEth] = useLocalStorage('useEth', false); const [usePermit2Setting] = useLocalStorage('usePermit2', true); @@ -37,16 +36,6 @@ export function SetupPositions() { const { switchChain } = useSwitchChain(); - // Redirect if no token selected - useEffect(() => { - if (!selectedToken) { - router.push('/positions/onboarding?step=asset-selection'); - return; - } - if (!selectedMarkets || selectedMarkets.length === 0) { - router.push('/positions/onboarding?step=risk-selection'); - } - }, [router, selectedToken, selectedMarkets]); // Compute token balance and decimals const tokenBalance = useMemo(() => { @@ -253,7 +242,7 @@ export function SetupPositions() { try { const success = await approveAndSupply(); if (success) { - router.push('/positions/onboarding?step=success'); + goToNextStep(); } } catch (supplyError) { console.error('Supply failed:', supplyError); @@ -316,10 +305,7 @@ export function SetupPositions() { - - - - + @@ -331,60 +317,24 @@ export function SetupPositions() { ); if (!collateralToken) return null; - const { vendors } = parseOracleVendors(market.oracle.data); const currentPercentage = percentages[market.uniqueKey] ?? 0; const isLocked = lockedAmounts.has(market.uniqueKey); return ( - - - - diff --git a/app/positions/components/onboarding/SuccessPage.tsx b/app/positions/components/onboarding/SuccessPage.tsx index ee7ec946..8639e052 100644 --- a/app/positions/components/onboarding/SuccessPage.tsx +++ b/app/positions/components/onboarding/SuccessPage.tsx @@ -1,19 +1,28 @@ -import { Button } from '@/components/common/Button'; +import { useMemo } from 'react'; import Link from 'next/link'; import { FaCheckCircle } from 'react-icons/fa'; import { useAccount } from 'wagmi'; +import { Button } from '@/components/common/Button'; import { useOnboarding } from './OnboardingContext'; -import { useMemo } from 'react'; -import { SupportedNetworks } from '@/utils/networks'; +// import { SupportedNetworks } from '@/utils/networks'; -export function SuccessPage() { - const { selectedToken } = useOnboarding(); +export function SuccessPage({ onClose }: { onClose: () => void }) { + const { selectedToken, setSelectedToken, setSelectedMarkets, setStep } = useOnboarding(); const { address } = useAccount(); const allowAgentSetting = useMemo(() => { - return selectedToken?.network === SupportedNetworks.Base + return false; + // TODO: enable for next release with agent + // return selectedToken?.network === SupportedNetworks.Base }, [selectedToken?.network]); + const handleFinished = () => { + onClose(); + setStep('asset-selection'); + setSelectedToken(null); + setSelectedMarkets([]); + }; + return (
@@ -22,19 +31,23 @@ export function SuccessPage() {

Success!

- Your {selectedToken?.symbol} has been successfully supplied to Morpho. {allowAgentSetting && 'You can set Monarch AutoPilot to automate reallocate your positions.'} + Your {selectedToken?.symbol} has been successfully supplied to Morpho.{' '} + {allowAgentSetting && + 'You can set Monarch AutoPilot to automate reallocate your positions.'}

-
- - { allowAgentSetting && - - } + {allowAgentSetting && ( + + + + )}
); diff --git a/app/positions/components/onboarding/content.tsx b/app/positions/components/onboarding/content.tsx index 82abedbf..a816976f 100644 --- a/app/positions/components/onboarding/content.tsx +++ b/app/positions/components/onboarding/content.tsx @@ -1,11 +1,12 @@ -'use client' +'use client'; -import React from 'react' +import React from 'react'; import { OnboardingModal } from './Modal'; export default function OnboardingContent() { - return (
- {}} /> -
) - + return ( +
+ {}} /> +
+ ); } diff --git a/app/positions/onboarding/page.tsx b/app/positions/onboarding/page.tsx deleted file mode 100644 index 51392362..00000000 --- a/app/positions/onboarding/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Metadata } from 'next'; -import OnboardingContent from '../components/onboarding/content'; - -export const metadata: Metadata = { - title: 'New Position | Monarch', - description: 'Create a new position on Morpho Blue', -}; - -export default function OnboardingPage() { - return () - -} diff --git a/docs/Styling.md b/docs/Styling.md index 525c8dd0..ca3e4883 100644 --- a/docs/Styling.md +++ b/docs/Styling.md @@ -46,7 +46,6 @@ import { Button } from '@/components/common/Button'; - Use for: Tertiary actions, subtle navigation - Example: "Refresh" buttons, utility actions - #### Examples ```tsx diff --git a/src/components/common/MarketInfoBlock.tsx b/src/components/common/MarketInfoBlock.tsx index 010ef846..0ab2b5ba 100644 --- a/src/components/common/MarketInfoBlock.tsx +++ b/src/components/common/MarketInfoBlock.tsx @@ -18,7 +18,7 @@ export function MarketInfoBlock({ market, amount, className }: MarketInfoBlockPr return (
{collateralToken?.img && ( diff --git a/src/components/providers/ClientProviders.tsx b/src/components/providers/ClientProviders.tsx index fdc7b726..130cc169 100644 --- a/src/components/providers/ClientProviders.tsx +++ b/src/components/providers/ClientProviders.tsx @@ -13,8 +13,8 @@ export function ClientProviders({ children }: ClientProvidersProps) { return ( - {children} - + {children} + ); diff --git a/src/hooks/useMultiMarketSupply.ts b/src/hooks/useMultiMarketSupply.ts index 54b574c8..0747bce8 100644 --- a/src/hooks/useMultiMarketSupply.ts +++ b/src/hooks/useMultiMarketSupply.ts @@ -22,6 +22,7 @@ export function useMultiMarketSupply( supplies: MarketSupply[], useEth: boolean, usePermit2Setting: boolean, + onSuccess?: () => void, ) { const [currentStep, setCurrentStep] = useState<'approve' | 'signing' | 'supplying'>('approve'); const [showProcessModal, setShowProcessModal] = useState(false); @@ -66,6 +67,7 @@ export function useMultiMarketSupply( successDescription: `Successfully supplied to ${supplies.length} market${ supplies.length > 1 ? 's' : '' }`, + onSuccess, }); const executeSupplyTransaction = useCallback(async () => { diff --git a/src/hooks/useTransactionWithToast.tsx b/src/hooks/useTransactionWithToast.tsx index 493a6537..d5d841c9 100644 --- a/src/hooks/useTransactionWithToast.tsx +++ b/src/hooks/useTransactionWithToast.tsx @@ -37,7 +37,7 @@ export function useTransactionWithToast({ hash, }); - console.log('isConfirmed', isConfirmed, toastId, hash) + console.log('isConfirmed', isConfirmed, toastId, hash); const onClick = useCallback(() => { if (hash) { diff --git a/src/imgs/tokens/usol.png b/src/imgs/tokens/usol.png new file mode 100644 index 0000000000000000000000000000000000000000..2e51e9262027bd484e0869b7739520a4533fcc5e GIT binary patch literal 1351 zcmV-N1-SZ&P)Px(@kvBMRCwC0mR(E~R~UxRbuSDzz(Rm*FM825ZLvfJ zLmN_KOq)#*8cw?$IF(^DG2mTh&OHAa-Rm|0c5!p>hH_E#OT3$?@=Ysdvb0^-Ua z#I98myJ02Pb%p?g|9xyJo=Q8nr&AanmCEoW?q-Ei&QwRe8gYc3M+>Nfkf< zvEp=-KWXS7T-V4&%`^6O8DakV4_7I^;0ggE?nFC>(4_$oK=@u8)`gS!Dy=_Y^EWr- zP2Y7;($VXX1_D|_8UPUx9=jwhw>$6Sue&JO+^LF#_>cw!2w&6C7bc?5$i7ACIXCH( z+-?F0huBiwMI8eZG(7I4!O2;T`lt2!Z^ogMb!13a8=9@i%9N#DSv|CU?>`!d9R;^k z3U)!j^EMHiX{$y+bI`a!u9h ziZ)%RXoeSrb0t4@zLD3Bo8V_Q|}Pu2C>vFK$jqi#pgy% zox*-H4Uv?5myUfn2f@=gws&B;AQqpZzr1sgiZ%@s2jLJ<;hw?efIw|%rl{tG{q0Rf zIh8a*0GXGDfVkV|1c*1GZMonryJU9BRM$IVda0;AExOoN_4e8T6NCHx8<48^)gf@jlyY4pkT*g>87!1a6LvU?)NHDp}1tEup z24(^b$G-YYt9t#Usq)ncvc`{VAbx#&L`|c5`xxDyB3~9FH>z(N5~yg+^&o&`Shh8e zfa(pCO4Tb91Q540{4vKe;i25zn~)IWt0`v2T)S?H&aIy$o`#?1GM*C_5>$wXS6a#` zCHZ|^g<)LmS}HFsp`F`4r>&_ekCHH$=2BQSV{D9dP+i=#3V}3+njes3kc1|O|6fq! z_ncsw!kEn*<<{cCOt6x}2+dfMt)4Oj`QeuMV6gb5EG|Cf3u`RqdbaMj-8mgR| z{VW}ak;ep(4E~0D*e-aBTClCUFE>Ze()=_4vRJ180CBnlByNnyRRd5`d_vuVterc= zeeN%|fRS|FKGY*vjjDO zNV(bmJ(;T6!2{W-34G@F0mX|@;=Z~hG!R~D1LGXOtilb3ES>_4QG`4Re&q!S@mZi- zye3eXfbgCMK*n;A0%GL`FhlI91zh=?Lx5%8mZ*!U*+O5^!1k$QI3&mp6P^4s*C)~% z5p9Ltv8PP(up*`{)YvIj;*O6JZFFP^|9HeF!@7TaGnn0|Ed5002ov JPDHLkV1gm)b*lgX literal 0 HcmV?d00001 diff --git a/src/imgs/tokens/usui.png b/src/imgs/tokens/usui.png new file mode 100644 index 0000000000000000000000000000000000000000..4610ffd53eee6b626bf0449f23dec0a6d26617dd GIT binary patch literal 1031 zcmV+i1o-=jP)&Q5?tduiA?!8fu7$xPwJQE)~%z7DE;sUtwiL>}$^=e5=1(yW))q+`cybjE46br(;z<`>OHOHsG zgy~$+@xACPb-xIwg>fTn#v*f$2LLJEWTnY!#?&28171xxG{^C}HQ}^!~t2wv7_I&5}1<<~7 z%qV}q#{d8VQAtEWRCwC$*F9(xVI0SCFCnqDnkJ%B5eHkHT8oJ2q=P6Z(hiQ2&5kO? z+QvZ;-5eaG3OcrR5z(QDFSXzxZYn6~aK3@I7ArbPZ5r>MJHNwk2uH8Ob4^Pj2Oq*c z_q_b_-2ZZ9RsE{)0B8Pjl8@#~g#ag@xBQOXN;6ikp6=EyxFJW*SO-UC|;Vk}P5-tK< zk1IAp?BUv1jp;)wNq|;-Lm5R}hJlMMF##N|ozR#Gv?ZzfbMWy5jZEIo=MNYRAs-W> zk!z3OqaThj1OqG39tbZ_=}FnH_YI;UPJ~S?qE|@pLUI%#73I{%}QUvf%%FJEnK^*g$(W@ zr}6tW_M`0%WHAPVfw)4_ui$CS!b308=!d~ms~XVbRD!|lgChxAIBefFygCi z9NqC%pFD)4U&2llQIewCcwg96ynP_l|0S*H<3D2e!4MqRukio?002ovPDHLkV1kPQ B;Nk!P literal 0 HcmV?d00001 diff --git a/src/utils/tokens.ts b/src/utils/tokens.ts index dea084b3..4ee28eab 100644 --- a/src/utils/tokens.ts +++ b/src/utils/tokens.ts @@ -240,7 +240,10 @@ const supportedTokens = [ symbol: 'lBTC', img: require('../imgs/tokens/lbtc.webp') as string, decimals: 8, - networks: [{ chain: mainnet, address: '0x8236a87084f8B84306f72007F36F2618A5634494' }], + networks: [ + { chain: mainnet, address: '0x8236a87084f8B84306f72007F36F2618A5634494' }, + { chain: base, address: '0xecAc9C5F704e954931349Da37F60E39f515c11c1' }, + ], }, { symbol: 'rsETH', @@ -382,6 +385,18 @@ const supportedTokens = [ decimals: 18, networks: [{ chain: base, address: '0x7FcD174E80f264448ebeE8c88a7C4476AAF58Ea6' }], }, + { + symbol: 'uSOL', + img: require('../imgs/tokens/usol.png') as string, + decimals: 18, + networks: [{ chain: base, address: '0x9B8Df6E244526ab5F6e6400d331DB28C8fdDdb55' }], + }, + { + symbol: 'uSui', + img: require('../imgs/tokens/usui.png') as string, + decimals: 18, + networks: [{ chain: base, address: '0xb0505e5a99abd03d94a1169e638B78EDfEd26ea4' }], + }, ]; const isWhitelisted = (address: string, chainId: number) => { From a9b2e7513ca7aa7fe37bce34172c72a49498c1a7 Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Mon, 16 Dec 2024 10:16:29 +0800 Subject: [PATCH 9/9] chore: review fixes --- app/positions/components/PositionsContent.tsx | 10 ++++------ app/positions/components/onboarding/Modal.tsx | 2 +- .../onboarding/OnboardingContext.tsx | 18 +++++++++--------- .../components/onboarding/RiskSelection.tsx | 6 ++---- .../components/onboarding/SetupPositions.tsx | 17 ++++++----------- .../components/onboarding/SuccessPage.tsx | 6 ++---- .../components/onboarding/content.tsx | 12 ------------ src/components/OracleVendorBadge.tsx | 2 +- 8 files changed, 25 insertions(+), 48 deletions(-) delete mode 100644 app/positions/components/onboarding/content.tsx diff --git a/app/positions/components/PositionsContent.tsx b/app/positions/components/PositionsContent.tsx index 2829c101..e8cf8db7 100644 --- a/app/positions/components/PositionsContent.tsx +++ b/app/positions/components/PositionsContent.tsx @@ -122,12 +122,10 @@ export default function Positions() { /> )} - {showOnboardingModal && ( - setShowOnboardingModal(false)} - /> - )} + setShowOnboardingModal(false)} + /> {isLoading ? ( diff --git a/app/positions/components/onboarding/Modal.tsx b/app/positions/components/onboarding/Modal.tsx index 29c98e8a..5f98717f 100644 --- a/app/positions/components/onboarding/Modal.tsx +++ b/app/positions/components/onboarding/Modal.tsx @@ -71,7 +71,7 @@ export function OnboardingModal({ isOpen, onClose }: { isOpen: boolean; onClose:

diff --git a/app/positions/components/onboarding/OnboardingContext.tsx b/app/positions/components/onboarding/OnboardingContext.tsx index 03393313..38064934 100644 --- a/app/positions/components/onboarding/OnboardingContext.tsx +++ b/app/positions/components/onboarding/OnboardingContext.tsx @@ -1,4 +1,4 @@ -import { createContext, useContext, useState, useMemo } from 'react'; +import { createContext, useContext, useState, useMemo, useCallback } from 'react'; import { Market } from '@/utils/types'; import { TokenWithMarkets } from './types'; @@ -25,6 +25,7 @@ type OnboardingContextType = { canGoNext: boolean; goToNextStep: () => void; goToPrevStep: () => void; + resetOnboarding: () => void; }; const OnboardingContext = createContext(null); @@ -33,13 +34,7 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) const [selectedToken, setSelectedToken] = useState(null); const [selectedMarkets, setSelectedMarkets] = useState([]); - const defaultStep = useMemo(() => { - if (!selectedToken) return 'asset-selection'; - if (selectedMarkets.length === 0) return 'risk-selection'; - return 'setup'; - }, [selectedToken, selectedMarkets]); - - const [currentStep, setStep] = useState(defaultStep); + const [currentStep, setStep] = useState('asset-selection'); const currentStepIndex = ONBOARDING_STEPS.findIndex((s) => s.id === currentStep); @@ -70,7 +65,11 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) } }; - console.log('can go next', currentStep, canGoNext); + const resetOnboarding = useCallback(() => { + setSelectedToken(null); + setSelectedMarkets([]); + setStep('asset-selection'); + }, [setSelectedToken, setSelectedMarkets, setStep]); const contextValue = useMemo( () => ({ @@ -86,6 +85,7 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) canGoNext, goToNextStep, goToPrevStep, + resetOnboarding, }), [selectedToken, selectedMarkets, currentStep, canGoNext], ); diff --git a/app/positions/components/onboarding/RiskSelection.tsx b/app/positions/components/onboarding/RiskSelection.tsx index 557fbae7..ec5447b8 100644 --- a/app/positions/components/onboarding/RiskSelection.tsx +++ b/app/positions/components/onboarding/RiskSelection.tsx @@ -84,9 +84,7 @@ export function RiskSelection() { }; const toggleMarketSelection = (market: Market) => { - const ids = selectedMarkets.map((m) => m.uniqueKey); - - if (ids.includes(market.uniqueKey)) { + if (selectedMarkets.some((m) => m.uniqueKey === market.uniqueKey)) { setSelectedMarkets(selectedMarkets.filter((m) => m.uniqueKey !== market.uniqueKey)); } else { setSelectedMarkets([...selectedMarkets, market]); @@ -227,7 +225,7 @@ export function RiskSelection() { {/* Navigation */}
- diff --git a/app/positions/components/onboarding/SuccessPage.tsx b/app/positions/components/onboarding/SuccessPage.tsx index 8639e052..fbc4efa9 100644 --- a/app/positions/components/onboarding/SuccessPage.tsx +++ b/app/positions/components/onboarding/SuccessPage.tsx @@ -7,7 +7,7 @@ import { useOnboarding } from './OnboardingContext'; // import { SupportedNetworks } from '@/utils/networks'; export function SuccessPage({ onClose }: { onClose: () => void }) { - const { selectedToken, setSelectedToken, setSelectedMarkets, setStep } = useOnboarding(); + const { selectedToken, resetOnboarding } = useOnboarding(); const { address } = useAccount(); const allowAgentSetting = useMemo(() => { @@ -18,9 +18,7 @@ export function SuccessPage({ onClose }: { onClose: () => void }) { const handleFinished = () => { onClose(); - setStep('asset-selection'); - setSelectedToken(null); - setSelectedMarkets([]); + resetOnboarding(); }; return ( diff --git a/app/positions/components/onboarding/content.tsx b/app/positions/components/onboarding/content.tsx deleted file mode 100644 index a816976f..00000000 --- a/app/positions/components/onboarding/content.tsx +++ /dev/null @@ -1,12 +0,0 @@ -'use client'; - -import React from 'react'; -import { OnboardingModal } from './Modal'; - -export default function OnboardingContent() { - return ( -
- {}} /> -
- ); -} diff --git a/src/components/OracleVendorBadge.tsx b/src/components/OracleVendorBadge.tsx index 896ac050..a03c1b45 100644 --- a/src/components/OracleVendorBadge.tsx +++ b/src/components/OracleVendorBadge.tsx @@ -28,7 +28,7 @@ function OracleVendorBadge({ const noFeeds = vendors.length === 0; const content = ( -
+
{showText && ( {noFeeds ? 'No Oracle' : vendors.join(', ')}
Market IDCollateralMarket ParamsSupply APYMarket Distribution
+ - {market.uniqueKey.slice(2, 8)} + -
- {collateralToken?.img && ( -
- {market.collateralAsset.symbol} -
- )} -
- {market.collateralAsset.symbol} - as collateral -
-
-
-
-
- {vendors.map((vendor) => ( - - ))} -
- - {formatUnits(BigInt(market.lltv), 16)}% LTV - -
-
- {formatReadable(market.state.supplyApy * 100)}% -
From cc750a53b2aaf491c60ec21fdb67ba1513e41aa2 Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 15 Dec 2024 20:37:38 +0800 Subject: [PATCH 7/9] chore: hint setup autopilot --- app/positions/components/onboarding/Modal.tsx | 2 +- .../components/onboarding/SuccessPage.tsx | 34 +++++++++++-------- src/hooks/useTransactionWithToast.tsx | 2 ++ 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/positions/components/onboarding/Modal.tsx b/app/positions/components/onboarding/Modal.tsx index bfdc7bd5..5fa164c8 100644 --- a/app/positions/components/onboarding/Modal.tsx +++ b/app/positions/components/onboarding/Modal.tsx @@ -11,7 +11,7 @@ const StepComponents = { 'asset-selection': AssetSelection, 'risk-selection': RiskSelection, 'setup': SetupPositions, - 'success': () => SuccessPage + 'success': SuccessPage } as const; function StepIndicator({ currentStep }: { currentStep: string }) { diff --git a/app/positions/components/onboarding/SuccessPage.tsx b/app/positions/components/onboarding/SuccessPage.tsx index 8b87ba90..ee7ec946 100644 --- a/app/positions/components/onboarding/SuccessPage.tsx +++ b/app/positions/components/onboarding/SuccessPage.tsx @@ -1,36 +1,40 @@ -import { Button } from '@nextui-org/react'; +import { Button } from '@/components/common/Button'; import Link from 'next/link'; import { FaCheckCircle } from 'react-icons/fa'; import { useAccount } from 'wagmi'; import { useOnboarding } from './OnboardingContext'; +import { useMemo } from 'react'; +import { SupportedNetworks } from '@/utils/networks'; export function SuccessPage() { const { selectedToken } = useOnboarding(); const { address } = useAccount(); + const allowAgentSetting = useMemo(() => { + return selectedToken?.network === SupportedNetworks.Base + }, [selectedToken?.network]); + return (
- -

Success!

+ +

Success!

-

- Your {selectedToken?.symbol} has been successfully supplied to Morpho Blue. +

+ Your {selectedToken?.symbol} has been successfully supplied to Morpho. {allowAgentSetting && 'You can set Monarch AutoPilot to automate reallocate your positions.'}

-
- - - - - + { allowAgentSetting && + - + }
); diff --git a/src/hooks/useTransactionWithToast.tsx b/src/hooks/useTransactionWithToast.tsx index 2b66fa03..493a6537 100644 --- a/src/hooks/useTransactionWithToast.tsx +++ b/src/hooks/useTransactionWithToast.tsx @@ -37,6 +37,8 @@ export function useTransactionWithToast({ hash, }); + console.log('isConfirmed', isConfirmed, toastId, hash) + const onClick = useCallback(() => { if (hash) { // if chainId is not supported, use 1 From 4e87c9f9b258ea0424d7a42b59c6777e79da791e Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Mon, 16 Dec 2024 09:30:27 +0800 Subject: [PATCH 8/9] feat: change to onboarding modal --- app/positions/components/PositionsContent.tsx | 32 +++++---- .../components/onboarding/AssetSelection.tsx | 48 ++++++------- app/positions/components/onboarding/Modal.tsx | 66 ++++++++++-------- .../onboarding/OnboardingContext.tsx | 19 +++-- .../components/onboarding/RiskSelection.tsx | 62 ++++++++-------- .../components/onboarding/SetupPositions.tsx | 15 ++-- .../components/onboarding/SuccessPage.tsx | 41 +++++++---- .../components/onboarding/content.tsx | 13 ++-- app/positions/onboarding/page.tsx | 12 ---- docs/Styling.md | 1 - src/components/common/MarketInfoBlock.tsx | 2 +- src/components/providers/ClientProviders.tsx | 4 +- src/hooks/useMultiMarketSupply.ts | 2 + src/hooks/useTransactionWithToast.tsx | 2 +- src/imgs/tokens/usol.png | Bin 0 -> 1351 bytes src/imgs/tokens/usui.png | Bin 0 -> 1031 bytes src/utils/tokens.ts | 17 ++++- 17 files changed, 184 insertions(+), 152 deletions(-) delete mode 100644 app/positions/onboarding/page.tsx create mode 100644 src/imgs/tokens/usol.png create mode 100644 src/imgs/tokens/usui.png diff --git a/app/positions/components/PositionsContent.tsx b/app/positions/components/PositionsContent.tsx index ddf24cdc..2829c101 100644 --- a/app/positions/components/PositionsContent.tsx +++ b/app/positions/components/PositionsContent.tsx @@ -16,11 +16,13 @@ import { SupplyModal } from '@/components/supplyModal'; import { WithdrawModal } from '@/components/withdrawModal'; import useUserPositionsWithEarning from '@/hooks/useUserPositionsWithEarning'; import { MarketPosition } from '@/utils/types'; +import { OnboardingModal } from './onboarding/Modal'; import { PositionsSummaryTable } from './PositionsSummaryTable'; export default function Positions() { const [showSupplyModal, setShowSupplyModal] = useState(false); const [showWithdrawModal, setShowWithdrawModal] = useState(false); + const [showOnboardingModal, setShowOnboardingModal] = useState(false); const [selectedPosition, setSelectedPosition] = useState(null); const { account } = useParams<{ account: string }>(); @@ -84,18 +86,17 @@ export default function Positions() { {isOwner && ( - - - + )}
@@ -121,6 +122,13 @@ export default function Positions() { /> )} + {showOnboardingModal && ( + setShowOnboardingModal(false)} + /> + )} + {isLoading ? ( ) : !hasSuppliedMarkets ? ( diff --git a/app/positions/components/onboarding/AssetSelection.tsx b/app/positions/components/onboarding/AssetSelection.tsx index 808f8c76..5cfbd4aa 100644 --- a/app/positions/components/onboarding/AssetSelection.tsx +++ b/app/positions/components/onboarding/AssetSelection.tsx @@ -1,17 +1,15 @@ import { useMemo } from 'react'; -import { Tooltip } from '@nextui-org/react'; import { motion } from 'framer-motion'; import Image from 'next/image'; import Link from 'next/link'; +import { formatUnits } from 'viem'; import { Button } from '@/components/common/Button'; -import { useRouter } from 'next/navigation'; +import { Spinner } from '@/components/common/Spinner'; import { useMarkets } from '@/hooks/useMarkets'; import { useUserBalances } from '@/hooks/useUserBalances'; -import { formatBalance } from '@/utils/balance'; import { getNetworkImg, getNetworkName, SupportedNetworks } from '@/utils/networks'; import { useOnboarding } from './OnboardingContext'; import { TokenWithMarkets } from './types'; -import { Spinner } from '@/components/common/Spinner'; function NetworkIcon({ networkId }: { networkId: number }) { const url = getNetworkImg(networkId); @@ -74,20 +72,21 @@ export function AssetSelection() { setSelectedToken(token); setSelectedMarkets([]); // Reset selected markets when changing token goToNextStep(); - }; if (balancesLoading || marketsLoading) { return (
-
+
+ {' '} + {' '} +
); } return (
- {tokensWithMarkets.length === 0 ? (

No assets available

@@ -135,29 +134,30 @@ export function AssetSelection() { {/* if base network, show agent badge */} {token.network === SupportedNetworks.Base && ( - -
Monarch Autopilot 🎉
-
- Monarch Autopilot is now in beta on Base! Setup the agent to start - automating your reallocations. -
-
- } - > -
- 🤖 - beta -
- + // + //
Monarch Autopilot 🎉
+ //
+ // Monarch Autopilot is now in beta on Base! Setup the agent to start + // automating your reallocations. + //
+ //
+ // } + // > + //
+ // 🤖 + // beta + //
+ // +
)}

- Balance: {formatBalance(token.balance, token.decimals)} {token.symbol} + Balance: {formatUnits(BigInt(token.balance), token.decimals)} {token.symbol}

diff --git a/app/positions/components/onboarding/Modal.tsx b/app/positions/components/onboarding/Modal.tsx index 5fa164c8..29c98e8a 100644 --- a/app/positions/components/onboarding/Modal.tsx +++ b/app/positions/components/onboarding/Modal.tsx @@ -1,33 +1,38 @@ import { Modal, ModalContent, ModalHeader, Button } from '@nextui-org/react'; -import { CrossCircledIcon } from '@radix-ui/react-icons'; -import { useOnboarding } from './OnboardingContext'; +import { motion, AnimatePresence } from 'framer-motion'; +import { RxCross2 } from 'react-icons/rx'; import { AssetSelection } from './AssetSelection'; +import { useOnboarding } from './OnboardingContext'; +import { ONBOARDING_STEPS } from './OnboardingContext'; import { RiskSelection } from './RiskSelection'; import { SetupPositions } from './SetupPositions'; -import { ONBOARDING_STEPS } from './OnboardingContext'; import { SuccessPage } from './SuccessPage'; const StepComponents = { 'asset-selection': AssetSelection, 'risk-selection': RiskSelection, - 'setup': SetupPositions, - 'success': SuccessPage + setup: SetupPositions, + success: SuccessPage, } as const; function StepIndicator({ currentStep }: { currentStep: string }) { - const currentIndex = ONBOARDING_STEPS.findIndex(s => s.id === currentStep); - + const currentIndex = ONBOARDING_STEPS.findIndex((s) => s.id === currentStep); + return (
{ONBOARDING_STEPS.map((step, index) => { const isPast = index < currentIndex; const isCurrent = index === currentIndex; - + return (
-
@@ -43,42 +48,47 @@ export function OnboardingModal({ isOpen, onClose }: { isOpen: boolean; onClose: const CurrentStepComponent = StepComponents[step]; return ( - - + {/* Header */} - +

{ONBOARDING_STEPS[currentStepIndex].title}

-

+

{ONBOARDING_STEPS[currentStepIndex].description}

-
{/* Content */}
-
- -
+ + + + +
{/* Footer with Step Indicator */} diff --git a/app/positions/components/onboarding/OnboardingContext.tsx b/app/positions/components/onboarding/OnboardingContext.tsx index d2f461c0..03393313 100644 --- a/app/positions/components/onboarding/OnboardingContext.tsx +++ b/app/positions/components/onboarding/OnboardingContext.tsx @@ -1,17 +1,19 @@ import { createContext, useContext, useState, useMemo } from 'react'; -import { useRouter, useSearchParams } from 'next/navigation'; import { Market } from '@/utils/types'; import { TokenWithMarkets } from './types'; -import { RiskSelection } from './RiskSelection'; export const ONBOARDING_STEPS = [ - { id: 'asset-selection', title: 'Select Asset', description: 'Choose the asset you want to supply' }, + { + id: 'asset-selection', + title: 'Select Asset', + description: 'Choose the asset you want to supply', + }, { id: 'risk-selection', title: 'Select Markets', description: 'Set your risk preferences' }, { id: 'setup', title: 'Position Setup', description: 'Configure your initial position' }, { id: 'success', title: 'Complete', description: 'Position created successfully' }, ] as const; -export type OnboardingStep = typeof ONBOARDING_STEPS[number]['id']; +export type OnboardingStep = (typeof ONBOARDING_STEPS)[number]['id']; type OnboardingContextType = { selectedToken: TokenWithMarkets | null; @@ -28,17 +30,15 @@ type OnboardingContextType = { const OnboardingContext = createContext(null); export function OnboardingProvider({ children }: { children: React.ReactNode }) { - const router = useRouter(); - const [selectedToken, setSelectedToken] = useState(null); const [selectedMarkets, setSelectedMarkets] = useState([]); - + const defaultStep = useMemo(() => { if (!selectedToken) return 'asset-selection'; if (selectedMarkets.length === 0) return 'risk-selection'; return 'setup'; }, [selectedToken, selectedMarkets]); - + const [currentStep, setStep] = useState(defaultStep); const currentStepIndex = ONBOARDING_STEPS.findIndex((s) => s.id === currentStep); @@ -50,14 +50,13 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) case 'risk-selection': return selectedMarkets.length > 0; case 'setup': - return true; + return true; default: return false; } }, [currentStep, selectedToken, selectedMarkets]); const goToNextStep = () => { - if (!canGoNext) return; const nextStep = ONBOARDING_STEPS[currentStepIndex + 1]; if (nextStep) { setStep(nextStep.id); diff --git a/app/positions/components/onboarding/RiskSelection.tsx b/app/positions/components/onboarding/RiskSelection.tsx index 0698dfe4..557fbae7 100644 --- a/app/positions/components/onboarding/RiskSelection.tsx +++ b/app/positions/components/onboarding/RiskSelection.tsx @@ -1,7 +1,8 @@ import { useMemo, useState } from 'react'; -import { useRouter, useSearchParams } from 'next/navigation'; import { motion } from 'framer-motion'; import { formatUnits } from 'viem'; +import { Button } from '@/components/common/Button'; +import { MarketInfoBlock } from '@/components/common/MarketInfoBlock'; import { formatReadable } from '@/utils/balance'; import { OracleVendors, parseOracleVendors } from '@/utils/oracle'; import { findToken, getUniqueTokens } from '@/utils/tokens'; @@ -13,13 +14,17 @@ import { MarketAssetIndicator, MarketOracleIndicator, } from 'app/markets/components/RiskIndicator'; -import { MarketInfoBlock } from '@/components/common/MarketInfoBlock'; -import { Button } from '@/components/common/Button'; import { useOnboarding } from './OnboardingContext'; export function RiskSelection() { - const searchParams = useSearchParams(); - const { selectedToken, selectedMarkets, setSelectedMarkets, canGoNext, goToNextStep, goToPrevStep } = useOnboarding(); + const { + selectedToken, + selectedMarkets, + setSelectedMarkets, + canGoNext, + goToNextStep, + goToPrevStep, + } = useOnboarding(); const [selectedCollaterals, setSelectedCollaterals] = useState([]); const [selectedOracles, setSelectedOracles] = useState([]); @@ -74,17 +79,13 @@ export function RiskSelection() { const handleMarketDetails = (market: Market, e: React.MouseEvent) => { e.stopPropagation(); - const currentParams = searchParams.toString(); const marketPath = `/market/${market.morphoBlue.chain.id}/${market.uniqueKey}`; - const targetPath = currentParams ? `${marketPath}?${currentParams}` : marketPath; - - // open in tab - window.open(targetPath, '_blank'); + window.open(marketPath, '_blank'); }; const toggleMarketSelection = (market: Market) => { const ids = selectedMarkets.map((m) => m.uniqueKey); - + if (ids.includes(market.uniqueKey)) { setSelectedMarkets(selectedMarkets.filter((m) => m.uniqueKey !== market.uniqueKey)); } else { @@ -96,8 +97,8 @@ export function RiskSelection() {
{/* Input Section */}
-

Choose collateral you want to trust

-

+

Choose collateral and oracle you trust

+

@@ -116,14 +117,12 @@ export function RiskSelection() {
-
-

Choose markets you want to trust

-

- {shouldShowMarkets - ? `selected markets: ${selectedMarkets.length}` - : 'Select collateral and oracles to view available markets'} -

-
+ {shouldShowMarkets && ( +
+

Choose markets

+

selected markets: {selectedMarkets.length}

+
+ )} {/* Markets List - Scrollable Section */}
@@ -134,12 +133,14 @@ export function RiskSelection() {

Select your preferences

{selectedCollaterals.length === 0 && 'Choose at least one collateral asset'} - {selectedCollaterals.length > 0 && selectedOracles.length === 0 && 'Now select oracle vendors'} + {selectedCollaterals.length > 0 && + selectedOracles.length === 0 && + 'Now select oracle vendors'}

) : ( - toggleMarketSelection(market)} - className={`rounded relative cursor-pointer transition-all duration-200 ease-in-out p-1 ${ + className={`relative cursor-pointer rounded p-1 transition-all duration-200 ease-in-out ${ index === 0 ? 'mt-2' : '' - } ${isSelected ? 'border-2 bg-hovered' : ''}`} + } ${isSelected ? 'bg-hovered border-2' : ''}`} >
- +
@@ -223,11 +227,7 @@ export function RiskSelection() { {/* Navigation */}
-