142 lines
4.8 KiB
TypeScript
142 lines
4.8 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useAccount } from "wagmi";
|
|
import { WalletConnect } from "@/components/web3/WalletConnect";
|
|
import { formatAddress } from "@/lib/utils";
|
|
import { format } from "date-fns";
|
|
import { formatEther } from "viem";
|
|
|
|
interface Transaction {
|
|
id: string;
|
|
proposalId: number;
|
|
to: string;
|
|
value: string;
|
|
status: "pending" | "executed" | "rejected";
|
|
createdAt: Date | string;
|
|
}
|
|
|
|
export default function ActivityPage() {
|
|
const { isConnected } = useAccount();
|
|
const [filter, setFilter] = useState<"all" | "pending" | "executed">("all");
|
|
|
|
// TODO: Fetch transactions from backend
|
|
const transactions: Transaction[] = [];
|
|
|
|
if (!isConnected) {
|
|
return (
|
|
<div className="min-h-screen p-8">
|
|
<div className="max-w-6xl mx-auto">
|
|
<WalletConnect />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const filteredTransactions = transactions.filter((tx) => {
|
|
if (filter === "all") return true;
|
|
return tx.status === filter;
|
|
});
|
|
|
|
const handleExport = async () => {
|
|
// TODO: Fetch CSV from backend API
|
|
const csv = ""; // Placeholder
|
|
const blob = new Blob([csv], { type: "text/csv" });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = `transactions-${Date.now()}.csv`;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen p-8">
|
|
<div className="max-w-6xl mx-auto">
|
|
<header className="flex justify-between items-center mb-8">
|
|
<h1 className="text-3xl font-bold">Transaction History</h1>
|
|
<div className="flex gap-4 items-center">
|
|
<WalletConnect />
|
|
<button
|
|
onClick={handleExport}
|
|
className="px-4 py-2 bg-green-600 hover:bg-green-700 rounded-lg transition-colors"
|
|
>
|
|
Export CSV
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div className="bg-gray-900 rounded-xl p-6">
|
|
{/* Filters */}
|
|
<div className="flex gap-4 mb-6">
|
|
{(["all", "pending", "executed"] as const).map((status) => (
|
|
<button
|
|
key={status}
|
|
onClick={() => setFilter(status)}
|
|
className={`px-4 py-2 rounded-lg transition-colors ${
|
|
filter === status
|
|
? "bg-blue-600 text-white"
|
|
: "bg-gray-800 text-gray-300 hover:bg-gray-700"
|
|
}`}
|
|
>
|
|
{status.charAt(0).toUpperCase() + status.slice(1)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Transaction List */}
|
|
{filteredTransactions.length === 0 ? (
|
|
<div className="text-center py-12 text-gray-400">
|
|
No transactions found
|
|
</div>
|
|
) : (
|
|
<div className="space-y-4">
|
|
{filteredTransactions.map((tx) => (
|
|
<div
|
|
key={tx.id}
|
|
className="bg-gray-800 rounded-lg p-6 border border-gray-700"
|
|
>
|
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
<div>
|
|
<div className="text-sm text-gray-400 mb-1">Proposal ID</div>
|
|
<div className="font-mono">#{tx.proposalId}</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-sm text-gray-400 mb-1">To</div>
|
|
<div className="font-mono text-sm">{formatAddress(tx.to)}</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-sm text-gray-400 mb-1">Amount</div>
|
|
<div className="font-semibold">{formatEther(BigInt(tx.value))} ETH</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-sm text-gray-400 mb-1">Status</div>
|
|
<span
|
|
className={`inline-block px-3 py-1 rounded text-sm font-semibold ${
|
|
tx.status === "executed"
|
|
? "bg-green-900/30 text-green-400"
|
|
: tx.status === "pending"
|
|
? "bg-yellow-900/30 text-yellow-400"
|
|
: "bg-red-900/30 text-red-400"
|
|
}`}
|
|
>
|
|
{tx.status}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div className="mt-4 pt-4 border-t border-gray-700">
|
|
<div className="text-sm text-gray-400">
|
|
Created: {format(new Date(tx.createdAt), "MMM dd, yyyy HH:mm:ss")}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|