diff --git a/src/features/autovault/components/vault-detail/modals/vault-settings/details/EditCapsDetail.tsx b/src/features/autovault/components/vault-detail/modals/vault-settings/details/EditCapsDetail.tsx index 45067146..37dea685 100644 --- a/src/features/autovault/components/vault-detail/modals/vault-settings/details/EditCapsDetail.tsx +++ b/src/features/autovault/components/vault-detail/modals/vault-settings/details/EditCapsDetail.tsx @@ -23,6 +23,7 @@ export function EditCapsDetail({ vaultAddress, chainId, onBack }: EditCapsDetail vaultAddress, chainId, connectedAddress, + onTransactionSuccess: onBack, // Navigate AFTER tx confirms, not when sent }); const { morphoMarketV1Adapter: adapterAddress } = useMorphoMarketV1Adapters({ vaultAddress, @@ -43,9 +44,8 @@ export function EditCapsDetail({ vaultAddress, chainId, onBack }: EditCapsDetail onCancel={onBack} onSave={async (caps) => { const success = await updateCaps(caps); - if (success) { - onBack(); - } + // Don't call onBack() here - onTransactionSuccess handles navigation after tx confirms + // This allows the toast to appear before the component unmounts return success; }} /> diff --git a/src/features/autovault/components/vault-detail/settings/EditCaps.tsx b/src/features/autovault/components/vault-detail/settings/EditCaps.tsx index 954f1e60..0611ac51 100644 --- a/src/features/autovault/components/vault-detail/settings/EditCaps.tsx +++ b/src/features/autovault/components/vault-detail/settings/EditCaps.tsx @@ -1,5 +1,6 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { PlusIcon } from '@radix-ui/react-icons'; +import { toast } from 'react-toastify'; import { type Address, parseUnits, maxUint128 } from 'viem'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; @@ -47,6 +48,9 @@ export function EditCaps({ existingCaps, vaultAsset, chainId, isOwner, isUpdatin const [collateralCaps, setCollateralCaps] = useState>(new Map()); const [showAddMarketModal, setShowAddMarketModal] = useState(false); + // Track if user has made edits to prevent state reset from background refetches + const hasUserEditsRef = useRef(false); + const { markets, loading: marketsLoading } = useProcessedMarkets(); const { needSwitchChain, switchToNetwork } = useMarketNetwork({ targetChainId: chainId, @@ -67,8 +71,10 @@ export function EditCaps({ existingCaps, vaultAsset, chainId, isOwner, isUpdatin return markets.filter((m) => m.loanAsset.address.toLowerCase() === vaultAsset.toLowerCase() && m.morphoBlue.chain.id === chainId); }, [markets, vaultAsset, chainId]); - // Initialize from existing caps + // Initialize from existing caps (only on first load, not after user edits) useEffect(() => { + // Don't reset state if user has made edits - prevents losing work on background refetch + if (hasUserEditsRef.current) return; if (availableMarkets.length === 0) return; // Initialize collateral caps @@ -125,6 +131,7 @@ export function EditCaps({ existingCaps, vaultAsset, chainId, isOwner, isUpdatin }, [availableMarkets, chainId, existingCaps, findToken, vaultAssetDecimals]); const handleAddMarkets = useCallback((newMarkets: Market[]) => { + hasUserEditsRef.current = true; setMarketCaps((prev) => { const next = new Map(prev); newMarkets.forEach((market) => { @@ -155,6 +162,7 @@ export function EditCaps({ existingCaps, vaultAsset, chainId, isOwner, isUpdatin }, []); const handleUpdateMarketCap = useCallback((marketId: string, field: 'relativeCap' | 'absoluteCap', value: string) => { + hasUserEditsRef.current = true; setMarketCaps((prev) => { const next = new Map(prev); const existing = next.get(marketId.toLowerCase()); @@ -166,6 +174,7 @@ export function EditCaps({ existingCaps, vaultAsset, chainId, isOwner, isUpdatin }, []); const handleUpdateCollateralCap = useCallback((collateralAddr: string, field: 'relativeCap' | 'absoluteCap', value: string) => { + hasUserEditsRef.current = true; setCollateralCaps((prev) => { const next = new Map(prev); const existing = next.get(collateralAddr.toLowerCase()); @@ -179,6 +188,11 @@ export function EditCaps({ existingCaps, vaultAsset, chainId, isOwner, isUpdatin }); }, []); + const handleCancel = useCallback(() => { + hasUserEditsRef.current = false; + onCancel(); + }, [onCancel]); + const hasChanges = useMemo(() => { // Check for new caps const hasNewMarkets = Array.from(marketCaps.values()).some((m) => !m.existingCapId); @@ -227,7 +241,7 @@ export function EditCaps({ existingCaps, vaultAsset, chainId, isOwner, isUpdatin } if (!adapterAddress || !vaultAsset) { - console.error('Adapter address and vault asset are required'); + toast.error('Unable to save: vault data not loaded'); return; } @@ -331,11 +345,23 @@ export function EditCaps({ existingCaps, vaultAsset, chainId, isOwner, isUpdatin } } - if (capsToUpdate.length === 0) return; + if (capsToUpdate.length === 0) { + toast.info('No changes to save'); + return; + } - const success = await onSave(capsToUpdate); - if (success) { - // Parent handles switching back to read mode + try { + const success = await onSave(capsToUpdate); + if (success) { + // Reset edit tracking on successful save + hasUserEditsRef.current = false; + // Parent handles switching back to read mode + } else { + toast.error('Failed to save changes'); + } + } catch (error) { + console.error('Save error:', error); + toast.error('Failed to save changes'); } }, [marketCaps, collateralCaps, needSwitchChain, switchToNetwork, onSave, adapterAddress, vaultAsset, vaultAssetDecimals, existingCaps]); @@ -552,7 +578,7 @@ export function EditCaps({ existingCaps, vaultAsset, chainId, isOwner, isUpdatin diff --git a/src/features/positions/components/position-actions-dropdown.tsx b/src/features/positions/components/position-actions-dropdown.tsx index d29e17e1..19333661 100644 --- a/src/features/positions/components/position-actions-dropdown.tsx +++ b/src/features/positions/components/position-actions-dropdown.tsx @@ -3,6 +3,7 @@ import type React from 'react'; import { useRouter } from 'next/navigation'; import { GoHistory } from 'react-icons/go'; +import { TbReport } from 'react-icons/tb'; import { IoEllipsisVertical } from 'react-icons/io5'; import { TbArrowsRightLeft } from 'react-icons/tb'; import { Button } from '@/components/ui/button'; @@ -33,6 +34,11 @@ export function PositionActionsDropdown({ account, chainId, tokenAddress, isOwne router.push(historyUrl); }; + const handleViewReport = () => { + const reportUrl = `/positions/report/${account}?chainId=${chainId}&tokenAddress=${tokenAddress}`; + router.push(reportUrl); + }; + return (
History + } + > + View Report +
diff --git a/src/hooks/useVaultV2.ts b/src/hooks/useVaultV2.ts index 135051f2..13da3bf8 100644 --- a/src/hooks/useVaultV2.ts +++ b/src/hooks/useVaultV2.ts @@ -381,7 +381,10 @@ export function useVaultV2({ const updateCaps = useCallback( async (caps: VaultV2Cap[]): Promise => { - if (!account || !vaultAddress) return false; + if (!account || !vaultAddress) { + console.warn('updateCaps: Missing account or vault address', { account: !!account, vaultAddress: !!vaultAddress }); + return false; + } const txs: `0x${string}`[] = []; @@ -457,7 +460,7 @@ export function useVaultV2({ }); if (txs.length === 0) { - // No cap changes to apply + console.warn('updateCaps: No transactions to execute - caps data may have no actual changes', { capsCount: caps.length }); return false; }