Files
container-census/web/analytics/index.html
2025-10-27 23:45:55 -04:00

243 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Container Census Analytics</title>
<link rel="stylesheet" href="styles.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
</head>
<body>
<div class="container">
<header>
<div class="header-content">
<div>
<h1>📊 Container Census Analytics</h1>
<p class="subtitle">Global container usage statistics (anonymous data)</p>
</div>
<div class="header-right">
<div id="liveIndicator" class="live-indicator">
<div class="pulse-dot"></div>
<span id="liveStatus">Waiting...</span>
<span id="eventCounter" class="event-counter" style="display: none;">0</span>
</div>
<span id="versionBadge" class="version-badge">v0.0.0</span>
</div>
</div>
</header>
<div class="summary-cards">
<div class="card">
<div class="card-value" id="totalInstallations">-</div>
<div class="card-label">Active Installations</div>
</div>
<div class="card">
<div class="card-value" id="totalSubmissions">-</div>
<div class="card-label">Total Submissions</div>
</div>
<div class="card">
<div class="card-value" id="totalContainers">-</div>
<div class="card-label">Total Containers</div>
</div>
<div class="card">
<div class="card-value" id="avgContainers">-</div>
<div class="card-label">Avg Containers/Install</div>
</div>
<div class="card">
<div class="card-value" id="totalHosts">-</div>
<div class="card-label">Total Hosts</div>
</div>
<div class="card">
<div class="card-value" id="totalAgents">-</div>
<div class="card-label">Total Agents</div>
</div>
<div class="card">
<div class="card-value" id="uniqueImages">-</div>
<div class="card-label">Unique Images</div>
</div>
</div>
<div class="filters">
<label>
Time Range:
<select id="timeRange">
<option value="7">Last 7 days</option>
<option value="30" selected>Last 30 days</option>
<option value="90">Last 90 days</option>
<option value="365">Last year</option>
</select>
</label>
<button onclick="refreshData()">🔄 Refresh</button>
</div>
<div class="tabs">
<button class="tab-button active" onclick="showTab('charts', this)">Charts</button>
<button class="tab-button" onclick="showTab('images', this)">Container Images</button>
<button class="tab-button" onclick="showTab('database', this)">Database</button>
</div>
<div id="chartsTab" class="tab-content active">
<div class="chart-container">
<h2>Top 20 Most Popular Container Images</h2>
<canvas id="topImagesChart"></canvas>
</div>
<div class="chart-container">
<h2>Installation Growth Over Time</h2>
<canvas id="growthChart"></canvas>
</div>
<div class="chart-row">
<div class="chart-container half-width">
<h2>Registry Distribution</h2>
<canvas id="registriesChart"></canvas>
</div>
<div class="chart-container half-width">
<h2>Version Adoption</h2>
<canvas id="versionsChart"></canvas>
</div>
</div>
<div class="chart-row">
<div class="chart-container half-width">
<h2>Scan Interval Configuration</h2>
<canvas id="scanIntervalsChart"></canvas>
</div>
<div class="chart-container half-width activity-heatmap-container">
<h2>Activity Pattern (UTC)</h2>
<canvas id="activityHeatmapChart"></canvas>
</div>
</div>
<div class="chart-row">
<div class="chart-container half-width">
<h2>🐳 Docker Compose Adoption</h2>
<canvas id="composeAdoptionChart"></canvas>
</div>
<div class="chart-container half-width">
<h2>🔗 Container Connectivity</h2>
<canvas id="connectivityChart"></canvas>
</div>
</div>
<div class="chart-row">
<div class="chart-container half-width">
<h2>📦 Shared Volumes Usage</h2>
<canvas id="sharedVolumesChart"></canvas>
</div>
<div class="chart-container half-width">
<h2>🕸️ Custom Networks</h2>
<canvas id="customNetworksChart"></canvas>
</div>
</div>
<div class="chart-container">
<h2>🌍 Geographic Distribution (by Timezone)</h2>
<canvas id="geographyChart"></canvas>
</div>
</div>
<div id="imagesTab" class="tab-content">
<div class="table-container">
<div class="table-controls">
<input type="text" id="imageSearch" placeholder="Search images..." onkeyup="filterImages()">
<div class="results-info">
<span id="resultsCount">Showing 0 images</span>
</div>
</div>
<div class="table-wrapper">
<table id="imagesTable" class="data-table">
<thead>
<tr>
<th onclick="sortTable('name', this)" class="sortable">
Image Name <span class="sort-indicator"></span>
</th>
<th onclick="sortTable('count', this)" class="sortable text-right">
Container Count <span class="sort-indicator"></span>
</th>
<th onclick="sortTable('registry', this)" class="sortable">
Registry <span class="sort-indicator"></span>
</th>
<th onclick="sortTable('installations', this)" class="sortable text-right">
Installations <span class="sort-indicator"></span>
</th>
<th onclick="sortTable('percentage', this)" class="sortable text-right">
% of Total <span class="sort-indicator"></span>
</th>
</tr>
</thead>
<tbody id="imagesTableBody">
<tr>
<td colspan="5" class="loading-cell">Loading...</td>
</tr>
</tbody>
</table>
</div>
<div class="pagination">
<button id="prevPage" onclick="changePage(-1)" disabled>← Previous</button>
<span id="pageInfo">Page 1</span>
<button id="nextPage" onclick="changePage(1)">Next →</button>
</div>
</div>
</div>
<div id="databaseTab" class="tab-content">
<div class="database-viewer">
<div class="db-controls">
<div class="control-group">
<label>
Table:
<select id="dbTableSelect" onchange="loadDatabaseView()">
<option value="telemetry_reports">Telemetry Reports</option>
<option value="submission_events">Submission Events</option>
<option value="image_stats">Image Stats</option>
</select>
</label>
<label>
Installation ID:
<input type="text" id="dbInstallationFilter" placeholder="Filter by ID..." onchange="loadDatabaseView()">
</label>
<button onclick="loadDatabaseView()">🔄 Refresh</button>
<button onclick="exportDatabaseView()">💾 Export JSON</button>
</div>
<div class="auto-refresh-control">
<label>
<input type="checkbox" id="dbAutoRefresh" onchange="toggleAutoRefresh()">
Auto-refresh (5s)
</label>
</div>
</div>
<div class="db-info">
<span id="dbRecordCount">0 records</span>
</div>
<div class="db-table-wrapper">
<div id="databaseContent" class="database-content">
<p class="loading-message">Select a table and click refresh to view data</p>
</div>
</div>
<div class="pagination">
<button id="dbPrevPage" onclick="changeDbPage(-1)" disabled>← Previous</button>
<span id="dbPageInfo">Page 1</span>
<button id="dbNextPage" onclick="changeDbPage(1)" disabled>Next →</button>
</div>
</div>
</div>
<footer>
<p>Data is collected anonymously from installations that opt-in to telemetry.</p>
<p>No personally identifiable information is collected.</p>
</footer>
</div>
<script src="app.js"></script>
</body>
</html>