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
25 changes: 22 additions & 3 deletions src/app/api/pnl/route.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
/**
* Profit and Loss (PnL) API Route.
* Generates synthetic historical performance data for the dashboard charts.
* This is used for demonstrating portfolio tracking features.
*/

import { NextResponse } from 'next/server';

/**
* Historical data point for the PnL chart.
*/
interface PnLData {
/** Localized date string (e.g., "Jan 12") */
date: string;
/** The portfolio value at that specific point in time */
value: number;
}

/**
* GET handler for the PnL endpoint.
* Returns a 30-day series of simulated portfolio values.
*/
export async function GET() {
// Generate dummy PnL data for the last 30 days
// Generate mock PnL data for the last 30 days
const data: PnLData[] = [];
const today = new Date();
let currentValue = 10000; // Starting value

// Starting seed value for the simulation
let currentValue = 10000;

for (let i = 29; i >= 0; i--) {
const date = new Date(today);
date.setDate(date.getDate() - i);

// Random walk with slight upward trend
// Simulate a random walk with a slight positive bias (0.45 instead of 0.50)
// and a volatility factor of 200
const change = (Math.random() - 0.45) * 200;
currentValue += change;

Expand All @@ -25,6 +43,7 @@ export async function GET() {
});
}

// Return the series as a JSON response
return NextResponse.json(data);
}

Expand Down
23 changes: 22 additions & 1 deletion src/app/api/upload/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
/**
* Document Upload API Route.
* Handles the secure uploading of invoice documents to IPFS/Pinata.
* Currently disabled for maintenance or pending further security implementation.
*/

import { NextRequest, NextResponse } from 'next/server';

/**
* POST handler for the upload endpoint.
* Currently returns a 503 Service Unavailable error as the feature is locked.
*
* @param {NextRequest} request - The incoming upload request.
*/
export async function POST(request: NextRequest) {
// 1. Log the attempt for security auditing
const clientIp = request.headers.get('x-forwarded-for') || 'unknown';
console.log(`[UploadAPI] Blocked upload attempt from ${clientIp}`);

// 2. Return a consistent error response
return NextResponse.json(
{ error: 'Upload temporarily disabled' },
{
error: 'Upload service temporarily disabled',
reason: 'Undergoing maintenance',
retryAfter: 3600
},
{ status: 503 }
);
}
Expand Down
23 changes: 22 additions & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* TradeFlow Main Dashboard Page.
* This is the primary entry point for the application, providing users with
* a high-level overview of their assets, protocol status, and the RWA pipeline.
*/

"use client";

import React, { useState, useEffect, useRef } from "react";
Expand Down Expand Up @@ -29,15 +35,24 @@ import { useWalletConnection } from "../stores/useWeb3Store";
import { showError, showSuccess } from "../lib/toast";
import Icon from "../components/ui/Icon";

/**
* The root component for the TradeFlow dashboard.
* Manages high-level state for wallet connection, active tabs, and invoice data.
*/
export default function Page() {
const router = useRouter();
const searchParams = useSearchParams();
const { isConnected, walletAddress, isConnecting } = useWalletConnection();
const [invoices, setInvoices] = useState<InvoiceSummary[]>([]);
const [loading, setLoading] = useState(false);
/** Controls visibility of the Invoice Minting modal */
const [showMintForm, setShowMintForm] = useState(false);
/** Controls visibility of the Wallet Selection modal */
const [isModalOpen, setIsModalOpen] = useState(false);
/** Currently active navigation tab (dashboard or watchlist) */
const [activeTab, setActiveTab] = useState("dashboard");

/** Watchlist management hook */
const { toggleWatchlist, isInWatchlist } = useWatchlist();
const riskSocketRef = useRef<RiskSocketClient | null>(null);

Expand Down Expand Up @@ -77,6 +92,8 @@ export default function Page() {
}
};

// --- Lifecycle Hooks ---

