Files
davila7__claude-code-templates/docs/download-stats.html
T
Dani 36714d2d8b feat: comprehensive templates system enhancement and code organization
## Major Features Added

### 🏗️ Templates System Overhaul
- **New data source**: Templates now load from `components.json` generated from `cli-tool/templates/`
- **Enhanced structure**: Support for both language and framework templates
- **Recursive file scanning**: Includes `.claude/` folder contents (agents, commands, settings)
- **Smart categorization**: Language-based category filters for templates
- **Proper install commands**: `--template=python --yes` for languages, `--template=django --yes` for frameworks

### 📦 Template Management
- **Updated generate_components_json.py**: Scans `cli-tool/templates/` recursively
- **File inventory**: Tracks all template files including Claude Code configuration
- **Template modal**: Shows complete file list for each template
- **GitHub integration**: Direct links to template folders

### 🎨 UI/UX Improvements
- **Card flip animations**: Restored original template card functionality from script.js
- **MCP optimization**: Removed unnecessary descriptions from MCP cards and modals
- **Category filters**: Templates now show language-based filters (python, ruby, etc.)
- **Filter reset**: Switching main filters automatically resets category selection
- **Enhanced modals**: Comprehensive template contribution guide with language vs framework instructions

### 🔧 Code Organization
- **Structured directories**: Moved all CSS files to `css/` and JS files to `js/`
- **Updated references**: All HTML files now reference organized file paths
- **Better maintainability**: Cleaner project structure for future development

### 🚀 Enhanced Contribution Flow
- **Detailed template modal**: Step-by-step guide for contributing languages vs frameworks
- **Required file structure**: Clear documentation of Claude Code configuration files
- **GitHub workflow**: Proper git commands and testing instructions

## Technical Improvements
- Fixed card flip functionality conflicts
- Enhanced pagination system
- Improved modal management
- Better error handling and fallbacks
- Consistent styling across components

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 19:59:20 -04:00

