Cursor Agent
Fix 404 errors: Add missing endpoints and chart page
312fc0e
// Chart Page - Real-time cryptocurrency price chart
class ChartPage {
constructor() {
this.currentSymbol = 'BTC';
this.currentTimeframe = '1d';
this.chartData = null;
this.init();
}
init() {
// Parse URL parameters
const params = new URLSearchParams(window.location.search);
const urlSymbol = params.get('symbol');
if (urlSymbol) {
this.currentSymbol = urlSymbol;
}
// Setup event listeners
this.setupEventListeners();
// Load initial data
this.loadChartData();
}
setupEventListeners() {
const symbolSelect = document.getElementById('symbol-select');
const timeframeSelect = document.getElementById('timeframe-select');
const refreshBtn = document.getElementById('refresh-btn');
if (symbolSelect) {
symbolSelect.value = this.currentSymbol;
symbolSelect.addEventListener('change', (e) => {
this.currentSymbol = e.target.value;
this.loadChartData();
});
}
if (timeframeSelect) {
timeframeSelect.addEventListener('change', (e) => {
this.currentTimeframe = e.target.value;
this.loadChartData();
});
}
if (refreshBtn) {
refreshBtn.addEventListener('click', () => {
this.loadChartData();
});
}
}
async loadChartData() {
try {
const chartCanvas = document.getElementById('price-chart');
if (chartCanvas) {
chartCanvas.innerHTML = '<div class="loading">⏳ در حال بارگذاری داده‌ها...</div>';
}
// Fetch market data
const response = await fetch(`/api/market?limit=10`);
if (!response.ok) {
throw new Error('Failed to fetch market data');
}
const data = await response.json();
// Find current symbol data
const symbolData = data.data?.find(coin =>
coin.symbol?.toUpperCase() === this.currentSymbol ||
coin.name?.toUpperCase().includes(this.currentSymbol)
);
if (symbolData) {
this.updateChartInfo(symbolData);
this.renderChart(symbolData);
} else {
throw new Error('Symbol not found');
}
} catch (error) {
console.error('Error loading chart data:', error);
const chartCanvas = document.getElementById('price-chart');
if (chartCanvas) {
chartCanvas.innerHTML = `
<div style="text-align: center; color: #ef4444;">
❌ خطا در بارگذاری داده‌ها<br>
<small>${error.message}</small>
</div>
`;
}
}
}
updateChartInfo(data) {
// Update title
const title = document.getElementById('chart-title');
if (title) {
title.textContent = `نمودار ${data.name || this.currentSymbol}`;
}
// Update price info
const currentPrice = document.getElementById('current-price');
if (currentPrice && data.current_price) {
currentPrice.textContent = `$${this.formatNumber(data.current_price)}`;
}
const change24h = document.getElementById('change-24h');
if (change24h && data.price_change_percentage_24h !== undefined) {
const changeValue = data.price_change_percentage_24h;
change24h.textContent = `${changeValue > 0 ? '+' : ''}${changeValue.toFixed(2)}%`;
change24h.className = 'info-value ' + (changeValue >= 0 ? 'positive' : 'negative');
}
const volume24h = document.getElementById('volume-24h');
if (volume24h && data.total_volume) {
volume24h.textContent = `$${this.formatLargeNumber(data.total_volume)}`;
}
const high24h = document.getElementById('high-24h');
if (high24h && data.high_24h) {
high24h.textContent = `$${this.formatNumber(data.high_24h)}`;
}
const low24h = document.getElementById('low-24h');
if (low24h && data.low_24h) {
low24h.textContent = `$${this.formatNumber(data.low_24h)}`;
}
}
renderChart(data) {
const chartCanvas = document.getElementById('price-chart');
if (!chartCanvas) return;
// Create a simple visualization
const price = data.current_price || 0;
const change = data.price_change_percentage_24h || 0;
const high = data.high_24h || price * 1.1;
const low = data.low_24h || price * 0.9;
chartCanvas.innerHTML = `
<div style="width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: space-between;">
<div style="text-align: center; padding: 2rem;">
<div style="font-size: 3rem; font-weight: 700; margin-bottom: 1rem;">
${change >= 0 ? '📈' : '📉'}
</div>
<div style="font-size: 2.5rem; font-weight: 700; color: ${change >= 0 ? '#10b981' : '#ef4444'};">
$${this.formatNumber(price)}
</div>
<div style="font-size: 1.2rem; color: ${change >= 0 ? '#10b981' : '#ef4444'}; margin-top: 0.5rem;">
${change >= 0 ? '+' : ''}${change.toFixed(2)}%
</div>
</div>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; padding: 1rem; background: rgba(0,0,0,0.2); border-radius: 10px;">
<div style="text-align: center;">
<div style="color: #94a3b8; font-size: 0.9rem;">بالاترین</div>
<div style="color: #10b981; font-size: 1.2rem; font-weight: 600;">$${this.formatNumber(high)}</div>
</div>
<div style="text-align: center;">
<div style="color: #94a3b8; font-size: 0.9rem;">میانگین</div>
<div style="color: #e2e8f0; font-size: 1.2rem; font-weight: 600;">$${this.formatNumber((high + low) / 2)}</div>
</div>
<div style="text-align: center;">
<div style="color: #94a3b8; font-size: 0.9rem;">پایین‌ترین</div>
<div style="color: #ef4444; font-size: 1.2rem; font-weight: 600;">$${this.formatNumber(low)}</div>
</div>
</div>
<div style="text-align: center; color: #64748b; font-size: 0.9rem; padding: 1rem;">
💡 برای نمایش نمودار تکنیکال پیشرفته، از صفحه تحلیل تکنیکال استفاده کنید
</div>
</div>
`;
}
formatNumber(num) {
if (num >= 1) {
return num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
return num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 8 });
}
formatLargeNumber(num) {
if (num >= 1e9) {
return (num / 1e9).toFixed(2) + 'B';
} else if (num >= 1e6) {
return (num / 1e6).toFixed(2) + 'M';
} else if (num >= 1e3) {
return (num / 1e3).toFixed(2) + 'K';
}
return num.toFixed(2);
}
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', () => {
new ChartPage();
});