chore: refactor logging module, update genesis timestamp, remove model relationships, and reorganize routers - Rename logging.py to logger.py and update import paths in poa.py and main.py - Update devnet genesis timestamp to 1766828620 - Remove SQLModel Relationship declarations from Block, Transaction, and Receipt models - Add SessionDep type alias and get_session dependency in coordinator-api deps - Reorganize coordinator-api routers: replace explorer/registry with exchange, users, marketplace
621 lines
29 KiB
HTML
621 lines
29 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>AITBC Trade Exchange - Buy AITBC with Bitcoin</title>
|
|
<base href="/Exchange/">
|
|
<!-- Production: Use local assets -->
|
|
<script src="/assets/js/tailwind.js"></script>
|
|
<script>
|
|
tailwind.config = {
|
|
darkMode: 'class'
|
|
}
|
|
</script>
|
|
<script src="/assets/js/axios.min.js"></script>
|
|
<script src="/assets/js/lucide.js"></script>
|
|
<style>
|
|
.gradient-bg {
|
|
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
|
|
}
|
|
.card-hover {
|
|
transition: all 0.3s ease;
|
|
}
|
|
.card-hover:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
}
|
|
.pulse-animation {
|
|
animation: pulse 2s infinite;
|
|
}
|
|
@keyframes pulse {
|
|
0% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
100% { opacity: 1; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-gray-50 dark:bg-gray-900 transition-colors duration-300">
|
|
<!-- Header -->
|
|
<header class="gradient-bg text-white shadow-lg">
|
|
<div class="container mx-auto px-4 py-6">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center space-x-3">
|
|
<i data-lucide="trending-up" class="w-8 h-8"></i>
|
|
<h1 class="text-2xl font-bold">AITBC Trade Exchange</h1>
|
|
</div>
|
|
<nav class="flex items-center space-x-6">
|
|
<button onclick="showSection('trade')" class="hover:text-orange-200 transition">Trade</button>
|
|
<button onclick="showSection('marketplace')" class="hover:text-orange-200 transition">Marketplace</button>
|
|
<button onclick="showSection('wallet')" class="hover:text-orange-200 transition">Wallet</button>
|
|
<button onclick="toggleDarkMode()" class="hover:text-orange-200 transition" title="Toggle dark mode">
|
|
<i data-lucide="moon" class="w-5 h-5" id="darkModeIcon"></i>
|
|
</button>
|
|
<button id="navConnectBtn" onclick="connectWallet()" class="bg-white text-orange-600 px-4 py-2 rounded-lg hover:bg-orange-100 transition">
|
|
<i data-lucide="wallet" class="w-4 h-4 inline mr-2"></i>Connect Wallet
|
|
</button>
|
|
<div id="navUserInfo" class="hidden flex items-center space-x-3">
|
|
<span class="text-sm" id="navUsername">-</span>
|
|
<button onclick="showSection('wallet')" class="text-white hover:text-orange-200 transition">
|
|
<i data-lucide="user" class="w-5 h-5"></i>
|
|
</button>
|
|
<button onclick="logout()" class="text-white hover:text-orange-200 transition">
|
|
<i data-lucide="log-out" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Price Ticker -->
|
|
<section class="bg-white dark:bg-gray-800 border-b dark:border-gray-700">
|
|
<div class="container mx-auto px-4 py-4">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center space-x-6">
|
|
<div class="flex items-center space-x-2">
|
|
<span class="text-gray-600 dark:text-gray-400">AITBC/BTC:</span>
|
|
<span class="text-2xl font-bold text-green-600 dark:text-green-400" id="aitbcBtcPrice">0.00001</span>
|
|
<span class="text-sm text-green-500 dark:text-green-400">+5.2%</span>
|
|
</div>
|
|
<div class="flex items-center space-x-2">
|
|
<span class="text-gray-600 dark:text-gray-400">24h Volume:</span>
|
|
<span class="font-semibold text-gray-900 dark:text-white">1,234 AITBC</span>
|
|
</div>
|
|
</div>
|
|
<div class="text-sm text-gray-500 dark:text-gray-400">
|
|
Last updated: <span id="lastUpdated">Just now</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Main Content -->
|
|
<main class="container mx-auto px-4 py-8">
|
|
<!-- Trade Section -->
|
|
<section id="tradeSection" class="section">
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
<!-- Buy AITBC -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
|
|
<h2 class="text-xl font-bold mb-6 flex items-center text-gray-900 dark:text-white">
|
|
<i data-lucide="arrow-down-left" class="w-5 h-5 mr-2 text-green-600 dark:text-green-400"></i>
|
|
Buy AITBC with Bitcoin
|
|
</h2>
|
|
|
|
<div id="tradeConnectPrompt" class="bg-orange-50 dark:bg-orange-900/20 border border-orange-200 dark:border-orange-800 rounded-lg p-4 mb-4">
|
|
<p class="text-sm text-orange-800 dark:text-orange-200 mb-3">
|
|
<i data-lucide="wallet" class="w-4 h-4 inline mr-1"></i>
|
|
Connect your wallet to start trading
|
|
</p>
|
|
<button onclick="connectWallet()" class="bg-orange-600 text-white px-4 py-2 rounded-lg hover:bg-orange-700 transition">
|
|
<i data-lucide="wallet" class="w-4 h-4 inline mr-2"></i>Connect Wallet
|
|
</button>
|
|
</div>
|
|
|
|
<div id="tradeForm" class="hidden">
|
|
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4 mb-4">
|
|
<p class="text-sm text-blue-800 dark:text-blue-200">
|
|
<i data-lucide="info" class="w-4 h-4 inline mr-1"></i>
|
|
Send Bitcoin to the generated address. Your AITBC will be credited after 1 confirmation.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Pay with Bitcoin</label>
|
|
<div class="relative">
|
|
<input type="number" id="btcAmount" class="w-full border dark:border-gray-600 rounded-lg px-4 py-3 pr-12 bg-white dark:bg-gray-700 text-gray-900 dark:text-white" placeholder="0.001" step="0.00001">
|
|
<span class="absolute right-3 top-3 text-gray-500 dark:text-gray-400">BTC</span>
|
|
</div>
|
|
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Available: 0.12345 BTC</p>
|
|
</div>
|
|
|
|
<div class="flex justify-center">
|
|
<button onclick="swapCurrencies()" class="p-2 bg-gray-100 dark:bg-gray-700 rounded-full hover:bg-gray-200 dark:hover:bg-gray-600 transition">
|
|
<i data-lucide="arrow-up-down" class="w-5 h-5 text-gray-700 dark:text-gray-300"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">You will receive</label>
|
|
<div class="relative">
|
|
<input type="number" id="aitbcAmount" class="w-full border dark:border-gray-600 rounded-lg px-4 py-3 pr-16 bg-white dark:bg-gray-700 text-gray-900 dark:text-white" placeholder="100" step="0.01">
|
|
<span class="absolute right-3 top-3 text-gray-500 dark:text-gray-400">AITBC</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
|
|
<div class="flex justify-between text-sm mb-2 text-gray-700 dark:text-gray-300">
|
|
<span>Price</span>
|
|
<span>0.00001 BTC/AITBC</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm mb-2 text-gray-700 dark:text-gray-300">
|
|
<span>Fee (0.5%)</span>
|
|
<span>0.000005 BTC</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm font-semibold text-gray-900 dark:text-white">
|
|
<span>Total</span>
|
|
<span>0.001005 BTC</span>
|
|
</div>
|
|
</div>
|
|
|
|
<button onclick="createPaymentRequest()" class="w-full bg-green-600 text-white py-3 rounded-lg hover:bg-green-700 transition font-semibold">
|
|
Create Payment Request
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Order Book -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
|
|
<h2 class="text-xl font-bold mb-6 flex items-center text-gray-900 dark:text-white">
|
|
<i data-lucide="book-open" class="w-5 h-5 mr-2 text-blue-600 dark:text-blue-400"></i>
|
|
Order Book
|
|
</h2>
|
|
|
|
<div class="space-y-4">
|
|
<div class="border-b dark:border-gray-700 pb-2">
|
|
<h3 class="text-sm font-semibold text-gray-600 dark:text-gray-400 mb-2">Buy Orders</h3>
|
|
<div class="space-y-1">
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-green-600 dark:text-green-400">100 AITBC</span>
|
|
<span class="text-gray-700 dark:text-gray-300">0.001 BTC</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-green-600 dark:text-green-400">50 AITBC</span>
|
|
<span class="text-gray-700 dark:text-gray-300">0.0005 BTC</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="text-sm font-semibold text-gray-600 dark:text-gray-400 mb-2">Sell Orders</h3>
|
|
<div class="space-y-1">
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-red-600 dark:text-red-400">200 AITBC</span>
|
|
<span class="text-gray-700 dark:text-gray-300">0.002 BTC</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-red-600 dark:text-red-400">150 AITBC</span>
|
|
<span class="text-gray-700 dark:text-gray-300">0.0015 BTC</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Marketplace Section -->
|
|
<section id="marketplaceSection" class="section hidden">
|
|
<h2 class="text-2xl font-bold mb-6 text-gray-900 dark:text-white">GPU Marketplace</h2>
|
|
<div id="gpuOffers" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
<!-- GPU offers will be loaded here -->
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Wallet Section -->
|
|
<section id="walletSection" class="section hidden">
|
|
<h2 class="text-2xl font-bold mb-6 text-gray-900 dark:text-white">My Wallet</h2>
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<h3 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Wallet Information</h3>
|
|
<div class="space-y-3">
|
|
<div>
|
|
<span class="text-sm text-gray-600 dark:text-gray-400">Address:</span>
|
|
<p class="font-mono text-sm bg-gray-100 dark:bg-gray-700 p-2 rounded" id="walletAddress">-</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm text-gray-600 dark:text-gray-400">Username:</span>
|
|
<p id="walletUsername">-</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Balances</h3>
|
|
<div class="space-y-3">
|
|
<div>
|
|
<span class="text-sm text-gray-600 dark:text-gray-400">AITBC Balance:</span>
|
|
<p class="text-2xl font-bold text-green-600 dark:text-green-400" id="aitbcBalance">0 AITBC</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<!-- QR Code Modal -->
|
|
<div id="qrModal" class="fixed inset-0 bg-black bg-opacity-50 hidden flex items-center justify-center z-50">
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md w-full mx-4">
|
|
<h3 class="text-lg font-bold mb-4 text-gray-900 dark:text-white">Send Bitcoin to this Address</h3>
|
|
<div class="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg mb-4">
|
|
<div id="qrCode" class="w-64 h-64 mx-auto mb-4 bg-white rounded"></div>
|
|
<p class="font-mono text-sm break-all" id="paymentAddress">-</p>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-4 mb-4 text-sm">
|
|
<div>
|
|
<p class="text-gray-600 dark:text-gray-400">Amount to Send:</p>
|
|
<p class="font-semibold" id="paymentAmount">0 BTC</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-gray-600 dark:text-gray-400">You'll Receive:</p>
|
|
<p class="font-semibold text-green-600" id="receiveAmount">0 AITBC</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-center mb-4">
|
|
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-green-600" id="paymentSpinner"></div>
|
|
<span class="ml-2 text-sm text-gray-600">Waiting for payment...</span>
|
|
</div>
|
|
|
|
<div class="flex space-x-3">
|
|
<button onclick="closeQRModal()" class="flex-1 bg-gray-200 text-gray-800 py-2 rounded-lg hover:bg-gray-300 transition">
|
|
Cancel
|
|
</button>
|
|
<button onclick="checkPaymentStatus()" class="flex-1 bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">
|
|
Check Payment
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// API Configuration
|
|
const API_BASE = window.location.origin + '/api';
|
|
const BLOCKCHAIN_API = window.location.origin + '/rpc';
|
|
const EXCHANGE_RATE = 0.00001; // 1 AITBC = 0.00001 BTC
|
|
|
|
let walletAddress = null;
|
|
let currentUser = null;
|
|
let sessionToken = null;
|
|
let aitbcBalance = 0;
|
|
|
|
// Initialize
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
lucide.createIcons();
|
|
updatePrices();
|
|
loadGPUOffers();
|
|
|
|
// Auto-refresh prices every 30 seconds
|
|
setInterval(updatePrices, 30000);
|
|
|
|
// Input handlers
|
|
document.getElementById('btcAmount').addEventListener('input', updateAITBCAmount);
|
|
document.getElementById('aitbcAmount').addEventListener('input', updateBTCAmount);
|
|
|
|
// Check for saved dark mode preference
|
|
if (localStorage.getItem('darkMode') === 'true' ||
|
|
(!localStorage.getItem('darkMode') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
|
document.documentElement.classList.add('dark');
|
|
updateDarkModeIcon(true);
|
|
}
|
|
});
|
|
|
|
// Dark mode toggle
|
|
function toggleDarkMode() {
|
|
const isDark = document.documentElement.classList.toggle('dark');
|
|
localStorage.setItem('darkMode', isDark);
|
|
updateDarkModeIcon(isDark);
|
|
}
|
|
|
|
function updateDarkModeIcon(isDark) {
|
|
const icon = document.getElementById('darkModeIcon');
|
|
if (isDark) {
|
|
icon.setAttribute('data-lucide', 'sun');
|
|
} else {
|
|
icon.setAttribute('data-lucide', 'moon');
|
|
}
|
|
lucide.createIcons();
|
|
}
|
|
|
|
// Section Navigation
|
|
function showSection(section) {
|
|
document.querySelectorAll('.section').forEach(s => s.classList.add('hidden'));
|
|
document.getElementById(section + 'Section').classList.remove('hidden');
|
|
|
|
if (section === 'marketplace') {
|
|
loadGPUOffers();
|
|
}
|
|
}
|
|
|
|
// Connect Wallet
|
|
async function connectWallet() {
|
|
try {
|
|
// Generate a random wallet address for demo
|
|
walletAddress = 'aitbc1' + Array(39).fill(0).map(() => Math.random().toString(36).substr(2, 1)).join('');
|
|
|
|
// Login or register via API
|
|
const response = await axios.post(`${API_BASE}/users/login`, {
|
|
wallet_address: walletAddress
|
|
});
|
|
|
|
currentUser = response.data;
|
|
sessionToken = response.data.session_token;
|
|
|
|
// Update UI
|
|
document.getElementById('tradeConnectPrompt').classList.add('hidden');
|
|
document.getElementById('tradeForm').classList.remove('hidden');
|
|
document.getElementById('navConnectBtn').classList.add('hidden');
|
|
document.getElementById('navUserInfo').classList.remove('hidden');
|
|
document.getElementById('navUsername').textContent = currentUser.username;
|
|
document.getElementById('walletAddress').textContent = walletAddress;
|
|
document.getElementById('walletUsername').textContent = currentUser.username;
|
|
|
|
// Load wallet balance
|
|
await loadWalletBalance();
|
|
|
|
showToast('Wallet connected successfully!', 'success');
|
|
} catch (error) {
|
|
console.error('Failed to connect wallet:', error);
|
|
showToast('Failed to connect wallet', 'error');
|
|
}
|
|
}
|
|
|
|
// Load Wallet Balance
|
|
async function loadWalletBalance() {
|
|
try {
|
|
const response = await axios.get(
|
|
`${API_BASE}/users/${currentUser.user_id}/balance`,
|
|
{ headers: { 'Authorization': `Bearer ${sessionToken}` } }
|
|
);
|
|
|
|
aitbcBalance = response.data.balance || 0;
|
|
document.getElementById('aitbcBalance').textContent = aitbcBalance + ' AITBC';
|
|
} catch (error) {
|
|
console.error('Failed to load balance:', error);
|
|
}
|
|
}
|
|
|
|
// Logout
|
|
async function logout() {
|
|
try {
|
|
if (sessionToken) {
|
|
await axios.post(`${API_BASE}/users/logout`, {}, {
|
|
headers: { 'Authorization': `Bearer ${sessionToken}` }
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Logout error:', error);
|
|
}
|
|
|
|
// Reset state
|
|
walletAddress = null;
|
|
currentUser = null;
|
|
sessionToken = null;
|
|
aitbcBalance = 0;
|
|
|
|
// Update UI
|
|
document.getElementById('tradeConnectPrompt').classList.remove('hidden');
|
|
document.getElementById('tradeForm').classList.add('hidden');
|
|
document.getElementById('navConnectBtn').classList.remove('hidden');
|
|
document.getElementById('navUserInfo').classList.add('hidden');
|
|
|
|
showToast('Logged out successfully', 'success');
|
|
}
|
|
|
|
// Update Prices
|
|
function updatePrices() {
|
|
// Simulate price updates
|
|
const variation = (Math.random() - 0.5) * 0.000001;
|
|
const newPrice = EXCHANGE_RATE + variation;
|
|
document.getElementById('aitbcBtcPrice').textContent = newPrice.toFixed(5);
|
|
document.getElementById('lastUpdated').textContent = 'Just now';
|
|
}
|
|
|
|
// Currency Conversion
|
|
function updateAITBCAmount() {
|
|
const btcAmount = parseFloat(document.getElementById('btcAmount').value) || 0;
|
|
const aitbcAmount = btcAmount / EXCHANGE_RATE;
|
|
document.getElementById('aitbcAmount').value = aitbcAmount.toFixed(2);
|
|
}
|
|
|
|
function updateBTCAmount() {
|
|
const aitbcAmount = parseFloat(document.getElementById('aitbcAmount').value) || 0;
|
|
const btcAmount = aitbcAmount * EXCHANGE_RATE;
|
|
document.getElementById('btcAmount').value = btcAmount.toFixed(5);
|
|
}
|
|
|
|
function swapCurrencies() {
|
|
const btcInput = document.getElementById('btcAmount');
|
|
const aitbcInput = document.getElementById('aitbcAmount');
|
|
|
|
const temp = btcInput.value;
|
|
btcInput.value = aitbcInput.value;
|
|
aitbcInput.value = temp;
|
|
}
|
|
|
|
// Create Payment Request
|
|
async function createPaymentRequest() {
|
|
const btcAmount = document.getElementById('btcAmount').value;
|
|
|
|
if (!btcAmount || btcAmount <= 0) {
|
|
showToast('Please enter a valid amount', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await axios.post(`${API_BASE}/exchange/create-payment`, {
|
|
amount: parseFloat(btcAmount),
|
|
currency: 'BTC'
|
|
}, {
|
|
headers: { 'Authorization': `Bearer ${sessionToken}` }
|
|
});
|
|
|
|
const payment = response.data;
|
|
showQRModal(payment);
|
|
} catch (error) {
|
|
console.error('Failed to create payment:', error);
|
|
showToast('Failed to create payment request', 'error');
|
|
}
|
|
}
|
|
|
|
// Show QR Modal
|
|
function showQRModal(payment) {
|
|
const modal = document.getElementById('qrModal');
|
|
const addressEl = document.getElementById('paymentAddress');
|
|
const amountEl = document.getElementById('paymentAmount');
|
|
const receiveEl = document.getElementById('receiveAmount');
|
|
|
|
addressEl.textContent = payment.address;
|
|
amountEl.textContent = payment.amount + ' BTC';
|
|
receiveEl.textContent = (payment.amount / EXCHANGE_RATE).toFixed(2) + ' AITBC';
|
|
|
|
// Generate QR code (simplified - in production use a proper QR library)
|
|
const qrDiv = document.getElementById('qrCode');
|
|
qrDiv.innerHTML = `
|
|
<div class="w-full h-full flex items-center justify-center border-2 border-gray-300 rounded">
|
|
<div class="text-center">
|
|
<i data-lucide="qr-code" class="w-32 h-32 mx-auto mb-2"></i>
|
|
<p class="text-sm">QR Code for ${payment.address}</p>
|
|
</div>
|
|
</div>
|
|
`;
|
|
lucide.createIcons();
|
|
|
|
modal.classList.remove('hidden');
|
|
window.currentPaymentId = payment.payment_id;
|
|
}
|
|
|
|
// Close QR Modal
|
|
function closeQRModal() {
|
|
document.getElementById('qrModal').classList.add('hidden');
|
|
window.currentPaymentId = null;
|
|
}
|
|
|
|
// Check Payment Status
|
|
async function checkPaymentStatus() {
|
|
if (!window.currentPaymentId) return;
|
|
|
|
try {
|
|
const response = await axios.get(
|
|
`${API_BASE}/exchange/payment-status/${window.currentPaymentId}`,
|
|
{ headers: { 'Authorization': `Bearer ${sessionToken}` } }
|
|
);
|
|
|
|
const status = response.data.status;
|
|
|
|
if (status === 'completed') {
|
|
showToast('Payment received! AITBC credited to your wallet.', 'success');
|
|
closeQRModal();
|
|
await loadWalletBalance();
|
|
} else if (status === 'pending') {
|
|
showToast('Payment still pending...', 'info');
|
|
} else {
|
|
showToast('Payment not found', 'error');
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to check payment:', error);
|
|
showToast('Failed to check payment status', 'error');
|
|
}
|
|
}
|
|
|
|
// Load GPU Offers
|
|
async function loadGPUOffers() {
|
|
try {
|
|
const response = await axios.get(`${API_BASE}/marketplace/offers`);
|
|
displayGPUOffers(response.data);
|
|
} catch (error) {
|
|
console.error('Failed to load GPU offers:', error);
|
|
// Display demo offers
|
|
displayGPUOffers([
|
|
{
|
|
id: 'demo-1',
|
|
provider: 'Demo Provider 1',
|
|
capacity: 'RTX 4090',
|
|
price: 0.01,
|
|
status: 'available'
|
|
},
|
|
{
|
|
id: 'demo-2',
|
|
provider: 'Demo Provider 2',
|
|
capacity: 'A100 80GB',
|
|
price: 0.05,
|
|
status: 'available'
|
|
}
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Display GPU Offers
|
|
function displayGPUOffers(offers) {
|
|
const container = document.getElementById('gpuOffers');
|
|
|
|
if (offers.length === 0) {
|
|
container.innerHTML = '<p class="text-gray-500 dark:text-gray-400">No GPU offers available at the moment.</p>';
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = offers.map(offer => `
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 card-hover">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<span class="text-sm font-semibold text-blue-600 dark:text-blue-400">${offer.capacity}</span>
|
|
<span class="text-xs px-2 py-1 bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded">
|
|
${offer.status || 'Available'}
|
|
</span>
|
|
</div>
|
|
<h3 class="font-semibold mb-2 text-gray-900 dark:text-white">${offer.provider}</h3>
|
|
<p class="text-2xl font-bold text-gray-900 dark:text-white mb-4">${offer.price} BTC/hour</p>
|
|
<button onclick="rentGPU('${offer.id}')" class="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">
|
|
Rent Now
|
|
</button>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
// Rent GPU
|
|
function rentGPU(gpuId) {
|
|
if (!currentUser) {
|
|
showToast('Please connect your wallet first', 'error');
|
|
showSection('trade');
|
|
return;
|
|
}
|
|
showToast(`Renting GPU ${gpuId}...`, 'info');
|
|
}
|
|
|
|
// Toast Notification
|
|
function showToast(message, type = 'info') {
|
|
const toast = document.createElement('div');
|
|
toast.className = `fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg transform transition-all duration-300 z-50`;
|
|
|
|
if (type === 'success') {
|
|
toast.classList.add('bg-green-500', 'text-white');
|
|
} else if (type === 'error') {
|
|
toast.classList.add('bg-red-500', 'text-white');
|
|
} else {
|
|
toast.classList.add('bg-blue-500', 'text-white');
|
|
}
|
|
|
|
toast.textContent = message;
|
|
document.body.appendChild(toast);
|
|
|
|
setTimeout(() => {
|
|
toast.classList.add('translate-y-full', 'opacity-0');
|
|
setTimeout(() => toast.remove(), 300);
|
|
}, 3000);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|