Skip to content

Commit d857f6e

Browse files
committed
feat(payments): add blockchain details and export options in payment components
- Add blockchain hash and explorer URL with clickable link in PaymentConfirmation details - Introduce date range filter state in PaymentHistory component - Add an export button with export menu offering CSV and PDF export options - Update UI in PaymentHistory to show export menu and interactive export buttons - Preserve existing transaction filtering and display functionality with new features integrated
1 parent 36fb386 commit d857f6e

6 files changed

Lines changed: 793 additions & 33 deletions

File tree

client/src/modules/payments/components/PaymentConfirmation.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ interface ConfirmationProps {
99
transactionId?: string;
1010
timestamp?: string;
1111
status?: "success" | "pending" | "failed";
12+
blockchainHash?: string;
13+
blockchainExplorerUrl?: string;
1214
onDone?: () => void;
1315
}
1416

@@ -20,6 +22,8 @@ export default function PaymentConfirmation({
2022
transactionId = "TXN-7F4A2B9E",
2123
timestamp = "Apr 13, 2026 · 10:32 AM",
2224
status = "success",
25+
blockchainHash = "0x3f4a8b2c9d1e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0",
26+
blockchainExplorerUrl = "https://etherscan.io/tx/0x3f4a8b2c9d1e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0",
2327
onDone,
2428
}: ConfirmationProps) {
2529
const statusConfig = {
@@ -97,6 +101,7 @@ export default function PaymentConfirmation({
97101
value: `${riskScore}/100`,
98102
valueColor: riskScore < 30 ? "#00C851" : riskScore < 70 ? "#FFA500" : "#FF3B5C",
99103
},
104+
{ label: "Blockchain Hash", value: blockchainHash.substring(0, 20) + "...", mono: true, link: true },
100105
].map((row, i, arr) => (
101106
<div
102107
key={row.label}
@@ -111,7 +116,22 @@ export default function PaymentConfirmation({
111116
className={`text-[12px] font-semibold ${row.mono ? "font-mono" : ""}`}
112117
style={{ color: row.valueColor ?? "rgba(255,255,255,0.75)" }}
113118
>
114-
{row.value}
119+
{row.link ? (
120+
<a
121+
href={blockchainExplorerUrl}
122+
target="_blank"
123+
rel="noopener noreferrer"
124+
className="hover:text-[#00C8FF] transition-colors flex items-center gap-1"
125+
style={{ color: "#00C8FF" }}
126+
>
127+
{row.value}
128+
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
129+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
130+
</svg>
131+
</a>
132+
) : (
133+
row.value
134+
)}
115135
</span>
116136
</div>
117137
))}

client/src/modules/payments/components/PaymentHistory.tsx

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, { useState } from "react";
33

44
type Status = "approved" | "blocked" | "received" | "pending";
55
type Filter = "all" | Status;
6+
type DateRange = "all" | "today" | "week" | "month" | "custom";
67

78
interface Transaction {
89
id: string;
@@ -58,6 +59,8 @@ const STATUS_CONFIG: Record<Status, { color: string; bg: string; label: string }
5859
export default function PaymentHistory() {
5960
const [filter, setFilter] = useState<Filter>("all");
6061
const [search, setSearch] = useState("");
62+
const [dateRange, setDateRange] = useState<DateRange>("all");
63+
const [showExportMenu, setShowExportMenu] = useState(false);
6164

6265
const filtered = TRANSACTIONS.filter((tx) => {
6366
const matchFilter = filter === "all" || tx.status === filter;
@@ -90,9 +93,34 @@ export default function PaymentHistory() {
9093
<span className="text-[11px] text-[#FF3B5C] font-semibold px-2 py-0.5 rounded-full" style={{ background: "rgba(255,59,92,0.1)", border: "1px solid rgba(255,59,92,0.2)" }}>
9194
{totalBlocked} blocked
9295
</span>
93-
<span className="text-[11px] text-[#00C8FF] font-semibold px-2 py-0.5 rounded-full" style={{ background: "rgba(0,200,255,0.08)", border: "1px solid rgba(0,200,255,0.15)" }}>
94-
Live
95-
</span>
96+
<div className="relative">
97+
<button
98+
onClick={() => setShowExportMenu(!showExportMenu)}
99+
className="text-[11px] font-semibold px-3 py-1.5 rounded-lg transition-all hover:scale-105"
100+
style={{ background: "rgba(0,200,255,0.08)", border: "1px solid rgba(0,200,255,0.2)", color: "#00C8FF" }}
101+
>
102+
📥 Export
103+
</button>
104+
{showExportMenu && (
105+
<div
106+
className="absolute right-0 top-full mt-2 p-2 rounded-xl z-10 min-w-[150px]"
107+
style={{ background: "rgba(8,12,30,0.95)", border: "1px solid rgba(0,200,255,0.2)", boxShadow: "0 8px 32px rgba(0,0,0,0.4)" }}
108+
>
109+
<button
110+
onClick={() => { console.log("Export CSV"); setShowExportMenu(false); }}
111+
className="w-full px-3 py-2 rounded-lg text-left text-[12px] text-white/70 hover:text-white hover:bg-white/5 transition-all"
112+
>
113+
📄 Export as CSV
114+
</button>
115+
<button
116+
onClick={() => { console.log("Export PDF"); setShowExportMenu(false); }}
117+
className="w-full px-3 py-2 rounded-lg text-left text-[12px] text-white/70 hover:text-white hover:bg-white/5 transition-all"
118+
>
119+
📑 Export as PDF
120+
</button>
121+
</div>
122+
)}
123+
</div>
96124
</div>
97125
</div>
98126

@@ -114,20 +142,33 @@ export default function PaymentHistory() {
114142
))}
115143
</div>
116144

117-
{/* Search */}
118-
<div
119-
className="flex items-center gap-2 px-3 py-2.5 rounded-xl mb-4"
120-
style={{ background: "rgba(255,255,255,0.03)", border: "1px solid rgba(255,255,255,0.07)" }}
121-
>
122-
<svg className="w-4 h-4 text-white/30 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
123-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
124-
</svg>
125-
<input
126-
value={search}
127-
onChange={(e) => setSearch(e.target.value)}
128-
placeholder="Search transactions..."
129-
className="flex-1 bg-transparent text-[13px] text-white/80 outline-none placeholder:text-white/25"
130-
/>
145+
{/* Search & Date Range */}
146+
<div className="flex gap-3 mb-4">
147+
<div
148+
className="flex-1 flex items-center gap-2 px-3 py-2.5 rounded-xl"
149+
style={{ background: "rgba(255,255,255,0.03)", border: "1px solid rgba(255,255,255,0.07)" }}
150+
>
151+
<svg className="w-4 h-4 text-white/30 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
152+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
153+
</svg>
154+
<input
155+
value={search}
156+
onChange={(e) => setSearch(e.target.value)}
157+
placeholder="Search transactions..."
158+
className="flex-1 bg-transparent text-[13px] text-white/80 outline-none placeholder:text-white/25"
159+
/>
160+
</div>
161+
<select
162+
value={dateRange}
163+
onChange={(e) => setDateRange(e.target.value as DateRange)}
164+
className="px-3 py-2.5 rounded-xl text-[12px] font-semibold bg-white/5 border border-white/10 text-white/70 outline-none focus:border-[#00C8FF]/50 cursor-pointer"
165+
>
166+
<option value="all">All Time</option>
167+
<option value="today">Today</option>
168+
<option value="week">This Week</option>
169+
<option value="month">This Month</option>
170+
<option value="custom">Custom Range</option>
171+
</select>
131172
</div>
132173

133174
{/* Filters */}

0 commit comments

Comments
 (0)