Skip to content

Instantly share code, notes, and snippets.

@manwar
Created April 3, 2025 20:10
Show Gist options
  • Save manwar/b7e210d6789385eb013da28b6375978b to your computer and use it in GitHub Desktop.
Save manwar/b7e210d6789385eb013da28b6375978b to your computer and use it in GitHub Desktop.
ChartJS Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>The Weekly Challenge :: Stats</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
line-height: 1.5;
color: #333;
background-color: #fff;
margin: 0;
padding: 0;
}
#page {
width: 1000px;
margin: 0 auto;
padding: 20px 0;
}
h1 {
color: #0066cc;
font-size: 28px;
margin: 0 0 20px 0;
padding: 0;
}
h2 {
color: #0066cc;
font-size: 22px;
margin: 30px 0 15px 0;
padding: 0 0 5px 0;
border-bottom: 1px solid #ddd;
}
.chart {
margin: 0 0 30px 0;
padding: 10px;
border: 1px solid #ddd;
background-color: #fff;
}
.chart-title {
font-size: 16px;
font-weight: bold;
margin: 0 0 10px 0;
padding: 0;
color: #333;
}
.chart-container {
position: relative;
height: 400px;
}
.chart-tabs {
display: flex;
margin-bottom: 10px;
border-bottom: 1px solid #ddd;
}
.chart-tab {
padding: 8px 15px;
cursor: pointer;
background: #f5f5f5;
border: 1px solid #ddd;
border-bottom: none;
margin-right: 5px;
border-radius: 3px 3px 0 0;
}
.chart-tab.active {
background: #4b8cf7;
color: white;
border-color: #3a6fc8;
}
.footer {
margin: 30px 0 0 0;
padding: 10px 0 0 0;
border-top: 1px solid #ddd;
font-size: 12px;
color: #666;
}
</style>
</head>
<body>
<div id="page">
<h1>The Weekly Challenge</h1>
<h2>Charts</h2>
<!-- Drilldown Chart with Tabs -->
<div class="chart">
<div class="chart-title">Participation Overview</div>
<div class="chart-tabs" id="chartTabs">
<!-- Tabs will be added dynamically -->
</div>
<div class="chart-container">
<canvas id="participationChart"></canvas>
</div>
</div>
<!-- Top Languages Chart -->
<div class="chart">
<div class="chart-title">Top 10 Languages (Last 10 Weeks)</div>
<div class="chart-container">
<canvas id="languagesChart"></canvas>
</div>
</div>
<!-- Guest Participation Chart -->
<div class="chart">
<div class="chart-title">Guest Participation (Last 10 Weeks)</div>
<div class="chart-container">
<canvas id="guestChart"></canvas>
</div>
</div>
<!-- Language Trend Chart -->
<div class="chart">
<div class="chart-title">Language Trend (Last 12 Weeks)</div>
<div class="chart-container">
<canvas id="trendChart"></canvas>
</div>
</div>
<div class="footer">
Generated with Chart.js | Last updated: <span id="update-date"></span>
</div>
</div>
<script>
// Set current date in footer
document.getElementById('update-date').textContent = new Date().toLocaleDateString();
// 1. DRILLDOWN CHART WITH TABS ====================================
const participationData = {
'2022': {
'Q1': { 'January': 45, 'February': 55, 'March': 65 },
'Q2': { 'April': 75, 'May': 85, 'June': 95 },
'Q3': { 'July': 105, 'August': 115, 'September': 125 },
'Q4': { 'October': 135, 'November': 145, 'December': 155 }
},
'2023': {
'Q1': { 'January': 55, 'February': 70, 'March': 90 },
'Q2': { 'April': 110, 'May': 130, 'June': 150 },
'Q3': { 'July': 160, 'August': 180, 'September': 200 },
'Q4': { 'October': 210, 'November': 230, 'December': 250 }
}
};
let participationChart;
const chartTabs = document.getElementById('chartTabs');
let currentLevel = 'years';
let currentYear = null;
let currentQuarter = null;
function initParticipationChart() {
const ctx = document.getElementById('participationChart').getContext('2d');
participationChart = new Chart(ctx, {
type: 'bar',
data: getYearsData(),
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
},
scales: {
y: {
beginAtZero: true,
title: { text: 'Participants' }
}
}
}
});
updateTabs();
}
function getYearsData() {
const years = Object.keys(participationData);
const totals = years.map(year => {
return Object.values(participationData[year]).reduce((sum, quarter) => {
return sum + Object.values(quarter).reduce((a, b) => a + b, 0);
}, 0);
});
return {
labels: years,
datasets: [{
data: totals,
backgroundColor: '#4b8cf7'
}]
};
}
function getQuartersData(year) {
const quarters = Object.keys(participationData[year]);
const totals = quarters.map(q => {
return Object.values(participationData[year][q]).reduce((a, b) => a + b, 0);
});
return {
labels: quarters,
datasets: [{
data: totals,
backgroundColor: '#4b8cf7'
}]
};
}
function getMonthsData(year, quarter) {
const months = Object.keys(participationData[year][quarter]);
const values = Object.values(participationData[year][quarter]);
return {
labels: months,
datasets: [{
data: values,
backgroundColor: '#4b8cf7'
}]
};
}
function updateTabs() {
chartTabs.innerHTML = '';
// Always show Year tab
const yearTab = document.createElement('div');
yearTab.className = `chart-tab ${currentLevel === 'years' ? 'active' : ''}`;
yearTab.textContent = 'Years';
yearTab.onclick = () => {
currentLevel = 'years';
currentYear = null;
currentQuarter = null;
participationChart.data = getYearsData();
participationChart.update();
updateTabs();
};
chartTabs.appendChild(yearTab);
// Show Year > Quarters tab if applicable
if (currentYear) {
const quarterTab = document.createElement('div');
quarterTab.className = `chart-tab ${currentLevel === 'quarters' ? 'active' : ''}`;
quarterTab.textContent = currentYear;
quarterTab.onclick = () => {
currentLevel = 'quarters';
currentQuarter = null;
participationChart.data = getQuartersData(currentYear);
participationChart.update();
updateTabs();
};
chartTabs.appendChild(quarterTab);
}
// Show Year > Quarter > Months tab if applicable
if (currentQuarter) {
const monthTab = document.createElement('div');
monthTab.className = `chart-tab ${currentLevel === 'months' ? 'active' : ''}`;
monthTab.textContent = currentQuarter;
monthTab.onclick = () => {
currentLevel = 'months';
participationChart.data = getMonthsData(currentYear, currentQuarter);
participationChart.update();
updateTabs();
};
chartTabs.appendChild(monthTab);
}
}
function handleChartClick(event, elements) {
if (elements.length === 0) return;
const clickedIndex = elements[0].index;
if (currentLevel === 'years') {
const years = Object.keys(participationData);
currentYear = years[clickedIndex];
currentLevel = 'quarters';
participationChart.data = getQuartersData(currentYear);
participationChart.update();
updateTabs();
}
else if (currentLevel === 'quarters') {
const quarters = Object.keys(participationData[currentYear]);
currentQuarter = quarters[clickedIndex];
currentLevel = 'months';
participationChart.data = getMonthsData(currentYear, currentQuarter);
participationChart.update();
updateTabs();
}
// No action when clicking at month level
}
// 2. TOP LANGUAGES CHART ==========================================
function initLanguagesChart() {
new Chart(
document.getElementById('languagesChart'),
{
type: 'bar',
data: {
labels: ['Perl', 'Python', 'Raku', 'Go', 'Haskell', 'Ruby', 'Java', 'C++', 'Swift', 'Rust'],
datasets: [{
label: 'Participants',
data: [35, 28, 18, 12, 10, 8, 6, 5, 4, 3],
backgroundColor: [
'#4b8cf7', '#4b8cf7', '#4b8cf7',
'#a9c9ff', '#a9c9ff', '#a9c9ff',
'#a9c9ff', '#a9c9ff', '#a9c9ff', '#a9c9ff'
],
borderWidth: 1
}]
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
}
}
}
);
}
// 3. GUEST PARTICIPATION CHART ===================================
function initGuestChart() {
new Chart(
document.getElementById('guestChart'),
{
type: 'pie',
data: {
labels: ['Regular Members', 'First Timers', 'Guests'],
datasets: [{
data: [60, 15, 25],
backgroundColor: [
'#4b8cf7',
'#f78c4b',
'#4bf78c'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'right' }
}
}
}
);
}
// 4. LANGUAGE TREND CHART =========================================
function initTrendChart() {
new Chart(
document.getElementById('trendChart'),
{
type: 'line',
data: {
labels: Array.from({length: 12}, (_, i) => `Week ${i+1}`),
datasets: [
{
label: 'Perl',
data: [32, 30, 28, 35, 34, 36, 38, 35, 34, 36, 35, 37],
borderColor: '#4b8cf7',
backgroundColor: 'rgba(75, 140, 247, 0.1)',
tension: 0.1,
fill: true
},
{
label: 'Python',
data: [18, 20, 22, 25, 24, 26, 28, 27, 25, 28, 27, 30],
borderColor: '#f78c4b',
backgroundColor: 'rgba(247, 140, 75, 0.1)',
tension: 0.1,
fill: true
},
{
label: 'Raku',
data: [12, 14, 15, 16, 15, 17, 18, 16, 15, 16, 17, 18],
borderColor: '#4bf78c',
backgroundColor: 'rgba(75, 247, 140, 0.1)',
tension: 0.1,
fill: true
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'top' }
}
}
}
);
}
// Initialize all charts when the page loads
window.onload = function() {
initParticipationChart();
initLanguagesChart();
initGuestChart();
initTrendChart();
// Add click handler after chart initialization
document.getElementById('participationChart').onclick = function(evt) {
const elements = participationChart.getElementsAtEventForMode(evt, 'nearest', { intersect: true }, true);
handleChartClick(evt, elements);
};
};
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment