Files
trading_bot_v3/components/BitqueryDashboard.tsx
mindesbunister de45349baa Restore working dashboard and TradingView analysis
- Fixed layout conflicts by removing minimal layout.tsx in favor of complete layout.js
- Restored original AI Analysis page with full TradingView integration
- Connected enhanced screenshot API to real TradingView automation service
- Fixed screenshot gallery to handle both string and object formats
- Added image serving API route for screenshot display
- Resolved hydration mismatch issues with suppressHydrationWarning
- All navigation pages working (Analysis, Trading, Automation, Settings)
- TradingView automation successfully capturing screenshots from AI and DIY layouts
- Docker Compose v2 compatibility ensured

Working features:
- Homepage with hero section and status cards
- Navigation menu with Trading Bot branding
- Real TradingView screenshot capture
- AI-powered chart analysis
- Multi-layout support (AI + DIY module)
- Screenshot gallery with image serving
- API endpoints for balance, status, screenshots, trading
2025-07-14 14:21:19 +02:00

182 lines
6.3 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
interface TokenPrice {
symbol: string;
price: number;
change24h: number;
volume24h: number;
marketCap?: number;
}
interface TradingBalance {
totalValue: number;
availableBalance: number;
positions: TokenPrice[];
}
export default function BitqueryDashboard() {
const [balance, setBalance] = useState<TradingBalance | null>(null);
const [prices, setPrices] = useState<TokenPrice[]>([]);
const [status, setStatus] = useState<any>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [tradingSymbol, setTradingSymbol] = useState('SOL');
const [tradeAmount, setTradeAmount] = useState('1');
const [tradeSide, setTradeSide] = useState<'BUY' | 'SELL'>('BUY');
const [tradeLoading, setTradeLoading] = useState(false);
const [tradeResult, setTradeResult] = useState<any>(null);
useEffect(() => {
fetchData();
const interval = setInterval(fetchData, 30000); // Refresh every 30 seconds
return () => clearInterval(interval);
}, []);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
// Fetch balance
const balanceResponse = await fetch('/api/balance');
const balanceData = await balanceResponse.json();
if (balanceData.success) {
setBalance(balanceData.data);
}
// Fetch prices
const pricesResponse = await fetch('/api/prices');
const pricesData = await pricesResponse.json();
if (pricesData.success) {
setPrices(pricesData.data);
}
// Fetch status
const statusResponse = await fetch('/api/status');
const statusData = await statusResponse.json();
if (statusData.success) {
setStatus(statusData.data);
}
} catch (err: any) {
setError(err.message || 'Failed to fetch data');
} finally {
setLoading(false);
}
};
if (loading && !balance) {
return (
<div className="min-h-screen bg-gray-900 text-white p-8">
<div className="max-w-6xl mx-auto">
<h1 className="text-3xl font-bold mb-8">Bitquery Trading Dashboard</h1>
<div className="text-center">Loading...</div>
</div>
</div>
);
}
if (error) {
return (
<div className="min-h-screen bg-gray-900 text-white p-8">
<div className="max-w-6xl mx-auto">
<h1 className="text-3xl font-bold mb-8">Bitquery Trading Dashboard</h1>
<div className="bg-red-900 border border-red-700 rounded-lg p-4">
<div className="text-red-200">Error: {error}</div>
<button
onClick={fetchData}
className="mt-2 px-4 py-2 bg-red-700 hover:bg-red-600 rounded"
>
Retry
</button>
</div>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-900 text-white p-8">
<div className="max-w-6xl mx-auto">
<h1 className="text-3xl font-bold mb-8">Bitquery Trading Dashboard</h1>
{/* Service Status */}
{status && (
<div className="mb-8 bg-gray-800 rounded-lg p-6">
<h2 className="text-xl font-semibold mb-4">Service Status</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="flex items-center">
<div className={`w-3 h-3 rounded-full mr-2 ${status.bitquery?.connected ? 'bg-green-500' : 'bg-red-500'}`}></div>
<span>Bitquery: {status.bitquery?.connected ? 'Connected' : 'Disconnected'}</span>
</div>
<div className="flex items-center">
<div className={`w-3 h-3 rounded-full mr-2 ${status.bitquery?.apiKey ? 'bg-green-500' : 'bg-red-500'}`}></div>
<span>API Key: {status.bitquery?.apiKey ? 'Configured' : 'Missing'}</span>
</div>
</div>
{status.bitquery?.error && (
<div className="mt-2 text-red-400">Error: {status.bitquery.error}</div>
)}
</div>
)}
{/* Balance Overview */}
{balance && (
<div className="mb-8 bg-gray-800 rounded-lg p-6">
<h2 className="text-xl font-semibold mb-4">Portfolio Balance</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<div className="text-sm text-gray-400">Total Value</div>
<div className="text-2xl font-bold">${balance.totalValue.toFixed(2)}</div>
</div>
<div>
<div className="text-sm text-gray-400">Available Balance</div>
<div className="text-2xl font-bold">${balance.availableBalance.toFixed(2)}</div>
</div>
</div>
</div>
)}
{/* Token Prices */}
<div className="mb-8 bg-gray-800 rounded-lg p-6">
<h2 className="text-xl font-semibold mb-4">Token Prices</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{prices.map((token) => (
<div key={token.symbol} className="bg-gray-700 rounded-lg p-4">
<div className="flex justify-between items-center mb-2">
<span className="font-semibold">{token.symbol}</span>
<span className={`text-sm ${token.change24h >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{token.change24h >= 0 ? '+' : ''}{token.change24h.toFixed(2)}%
</span>
</div>
<div className="text-2xl font-bold">${token.price.toFixed(2)}</div>
<div className="text-sm text-gray-400">
Vol: ${token.volume24h.toLocaleString()}
</div>
{token.marketCap && (
<div className="text-sm text-gray-400">
Cap: ${(token.marketCap / 1e9).toFixed(2)}B
</div>
)}
</div>
))}
</div>
</div>
{/* Refresh Button */}
<div className="text-center">
<button
onClick={fetchData}
disabled={loading}
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-600 rounded-lg font-semibold"
>
{loading ? 'Refreshing...' : 'Refresh Data'}
</button>
</div>
</div>
</div>
);
}