elastic-dashboard / index.html
x3v's picture
Add 2 files
53f8d4a verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Elasticsearch Data Visualization</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/luxon@3.0.1"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.2.0"></script>
<style>
.chart-container {
position: relative;
height: 300px;
width: 100%;
}
.card {
transition: all 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-8">
<h1 class="text-3xl font-bold text-gray-800">
<i class="fas fa-chart-line text-blue-500 mr-2"></i> Elasticsearch Data Dashboard
</h1>
<div class="flex items-center space-x-4">
<div class="relative">
<select id="time-range" class="bg-white border border-gray-300 rounded-md px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="24h">Last 24 Hours</option>
<option value="7d" selected>Last 7 Days</option>
<option value="30d">Last 30 Days</option>
</select>
</div>
<button id="refresh-btn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md flex items-center">
<i class="fas fa-sync-alt mr-2"></i> Refresh
</button>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<!-- Summary Cards -->
<div class="card bg-white rounded-lg shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4">
<i class="fas fa-database text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Total Documents</p>
<h3 class="text-2xl font-bold" id="total-docs">12,458</h3>
</div>
</div>
</div>
<div class="card bg-white rounded-lg shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-green-100 text-green-600 mr-4">
<i class="fas fa-bolt text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Indexing Rate</p>
<h3 class="text-2xl font-bold" id="index-rate">1,245 <span class="text-sm">docs/sec</span></h3>
</div>
</div>
</div>
<div class="card bg-white rounded-lg shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-yellow-100 text-yellow-600 mr-4">
<i class="fas fa-search text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Search Queries</p>
<h3 class="text-2xl font-bold" id="search-count">3,421 <span class="text-sm">/day</span></h3>
</div>
</div>
</div>
<div class="card bg-white rounded-lg shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-red-100 text-red-600 mr-4">
<i class="fas fa-exclamation-triangle text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Failed Queries</p>
<h3 class="text-2xl font-bold" id="failed-queries">42 <span class="text-sm">(1.2%)</span></h3>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
<!-- Time Series Charts -->
<div class="card bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-semibold mb-4 text-gray-800">
<i class="fas fa-chart-bar text-blue-500 mr-2"></i> Document Count Over Time
</h2>
<div class="chart-container">
<canvas id="docCountChart"></canvas>
</div>
</div>
<div class="card bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-semibold mb-4 text-gray-800">
<i class="fas fa-chart-line text-green-500 mr-2"></i> Indexing Rate Over Time
</h2>
<div class="chart-container">
<canvas id="indexRateChart"></canvas>
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
<!-- Other Charts -->
<div class="card bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-semibold mb-4 text-gray-800">
<i class="fas fa-chart-pie text-purple-500 mr-2"></i> Index Distribution
</h2>
<div class="chart-container">
<canvas id="indexDistributionChart"></canvas>
</div>
</div>
<div class="card bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-semibold mb-4 text-gray-800">
<i class="fas fa-stopwatch text-yellow-500 mr-2"></i> Query Response Times
</h2>
<div class="chart-container">
<canvas id="queryTimeChart"></canvas>
</div>
</div>
</div>
<div class="card bg-white rounded-lg shadow-md p-6 mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-800">
<i class="fas fa-table text-blue-500 mr-2"></i> Recent Documents
</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Index</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Timestamp</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Source</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody id="recent-docs" class="bg-white divide-y divide-gray-200">
<!-- Table rows will be populated here -->
</tbody>
</table>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Font Awesome for icons
const faScript = document.createElement('script');
faScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js';
document.head.appendChild(faScript);
// Sample Elasticsearch data
const sampleData = {
summary: {
totalDocs: 12458,
indexRate: 1245,
searchCount: 3421,
failedQueries: 42
},
timeSeries: {
docCount: {
labels: generateTimeLabels(7),
data: generateRandomData(7, 8000, 15000)
},
indexRate: {
labels: generateTimeLabels(7),
data: generateRandomData(7, 500, 2000)
}
},
indexDistribution: {
labels: ['logs-2023', 'metrics-2023', 'transactions', 'users', 'products', 'others'],
data: [35, 25, 15, 10, 8, 7]
},
queryTimes: {
labels: ['<50ms', '50-100ms', '100-200ms', '200-500ms', '500ms-1s', '>1s'],
data: [45, 30, 15, 6, 3, 1]
},
recentDocs: [
{ index: 'logs-2023.06.15', type: 'log', timestamp: '2023-06-15T08:23:45Z', source: 'web-server-1', status: 'success' },
{ index: 'metrics-2023.06', type: 'metric', timestamp: '2023-06-15T08:22:30Z', source: 'system', status: 'success' },
{ index: 'transactions', type: 'txn', timestamp: '2023-06-15T08:21:15Z', source: 'payment-gateway', status: 'success' },
{ index: 'users', type: 'user', timestamp: '2023-06-15T08:20:00Z', source: 'api', status: 'success' },
{ index: 'logs-2023.06.15', type: 'log', timestamp: '2023-06-15T08:19:45Z', source: 'web-server-2', status: 'failed' }
]
};
// Initialize charts
const docCountChart = createTimeSeriesChart('docCountChart', 'Document Count', sampleData.timeSeries.docCount);
const indexRateChart = createTimeSeriesChart('indexRateChart', 'Indexing Rate (docs/sec)', sampleData.timeSeries.indexRate);
const indexDistributionChart = createDoughnutChart('indexDistributionChart', sampleData.indexDistribution);
const queryTimeChart = createBarChart('queryTimeChart', 'Query Response Times', sampleData.queryTimes);
// Populate summary cards
document.getElementById('total-docs').textContent = sampleData.summary.totalDocs.toLocaleString();
document.getElementById('index-rate').innerHTML = `${sampleData.summary.indexRate.toLocaleString()} <span class="text-sm">docs/sec</span>`;
document.getElementById('search-count').innerHTML = `${sampleData.summary.searchCount.toLocaleString()} <span class="text-sm">/day</span>`;
document.getElementById('failed-queries').innerHTML = `${sampleData.summary.failedQueries} <span class="text-sm">(${((sampleData.summary.failedQueries / sampleData.summary.searchCount) * 100).toFixed(1)}%)</span>`;
// Populate recent documents table
const recentDocsTable = document.getElementById('recent-docs');
sampleData.recentDocs.forEach(doc => {
const row = document.createElement('tr');
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${doc.index}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${doc.type}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${formatDate(doc.timestamp)}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${doc.source}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${doc.status === 'success' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}">
${doc.status}
</span>
</td>
`;
recentDocsTable.appendChild(row);
});
// Event listeners
document.getElementById('refresh-btn').addEventListener('click', function() {
// Simulate data refresh
this.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Refreshing...';
setTimeout(() => {
// Update charts with new random data
updateChartWithRandomData(docCountChart, 8000, 15000);
updateChartWithRandomData(indexRateChart, 500, 2000);
// Reset button
this.innerHTML = '<i class="fas fa-sync-alt mr-2"></i> Refresh';
// Show notification
showNotification('Data refreshed successfully!', 'success');
}, 1500);
});
document.getElementById('time-range').addEventListener('change', function() {
const value = this.value;
let days = 7;
if (value === '24h') days = 1;
else if (value === '30d') days = 30;
// Update time series charts with new time range
const newLabels = generateTimeLabels(days);
docCountChart.data.labels = newLabels;
docCountChart.data.datasets[0].data = generateRandomData(days, 8000, 15000);
docCountChart.update();
indexRateChart.data.labels = newLabels;
indexRateChart.data.datasets[0].data = generateRandomData(days, 500, 2000);
indexRateChart.update();
showNotification(`Time range changed to ${this.options[this.selectedIndex].text}`, 'info');
});
// Helper functions
function generateTimeLabels(days) {
const labels = [];
const now = new Date();
for (let i = days; i >= 0; i--) {
const date = new Date(now);
date.setDate(date.getDate() - i);
labels.push(date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
}
return labels;
}
function generateRandomData(count, min, max) {
const data = [];
for (let i = 0; i < count; i++) {
data.push(Math.floor(Math.random() * (max - min + 1)) + min);
}
return data;
}
function formatDate(isoString) {
const date = new Date(isoString);
return date.toLocaleString('en-US', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
function showNotification(message, type) {
const notification = document.createElement('div');
notification.className = `fixed bottom-4 right-4 px-4 py-2 rounded-md shadow-md text-white ${
type === 'success' ? 'bg-green-500' :
type === 'error' ? 'bg-red-500' : 'bg-blue-500'
}`;
notification.innerHTML = `
<i class="fas ${type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-exclamation-circle' : 'fa-info-circle'} mr-2"></i>
${message}
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('opacity-0', 'transition-opacity', 'duration-500');
setTimeout(() => {
document.body.removeChild(notification);
}, 500);
}, 3000);
}
function updateChartWithRandomData(chart, min, max) {
chart.data.datasets[0].data = generateRandomData(chart.data.labels.length, min, max);
chart.update();
}
function createTimeSeriesChart(canvasId, label, data) {
const ctx = document.getElementById(canvasId).getContext('2d');
return new Chart(ctx, {
type: 'line',
data: {
labels: data.labels,
datasets: [{
label: label,
data: data.data,
backgroundColor: 'rgba(59, 130, 246, 0.1)',
borderColor: 'rgba(59, 130, 246, 1)',
borderWidth: 2,
tension: 0.3,
fill: true,
pointBackgroundColor: 'rgba(59, 130, 246, 1)',
pointRadius: 3,
pointHoverRadius: 5
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
mode: 'index',
intersect: false
}
},
scales: {
y: {
beginAtZero: false,
grid: {
drawBorder: false
}
},
x: {
grid: {
display: false
}
}
}
}
});
}
function createDoughnutChart(canvasId, data) {
const ctx = document.getElementById(canvasId).getContext('2d');
return new Chart(ctx, {
type: 'doughnut',
data: {
labels: data.labels,
datasets: [{
data: data.data,
backgroundColor: [
'rgba(59, 130, 246, 0.7)',
'rgba(16, 185, 129, 0.7)',
'rgba(245, 158, 11, 0.7)',
'rgba(139, 92, 246, 0.7)',
'rgba(220, 38, 38, 0.7)',
'rgba(75, 85, 99, 0.7)'
],
borderColor: [
'rgba(59, 130, 246, 1)',
'rgba(16, 185, 129, 1)',
'rgba(245, 158, 11, 1)',
'rgba(139, 92, 246, 1)',
'rgba(220, 38, 38, 1)',
'rgba(75, 85, 99, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
boxWidth: 12,
padding: 20
}
}
},
cutout: '65%'
}
});
}
function createBarChart(canvasId, label, data) {
const ctx = document.getElementById(canvasId).getContext('2d');
return new Chart(ctx, {
type: 'bar',
data: {
labels: data.labels,
datasets: [{
label: label,
data: data.data,
backgroundColor: 'rgba(59, 130, 246, 0.7)',
borderColor: 'rgba(59, 130, 246, 1)',
borderWidth: 1,
borderRadius: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false
}
},
x: {
grid: {
display: false
}
}
}
}
});
}
});
</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=x3v/elastic-dashboard" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>