-
Pro Mode Charts
-
- Enable advanced TradingView-style charts with live data.
- {!isConnected && " (Connect wallet to enable)"}
- {isConnected && !hasProModeAccess() && " (Requires 1,000+ TF tokens)"}
+
+
+
+
+
+ {isProMode ? : }
+
+
Pro Mode Analytics
+
+
+ Unlock advanced technical indicators and sub-second price feeds.
+ {!isConnected && (
+
+ Connect wallet to unlock
+
+ )}
+ {isConnected && !hasProModeAccess() && (
+
+ Requires 1,000+ TF utility tokens
+
+ )}
-
+
+
+
+ {isProMode ? "Active" : "Inactive"}
+
+
+
+ {/* Conditional Rendering of Pro Content */}
{isProMode && (
-
+
+ {/* Paywall Modal Overlay */}
setShowPaywall(false)}
@@ -81,6 +133,9 @@ export default function ProModeSection() {
);
}
+<<<<<<< HEAD
+=======
// Inconsequential change for repo health
// Maintenance: minor update
+>>>>>>> upstream/main
diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx
index 7f3bff7..2f51e1c 100644
--- a/src/components/SettingsModal.tsx
+++ b/src/components/SettingsModal.tsx
@@ -1,21 +1,203 @@
+/**
+ * Settings Modal Component.
+ * Provides a centralized interface for configuring trade parameters
+ * like slippage tolerance, transaction deadlines, and Expert Mode.
+ */
+
"use client";
+<<<<<<< HEAD
+import React, { useState } from 'react';
+import { X, Settings, ShieldAlert, Zap, Clock } from 'lucide-react';
+import Card from './Card';
+import { useSlippage } from '../contexts/SlippageContext';
+import { useExpertMode } from '../contexts/ExpertModeContext';
+import ExpertModeModal from './ExpertModeModal';
+=======
import React from "react";
import { X, Info } from "lucide-react";
import { useSettings } from "../lib/context/SettingsContext";
import Icon from "./ui/Icon";
+>>>>>>> upstream/main
+/**
+ * Props for the SettingsModal component.
+ */
interface SettingsModalProps {
+ /** Visibility toggle for the modal */
isOpen: boolean;
+ /** Callback to close the modal */
onClose: () => void;
}
+/**
+ * A modal overlay for managing user preferences and trade constraints.
+ */
export default function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
+<<<<<<< HEAD
+ // --- Context & Store Hooks ---
+ const {
+ slippageTolerance,
+ setSlippageTolerance,
+ isSlippageAuto,
+ setIsSlippageAuto,
+ transactionDeadline,
+ setTransactionDeadline
+ } = useSlippage();
+
+ const { isExpertMode, setExpertMode, hasAcceptedRisk, acceptRisk } = useExpertMode();
+
+ // --- Component State ---
+ /** Controls visibility of the high-risk confirmation modal */
+ const [isExpertModalOpen, setIsExpertModalOpen] = useState(false);
+
+ /** Standard slippage preset options (as percentages) */
+ const presetOptions = [0.1, 0.5, 1.0, 3.0, 5.0];
+
+ // --- Handlers ---
+
+ /**
+ * Updates the slippage tolerance to a preset value.
+ * Only applicable when "Auto" mode is disabled.
+ *
+ * @param {number} value - The preset percentage.
+ */
+ const handlePresetClick = (value: number) => {
+ if (!isSlippageAuto) {
+ setSlippageTolerance(value);
+ console.log(`[Settings] Slippage preset applied: ${value}%`);
+ }
+ };
+
+ /**
+ * Handles manual entry of custom slippage values.
+ * Constraints: 0% to 50%.
+ *
+ * @param {React.ChangeEvent} e - The input change event.
+ */
+ const handleCustomChange = (e: React.ChangeEvent) => {
+ if (!isSlippageAuto) {
+ const value = parseFloat(e.target.value);
+ if (!isNaN(value) && value >= 0 && value <= 50) {
+ setSlippageTolerance(value);
+ }
+ }
+ };
+
+ /**
+ * Toggles between automatic and manual slippage management.
+ */
+ const handleAutoToggle = () => {
+ const newAutoState = !isSlippageAuto;
+ setIsSlippageAuto(newAutoState);
+
+ // Reset to a safe protocol default (0.5%) when re-enabling Auto
+ if (newAutoState) {
+ setSlippageTolerance(0.5);
+ }
+ };
+
+ /**
+ * Toggles Expert Mode with security checks.
+ */
+ const handleExpertModeToggle = () => {
+ if (isExpertMode) {
+ // Disabling expert mode is always allowed
+ setExpertMode(false);
+ } else {
+ // Enabling expert mode requires explicit risk acknowledgment
+ if (hasAcceptedRisk) {
+ setExpertMode(true);
+ } else {
+ setIsExpertModalOpen(true);
+ }
+ }
+ };
+
+ /**
+ * Callback for the Expert Mode confirmation modal.
+ */
+ const handleExpertModeConfirm = () => {
+ acceptRisk();
+ setExpertMode(true);
+ setIsExpertModalOpen(false);
+ };
+=======
const { slippage, setSlippage, deadline, setDeadline } = useSettings();
+>>>>>>> upstream/main
+ // 1. Conditional Rendering for Visibility
if (!isOpen) return null;
return (
+<<<<<<< HEAD
+ <>
+
+
+ {/* Modal Header */}
+
+
+
+ {/* --- Expert Mode Configuration --- */}
+
+
+
+
Expert Mode
+
+
+ Allows high-slippage trades and customizes complex transaction parameters. Use with extreme caution.
+
+
+
+ Active Status
+
+
+
+
+
+
+ {/* Slippage Tolerance */}
+
+
Slippage Tolerance
+
+ Your transaction will revert if the price changes unfavorably by more than this percentage
+
+
+ {/* Auto Toggle */}
+
+
+
Auto Slippage
+
Use algorithmically safe default (0.5%)
+=======
@@ -41,6 +223,7 @@ export default function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
Your transaction will revert if the price changes unfavorably by more than this percentage.
+>>>>>>> upstream/main
@@ -69,10 +252,68 @@ export default function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
className="w-full bg-slate-800 border border-slate-700 rounded-xl py-2 px-3 text-sm text-white focus:outline-none focus:ring-2 focus:ring-blue-500/50 pr-8"
placeholder="Custom"
/>
+<<<<<<< HEAD
+ %
+
+
+ {/* Auto Indicator */}
+ {isSlippageAuto && (
+
+
+ 🤖 Auto slippage is enabled at 0.5% - Turn off "Auto" to set custom values
+
+
+ )}
+
+ {/* Warnings - Only show when not in Auto mode */}
+ {!isSlippageAuto && (
+ <>
+ {slippageTolerance < 0.1 && (
+
+ Your transaction may fail
+
+ )}
+ {slippageTolerance > 5 && (
+
+ High slippage tolerance may result in a bad trade
+
+ )}
+ >
+ )}
+ {/* Transaction Deadline */}
+
+
+
+
Transaction Deadline
+
+
+ Your transaction will revert if it is pending for more than this period.
+
+
+ {
+ const val = parseInt(e.target.value);
+ if (!isNaN(val) && val > 0) setTransactionDeadline(val);
+ }}
+ min="1"
+ className="w-20 bg-slate-800/50 border border-slate-700 rounded-xl px-4 py-2.5 text-white font-mono font-bold focus:outline-none focus:border-blue-500 transition-colors"
+ placeholder="20"
+ />
+ Minutes
+
+
+
+
+
+
+=======
%
+>>>>>>> upstream/main
{/* Transaction Deadline */}
@@ -119,6 +360,9 @@ export default function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
);
}
+<<<<<<< HEAD
+=======
// Inconsequential change for repo health
// Maintenance: minor update
+>>>>>>> upstream/main
diff --git a/src/components/SkeletonRow.tsx b/src/components/SkeletonRow.tsx
index ee2912a..02029db 100644
--- a/src/components/SkeletonRow.tsx
+++ b/src/components/SkeletonRow.tsx
@@ -1,19 +1,44 @@
+/**
+ * Skeleton Row Component.
+ * A loading placeholder for table rows, providing visual feedback while
+ * asynchronous data (like invoice lists) is being fetched.
+ */
+
import React from 'react';
+/**
+ * Renders a set of pulsing placeholder elements that mimic the layout
+ * of a standard table row.
+ */
const SkeletonRow = () => {
return (
-
-
-
+
+ {/* 1. Asset ID Placeholder */}
+
+
+
+
+ {/* 2. Amount Placeholder */}
+
+
-
-
+
+ {/* 3. Interest/Badge Placeholder */}
+
+
-
-
+
+ {/* 4. Status Placeholder */}
+
+
-
-
+
+ {/* 5. Action Button Placeholder */}
+
+
);
@@ -21,6 +46,9 @@ const SkeletonRow = () => {
export default SkeletonRow;
+<<<<<<< HEAD
+=======
// Inconsequential change for repo health
// Maintenance: minor update
+>>>>>>> upstream/main
diff --git a/src/components/StarIcon.tsx b/src/components/StarIcon.tsx
index 7c55b44..a8f3a03 100644
--- a/src/components/StarIcon.tsx
+++ b/src/components/StarIcon.tsx
@@ -1,36 +1,68 @@
+<<<<<<< HEAD
+/**
+ * Star Icon Component.
+ * A reusable interactive toggle for marking assets as favorites or
+ * adding them to a watchlist.
+ */
+=======
"use client";
+>>>>>>> upstream/main
import React from 'react';
import { Star } from 'lucide-react';
import Icon from './ui/Icon';
+/**
+ * Props for the StarIcon component.
+ */
interface StarIconProps {
+ /** Current state of the star (filled vs outline) */
isStarred: boolean;
- onClick: () => void;
+ /** Callback triggered when the icon is clicked */
+ onClick: (e: React.MouseEvent) => void;
+ /** Size of the icon in pixels (default: 16) */
size?: number;
+ /** Additional CSS classes for custom styling */
className?: string;
}
-export default function StarIcon({ isStarred, onClick, size = 16, className = '' }: StarIconProps) {
+/**
+ * A stylized star toggle button with smooth transitions and accessibility.
+ */
+export default function StarIcon({
+ isStarred,
+ onClick,
+ size = 16,
+ className = ''
+}: StarIconProps) {
return (
{
+ e.stopPropagation(); // Prevent triggering parent click handlers
+ onClick(e);
+ }}
+ className={`p-2 rounded-xl transition-all duration-300 hover:bg-yellow-400/10 group active:scale-[0.85] ${className}`}
aria-label={isStarred ? 'Remove from watchlist' : 'Add to watchlist'}
+ aria-pressed={isStarred}
+ title={isStarred ? 'Remove from watchlist' : 'Add to watchlist'}
>
);
}
+<<<<<<< HEAD
+=======
// Inconsequential change for repo health
// Maintenance: minor update
+>>>>>>> upstream/main
diff --git a/src/components/TabNavigation.tsx b/src/components/TabNavigation.tsx
index 83fe389..6e1d08b 100644
--- a/src/components/TabNavigation.tsx
+++ b/src/components/TabNavigation.tsx
@@ -1,45 +1,101 @@
+<<<<<<< HEAD
+/**
+ * Tab Navigation Component.
+ * A reusable horizontal navigation bar for switching between different
+ * content views within a page.
+ */
+=======
"use client";
import React, { useState } from 'react';
+>>>>>>> upstream/main
+import React from 'react';
+
+/**
+ * Represents a single tab item in the navigation.
+ */
interface Tab {
+ /** Unique identifier for the tab (e.g., "overview") */
id: string;
+ /** Human-readable text label for the tab */
label: string;
+ /** Optional Lucide icon to display alongside the label */
icon?: React.ReactNode;
}
+/**
+ * Props for the TabNavigation component.
+ */
interface TabNavigationProps {
+ /** Array of tab definitions to render */
tabs: Tab[];
+ /** The ID of the currently selected tab */
activeTab: string;
+ /** Callback triggered when a new tab is selected */
onTabChange: (tabId: string) => void;
+ /** Additional CSS classes for the container */
className?: string;
}
-export default function TabNavigation({ tabs, activeTab, onTabChange, className = '' }: TabNavigationProps) {
+/**
+ * A stylized tab bar with animated indicators and accessibility support.
+ */
+export default function TabNavigation({
+ tabs,
+ activeTab,
+ onTabChange,
+ className = ''
+}: TabNavigationProps) {
return (
-
-
- {tabs.map((tab) => (
- onTabChange(tab.id)}
- className={`py-3 px-1 border-b-2 font-medium text-sm transition-colors ${
- activeTab === tab.id
- ? 'border-tradeflow-accent text-tradeflow-accent'
- : 'border-transparent text-tradeflow-muted hover:text-white hover:border-tradeflow-muted/50'
- }`}
- >
-
- {tab.icon}
- {tab.label}
-
-
- ))}
+
+
+ {tabs.map((tab) => {
+ const isActive = activeTab === tab.id;
+
+ return (
+ onTabChange(tab.id)}
+ className={`py-4 px-2 border-b-2 font-bold text-xs uppercase tracking-widest transition-all duration-300 relative group ${
+ isActive
+ ? 'border-blue-500 text-blue-400'
+ : 'border-transparent text-slate-500 hover:text-slate-300 hover:border-slate-700'
+ }`}
+ role="tab"
+ aria-selected={isActive}
+ aria-controls={`${tab.id}-panel`}
+ id={`${tab.id}-tab`}
+ >
+
+ {/* Optional Icon Rendering */}
+ {tab.icon && (
+
+ {tab.icon}
+
+ )}
+ {tab.label}
+
+
+ {/* Active Indicator Glow */}
+ {isActive && (
+
+ )}
+
+ );
+ })}
);
}
+<<<<<<< HEAD
+=======
// Inconsequential change for repo health
// Maintenance: minor update
+>>>>>>> upstream/main
diff --git a/src/components/TokenDropdown.tsx b/src/components/TokenDropdown.tsx
index 9dea944..5aa548b 100644
--- a/src/components/TokenDropdown.tsx
+++ b/src/components/TokenDropdown.tsx
@@ -1,7 +1,13 @@
+/**
+ * Token Dropdown Component.
+ * Allows users to select an asset for trading. Includes search functionality,
+ * recent token history, and watchlist integration.
+ */
+
"use client";
import React, { useState, useRef, useEffect, useMemo } from "react";
-import { ChevronDown, Search, X, Copy } from "lucide-react";
+import { ChevronDown, Search, X, Copy, History, Star } from "lucide-react";
import { useDebounce } from "../hooks/useDebounce";
import { useRecentTokens } from "../hooks/useRecentTokens";
import { useWatchlist } from "../hooks/useWatchlist";
@@ -10,27 +16,37 @@ import { showError, showSuccess } from "../lib/toast";
import toast from "react-hot-toast";
import Icon from "./ui/Icon";
+/**
+ * Props for the TokenDropdown component.
+ */
interface TokenDropdownProps {
+ /** Callback triggered when the selected asset changes */
onTokenChange?: (token: string) => void;
}
+/**
+ * A specialized searchable dropdown for Stellar assets.
+ */
export default function TokenDropdown({ onTokenChange }: TokenDropdownProps) {
+ // --- Component State ---
const [selectedToken, setSelectedToken] = useState("XLM");
const [isOpen, setIsOpen] = useState(false);
const [searchInput, setSearchInput] = useState("");
+
+ // --- Refs ---
const dropdownRef = useRef(null);
const searchInputRef = useRef(null);
+
+ // --- Custom Hooks ---
const { recentTokens, addRecentToken } = useRecentTokens();
const { toggleWatchlist, isInWatchlist } = useWatchlist();
-
- // Debounce the search input with 300ms delay
const debouncedSearch = useDebounce(searchInput, 300);
- // Hardcoded array of tokens
+ /** List of available assets (standardized symbols) */
const tokens = ["XLM", "USDC", "yXLM"];
- /** * ISSUE #86: Mock addresses for copy functionality.
- * In a production environment, these would come from a token list API.
+ /**
+ * Mock contract addresses for demonstration.
*/
const mockAddresses: Record = {
"XLM": "native",
@@ -38,34 +54,46 @@ export default function TokenDropdown({ onTokenChange }: TokenDropdownProps) {
"yXLM": "CBP3T2... (Yield Stellar Asset)",
};
- // Memoize filtered tokens based on debounced search value
+ /**
+ * Filtered token list based on the user's search query.
+ */
const filteredTokens = useMemo(() => {
return tokens.filter((token) =>
token.toLowerCase().includes(debouncedSearch.toLowerCase())
);
}, [debouncedSearch]);
+ // --- Handlers ---
+
+ /**
+ * Effect: Handles clicks outside the component to close the dropdown.
+ */
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
- setSearchInput(""); // Clear search when closing
+ setSearchInput("");
}
};
document.addEventListener("mousedown", handleClickOutside);
- return () => {
- document.removeEventListener("mousedown", handleClickOutside);
- };
+ return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
- // Focus search input when dropdown opens
+ /**
+ * Effect: Automatically focus the search input when the dropdown opens.
+ */
useEffect(() => {
if (isOpen && searchInputRef.current) {
searchInputRef.current.focus();
}
}, [isOpen]);
+ /**
+ * Updates the selected asset and persists to history.
+ *
+ * @param {string} token - The asset symbol.
+ */
const handleTokenSelect = (token: string) => {
setSelectedToken(token);
addRecentToken(token);
@@ -76,60 +104,124 @@ export default function TokenDropdown({ onTokenChange }: TokenDropdownProps) {
};
/**
- * ISSUE #86: Clipboard handler.
- * Uses stopPropagation to ensure the dropdown doesn't close when copying.
+ * Copies the token's contract address to the clipboard.
*/
const handleCopyAddress = (e: React.MouseEvent, token: string) => {
- e.stopPropagation();
+ e.stopPropagation(); // Prevent dropdown from closing
const address = mockAddresses[token] || "Address not found";
navigator.clipboard.writeText(address)
+<<<<<<< HEAD
+ .then(() => toast.success("Contract address copied!"))
+ .catch(() => toast.error("Failed to copy address"));
+=======
.then(() => {
showSuccess("Token Address Copied!");
})
.catch(() => {
showError("Failed to copy address");
});
+>>>>>>> upstream/main
};
return (
- {/* Selected Token Button */}
+ {/* Trigger Button */}
setIsOpen(!isOpen)}
- className="flex items-center gap-2 bg-slate-800 hover:bg-slate-700 border border-slate-600 rounded-lg px-4 py-2 transition-colors min-w-[120px] justify-between"
+ className="flex items-center gap-2 bg-slate-800/80 hover:bg-slate-700/80 border border-slate-700 rounded-xl px-4 py-2.5 transition-all min-w-[130px] justify-between shadow-sm active:scale-[0.98]"
+ aria-haspopup="listbox"
+ aria-expanded={isOpen}
>
+<<<<<<< HEAD
+ {selectedToken}
+
+=======
{selectedToken}
+>>>>>>> upstream/main
- {/* Dropdown Menu */}
+ {/* Dropdown Content */}
{isOpen && (
+<<<<<<< HEAD
+
+ {/* Search Header */}
+
+
+
+=======
{/* Search Input */}
+<<<<<<< HEAD
+ {/* List Content */}
+
+ {filteredTokens.length > 0 ? (
+
+ {/* 1. Recent Assets Section */}
+ {searchInput === "" && recentTokens.length > 0 && (
+ <>
+
+ Recent History
+
+ {recentTokens.map((token) => (
+
handleTokenSelect(token)}
+ className="w-full text-left px-3 py-2.5 rounded-xl transition-all flex items-center justify-between hover:bg-slate-700/50 text-white group"
+ >
+
+
{token}
+
handleCopyAddress(e, token)}
+ className="opacity-0 group-hover:opacity-100 text-slate-500 hover:text-blue-400 transition-all p-1"
+ title="Copy Contract"
+ >
+
+
+
+ {
+ e.stopPropagation();
+ toggleWatchlist(token);
+ }}
+ size={12}
+=======
{/* Token List */}
{filteredTokens.length > 0 ? (
@@ -158,8 +250,55 @@ export default function TokenDropdown({ onTokenChange }: TokenDropdownProps) {
toggleWatchlist(token)}
+>>>>>>> upstream/main
/>
+
+ ))}
+
+ >
+ )}
+
+ {/* 2. All Assets Section */}
+
+ All Protocol Assets
+
+ {filteredTokens.map((token) => (
+ handleTokenSelect(token)}
+ className={`w-full text-left px-3 py-2.5 rounded-xl transition-all flex items-center justify-between group ${
+ token === selectedToken
+ ? "bg-blue-600/10 text-blue-400 border border-blue-500/20"
+ : "hover:bg-slate-700/50 text-white"
+ }`}
+ >
+
+
{token}
+
handleCopyAddress(e, token)}
+ className="opacity-0 group-hover:opacity-100 text-slate-500 hover:text-blue-400 transition-all p-1"
+ title="Copy Contract"
+ >
+
+<<<<<<< HEAD
+
+
+
{
+ e.stopPropagation();
+ toggleWatchlist(token);
+ }}
+ size={12}
+ />
+ {token === selectedToken && (
+
+ )}
+
+
+ ))}
+=======
{token === selectedToken && (
)}
@@ -208,18 +347,28 @@ export default function TokenDropdown({ onTokenChange }: TokenDropdownProps) {
+>>>>>>> upstream/main
-
No tokens found
-
- Try searching for a different name or pasting a valid Stellar contract address.
-
-
- )}
+ ) : (
+
+
+
+
+
Asset Not Found
+
+ Try searching for a different name or symbol.
+
+
+ )}
+
)}
);
}
+<<<<<<< HEAD
+=======
// Inconsequential change for repo health
// Maintenance: minor update
+>>>>>>> upstream/main
diff --git a/src/components/TokenInput.tsx b/src/components/TokenInput.tsx
index 35cbd88..308afda 100644
--- a/src/components/TokenInput.tsx
+++ b/src/components/TokenInput.tsx
@@ -1,10 +1,79 @@
+<<<<<<< HEAD
+/**
+ * Token Input Component.
+ * A specialized numerical input for asset amounts. Features validation for
+ * decimal numbers and a "MAX" utility for quick balance allocation.
+ */
+=======
"use client";
+>>>>>>> upstream/main
import React from 'react';
import { ChevronDown } from 'lucide-react';
import Icon from './ui/Icon';
+/**
+ * Props for the TokenInput component.
+ */
interface TokenInputProps {
+<<<<<<< HEAD
+ /** The current numeric value as a string */
+ value: string;
+ /** Callback triggered when the input value changes */
+ onChange: (value: string) => void;
+ /** The user's available balance for this asset */
+ balance: string;
+ /** Optional placeholder text (default: "0.00") */
+ placeholder?: string;
+ /** Optional ID for form labeling */
+ id?: string;
+}
+
+/**
+ * A robust numerical input with built-in validation and balance utilities.
+ */
+const TokenInput: React.FC
= ({
+ value,
+ onChange,
+ balance,
+ placeholder = "0.00",
+ id
+}) => {
+ return (
+
+ {
+ const val = e.target.value.replace(',', '.'); // Normalize decimal separators
+ if (val === '' || /^[0-9]*\.?[0-9]*$/.test(val)) {
+ onChange(val);
+ }
+ }}
+ placeholder={placeholder}
+ className="w-full bg-slate-900/50 border border-slate-700 rounded-2xl px-4 py-4 pr-16 text-lg font-mono font-bold text-white placeholder-slate-600 focus:outline-none focus:ring-2 focus:ring-blue-500/50 focus:border-blue-500 transition-all group-hover:border-slate-600"
+ aria-label="Token Amount"
+ />
+
+ {/* MAX Utility Button */}
+ {
+ onChange(balance);
+ console.log(`[TokenInput] Balance maximized: ${balance}`);
+ }}
+ className="absolute right-3 px-2.5 py-1.5 text-[10px] font-black text-blue-400 hover:text-white hover:bg-blue-600 bg-blue-500/10 rounded-xl transition-all active:scale-90"
+ title={`Set to maximum balance: ${balance}`}
+ >
+ MAX
+
+=======
label: string;
value: string;
tokenSymbol: string;
@@ -31,9 +100,14 @@ export const TokenInput: React.FC = ({ label, value, tokenSymbo
+>>>>>>> upstream/main
);
};
// Inconsequential change for repo health
+<<<<<<< HEAD
+export default TokenInput;
+=======
// Maintenance: minor update
+>>>>>>> upstream/main
diff --git a/src/components/TradeReviewModal.tsx b/src/components/TradeReviewModal.tsx
index 511cb21..04e1dc2 100644
--- a/src/components/TradeReviewModal.tsx
+++ b/src/components/TradeReviewModal.tsx
@@ -1,26 +1,53 @@
+/**
+ * Trade Review Modal Component.
+ * Provides a final breakdown of trade parameters (amounts, impact, fees, routing)
+ * before the user signs the transaction in their wallet.
+ */
+
"use client";
import React, { useState } from "react";
-import { X, ArrowDown, Info } from "lucide-react";
+import { X, ArrowDown, Info, ShieldCheck, Zap } from "lucide-react";
import Card from "./Card";
import Button from "./ui/Button";
+<<<<<<< HEAD
+import WhaleConfetti from "./ui/WhaleConfetti";
+=======
import WhaleConfetti from "./ui/WhaleConfetti"; // ← New import for confetti
import Icon from "./ui/Icon";
+>>>>>>> upstream/main
+/**
+ * Props for the TradeReviewModal component.
+ */
interface TradeReviewModalProps {
+ /** Visibility toggle for the modal */
isOpen: boolean;
+ /** Callback to close/cancel the review */
onClose: () => void;
+ /** Callback triggered when the user confirms the trade */
onConfirm: () => void;
+ /** The numeric amount of the source asset */
fromAmount: string;
+ /** Symbol of the source asset (e.g., "XLM") */
fromToken: string;
+ /** The estimated amount of the destination asset */
toAmount: string;
+ /** Symbol of the destination asset (e.g., "USDC") */
toToken: string;
+ /** Calculated price impact as a percentage */
priceImpact: number;
+ /** Maximum slippage tolerance allowed for this trade */
slippageTolerance: number;
+ /** Estimated protocol fee for the swap */
fee: string;
+ /** Human-readable routing path (e.g., "XLM -> USDC") */
route: string;
}
+/**
+ * A modal overlay for verifying trade details and initiating signing.
+ */
export default function TradeReviewModal({
isOpen,
onClose,
@@ -34,13 +61,19 @@ export default function TradeReviewModal({
fee,
route,
}: TradeReviewModalProps) {
+ // --- Component State ---
+ /** Controls the celebratory confetti for high-value trades */
const [showConfetti, setShowConfetti] = useState(false);
- // Calculate approximate USD value for whale detection
+ /**
+ * Calculates the approximate USD value of the trade for analytics and UX.
+ *
+ * @returns {number} The estimated USD value.
+ */
const calculateUSDValue = (): number => {
const amount = parseFloat(fromAmount) || 0;
- // Mock conversion rates (replace with real price feed later)
+ // Mock conversion rates (TODO: Connect to a real-time price oracle)
const mockRates: Record
= {
'XLM': 0.12,
'USDC': 1.00,
@@ -53,15 +86,19 @@ export default function TradeReviewModal({
return amount * rate;
};
+ /**
+ * Handles the confirmation action, checking for "whale" thresholds.
+ */
const handleConfirm = async () => {
const usdValue = calculateUSDValue();
- // Trigger confetti for "whale" trades (over $10,000 USD)
+ // Trigger celebratory confetti for trades exceeding $10,000 USD
if (usdValue > 10000) {
setShowConfetti(true);
+ console.log(`[TradeReview] Whale swap detected: $${usdValue.toLocaleString()}`);
}
- // Call the original onConfirm handler
+ // Initiate the transaction signing flow
await onConfirm();
};
@@ -69,98 +106,131 @@ export default function TradeReviewModal({
setShowConfetti(false);
};
+ // 1. Conditional Rendering for Visibility
if (!isOpen) return null;
return (
<>
- {/* Confetti Celebration for Whale Swaps */}
+ {/* Confetti celebration logic */}
- {/* Main Modal */}
-
-
-
-
Review Trade
+ {/* Backdrop & Modal Container */}
+
+
+ {/* Header */}
+
-
- {/* Swap Summary */}
-
+
+ {/* Swap Visualization */}
+
-
You Pay
-
- {fromAmount} {fromToken}
+
You Pay
+
+ {fromAmount} {fromToken}
+<<<<<<< HEAD
+
+
+
+
+=======
-
You Receive
-
- {toAmount} {toToken}
+
You Receive
+
+ {toAmount} {toToken}
- {/* Details List */}
-
-
-
Price Impact
-
5 ? "text-red-400 font-bold" : "text-slate-200"}`}>
+ {/* Parameter Breakdown */}
+
+
+ Price Impact
+ 5 ? "text-rose-400" : "text-slate-300"}`}>
{priceImpact.toFixed(2)}%
-
-
Max Slippage
-
{slippageTolerance}%
+
+ Max Slippage
+ {slippageTolerance}%
-
-
Fee (0.3%)
-
{fee}
+
+ Protocol Fee (0.3%)
+ {fee}
+<<<<<<< HEAD
+
+
+ Network Cost
+=======
Network Cost
~0.00001 XLM
+>>>>>>> upstream/main
+ ~0.00001 XLM
-
-
Routing Path
-
{route}
+
+
Routing
+
+
+ {route}
+
-
+ {/* Action Buttons */}
+
Confirm & Sign in Wallet
- Cancel
+ Cancel Trade
@@ -168,6 +238,9 @@ export default function TradeReviewModal({
>
);
}
+<<<<<<< HEAD
+=======
// Inconsequential change for repo health
// Maintenance: minor update
+>>>>>>> upstream/main
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
index f36a37c..b32eee0 100644
--- a/src/components/layout/Footer.tsx
+++ b/src/components/layout/Footer.tsx
@@ -180,3 +180,4 @@ export default function Footer() {
// Inconsequential change for repo health
// Maintenance: minor update
+
diff --git a/src/contexts/SlippageContext.tsx b/src/contexts/SlippageContext.tsx
index ea7892a..a1e8ff8 100644
--- a/src/contexts/SlippageContext.tsx
+++ b/src/contexts/SlippageContext.tsx
@@ -1,23 +1,65 @@
+/**
+ * Slippage and Transaction Context.
+ * Manages global trade constraints including slippage tolerance and
+ * transaction expiration deadlines.
+ */
+
"use client";
-import React, { createContext, useContext, useState, ReactNode } from 'react';
+import React, { createContext, useContext, useState, ReactNode, useEffect } from 'react';
+/**
+ * Shape of the Slippage context state and actions.
+ */
interface SlippageContextType {
+ /** Maximum price change allowed between submission and execution (percentage) */
slippageTolerance: number;
+ /** Updates the slippage tolerance value */
setSlippageTolerance: (value: number) => void;
+ /** True if the protocol should automatically determine optimal slippage */
isSlippageAuto: boolean;
+ /** Toggles the automatic slippage logic */
setIsSlippageAuto: (value: boolean) => void;
- transactionDeadline: number; // in minutes
+ /** Time in minutes before a pending transaction is considered expired */
+ transactionDeadline: number;
+ /** Updates the transaction expiration deadline */
setTransactionDeadline: (value: number) => void;
}
+/** Internal context object for slippage settings */
const SlippageContext = createContext
(undefined);
+/**
+ * Provider component that wraps the application to provide slippage settings.
+ */
export function SlippageProvider({ children }: { children: ReactNode }) {
+ // --- State Initialization ---
+ /** Defaults to 0.5% - a safe standard for most high-liquidity assets */
const [slippageTolerance, setSlippageTolerance] = useState(0.5);
+ /** Defaults to true to simplify the UX for new users */
const [isSlippageAuto, setIsSlippageAuto] = useState(true);
+ /** Defaults to 20 minutes - standard for Stellar network congestion */
const [transactionDeadline, setTransactionDeadline] = useState(20);
+ /**
+ * Effect: Persist settings to localStorage to maintain preferences across sessions.
+ */
+ useEffect(() => {
+ const savedSlippage = localStorage.getItem('tradeflow_slippage');
+ const savedDeadline = localStorage.getItem('tradeflow_deadline');
+
+ if (savedSlippage) setSlippageTolerance(parseFloat(savedSlippage));
+ if (savedDeadline) setTransactionDeadline(parseInt(savedDeadline));
+ }, []);
+
+ /**
+ * Effect: Save to localStorage whenever values change.
+ */
+ useEffect(() => {
+ localStorage.setItem('tradeflow_slippage', slippageTolerance.toString());
+ localStorage.setItem('tradeflow_deadline', transactionDeadline.toString());
+ }, [slippageTolerance, transactionDeadline]);
+
return (
>>>>>> upstream/main