132 lines
7.1 KiB
TypeScript
132 lines
7.1 KiB
TypeScript
'use client';
|
||
|
||
import React, { useState, useEffect } from 'react';
|
||
import { Wallet, RefreshCw, Zap, TrendingUp, AlertCircle } from 'lucide-react';
|
||
|
||
export default function PlatformTreasuryWidget() {
|
||
const [treasuryData, setTreasuryData] = useState<any>(null);
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
|
||
|
||
const fetchTreasury = async () => {
|
||
setIsLoading(true);
|
||
try {
|
||
const res = await fetch('/api/admin/treasury/balances');
|
||
const data = await res.json();
|
||
if (res.ok) {
|
||
setTreasuryData(data.balances);
|
||
setLastUpdated(new Date());
|
||
}
|
||
} catch (e) {
|
||
console.error('[Treasury Widget] Error:', e);
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
fetchTreasury();
|
||
}, []);
|
||
|
||
const networks = [
|
||
{ id: 'SOLANA', color: 'text-emerald-500', bg: 'bg-emerald-50', icon: '🟢' },
|
||
{ id: 'POLYGON', color: 'text-purple-500', bg: 'bg-purple-50', icon: '🟣' },
|
||
{ id: 'TRON', color: 'text-red-500', bg: 'bg-red-50', icon: '🔴' },
|
||
{ id: 'BSC', color: 'text-yellow-500', bg: 'bg-yellow-50', icon: '🟡' },
|
||
{ id: 'ETH', color: 'text-blue-500', bg: 'bg-blue-50', icon: '🔵' },
|
||
{ id: 'BITCOIN', color: 'text-orange-500', bg: 'bg-orange-50', icon: '🟠' }
|
||
];
|
||
|
||
return (
|
||
<div className="bg-white rounded-[40px] border border-gray-100 shadow-sm overflow-hidden">
|
||
<div className="p-8 border-b border-gray-50 flex justify-between items-center">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-12 h-12 bg-gray-900 rounded-2xl flex items-center justify-center text-white shadow-xl">
|
||
<Wallet size={24} />
|
||
</div>
|
||
<div>
|
||
<h2 className="text-xl font-black text-gray-900 leading-none">Platform Hazinesi</h2>
|
||
<p className="text-[10px] text-gray-400 font-bold uppercase tracking-widest mt-2 flex items-center gap-1.5">
|
||
<Zap size={10} className="text-emerald-500 fill-emerald-500" />
|
||
Sistem Genelindeki On-Chain Likidite
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-4">
|
||
{lastUpdated && (
|
||
<span className="text-[9px] font-black text-gray-300 uppercase tracking-tighter">
|
||
Son Güncelleme: {lastUpdated.toLocaleTimeString('tr-TR')}
|
||
</span>
|
||
)}
|
||
<button
|
||
onClick={fetchTreasury}
|
||
disabled={isLoading}
|
||
className={`p-3 rounded-2xl border border-gray-100 text-gray-400 hover:text-gray-900 hover:border-gray-300 transition-all ${isLoading ? 'animate-spin border-transparent text-blue-600' : ''}`}
|
||
>
|
||
<RefreshCw size={20} />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="p-8">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6 gap-6">
|
||
{networks.map((net) => {
|
||
const data = treasuryData?.[net.id];
|
||
const nativeSymbol = data?.nativeSymbol || net.id;
|
||
const balance = data?.native || '0.00';
|
||
const tokenList = data?.tokens ? Object.entries(data.tokens) : [];
|
||
|
||
return (
|
||
<div key={net.id} className="group relative">
|
||
<div className={`p-6 rounded-[32px] border border-gray-100 shadow-sm transition-all duration-300 hover:shadow-xl hover:-translate-y-1 ${net.bg}/20`}>
|
||
<div className="flex items-center justify-between mb-4">
|
||
<span className="text-2xl drop-shadow-sm">{net.icon}</span>
|
||
<span className={`text-[10px] font-black uppercase tracking-widest px-2 py-0.5 rounded-lg bg-white/80 border ${net.color}`}>
|
||
{net.id}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="space-y-1">
|
||
<p className="text-xs font-bold text-gray-400 uppercase tracking-tighter leading-none">Ana Varlık</p>
|
||
<div className="flex items-baseline gap-1.5 overflow-hidden">
|
||
<h3 className={`text-xl font-black tabular-nums transition-all ${isLoading ? 'blur-sm' : ''} ${net.color}`}>
|
||
{balance}
|
||
</h3>
|
||
<span className="text-[10px] font-black text-gray-400 uppercase">{nativeSymbol}</span>
|
||
</div>
|
||
</div>
|
||
|
||
{tokenList.length > 0 && (
|
||
<div className="mt-4 pt-4 border-t border-gray-50 space-y-2">
|
||
{tokenList.map(([symbol, bal]) => (
|
||
<div key={symbol as string} className="flex justify-between items-center text-[10px] font-bold">
|
||
<span className="text-gray-400 uppercase">{symbol as string}</span>
|
||
<span className="text-gray-900 tabular-nums">{bal as string}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
{balance === "Error" && (
|
||
<div className="mt-2 text-[8px] font-black text-red-500 uppercase flex items-center gap-1">
|
||
<AlertCircle size={10} /> Bağlantı Hatası
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
<div className="mt-8 p-4 bg-blue-50/50 rounded-2xl border border-blue-100/50 flex items-center gap-3">
|
||
<TrendingUp className="text-blue-600" size={18} />
|
||
<p className="text-[10px] font-bold text-blue-800 leading-relaxed uppercase tracking-tight">
|
||
Yukarıdaki bakiyeler platformun operasyonel cüzdanlarından (Platform Treasury) anlık olarak çekilir. Ödemeler bu likidite üzerinden karşılanmaktadır.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|