|
|
<!DOCTYPE html>
|
|
|
<html lang="en" dir="ltr" data-theme="light">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<meta name="description" content="Crypto Monitor - Price Chart">
|
|
|
<title>Chart | Crypto Monitor</title>
|
|
|
|
|
|
<link rel="icon" type="image/svg+xml" href="/static/assets/icons/favicon.svg">
|
|
|
|
|
|
<link rel="stylesheet" href="/static/shared/css/design-system.css">
|
|
|
<link rel="stylesheet" href="/static/shared/css/global.css">
|
|
|
<link rel="stylesheet" href="/static/shared/css/components.css">
|
|
|
<link rel="stylesheet" href="/static/shared/css/layout.css">
|
|
|
|
|
|
<style>
|
|
|
.chart-container {
|
|
|
padding: 2rem;
|
|
|
max-width: 1400px;
|
|
|
margin: 0 auto;
|
|
|
}
|
|
|
.chart-header {
|
|
|
margin-bottom: 2rem;
|
|
|
}
|
|
|
.chart-title {
|
|
|
font-size: 1.5rem;
|
|
|
font-weight: 600;
|
|
|
color: var(--text-primary);
|
|
|
margin-bottom: 0.5rem;
|
|
|
}
|
|
|
.chart-controls {
|
|
|
display: flex;
|
|
|
gap: 1rem;
|
|
|
margin-bottom: 1rem;
|
|
|
flex-wrap: wrap;
|
|
|
}
|
|
|
.chart-wrapper {
|
|
|
background: white;
|
|
|
border-radius: 12px;
|
|
|
padding: 1.5rem;
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
|
min-height: 500px;
|
|
|
}
|
|
|
#priceChart {
|
|
|
width: 100%;
|
|
|
height: 500px;
|
|
|
}
|
|
|
</style>
|
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
|
|
|
<script src="/static/shared/js/utils/error-suppressor.js"></script>
|
|
|
</head>
|
|
|
<body>
|
|
|
<div class="app-container">
|
|
|
<aside id="sidebar-container"></aside>
|
|
|
|
|
|
<main class="main-content">
|
|
|
<header id="header-container"></header>
|
|
|
|
|
|
<div class="chart-container">
|
|
|
<div class="chart-header">
|
|
|
<h1 class="chart-title" id="chartTitle">Loading Chart...</h1>
|
|
|
<div class="chart-controls">
|
|
|
<select id="timeframeSelect" class="form-select" style="width: auto;">
|
|
|
<option value="1h">1 Hour</option>
|
|
|
<option value="4h">4 Hours</option>
|
|
|
<option value="1d" selected>1 Day</option>
|
|
|
<option value="1w">1 Week</option>
|
|
|
</select>
|
|
|
<button id="refreshBtn" class="btn btn-primary">Refresh</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="chart-wrapper">
|
|
|
<canvas id="priceChart"></canvas>
|
|
|
</div>
|
|
|
</div>
|
|
|
</main>
|
|
|
</div>
|
|
|
|
|
|
<script src="/static/shared/js/core/layout-manager.js"></script>
|
|
|
<script>
|
|
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
const symbol = urlParams.get('symbol') || 'BTC';
|
|
|
const symbolParts = symbol.split(':');
|
|
|
const baseSymbol = symbolParts[0].toUpperCase();
|
|
|
|
|
|
let chart = null;
|
|
|
|
|
|
|
|
|
if (window.LayoutManager) {
|
|
|
window.LayoutManager.init();
|
|
|
}
|
|
|
|
|
|
|
|
|
document.getElementById('chartTitle').textContent = `${baseSymbol} Price Chart`;
|
|
|
|
|
|
|
|
|
async function loadChartData() {
|
|
|
const timeframe = document.getElementById('timeframeSelect').value;
|
|
|
const limit = 100;
|
|
|
|
|
|
try {
|
|
|
|
|
|
let response = await fetch(`/api/market/ohlc?symbol=${baseSymbol}&interval=${timeframe}&limit=${limit}`);
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
|
response = await fetch(`/api/ohlcv?symbol=${baseSymbol}&timeframe=${timeframe}&limit=${limit}`);
|
|
|
}
|
|
|
|
|
|
if (!response.ok) {
|
|
|
throw new Error(`Failed to fetch data: ${response.status}`);
|
|
|
}
|
|
|
|
|
|
const data = await response.json();
|
|
|
const ohlcData = data.ohlc || data.data || [];
|
|
|
|
|
|
if (ohlcData.length === 0) {
|
|
|
throw new Error('No data available');
|
|
|
}
|
|
|
|
|
|
|
|
|
const labels = ohlcData.map(item => {
|
|
|
const date = new Date(item.timestamp * 1000);
|
|
|
return date.toLocaleString();
|
|
|
});
|
|
|
|
|
|
const prices = ohlcData.map(item => item.close || item.price || 0);
|
|
|
|
|
|
|
|
|
const ctx = document.getElementById('priceChart').getContext('2d');
|
|
|
|
|
|
if (chart) {
|
|
|
chart.destroy();
|
|
|
}
|
|
|
|
|
|
chart = new Chart(ctx, {
|
|
|
type: 'line',
|
|
|
data: {
|
|
|
labels: labels,
|
|
|
datasets: [{
|
|
|
label: `${baseSymbol} Price`,
|
|
|
data: prices,
|
|
|
borderColor: '#14b8a6',
|
|
|
backgroundColor: 'rgba(20, 184, 166, 0.1)',
|
|
|
borderWidth: 2,
|
|
|
fill: true,
|
|
|
tension: 0.4
|
|
|
}]
|
|
|
},
|
|
|
options: {
|
|
|
responsive: true,
|
|
|
maintainAspectRatio: false,
|
|
|
plugins: {
|
|
|
legend: {
|
|
|
display: true,
|
|
|
position: 'top'
|
|
|
},
|
|
|
tooltip: {
|
|
|
mode: 'index',
|
|
|
intersect: false
|
|
|
}
|
|
|
},
|
|
|
scales: {
|
|
|
y: {
|
|
|
beginAtZero: false,
|
|
|
ticks: {
|
|
|
callback: function(value) {
|
|
|
return '$' + value.toLocaleString();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
console.error('Error loading chart data:', error);
|
|
|
document.getElementById('chartTitle').textContent = `Error loading ${baseSymbol} chart`;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
document.getElementById('timeframeSelect').addEventListener('change', loadChartData);
|
|
|
document.getElementById('refreshBtn').addEventListener('click', loadChartData);
|
|
|
|
|
|
|
|
|
loadChartData();
|
|
|
</script>
|
|
|
</body>
|
|
|
</html>
|
|
|
|
|
|
|