diff --git a/apps/web/src/components/offramp/OfframpSuccessModal.tsx b/apps/web/src/components/offramp/OfframpSuccessModal.tsx
index d9e1fdc..9d50669 100644
--- a/apps/web/src/components/offramp/OfframpSuccessModal.tsx
+++ b/apps/web/src/components/offramp/OfframpSuccessModal.tsx
@@ -1,7 +1,7 @@
"use client";
import React, { useState, useRef, useEffect } from "react";
-import { CheckCircle2, Copy, ExternalLink, X, Loader2, XCircle, Clock } from "lucide-react";
+import { CheckCircle2, Copy, ExternalLink, X, Loader2, XCircle, Clock, Info } from "lucide-react";
import { Button } from "@/components/ui/button";
import toast from "react-hot-toast";
import type { QuoteStatusData, BridgeFeeBreakdown } from "@/types/offramp";
@@ -45,8 +45,10 @@ export default function OfframpSuccessModal({
if (!isOpen) return null;
- const isCompleted = payoutStatus?.status === "completed" || payoutStatus?.status === "confirmed";
- const isFailed = payoutStatus?.status === "failed";
+ const status = payoutStatus?.status;
+ const isCompleted = status === "completed" || status === "confirmed";
+ const isFailed = status === "failed" || status === "expired";
+ const isProcessing = status === "pending" || status === "processing" || !status;
return (
@@ -57,7 +59,7 @@ export default function OfframpSuccessModal({
/>
{/* Modal */}
-
+
{/* Close Button */}
- {/* Success Icon */}
+ {/* Status Icon */}
-
+
{isCompleted ? (
) : isFailed ? (
) : (
-
+
)}
- {/* Title */}
+ {/* Title & Processing Time Banner */}
- {isCompleted ? "Offramp Complete! 🎉" : isFailed ? "Offramp Failed" : "Offramp Processing"}
+ {isCompleted ? "Offramp Complete! 🎉" : isFailed ? "Offramp Failed" : "Processing Transfer"}
-
+
+ {/* Added: Processing Time Banner for Task #62 */}
+ {isProcessing && (
+
+
+
+ Estimated: 2-5 Minutes
+
+
+ )}
+
+
{isCompleted
? "Your funds have been successfully sent to your bank account."
: isFailed
- ? payoutStatus?.providerMessage || "There was an issue with your transfer. Please contact support."
- : "Your transaction is being processed. You can close this window and check back later."}
+ ? payoutStatus?.providerMessage || "There was an issue with your transfer."
+ : "Your transaction is on the way. You can safely close this window."}
- {/* Summary Card */}
+ {/* Summary Card with Exchange Rate */}
{feeBreakdown && (
@@ -102,33 +115,46 @@ export default function OfframpSuccessModal({
{parseFloat(feeBreakdown.sendAmount).toFixed(4)} USDC
+
+ {/* Added: Exchange Rate for Transparency */}
+
+
+ Exchange Rate
+
+
+ 1 USDC = ₦{(parseFloat(feeBreakdown.fiatPayout) / (parseFloat(feeBreakdown.sendAmount) - parseFloat(feeBreakdown.bridgeFee))).toLocaleString()}
+
+
+
Bridge Fee
-
+
-{parseFloat(feeBreakdown.bridgeFee).toFixed(4)} USDC
+
+
- Total Received
-
+ Total Payout
+
₦{parseFloat(feeBreakdown.fiatPayout).toLocaleString()}
)}
- {/* Transaction Info */}
-
+ {/* Transaction Reference & Explorer */}
+
{payoutStatus?.transactionReference && (
-
-
Reference ID
-
-
{payoutStatus.transactionReference}
-
- {copied ? : }
-
+
+
+
Ref ID
+
{payoutStatus.transactionReference}
+
+ {copied ? : }
+
)}
@@ -137,7 +163,7 @@ export default function OfframpSuccessModal({
href={`https://stellar.expert/explorer/public/tx/${bridgeTxHash}`}
target="_blank"
rel="noopener noreferrer"
- className="flex items-center justify-center gap-2 w-full py-3 text-xs text-fundable-purple hover:text-fundable-violet transition-colors font-medium"
+ className="flex items-center justify-center gap-2 w-full py-2 text-xs text-fundable-purple hover:underline font-medium"
>
View Stellar Explorer
@@ -147,7 +173,7 @@ export default function OfframpSuccessModal({
{/* Action Button */}
{isCompleted ? "Done" : "Close"}
diff --git a/apps/web/src/components/offramp/OfframpSummary.tsx b/apps/web/src/components/offramp/OfframpSummary.tsx
index c400d7f..b976ff0 100644
--- a/apps/web/src/components/offramp/OfframpSummary.tsx
+++ b/apps/web/src/components/offramp/OfframpSummary.tsx
@@ -1,7 +1,8 @@
"use client";
import { Button } from "@/components/ui/button";
-import { Loader2 } from "lucide-react";
+// Added Clock and ShieldCheck icons
+import { Loader2, Clock, ShieldCheck, Info } from "lucide-react";
import type { OfframpFormState, ProviderRate } from "@/types/offramp";
import { SUPPORTED_COUNTRIES } from "@/types/offramp";
@@ -33,114 +34,119 @@ export default function OfframpSummary({
const canProceed = isFormValid && quote && !isLoading;
+ // Helper to render currency symbols
+ const getCurrencySymbol = () => {
+ if (!selectedCountry) return "";
+ switch (selectedCountry.currency) {
+ case "NGN": return "₦";
+ case "GHS": return "₵";
+ case "KES": return "KSh ";
+ default: return selectedCountry.currency + " ";
+ }
+ };
+
return (
-
-
- Quote Summary
+
+
+
+ Transaction Summary
- {/* Real-time Quote Info */}
{isLoading ? (
-
-
-
Fetching quote...
+
+
+ Calculating best rates...
) : quote ? (
-
-
- You Send
-
- {quote.cryptoAmount} {formState.token}
-
-
-
- Exchange Rate
-
- 1 {formState.token} = {quote.currency} {quote.rate?.toLocaleString() ?? "N/A"}
-
-
-
- Provider
- {quote.displayName}
-
- {quote.expiresAt && (
+
+ {/* THE BREAKDOWN CARD */}
+
- Expires At
-
- {new Date(quote.expiresAt).toLocaleTimeString()}
+ Exchange Rate
+
+ 1 {formState.token} = {quote.currency} {quote.rate?.toLocaleString()}
- )}
+
+
+
+ Service Fee
+
+
+ -{quote.fee} {quote.currency}
+
+
+
+ {/* ESTIMATED TIME COMPONENT */}
+
+
+
+ Est. Arrival: 2-5 Minutes
+
+
+
+
+ {/* PROVIDER INFO */}
+
+ Provider: {quote.displayName}
+ {quote.expiresAt && (
+
+ Quote expires: {new Date(quote.expiresAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
+
+ )}
+
) : quoteError ? (
-
-
{quoteError}
+
) : (
-
-
- You Send
-
- {formState.amount || "0"} {formState.token}
-
-
-
- Exchange Rate
- Enter amount for quote
-
+
+
Enter an amount to see the fee breakdown
)}
-
-
-
You Receive
+ {/* FINAL PAYOUT SECTION */}
+
+
+
+
Total Payout
+
Includes all network and provider fees
+
{quote ? (
- <>
-
- {selectedCountry?.currency === "NGN" ? "₦" :
- selectedCountry?.currency === "GHS" ? "₵" :
- selectedCountry?.currency === "KES" ? "KSh " : ""}
- {quote.fiatAmount?.toLocaleString()}
-
-
- Fee: {quote.fee} {quote.currency}
-
- >
+
+ {getCurrencySymbol()}
+ {quote.fiatAmount?.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
+
) : (
- <>
-
- {selectedCountry?.currency || "---"}
-
-
- ---
-
- >
+
+ {selectedCountry?.currency || "---"}
+
)}
-
- {/* Proceed Button */}
-
- {isLoading ? (
- <>
-
- Loading Quote...
- >
- ) : quote ? (
- "Proceed to Confirm"
- ) : (
- "Enter Amount for Quote"
- )}
-
+
+ {isLoading ? (
+ <>
+
+ Processing...
+ >
+ ) : quote ? (
+ "Confirm Withdrawal"
+ ) : (
+ "Verify Amount"
+ )}
+
+
);