514 lines
17 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Download Analytics - Claude Code Templates</title>
<meta name="description" content="Detailed download analytics and usage statistics for Claude Code Templates">
<link rel="stylesheet" href="css/styles.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.analytics-page {
min-height: 100vh;
padding: 2rem 0;
}
.analytics-header {
text-align: center;
margin-bottom: 3rem;
}
.analytics-title {
font-size: 2.5rem;
color: var(--text-accent);
margin-bottom: 1rem;
font-weight: 300;
}
.analytics-subtitle {
color: var(--text-secondary);
font-size: 1.1rem;
}
.back-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
color: var(--text-info);
text-decoration: none;
margin-bottom: 2rem;
padding: 0.5rem 0;
transition: color 0.2s ease;
}
.back-link:hover {
color: var(--text-accent);
}
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 3rem;
}
.metric-card {
background: var(--bg-secondary);
border: 1px solid var(--border-primary);
border-radius: 8px;
padding: 1.5rem;
text-align: center;
}
.metric-value {
font-size: 2.5rem;
font-weight: bold;
color: var(--text-accent);
margin-bottom: 0.5rem;
}
.metric-label {
color: var(--text-secondary);
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.metric-change {
font-size: 0.8rem;
margin-top: 0.5rem;
opacity: 0.8;
}
.change-positive {
color: var(--text-success);
}
.change-neutral {
color: var(--text-secondary);
}
.charts-section {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin-bottom: 3rem;
}
.chart-container {
background: var(--bg-secondary);
border: 1px solid var(--border-primary);
border-radius: 8px;
padding: 1.5rem;
}
.chart-title {
color: var(--text-accent);
font-size: 1.2rem;
margin-bottom: 1rem;
text-align: center;
}
.chart-canvas {
position: relative;
height: 300px;
}
.detailed-tables {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.table-container {
background: var(--bg-secondary);
border: 1px solid var(--border-primary);
border-radius: 8px;
overflow: hidden;
}
.table-header {
background: var(--bg-tertiary);
padding: 1rem;
border-bottom: 1px solid var(--border-primary);
}
.table-title {
color: var(--text-accent);
font-size: 1.1rem;
margin: 0;
}
.table-content {
padding: 0;
}
.data-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border-secondary);
}
.data-row:last-child {
border-bottom: none;
}
.data-label {
color: var(--text-primary);
font-size: 0.9rem;
}
.data-value {
color: var(--text-accent);
font-weight: bold;
}
.progress-bar {
width: 60px;
height: 4px;
background: var(--border-primary);
border-radius: 2px;
overflow: hidden;
margin-left: 1rem;
}
.progress-fill {
height: 100%;
background: var(--text-accent);
transition: width 0.3s ease;
}
@media (max-width: 1024px) {
.charts-section,
.detailed-tables {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.analytics-title {
font-size: 2rem;
}
.metrics-grid {
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
.metric-card {
padding: 1rem;
}
.metric-value {
font-size: 2rem;
}
}
</style>
</head>
<body>
<div class="analytics-page">
<div class="container">
<a href="/" class="back-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"/>
</svg>
Back to Templates
</a>
<div class="analytics-header">
<h1 class="analytics-title">📊 Download Analytics</h1>
<p class="analytics-subtitle">Comprehensive usage statistics and trends</p>
</div>
<div class="metrics-grid" id="metricsGrid">
<!-- Metrics will be populated by JavaScript -->
</div>
<div class="charts-section">
<div class="chart-container">
<h3 class="chart-title">Downloads by Component Type</h3>
<div class="chart-canvas">
<canvas id="typeChart"></canvas>
</div>
</div>
<div class="chart-container">
<h3 class="chart-title">Downloads Over Time</h3>
<div class="chart-canvas">
<canvas id="timeChart"></canvas>
</div>
</div>
</div>
<div class="detailed-tables">
<div class="table-container">
<div class="table-header">
<h3 class="table-title">🏆 Popular Components</h3>
</div>
<div class="table-content" id="popularComponentsTable">
<!-- Table will be populated by JavaScript -->
</div>
</div>
<div class="table-container">
<div class="table-header">
<h3 class="table-title">📊 Category Breakdown</h3>
</div>
<div class="table-content" id="categoryBreakdownTable">
<!-- Table will be populated by JavaScript -->
</div>
</div>
</div>
<div class="stats-updated" style="margin-top: 2rem; text-align: center;">
<span>Data last updated: <span id="lastUpdatedTime">Loading...</span></span>
</div>
</div>
</div>
<script>
let statsData = null;
// Load analytics data
async function loadAnalyticsData() {
try {
const response = await fetch('/analytics/download-stats.json');
statsData = await response.json();
populateMetrics();
createCharts();
populateTables();
updateTimestamp();
} catch (error) {
console.error('Failed to load analytics data:', error);
showErrorState();
}
}
function populateMetrics() {
const metricsGrid = document.getElementById('metricsGrid');
const totalComponents = Object.keys(statsData.downloads_by_component).length;
const todayDate = new Date().toISOString().split('T')[0];
const todayDownloads = statsData.downloads_by_date[todayDate] || 0;
const metrics = [
{
value: statsData.total_downloads,
label: 'Total Downloads',
change: `+${statsData.data_points} tracked events`,
changeType: 'positive'
},
{
value: totalComponents,
label: 'Unique Components',
change: 'across all categories',
changeType: 'neutral'
},
{
value: todayDownloads,
label: 'Downloads Today',
change: new Date().toLocaleDateString(),
changeType: 'positive'
},
{
value: Object.keys(statsData.downloads_by_date).length,
label: 'Active Days',
change: 'with recorded activity',
changeType: 'neutral'
}
];
metricsGrid.innerHTML = metrics.map(metric => `
<div class="metric-card">
<div class="metric-value">${metric.value}</div>
<div class="metric-label">${metric.label}</div>
<div class="metric-change change-${metric.changeType}">${metric.change}</div>
</div>
`).join('');
}
function createCharts() {
createTypeChart();
createTimeChart();
}
function createTypeChart() {
const ctx = document.getElementById('typeChart').getContext('2d');
const data = statsData.downloads_by_type;
const labels = Object.keys(data).filter(key => data[key] > 0);
const values = labels.map(key => data[key]);
new Chart(ctx, {
type: 'doughnut',
data: {
labels: labels.map(label => label.charAt(0).toUpperCase() + label.slice(1)),
datasets: [{
data: values,
backgroundColor: [
'#d57455',
'#f97316',
'#a5d6ff',
'#3fb950',
'#f85149',
'#7d8590'
],
borderWidth: 2,
borderColor: '#0d1117'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
color: '#c9d1d9',
padding: 20
}
}
}
}
});
}
function createTimeChart() {
const ctx = document.getElementById('timeChart').getContext('2d');
const dates = Object.keys(statsData.downloads_by_date).sort();
const values = dates.map(date => statsData.downloads_by_date[date]);
new Chart(ctx, {
type: 'line',
data: {
labels: dates.map(date => new Date(date).toLocaleDateString()),
datasets: [{
label: 'Downloads',
data: values,
borderColor: '#d57455',
backgroundColor: 'rgba(213, 116, 85, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
ticks: {
color: '#c9d1d9'
},
grid: {
color: '#30363d'
}
},
x: {
ticks: {
color: '#c9d1d9'
},
grid: {
color: '#30363d'
}
}
},
plugins: {
legend: {
labels: {
color: '#c9d1d9'
}
}
}
}
});
}
function populateTables() {
populatePopularComponents();
populateCategoryBreakdown();
}
function populatePopularComponents() {
const table = document.getElementById('popularComponentsTable');
const components = Object.entries(statsData.downloads_by_component)
.sort(([,a], [,b]) => b - a)
.slice(0, 10);
const maxDownloads = Math.max(...Object.values(statsData.downloads_by_component));
table.innerHTML = components.map(([name, downloads]) => {
const percentage = (downloads / maxDownloads) * 100;
return `
<div class="data-row">
<div class="data-label">${name}</div>
<div style="display: flex; align-items: center;">
<div class="data-value">${downloads}</div>
<div class="progress-bar">
<div class="progress-fill" style="width: ${percentage}%"></div>
</div>
</div>
</div>
`;
}).join('');
}
function populateCategoryBreakdown() {
const table = document.getElementById('categoryBreakdownTable');
const categories = Object.entries(statsData.downloads_by_type)
.filter(([,count]) => count > 0)
.sort(([,a], [,b]) => b - a);
const maxDownloads = Math.max(...Object.values(statsData.downloads_by_type));
table.innerHTML = categories.map(([type, downloads]) => {
const percentage = (downloads / maxDownloads) * 100;
const emoji = {
'agent': '🤖',
'command': '⚡',
'mcp': '🔌',
'template': '📦',
'health-check': '🔍',
'analytics': '📊'
}[type] || '📄';
return `
<div class="data-row">
<div class="data-label">${emoji} ${type.charAt(0).toUpperCase() + type.slice(1)}</div>
<div style="display: flex; align-items: center;">
<div class="data-value">${downloads}</div>
<div class="progress-bar">
<div class="progress-fill" style="width: ${percentage}%"></div>
</div>
</div>
</div>
`;
}).join('');
}
function updateTimestamp() {
const timestamp = new Date(statsData.last_updated);
document.getElementById('lastUpdatedTime').textContent = timestamp.toLocaleString();
}
function showErrorState() {
document.getElementById('metricsGrid').innerHTML = `
<div class="metric-card" style="grid-column: 1 / -1; text-align: center; padding: 2rem;">
<div style="color: var(--text-error); font-size: 1.5rem; margin-bottom: 1rem;">⚠️</div>
<div class="metric-label">Failed to load analytics data</div>
<div class="metric-change change-neutral">Please try again later</div>
</div>
`;
}
// Load data when page loads
loadAnalyticsData();
</script>
</body>
</html>