mauryaR8's picture
Add 3 files
ff68610 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Real-Time API Data Visualization</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.gradient-bg {
background: linear-gradient(135deg, #6b73ff 0%, #000dff 100%);
}
.chart-container {
position: relative;
height: 300px;
width: 100%;
}
.api-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="gradient-bg text-white py-6 shadow-lg">
<div class="container mx-auto px-4">
<div class="flex justify-between items-center">
<h1 class="text-3xl font-bold">Real-Time API Data Visualization</h1>
<div class="flex items-center space-x-2">
<span id="connection-status" class="flex items-center">
<span class="h-3 w-3 rounded-full bg-green-500 mr-2"></span>
<span>Connected</span>
</span>
</div>
</div>
<p class="mt-2 opacity-90">Visualize data from multiple APIs in real-time with interactive charts</p>
</div>
</div>
<div class="container mx-auto px-4 py-8">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- API Configuration Panel -->
<div class="bg-white rounded-xl shadow-md p-6 lg:col-span-1">
<h2 class="text-xl font-semibold mb-4 text-gray-800">API Configuration</h2>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-medium mb-2" for="api-select">
Select API Source
</label>
<select id="api-select" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
<option value="crypto">Crypto Prices (CoinGecko)</option>
<option value="weather">Weather Data (OpenWeather)</option>
<option value="stocks">Stock Prices (Alpha Vantage)</option>
<option value="custom">Custom API</option>
</select>
</div>
<div id="api-config-section">
<!-- Crypto API Config -->
<div id="crypto-config" class="api-config">
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">
Cryptocurrency
</label>
<select class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
<option value="bitcoin">Bitcoin (BTC)</option>
<option value="ethereum">Ethereum (ETH)</option>
<option value="ripple">Ripple (XRP)</option>
<option value="cardano">Cardano (ADA)</option>
</select>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">
Currency
</label>
<select class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
<option value="usd">US Dollar (USD)</option>
<option value="eur">Euro (EUR)</option>
<option value="gbp">British Pound (GBP)</option>
<option value="jpy">Japanese Yen (JPY)</option>
</select>
</div>
</div>
<!-- Custom API Config -->
<div id="custom-config" class="api-config hidden">
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2" for="custom-url">
API Endpoint URL
</label>
<input type="text" id="custom-url" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" placeholder="https://api.example.com/data">
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2" for="custom-key">
API Key (if required)
</label>
<input type="text" id="custom-key" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" placeholder="Your API key">
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2" for="data-path">
Data Path (JSON path to numeric values)
</label>
<input type="text" id="data-path" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" placeholder="$.data.values">
</div>
</div>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-medium mb-2">
Refresh Interval (seconds)
</label>
<div class="flex items-center">
<input type="range" id="refresh-interval" min="1" max="60" value="5" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<span id="interval-value" class="ml-4 text-gray-700 w-12 text-center">5s</span>
</div>
</div>
<div class="flex space-x-3">
<button id="start-btn" class="flex-1 bg-indigo-600 hover:bg-indigo-700 text-white py-2 px-4 rounded-md font-medium transition duration-150 ease-in-out">
<i class="fas fa-play mr-2"></i> Start
</button>
<button id="stop-btn" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md font-medium transition duration-150 ease-in-out">
<i class="fas fa-stop mr-2"></i> Stop
</button>
<button id="add-chart-btn" class="flex-1 bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded-md font-medium transition duration-150 ease-in-out">
<i class="fas fa-plus mr-2"></i> Add Chart
</button>
</div>
</div>
<!-- Charts Display Area -->
<div class="bg-white rounded-xl shadow-md p-6 lg:col-span-2">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-800">Data Visualization</h2>
<div class="flex space-x-2">
<button id="export-btn" class="bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded-md text-sm font-medium">
<i class="fas fa-download mr-1"></i> Export
</button>
<button id="clear-btn" class="bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded-md text-sm font-medium">
<i class="fas fa-trash-alt mr-1"></i> Clear All
</button>
</div>
</div>
<div id="charts-container">
<!-- Placeholder when no charts are added -->
<div id="empty-state" class="flex flex-col items-center justify-center py-12 text-center">
<i class="fas fa-chart-line text-4xl text-gray-300 mb-4"></i>
<h3 class="text-lg font-medium text-gray-500">No charts added yet</h3>
<p class="text-gray-400 mt-1">Configure an API and click "Add Chart" to visualize data</p>
</div>
<!-- Chart templates will be added here dynamically -->
</div>
</div>
</div>
<!-- API Status Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mt-6">
<div class="api-card bg-white rounded-xl shadow-md p-4 transition duration-300 ease-in-out">
<div class="flex items-center">
<div class="p-3 rounded-full bg-indigo-100 text-indigo-600 mr-4">
<i class="fas fa-coins text-xl"></i>
</div>
<div>
<h3 class="font-medium text-gray-800">Crypto API</h3>
<p class="text-sm text-gray-500">CoinGecko</p>
</div>
</div>
<div class="mt-4 flex justify-between items-center">
<span class="text-sm text-gray-500">Status</span>
<span class="px-2 py-1 text-xs rounded-full bg-green-100 text-green-800">Active</span>
</div>
</div>
<div class="api-card bg-white rounded-xl shadow-md p-4 transition duration-300 ease-in-out">
<div class="flex items-center">
<div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4">
<i class="fas fa-cloud-sun text-xl"></i>
</div>
<div>
<h3 class="font-medium text-gray-800">Weather API</h3>
<p class="text-sm text-gray-500">OpenWeather</p>
</div>
</div>
<div class="mt-4 flex justify-between items-center">
<span class="text-sm text-gray-500">Status</span>
<span class="px-2 py-1 text-xs rounded-full bg-green-100 text-green-800">Active</span>
</div>
</div>
<div class="api-card bg-white rounded-xl shadow-md p-4 transition duration-300 ease-in-out">
<div class="flex items-center">
<div class="p-3 rounded-full bg-purple-100 text-purple-600 mr-4">
<i class="fas fa-chart-line text-xl"></i>
</div>
<div>
<h3 class="font-medium text-gray-800">Stocks API</h3>
<p class="text-sm text-gray-500">Alpha Vantage</p>
</div>
</div>
<div class="mt-4 flex justify-between items-center">
<span class="text-sm text-gray-500">Status</span>
<span class="px-2 py-1 text-xs rounded-full bg-green-100 text-green-800">Active</span>
</div>
</div>
</div>
</div>
<script>
// DOM Elements
const apiSelect = document.getElementById('api-select');
const apiConfigSections = document.querySelectorAll('.api-config');
const refreshInterval = document.getElementById('refresh-interval');
const intervalValue = document.getElementById('interval-value');
const startBtn = document.getElementById('start-btn');
const stopBtn = document.getElementById('stop-btn');
const addChartBtn = document.getElementById('add-chart-btn');
const clearBtn = document.getElementById('clear-btn');
const exportBtn = document.getElementById('export-btn');
const chartsContainer = document.getElementById('charts-container');
const emptyState = document.getElementById('empty-state');
// Chart counter and data storage
let chartCounter = 0;
let charts = [];
let fetchInterval = null;
let isRunning = false;
// Event Listeners
apiSelect.addEventListener('change', handleApiSelectChange);
refreshInterval.addEventListener('input', updateIntervalValue);
startBtn.addEventListener('click', startFetching);
stopBtn.addEventListener('click', stopFetching);
addChartBtn.addEventListener('click', addNewChart);
clearBtn.addEventListener('click', clearAllCharts);
exportBtn.addEventListener('click', exportData);
// Functions
function handleApiSelectChange() {
const selectedApi = apiSelect.value;
// Hide all config sections
apiConfigSections.forEach(section => {
section.classList.add('hidden');
});
// Show selected config section
document.getElementById(`${selectedApi}-config`).classList.remove('hidden');
}
function updateIntervalValue() {
intervalValue.textContent = `${refreshInterval.value}s`;
}
function startFetching() {
if (charts.length === 0) {
alert('Please add at least one chart before starting.');
return;
}
if (isRunning) return;
isRunning = true;
startBtn.classList.remove('bg-indigo-600', 'hover:bg-indigo-700');
startBtn.classList.add('bg-indigo-400', 'cursor-not-allowed');
stopBtn.classList.remove('bg-gray-200', 'hover:bg-gray-300');
stopBtn.classList.add('bg-gray-100', 'cursor-not-allowed');
// Initial fetch
fetchDataForAllCharts();
// Set up interval
const interval = parseInt(refreshInterval.value) * 1000;
fetchInterval = setInterval(fetchDataForAllCharts, interval);
// Update connection status
document.querySelector('#connection-status span').classList.remove('bg-green-500');
document.querySelector('#connection-status span').classList.add('bg-yellow-500', 'pulse');
document.querySelector('#connection-status span + span').textContent = 'Fetching data...';
}
function stopFetching() {
if (!isRunning) return;
isRunning = false;
clearInterval(fetchInterval);
startBtn.classList.add('bg-indigo-600', 'hover:bg-indigo-700');
startBtn.classList.remove('bg-indigo-400', 'cursor-not-allowed');
stopBtn.classList.add('bg-gray-200', 'hover:bg-gray-300');
stopBtn.classList.remove('bg-gray-100', 'cursor-not-allowed');
// Update connection status
document.querySelector('#connection-status span').classList.remove('bg-yellow-500', 'pulse');
document.querySelector('#connection-status span').classList.add('bg-green-500');
document.querySelector('#connection-status span + span').textContent = 'Connected';
}
function addNewChart() {
// Hide empty state if it's the first chart
if (charts.length === 0) {
emptyState.classList.add('hidden');
}
chartCounter++;
const chartId = `chart-${chartCounter}`;
// Create chart container
const chartDiv = document.createElement('div');
chartDiv.className = 'mb-8';
chartDiv.id = `container-${chartId}`;
// Chart header with title and controls
const chartHeader = document.createElement('div');
chartHeader.className = 'flex justify-between items-center mb-2';
const chartTitle = document.createElement('h3');
chartTitle.className = 'font-medium text-gray-800';
chartTitle.textContent = `Chart ${chartCounter} - ${apiSelect.options[apiSelect.selectedIndex].text}`;
const chartControls = document.createElement('div');
chartControls.className = 'flex space-x-2';
const removeBtn = document.createElement('button');
removeBtn.className = 'text-red-500 hover:text-red-700 text-sm';
removeBtn.innerHTML = '<i class="fas fa-times"></i>';
removeBtn.onclick = () => removeChart(chartId);
chartControls.appendChild(removeBtn);
chartHeader.appendChild(chartTitle);
chartHeader.appendChild(chartControls);
// Chart canvas
const chartCanvas = document.createElement('canvas');
chartCanvas.id = chartId;
// Assemble the chart container
chartDiv.appendChild(chartHeader);
chartDiv.appendChild(document.createElement('div').appendChild(chartCanvas).parentNode);
// Add to DOM
chartsContainer.appendChild(chartDiv);
// Create Chart.js instance
const ctx = document.getElementById(chartId).getContext('2d');
const newChart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Value',
data: [],
borderColor: getRandomColor(),
backgroundColor: 'rgba(0, 0, 0, 0.1)',
borderWidth: 2,
tension: 0.1,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: false
}
},
plugins: {
legend: {
display: false
},
tooltip: {
mode: 'index',
intersect: false
}
},
hover: {
mode: 'nearest',
intersect: true
}
}
});
// Store chart reference
charts.push({
id: chartId,
instance: newChart,
apiType: apiSelect.value,
config: getCurrentConfig()
});
}
function removeChart(chartId) {
// Find and destroy chart
const chartIndex = charts.findIndex(chart => chart.id === chartId);
if (chartIndex !== -1) {
charts[chartIndex].instance.destroy();
charts.splice(chartIndex, 1);
// Remove from DOM
document.getElementById(`container-${chartId}`).remove();
// Show empty state if no charts left
if (charts.length === 0) {
emptyState.classList.remove('hidden');
}
}
}
function clearAllCharts() {
// Stop any running fetches
stopFetching();
// Destroy all charts
charts.forEach(chart => {
chart.instance.destroy();
});
// Clear the array
charts = [];
chartCounter = 0;
// Clear the container
chartsContainer.innerHTML = '';
emptyState.classList.remove('hidden');
}
function fetchDataForAllCharts() {
if (charts.length === 0) return;
// For demo purposes, we'll simulate API calls with random data
charts.forEach(chart => {
// In a real app, you would make actual API calls here
// For now, we'll simulate with random data
simulateApiCall(chart);
});
}
function simulateApiCall(chart) {
// Simulate API delay
setTimeout(() => {
const now = new Date();
const timeLabel = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
// Generate random data based on API type
let newValue;
switch(chart.apiType) {
case 'crypto':
newValue = Math.random() * 10000 + 20000; // BTC-like prices
break;
case 'weather':
newValue = Math.random() * 30 + 10; // Temperature-like values
break;
case 'stocks':
newValue = Math.random() * 100 + 150; // Stock-like prices
break;
default:
newValue = Math.random() * 100;
}
// Update chart
const chartInstance = chart.instance;
const labels = chartInstance.data.labels;
const data = chartInstance.data.datasets[0].data;
// Keep only last 20 data points for performance
if (labels.length >= 20) {
labels.shift();
data.shift();
}
labels.push(timeLabel);
data.push(newValue);
chartInstance.update();
}, 500); // Simulate network delay
}
function getCurrentConfig() {
// In a real app, this would return the current configuration
// from the form inputs for the selected API
return {
api: apiSelect.value,
// Add other config properties here
};
}
function getRandomColor() {
const colors = [
'#3B82F6', // blue-500
'#EF4444', // red-500
'#10B981', // emerald-500
'#F59E0B', // amber-500
'#8B5CF6', // violet-500
'#EC4899', // pink-500
'#14B8A6', // teal-500
'#F97316' // orange-500
];
return colors[Math.floor(Math.random() * colors.length)];
}
function exportData() {
if (charts.length === 0) {
alert('No data to export. Add charts and fetch data first.');
return;
}
// In a real app, this would export the chart data
alert('Export functionality would save chart data here.\nIn a real implementation, this would download a CSV or image.');
}
// Initialize
handleApiSelectChange();
updateIntervalValue();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=mauryaR8/realtime-api-data-visualization" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>