Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 18 additions & 18 deletions app/positions/components/PositionsContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean>(false);
const [showWithdrawModal, setShowWithdrawModal] = useState<boolean>(false);
const [showOnboardingModal, setShowOnboardingModal] = useState<boolean>(false);
const [selectedPosition, setSelectedPosition] = useState<MarketPosition | null>(null);

const { account } = useParams<{ account: string }>();
Expand Down Expand Up @@ -84,18 +86,17 @@ export default function Positions() {
</Button>
</Link>
{isOwner && (
<Link href="/positions/onboarding">
<Button
variant="solid"
color="primary"
size="md"
className="font-zen"
disabled={account !== address}
>
<FaPlus size={14} className="mr-2" />
New Position
</Button>
</Link>
<Button
variant="solid"
color="primary"
size="md"
className="font-zen"
isDisabled={account !== address}
onClick={() => setShowOnboardingModal(true)}
>
<FaPlus size={14} className="mr-2" />
New Position
</Button>
)}
</div>
</div>
Expand All @@ -121,17 +122,16 @@ export default function Positions() {
/>
)}

<OnboardingModal
isOpen={showOnboardingModal}
onClose={() => setShowOnboardingModal(false)}
/>

{isLoading ? (
<LoadingScreen message="Loading Supplies..." />
) : !hasSuppliedMarkets ? (
<div className="flex flex-col items-center gap-8">
<EmptyScreen message="No open supplies. Start lending now!" />
<Link href="/positions/onboarding">
<Button variant="solid" color="primary" size="lg" className="font-zen">
<FaPlus size={14} className="mr-2" />
Start Lending
</Button>
</Link>
</div>
) : (
<div className="mt-4">
Expand Down
14 changes: 0 additions & 14 deletions app/positions/components/SmartOnboarding.tsx

This file was deleted.

53 changes: 33 additions & 20 deletions app/positions/components/onboarding/AssetSelection.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useMemo } from 'react';
import { Button } from '@nextui-org/react';
import { motion } from 'framer-motion';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { formatUnits } from 'viem';
import { Button } from '@/components/common/Button';
import { Spinner } from '@/components/common/Spinner';
import { useMarkets } from '@/hooks/useMarkets';
import { useUserBalances } from '@/hooks/useUserBalances';
import { formatBalance } from '@/utils/balance';
import { getNetworkImg, getNetworkName } from '@/utils/networks';
import { getNetworkImg, getNetworkName, SupportedNetworks } from '@/utils/networks';
import { useOnboarding } from './OnboardingContext';
import { TokenWithMarkets } from './types';

Expand All @@ -27,8 +27,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 [];
Expand Down Expand Up @@ -72,28 +71,22 @@ 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 (
<div className="flex h-full flex-col">
<div>
<h2 className="font-zen text-2xl">Select an Asset</h2>
<p className="mt-2 text-gray-400">Choose which asset you want to supply</p>
<div className="flex-col">
<div className="flex min-h-[400px] items-center justify-center">
{' '}
<Spinner />{' '}
</div>
<div className="mt-6">Loading...</div>
</div>
);
}

