From 5e19300f4d89e37a5705c2cf4f336f44cb30e38c Mon Sep 17 00:00:00 2001 From: ezekiel Date: Sun, 25 Jan 2026 08:53:25 +0100 Subject: [PATCH] feat: add TransactionProofCard component with trust indicators and status states --- components/TransactionProofCard.tsx | 226 ++++++++++++++++++++++++++++ package-lock.json | 32 +++- 2 files changed, 254 insertions(+), 4 deletions(-) create mode 100644 components/TransactionProofCard.tsx diff --git a/components/TransactionProofCard.tsx b/components/TransactionProofCard.tsx new file mode 100644 index 0000000..cd46053 --- /dev/null +++ b/components/TransactionProofCard.tsx @@ -0,0 +1,226 @@ +"use client"; + +import React, { useState } from "react"; +import { + CheckCircle2, + XCircle, + Loader2, // Spinner for pending + ExternalLink, + Copy, + ShieldCheck, + AlertCircle, // Icon for failed badge + Clock, + Wallet, + ArrowRightLeft, +} from "lucide-react"; + +interface TransactionProofProps { + hash: string; + amount: string; + currency: string; + senderAddress: string; + recipientAddress: string; + date: string; + status: "success" | "pending" | "failed"; + fee: string; + memo?: string; +} + +export default function TransactionProofCard({ + hash, + amount, + currency, + senderAddress, + recipientAddress, + date, + status, + fee, + memo, +}: TransactionProofProps) { + const [copied, setCopied] = useState(null); + + // 1. Configuration Map: Handles all Status Logic + const statusConfig = { + success: { + icon: CheckCircle2, + color: "text-emerald-500", + bgColor: "bg-emerald-50", + badgeColor: "text-emerald-700 bg-emerald-50 border-emerald-100", + badgeIcon: ShieldCheck, + badgeText: "On-Chain Verified", + title: "Transfer Successful", + spin: false, + }, + pending: { + icon: Loader2, + color: "text-amber-500", + bgColor: "bg-amber-50", + badgeColor: "text-amber-700 bg-amber-50 border-amber-100", + badgeIcon: Clock, + badgeText: "Processing Transaction", + title: "Transfer Pending", + spin: true, // To animate the spinner + }, + failed: { + icon: XCircle, + color: "text-rose-500", + bgColor: "bg-rose-50", + badgeColor: "text-rose-700 bg-rose-50 border-rose-100", + badgeIcon: AlertCircle, + badgeText: "Transaction Failed", + title: "Transfer Failed", + spin: false, + }, + }; + + // Get current config based on status + const currentStatus = statusConfig[status]; + const StatusIcon = currentStatus.icon; + const BadgeIcon = currentStatus.badgeIcon; + + // Helper to truncate long addresses + const truncate = (str: string) => + `${str.substring(0, 4)}...${str.substring(str.length - 4)}`; + + const copyToClipboard = (text: string, label: string) => { + navigator.clipboard.writeText(text); + setCopied(label); + setTimeout(() => setCopied(null), 2000); + }; + + return ( +
+ {/* 2. Header Section: Dynamic Status & Trust */} +
+ {/* Dynamic Status Icon Circle */} +
+ +
+ + {/* Dynamic Title */} +

+ {currentStatus.title} +

+ + {/* Dynamic Verification Badge */} +
+ + {currentStatus.badgeText} +
+ + {/* Amount Hero */} +
+ + {amount} + + + {currency} + +
+
+ + {/* 3. Details Section (Remains Consistent) */} +
+
+
+ + Time Completed +
+ {date} +
+ +
+
+ + From +
+
+ + {truncate(senderAddress)} + + +
+
+ +
+
+ + To +
+
+ + {truncate(recipientAddress)} + + +
+
+ +
+
+ + Network Fee + + {fee} +
+ {memo && ( +
+ + Memo + + + {memo} + +
+ )} +
+
+ + {/* 4. Footer */} +
+
+ +
+ + {hash} + + +
+
+ + + View on Stellar Explorer + + +
+
+ ); +} diff --git a/package-lock.json b/package-lock.json index 03470e9..513c92f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -554,6 +554,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "dev": true, + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -610,6 +611,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz", "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -1084,6 +1086,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1574,6 +1577,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2204,6 +2208,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -2366,6 +2371,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -3684,6 +3690,7 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -4399,6 +4406,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -4602,6 +4610,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -4613,6 +4622,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5502,6 +5512,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "peer": true, "engines": { "node": ">=12" }, @@ -5681,6 +5692,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6394,6 +6406,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "dev": true, + "peer": true, "requires": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -6435,6 +6448,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz", "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -6703,7 +6717,8 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true + "dev": true, + "peer": true }, "acorn-jsx": { "version": "5.3.2", @@ -7022,6 +7037,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, + "peer": true, "requires": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7474,6 +7490,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, + "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -7595,6 +7612,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, + "peer": true, "requires": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -8489,7 +8507,8 @@ "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true + "dev": true, + "peer": true }, "js-tokens": { "version": "4.0.0", @@ -8974,6 +8993,7 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, + "peer": true, "requires": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -9080,6 +9100,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "requires": { "loose-envify": "^1.1.0" } @@ -9088,6 +9109,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "requires": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -9692,7 +9714,8 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true + "dev": true, + "peer": true } } }, @@ -9826,7 +9849,8 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true + "dev": true, + "peer": true }, "unbox-primitive": { "version": "1.1.0",