useEffect(() => {
const controller = new AbortController();

Expand Down Expand Up @@ -147,14 +164,18 @@ export default function Page() {
const handleInvoiceMint = (data: Record<string, unknown>) => {
console.log("Invoice data received:", data);
setShowMintForm(false);
// TODO: Chain integration will be handled separately
// TODO: Initiate Soroban contract call for minting the NFT
};

// --- Configuration ---

/** Tab definitions for the main navigation */
const tabs = [
{ id: "dashboard", label: "Dashboard" },
{ id: "watchlist", label: "Watchlist", icon: <Icon icon={Star} dense /> },
];


return (
<div className="min-h-screen bg-slate-900 text-white font-sans flex flex-col">
{/* Header */}
Expand Down
53 changes: 42 additions & 11 deletions src/components/SwapInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,25 @@ import { dismissToast, showError, showLoading, showSuccess } from "../lib/toast"
import { useSigningActions } from "../stores/signatureStore";
import Icon from "./ui/Icon";

/**
* Main component for the token swap functionality.
*/
export default function SwapInterface() {
// --- Token Selection State ---
/** The asset code of the token being sold */
const [fromToken, setFromToken] = useState("XLM");
/** The asset code of the token being bought */
const [toToken, setToToken] = useState("USDC");

// --- UI Visibility State ---
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
const [isProMode, setIsProMode] = useState(false);

const { deadline } = useSettings();

/**
* Swaps the 'from' and 'to' tokens and their amounts.
*/
const handleSwap = () => {
const temp = fromToken;
setFromToken(toToken);
Expand All @@ -23,19 +34,28 @@ export default function SwapInterface() {
setToAmount(fromAmount);
};

/**
* Updates the source amount and recalculates the destination amount and price impact.
*
* @param {string} value - The new input amount.
*/
const handleFromAmountChange = (value: string) => {
setFromAmount(value);
const impact = calculatePriceImpact(value);
setPriceImpact(impact);

if (value && parseFloat(value) > 0) {
// Mock exchange rate logic
const mockRate = fromToken === "XLM" ? 0.15 : 6.67;
setToAmount((parseFloat(value) * mockRate * (1 - impact / 100)).toFixed(6));
} else {
setToAmount("");
}
};

/**
* Initiates the swap flow, validating inputs and checking for high slippage.
*/
const handleSwapClick = async () => {
if (!fromAmount || parseFloat(fromAmount) <= 0) {
showError("Please enter an amount to swap");
Expand All @@ -45,41 +65,43 @@ export default function SwapInterface() {
const loadingToast = showLoading("Processing swap...");

try {
// Threshold check for high slippage warning
if (priceImpact > 5) {
setIsHighSlippageWarningOpen(true);
dismissToast(loadingToast);
return;
}

await new Promise((resolve) => setTimeout(resolve, 1800));
// Simulate network delay
await new Promise((resolve) => setTimeout(resolve, 1500));

showSuccess(`Swapped ${fromAmount} ${fromToken} → ${toAmount} ${toToken}`, {
id: loadingToast,
});

if (priceImpact > 5) {
setIsHighSlippageWarningOpen(true);
} else {
setIsTradeReviewOpen(true);
}
setIsTradeReviewOpen(true);
} catch (error) {
showError("Failed to process swap", {
id: loadingToast,
});
}
};

/**
* Confirms the trade and prepares the transaction for signing.
*/
const handleTradeConfirm = async () => {
setIsTradeReviewOpen(false);
setIsSubmitting(true);
setSubmissionStartTime(Date.now());

try {
// Simulate transaction building time
await new Promise(resolve => setTimeout(resolve, 2000));

// Generate mock transaction XDR
// Mock transaction XDR for demonstration
const mockTransactionXDR = "AAAAAK/eFzA7Jf5Xf3Xf3Xf3Xf3Xf3Xf3Xf3Xf3Xf3Xf3Xf3Xf3Xf3Xf3Xf3Xf3XAAAABQAAAAAAAAAAA==";
console.log("Mock XDR generated:", mockTransactionXDR);
console.log("[SwapInterface] Mock XDR generated:", mockTransactionXDR);

setIsTransactionSignatureOpen(true);
} catch (error) {
Expand All @@ -89,6 +111,9 @@ export default function SwapInterface() {
}
};

/**
* Handles confirmation from the high slippage warning modal.
*/
const handleHighSlippageConfirm = async () => {
const loadingToast = showLoading("Processing high slippage swap...");

Expand All @@ -105,9 +130,13 @@ export default function SwapInterface() {
}
};

/* ISSUE #87: Trigger the success modal when the transaction is signed */
/**
* Callback for when the user successfully signs the transaction.
*
* @param {string} signedXDR - The base64 signed transaction XDR.
*/
const handleTransactionSuccess = (signedXDR: string) => {
console.log("Transaction signed:", signedXDR);
console.log("[SwapInterface] Transaction signed:", signedXDR);

showSuccess("Transaction signed successfully!", {
icon: "✅",
Expand All @@ -117,16 +146,18 @@ export default function SwapInterface() {
setIsSubmitting(false);
setSubmissionStartTime(null);

// Show the Growth/Share modal
// Show the post-trade share/growth modal
setIsSuccessModalOpen(true);

// Reset form after a short delay
setTimeout(() => {
setFromAmount("");
setToAmount("");
setPriceImpact(0);
}, 1500);
};


const isAnyModalOpen = isSettingsOpen || isHighSlippageWarningOpen || isTradeReviewOpen || isSuccessModalOpen;
const isSwapValid = fromAmount && parseFloat(fromAmount) > 0 && !isSubmitting;

Expand Down
Loading
Loading