return (
<div className="flex h-full flex-col">
<div>
<h2 className="font-zen text-2xl">Select an Asset</h2>
<p className="mt-2 text-gray-400">Choose which asset you want to supply</p>
</div>

{tokensWithMarkets.length === 0 ? (
<div className="mt-6 flex flex-col items-center justify-center gap-4 rounded border border-gray-200 p-8 text-center dark:border-gray-700">
<p className="text-lg">No assets available</p>
Expand All @@ -107,15 +100,14 @@ export function AssetSelection() {
</Link>
</div>
) : (
<div className="mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<div className="mt-6 grid grid-cols-1 gap-4 sm:grid-cols-1 lg:grid-cols-2">
{tokensWithMarkets.map((token) => (
<motion.button
aria-label={`Select ${token.symbol} on ${getNetworkName(token.network)}`}
role="button"
key={`${token.symbol}-${token.network}`}
onClick={() => 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 }}
>
<div className="relative flex h-12 w-12 items-center justify-center rounded-full bg-gray-100 transition-transform duration-300 group-hover:scale-110 dark:bg-gray-700">
Expand All @@ -139,12 +131,33 @@ export function AssetSelection() {
<NetworkIcon networkId={token.network} />
<span>{getNetworkName(token.network)}</span>
</div>

{/* if base network, show agent badge */}
{token.network === SupportedNetworks.Base && (
// <Tooltip
// content={
// <div className="flex flex-col gap-2 p-2 font-zen">
// <div className="text-base">Monarch Autopilot 🎉</div>
// <div className="text-sm">
// Monarch Autopilot is now in beta on Base! Setup the agent to start
// automating your reallocations.
// </div>
// </div>
// }
// >
// <div className="flex gap-2 rounded bg-primary bg-opacity-50 px-1.5 py-0.5 text-xs text-gray-100">
// 🤖
// <span className="opacity-100">beta</span>
// </div>
// </Tooltip>
<div />
)}
</div>
</div>

<div className="space-y-2">
<p className="text-sm text-gray-400 transition-opacity duration-300 group-hover:opacity-80">
Balance: {formatBalance(token.balance, token.decimals)} {token.symbol}
Balance: {formatUnits(BigInt(token.balance), token.decimals)} {token.symbol}
</p>
<div className="flex flex-col gap-1">
<div className="flex items-center gap-2">
Expand Down
101 changes: 101 additions & 0 deletions app/positions/components/onboarding/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Modal, ModalContent, ModalHeader, Button } from '@nextui-org/react';
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 { SuccessPage } from './SuccessPage';

const StepComponents = {
'asset-selection': AssetSelection,
'risk-selection': RiskSelection,
setup: SetupPositions,
success: SuccessPage,
} as const;

function StepIndicator({ currentStep }: { currentStep: string }) {
const currentIndex = ONBOARDING_STEPS.findIndex((s) => s.id === currentStep);

return (
<div className="flex w-full items-center justify-center gap-2 px-4">
{ONBOARDING_STEPS.map((step, index) => {
const isPast = index < currentIndex;
const isCurrent = index === currentIndex;

return (
<div key={step.id} className="flex items-center">
<div
className={`h-[6px] w-8 gap-2 rounded transition-colors duration-300 ${
isCurrent
? 'bg-primary'
: isPast
? 'bg-primary bg-opacity-50'
: 'bg-gray-200 dark:bg-gray-700'
}`}
/>
</div>
);
})}
</div>
);
}

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];

return (
<Modal
isOpen={isOpen}
onClose={onClose}
size="3xl"
scrollBehavior="inside"
classNames={{
base: 'bg-background',
body: 'py-6',
closeButton: 'hidden',
}}
>
<ModalContent className="p-4">
{/* Header */}
<ModalHeader className="flex justify-between">
<div>
<h2 className="font-zen text-2xl font-normal">
{ONBOARDING_STEPS[currentStepIndex].title}
</h2>
<p className="mt-1 font-zen text-sm font-normal text-secondary">
{ONBOARDING_STEPS[currentStepIndex].description}
</p>
</div>
<Button isIconOnly onClick={onClose} className="bg-surface">
<RxCross2 size={16} />
</Button>
</ModalHeader>

{/* Content */}
<div className="flex-1 overflow-hidden px-6">
<AnimatePresence mode="wait">
<motion.div
key={step}
className="h-full overflow-y-auto font-zen"
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.2, ease: 'easeInOut' }}
>
<CurrentStepComponent onClose={onClose} />
</motion.div>
</AnimatePresence>
</div>

{/* Footer with Step Indicator */}
<div className="mt-6 pt-4">
<StepIndicator currentStep={step} />
</div>
</ModalContent>
</Modal>
);
}
40 changes: 0 additions & 40 deletions app/positions/components/onboarding/OnboardingContent.tsx

This file was deleted.

Loading