<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Analytics - STARFLEET COMMAND</title>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Rajdhani:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/pages/shared/lcars-styles.css">
<style>
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.metric-card {
background: linear-gradient(135deg, rgba(0,80,120,0.5) 0%, rgba(0,40,80,0.7) 100%);
border: 2px solid var(--lcars-blue);
border-radius: 16px;
padding: 25px 20px;
text-align: center;
position: relative;
overflow: hidden;
}
.metric-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--lcars-blue), var(--enterprise-gold));
}
.metric-value {
font-family: 'Orbitron', monospace;
font-size: 2.5rem;
font-weight: 900;
color: var(--enterprise-gold);
text-shadow: 0 0 20px var(--enterprise-gold);
line-height: 1;
}
.metric-label {
font-family: 'Rajdhani', sans-serif;
font-size: 0.9rem;
color: var(--lcars-light-blue);
text-transform: uppercase;
margin-top: 10px;
}
.metric-change {
font-size: 0.85rem;
margin-top: 8px;
}
.metric-change.positive {
color: var(--console-green);
}
.metric-change.negative {
color: #ff6666;
}
.chart-container {
background: rgba(0,20,40,0.8);
border: 1px solid var(--lcars-blue);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
}
.chart-title {
font-family: 'Orbitron', monospace;
font-size: 1rem;
color: var(--enterprise-gold);
margin-bottom: 15px;
}
.live-chart {
height: 200px;
position: relative;
}
.chart-svg {
width: 100%;
height: 100%;
}
.chart-line {
fill: none;
stroke: var(--console-green);
stroke-width: 2;
}
.chart-area {
fill: url(#chartGradient);
opacity: 0.3;
}
.chart-grid line {
stroke: rgba(85,85,255,0.2);
stroke-width: 1;
}
.gauge-container {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
gap: 30px;
}
.gauge {
position: relative;
width: 150px;
height: 150px;
}
.gauge-svg {
transform: rotate(-90deg);
}
.gauge-bg {
fill: none;
stroke: rgba(85,85,255,0.3);
stroke-width: 12;
}
.gauge-fill {
fill: none;
stroke: var(--console-green);
stroke-width: 12;
stroke-linecap: round;
transition: stroke-dashoffset 0.5s ease;
}
.gauge-value {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-family: 'Orbitron', monospace;
font-size: 1.5rem;
font-weight: 900;
color: var(--enterprise-gold);
}
.gauge-label {
text-align: center;
margin-top: 10px;
font-family: 'Rajdhani', sans-serif;
color: var(--lcars-light-blue);
text-transform: uppercase;
}
.heatmap-grid {
display: grid;
grid-template-columns: repeat(24, 1fr);
gap: 3px;
}
.heatmap-cell {
aspect-ratio: 1;
border-radius: 3px;
transition: all 0.3s ease;
}
.heatmap-cell:hover {
transform: scale(1.3);
z-index: 10;
}
.activity-feed {
max-height: 300px;
overflow-y: auto;
}
.activity-item {
display: flex;
align-items: center;
padding: 12px 15px;
border-bottom: 1px solid rgba(85,85,255,0.2);
gap: 15px;
}
.activity-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
background: rgba(0,100,150,0.4);
}
.activity-content {
flex: 1;
}
.activity-title {
color: var(--lcars-light-blue);
font-weight: 600;
}
.activity-time {
font-size: 0.8rem;
color: #666;
}
.sparkline-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.sparkline-card {
background: rgba(0,40,80,0.4);
border: 1px solid var(--lcars-blue);
border-radius: 10px;
padding: 15px;
}
.sparkline-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.sparkline-title {
color: var(--lcars-light-blue);
font-size: 0.9rem;
}
.sparkline-value {
color: var(--enterprise-gold);
font-family: 'Orbitron', monospace;
font-weight: 700;
}
.sparkline-svg {
width: 100%;
height: 40px;
}
.sparkline-line {
fill: none;
stroke: var(--console-green);
stroke-width: 2;
}
.realtime-indicator {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background: rgba(0,255,100,0.1);
border: 1px solid var(--console-green);
border-radius: 20px;
font-size: 0.85rem;
color: var(--console-green);
}
.realtime-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--console-green);
animation: pulse 1s infinite;
}
</style>
</head>
<body>
<div class="lcars-container">
<!-- Header -->
<div class="lcars-header">
<div class="header-left">
<a href="/pages/index.html" class="back-btn">β BACK</a>
<div>
<h1 class="page-title">π Analytics Dashboard</h1>
<p class="page-subtitle">Real-time Metrics & Performance Monitoring</p>
</div>
</div>
<div class="header-right">
<div class="realtime-indicator">
<div class="realtime-dot"></div>
<span>LIVE</span>
</div>
<span id="stardate" class="stardate"></span>
</div>
</div>
<!-- Quick Actions -->
<div class="lcars-panel">
<div class="panel-header">
<span class="panel-icon">β‘</span>
<h2 class="panel-title">Controls</h2>
</div>
<div class="panel-content">
<div class="quick-actions">
<button class="lcars-btn primary" onclick="refreshAll()">π Refresh All</button>
<button class="lcars-btn" onclick="toggleLiveUpdates()">π‘ Toggle Live</button>
<button class="lcars-btn" onclick="exportData()">π€ Export Data</button>
<button class="lcars-btn" onclick="resetStats()">π Reset Stats</button>
<select id="time-range" class="lcars-input" style="width: auto;" onchange="changeTimeRange()">
<option value="1h">Last Hour</option>
<option value="6h">Last 6 Hours</option>
<option value="24h" selected>Last 24 Hours</option>
<option value="7d">Last 7 Days</option>
</select>
</div>
</div>
</div>
<!-- Key Metrics -->
<div class="metrics-grid" id="metrics-grid">
<div class="metric-card">
<div class="metric-value" id="metric-requests">0</div>
<div class="metric-label">Total Requests</div>
<div class="metric-change positive" id="metric-requests-change">+0%</div>
</div>
<div class="metric-card">
<div class="metric-value" id="metric-latency">0</div>
<div class="metric-label">Avg Latency (ms)</div>
<div class="metric-change positive" id="metric-latency-change">-0%</div>
</div>
<div class="metric-card">
<div class="metric-value" id="metric-errors">0</div>
<div class="metric-label">Error Rate</div>
<div class="metric-change positive" id="metric-errors-change">-0%</div>
</div>
<div class="metric-card">
<div class="metric-value" id="metric-tools">0</div>
<div class="metric-label">Tool Calls</div>
<div class="metric-change positive" id="metric-tools-change">+0%</div>
</div>
<div class="metric-card">
<div class="metric-value" id="metric-uptime">99.9</div>
<div class="metric-label">Uptime %</div>
<div class="metric-change positive">Excellent</div>
</div>
<div class="metric-card">
<div class="metric-value" id="metric-active">0</div>
<div class="metric-label">Active Sessions</div>
<div class="metric-change positive" id="metric-active-change">+0</div>
</div>
</div>
<!-- Performance Chart -->
<div class="lcars-panel">
<div class="panel-header">
<span class="panel-icon">π</span>
<h2 class="panel-title">Performance Over Time</h2>
</div>
<div class="panel-content">
<div class="chart-container">
<div class="chart-title">Request Throughput & Latency</div>
<div class="live-chart">
<svg class="chart-svg" id="main-chart" viewBox="0 0 800 200">
<defs>
<linearGradient id="chartGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:var(--console-green);stop-opacity:0.5"/>
<stop offset="100%" style="stop-color:var(--console-green);stop-opacity:0"/>
</linearGradient>
</defs>
<g class="chart-grid" id="chart-grid"></g>
<path class="chart-area" id="chart-area"></path>
<path class="chart-line" id="chart-line"></path>
</svg>
</div>
</div>
</div>
</div>
<div class="two-column">
<!-- Gauges -->
<div class="lcars-panel">
<div class="panel-header">
<span class="panel-icon">π―</span>
<h2 class="panel-title">System Health</h2>
</div>
<div class="panel-content">
<div class="gauge-container">
<div>
<div class="gauge">
<svg class="gauge-svg" viewBox="0 0 150 150">
<circle class="gauge-bg" cx="75" cy="75" r="60"/>
<circle class="gauge-fill" cx="75" cy="75" r="60"
stroke-dasharray="377"
stroke-dashoffset="94"
id="cpu-gauge"/>
</svg>
<div class="gauge-value" id="cpu-value">75%</div>
</div>
<div class="gauge-label">CPU Usage</div>
</div>
<div>
<div class="gauge">
<svg class="gauge-svg" viewBox="0 0 150 150">
<circle class="gauge-bg" cx="75" cy="75" r="60"/>
<circle class="gauge-fill" cx="75" cy="75" r="60"
stroke-dasharray="377"
stroke-dashoffset="150"
id="memory-gauge"
style="stroke: var(--lcars-blue);"/>
</svg>
<div class="gauge-value" id="memory-value">60%</div>
</div>
<div class="gauge-label">Memory</div>
</div>
<div>
<div class="gauge">
<svg class="gauge-svg" viewBox="0 0 150 150">
<circle class="gauge-bg" cx="75" cy="75" r="60"/>
<circle class="gauge-fill" cx="75" cy="75" r="60"
stroke-dasharray="377"
stroke-dashoffset="283"
id="disk-gauge"
style="stroke: var(--enterprise-gold);"/>
</svg>
<div class="gauge-value" id="disk-value">25%</div>
</div>
<div class="gauge-label">Disk I/O</div>
</div>
</div>
</div>
</div>
<!-- Activity Heatmap -->
<div class="lcars-panel">
<div class="panel-header">
<span class="panel-icon">π₯</span>
<h2 class="panel-title">Activity Heatmap (24h)</h2>
</div>
<div class="panel-content">
<div class="heatmap-grid" id="heatmap">
<!-- Heatmap cells will be generated -->
</div>
<div style="display: flex; justify-content: space-between; margin-top: 15px; font-size: 0.8rem; color: #666;">
<span>00:00</span>
<span>06:00</span>
<span>12:00</span>
<span>18:00</span>
<span>24:00</span>
</div>
</div>
</div>
</div>
<!-- Sparklines -->
<div class="lcars-panel">
<div class="panel-header">
<span class="panel-icon">β¨</span>
<h2 class="panel-title">Tool Usage Trends</h2>
</div>
<div class="panel-content">
<div class="sparkline-container" id="sparklines">
<!-- Sparklines will be generated -->
</div>
</div>
</div>
<div class="two-column">
<!-- Top Tools -->
<div class="lcars-panel">
<div class="panel-header">
<span class="panel-icon">π§</span>
<h2 class="panel-title">Most Used Tools</h2>
</div>
<div class="panel-content">
<div id="top-tools">
<!-- Top tools will be rendered -->
</div>
</div>
</div>
<!-- Activity Feed -->
<div class="lcars-panel">
<div class="panel-header">
<span class="panel-icon">π‘</span>
<h2 class="panel-title">Live Activity</h2>
</div>
<div class="panel-content">
<div class="activity-feed" id="activity-feed">
<!-- Activity items will be generated -->
</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="lcars-footer">
<p class="footer-text">STARFLEET COMMAND β’ Analytics Module β’ LCARS Interface</p>
</div>
</div>
<script src="/pages/shared/lcars-core.js"></script>
<script>
// State
let liveUpdatesEnabled = true;
let updateInterval = null;
let chartData = [];
let metrics = {
requests: 0,
latency: 0,
errors: 0,
tools: 0,
active: 0
};
const toolUsage = [
{ name: 'run_command', calls: 0, color: '#00ff66' },
{ name: 'list_files', calls: 0, color: '#00ccff' },
{ name: 'read_file', calls: 0, color: '#ffcc00' },
{ name: 'git_status', calls: 0, color: '#ff66cc' },
{ name: 'system_info', calls: 0, color: '#cc99ff' },
{ name: 'write_file', calls: 0, color: '#ff9966' }
];
// Initialize
document.addEventListener('DOMContentLoaded', () => {
initChartData();
generateHeatmap();
generateSparklines();
renderTopTools();
renderActivityFeed();
updateMetrics();
startLiveUpdates();
});
function initChartData() {
chartData = Array.from({ length: 50 }, () => Math.random() * 80 + 20);
drawChart();
}
function drawChart() {
const width = 800;
const height = 200;
const padding = 20;
// Generate path
const points = chartData.map((value, index) => {
const x = padding + (index / (chartData.length - 1)) * (width - 2 * padding);
const y = height - padding - (value / 100) * (height - 2 * padding);
return `${x},${y}`;
});
const linePath = `M ${points.join(' L ')}`;
const areaPath = linePath + ` L ${width - padding},${height - padding} L ${padding},${height - padding} Z`;
document.getElementById('chart-line').setAttribute('d', linePath);
document.getElementById('chart-area').setAttribute('d', areaPath);
// Draw grid
const gridHtml = [];
for (let i = 0; i <= 4; i++) {
const y = padding + (i / 4) * (height - 2 * padding);
gridHtml.push(`<line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}"/>`);
}
document.getElementById('chart-grid').innerHTML = gridHtml.join('');
}
function generateHeatmap() {
const heatmap = document.getElementById('heatmap');
const cells = [];
for (let i = 0; i < 168; i++) { // 24 hours * 7 rows
const intensity = Math.random();
const color = getHeatmapColor(intensity);
cells.push(`<div class="heatmap-cell" style="background: ${color};" title="${Math.floor(intensity * 100)} requests"></div>`);
}
heatmap.innerHTML = cells.join('');
}
function getHeatmapColor(intensity) {
if (intensity < 0.2) return 'rgba(0,50,100,0.3)';
if (intensity < 0.4) return 'rgba(0,100,150,0.5)';
if (intensity < 0.6) return 'rgba(0,150,200,0.7)';
if (intensity < 0.8) return 'rgba(0,200,100,0.8)';
return 'rgba(0,255,100,0.9)';
}
function generateSparklines() {
const container = document.getElementById('sparklines');
const html = toolUsage.map(tool => {
const points = generateSparklinePoints();
return `
<div class="sparkline-card">
<div class="sparkline-header">
<span class="sparkline-title">${tool.name}</span>
<span class="sparkline-value">${tool.calls}</span>
</div>
<svg class="sparkline-svg" viewBox="0 0 200 40">
<path class="sparkline-line" d="${points}" style="stroke: ${tool.color};"/>
</svg>
</div>
`;
}).join('');
container.innerHTML = html;
}
function generateSparklinePoints() {
const points = [];
for (let i = 0; i < 20; i++) {
const x = (i / 19) * 200;
const y = 35 - Math.random() * 30;
points.push(`${i === 0 ? 'M' : 'L'} ${x},${y}`);
}
return points.join(' ');
}
function renderTopTools() {
const sorted = [...toolUsage].sort((a, b) => b.calls - a.calls);
const maxCalls = Math.max(...sorted.map(t => t.calls), 1);
const html = sorted.map((tool, index) => {
const percentage = (tool.calls / maxCalls) * 100;
return `
<div style="margin-bottom: 15px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
<span style="color: var(--lcars-light-blue);">${index + 1}. ${tool.name}</span>
<span style="color: var(--enterprise-gold);">${tool.calls} calls</span>
</div>
<div class="stats-bar" style="height: 8px;">
<div style="width: ${percentage}%; height: 100%; background: ${tool.color}; border-radius: 4px;"></div>
</div>
</div>
`;
}).join('');
document.getElementById('top-tools').innerHTML = html;
}
function renderActivityFeed() {
const activities = [
{ icon: 'π§', title: 'Tool executed: run_command', time: 'Just now' },
{ icon: 'π', title: 'Files listed in /pages', time: '1m ago' },
{ icon: 'π', title: 'File read: server.js', time: '2m ago' },
{ icon: 'π', title: 'Git status checked', time: '3m ago' },
{ icon: 'π»', title: 'System info requested', time: '5m ago' },
{ icon: 'π', title: 'Analytics updated', time: '6m ago' },
{ icon: 'π§', title: 'Tool executed: list_files', time: '8m ago' },
{ icon: 'π', title: 'File written: config.json', time: '10m ago' }
];
const html = activities.map(act => `
<div class="activity-item">
<div class="activity-icon">${act.icon}</div>
<div class="activity-content">
<div class="activity-title">${act.title}</div>
<div class="activity-time">${act.time}</div>
</div>
</div>
`).join('');
document.getElementById('activity-feed').innerHTML = html;
}
function updateMetrics() {
// Simulate metric updates
metrics.requests += Math.floor(Math.random() * 10);
metrics.latency = Math.floor(Math.random() * 50 + 20);
metrics.errors = (Math.random() * 2).toFixed(2);
metrics.tools += Math.floor(Math.random() * 5);
metrics.active = Math.floor(Math.random() * 10 + 1);
// Update tool usage
toolUsage.forEach(tool => {
tool.calls += Math.floor(Math.random() * 3);
});
// Update DOM
document.getElementById('metric-requests').textContent = metrics.requests;
document.getElementById('metric-latency').textContent = metrics.latency;
document.getElementById('metric-errors').textContent = metrics.errors + '%';
document.getElementById('metric-tools').textContent = metrics.tools;
document.getElementById('metric-active').textContent = metrics.active;
// Update change indicators
document.getElementById('metric-requests-change').textContent = '+' + Math.floor(Math.random() * 20) + '%';
document.getElementById('metric-latency-change').textContent = '-' + Math.floor(Math.random() * 10) + '%';
// Update gauges
updateGauge('cpu', Math.random() * 100);
updateGauge('memory', Math.random() * 100);
updateGauge('disk', Math.random() * 100);
}
function updateGauge(id, value) {
const circumference = 377; // 2 * PI * 60
const offset = circumference - (value / 100) * circumference;
document.getElementById(`${id}-gauge`).setAttribute('stroke-dashoffset', offset);
document.getElementById(`${id}-value`).textContent = Math.floor(value) + '%';
}
function updateChart() {
chartData.shift();
chartData.push(Math.random() * 80 + 20);
drawChart();
}
function startLiveUpdates() {
if (updateInterval) clearInterval(updateInterval);
updateInterval = setInterval(() => {
if (liveUpdatesEnabled) {
updateMetrics();
updateChart();
renderTopTools();
}
}, 2000);
}
function toggleLiveUpdates() {
liveUpdatesEnabled = !liveUpdatesEnabled;
if (liveUpdatesEnabled) {
playSuccessSound();
startLiveUpdates();
} else {
clearInterval(updateInterval);
}
}
function refreshAll() {
updateMetrics();
updateChart();
generateHeatmap();
generateSparklines();
renderTopTools();
renderActivityFeed();
playSuccessSound();
}
function exportData() {
const data = {
metrics,
toolUsage,
chartData,
exportedAt: new Date().toISOString()
};
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'analytics-export.json';
a.click();
URL.revokeObjectURL(url);
playSuccessSound();
}
function resetStats() {
if (!confirm('Reset all statistics?')) return;
metrics = { requests: 0, latency: 0, errors: 0, tools: 0, active: 0 };
toolUsage.forEach(tool => tool.calls = 0);
refreshAll();
}
function changeTimeRange() {
const range = document.getElementById('time-range').value;
console.log('Time range changed to:', range);
refreshAll();
}
</script>
</body>
</html>