<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
.programs4-stat-card {
border: 1px solid #e8e8e8;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
[data-bs-theme="dark"] .programs4-stat-card {
border-color: var(--bs-border-color);
box-shadow: none;
}
.programs4-stat-icon {
width: 3.25rem;
height: 3.25rem;
background-color: #e6f4f1;
border-radius: 0.65rem;
}
[data-bs-theme="dark"] .programs4-stat-icon {
background-color: rgba(230, 244, 241, 0.18);
}
.programs4-cycle-wrap {
position: relative;
width: 100%;
max-width: 220px;
height: 220px;
margin-left: auto;
margin-right: auto;
}
.comparison-chart-wrap {
position: relative;
height: 280px;
}
<script>
const themeToggle = document.getElementById('themeToggle');
const themeIcon = document.getElementById('themeIcon');
const htmlElement = document.documentElement;
const savedTheme = localStorage.getItem('theme') || 'light';
if (savedTheme === 'dark') {
htmlElement.setAttribute('data-bs-theme', 'dark');
themeIcon.classList.remove('bi-moon-fill');
themeIcon.classList.add('bi-sun-fill');
} else {
themeIcon.classList.remove('bi-sun-fill');
themeIcon.classList.add('bi-moon-fill');
}
themeToggle.addEventListener('click', () => {
const currentTheme = htmlElement.getAttribute('data-bs-theme');
if (currentTheme === 'dark') {
htmlElement.removeAttribute('data-bs-theme');
themeIcon.classList.remove('bi-sun-fill');
themeIcon.classList.add('bi-moon-fill');
localStorage.setItem('theme', 'light');
} else {
htmlElement.setAttribute('data-bs-theme', 'dark');
themeIcon.classList.remove('bi-moon-fill');
themeIcon.classList.add('bi-sun-fill');
localStorage.setItem('theme', 'dark');
}
});
const body = document.body;
const sidebarToggle = document.getElementById('sidebarToggle');
const sidebarBackdrop = document.getElementById('sidebarBackdrop');
const savedSidebar = localStorage.getItem('sidebar') || 'expanded';
if (savedSidebar === 'collapsed') body.classList.add('sidebar-collapsed');
sidebarToggle.addEventListener('click', () => {
const isMobile = window.matchMedia('(max-width: 992px)').matches;
if (isMobile) {
body.classList.toggle('sidebar-open');
if (body.classList.contains('sidebar-open')) body.classList.remove('sidebar-collapsed');
else if (savedSidebar === 'collapsed') body.classList.add('sidebar-collapsed');
} else {
body.classList.toggle('sidebar-collapsed');
localStorage.setItem('sidebar', body.classList.contains('sidebar-collapsed') ? 'collapsed' : 'expanded');
}
});
sidebarBackdrop.addEventListener('click', () => {
body.classList.remove('sidebar-open');
if (savedSidebar === 'collapsed') body.classList.add('sidebar-collapsed');
});
document.getElementById('userMenuLogout').addEventListener('click', () => {
window.location.href = 'index.html';
});
const chartCss = getComputedStyle(document.documentElement);
Chart.defaults.color = chartCss.getPropertyValue('--text-color-muted').trim() || '#6c757d';
Chart.defaults.font.family = "'Lato', -apple-system, BlinkMacSystemFont, sans-serif";
const cycleNotStarted = 14;
const cycleInProgress = 32;
const cycleCompleted = 54;
const annualReportsCompleted = 7;
const annualReportsPending = 12;
const annualReturnsTotal = 24;
const cycleColors = {
notStarted: '#94a3b8',
inProgress: '#f59e0b',
completed: '#22c55e'
};
document.getElementById('cycleNotStartedCount').textContent = cycleNotStarted;
document.getElementById('cycleInProgressCount').textContent = cycleInProgress;
document.getElementById('cycleCompletedCount').textContent = cycleCompleted;
document.getElementById('annualReportsCompletedCount').textContent = annualReportsCompleted;
document.getElementById('annualReportsPendingCount').textContent = annualReportsPending;
document.getElementById('annualReturnsTotalCount').textContent = annualReturnsTotal;
const cycleCanvas = document.getElementById('cycleStatusDonut');
if (cycleCanvas) {
new Chart(cycleCanvas.getContext('2d'), {
type: 'doughnut',
data: {
labels: ['Not started', 'In progress', 'Completed'],
datasets: [{
data: [cycleNotStarted, cycleInProgress, cycleCompleted],
backgroundColor: [cycleColors.notStarted, cycleColors.inProgress, cycleColors.completed],
borderWidth: 2,
borderColor: document.documentElement.getAttribute('data-bs-theme') === 'dark' ? '#212529' : '#fff'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '62%',
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label(ctx) {
const total = ctx.dataset.data.reduce((a, b) => a + b, 0);
const v = ctx.raw;
const pct = total ? ((v / total) * 100).toFixed(1) : 0;
return ' ' + ctx.label + ': ' + v + ' (' + pct + '%)';
}
}
}
}
}
});
}
const comparisonReportConfigs = [{
id: 'compQ1',
title: 'Completion rate by reporting year (%)',
datasetLabel: 'Rate',
labels: ['2023', '2024', '2025', '2026'],
data: [56, 50, 45, 40],
color: '#667eea'
},
{
id: 'compQ2',
title: 'Average days to submit by reporting year',
datasetLabel: 'Days',
labels: ['2023', '2024', '2025', '2026'],
data: [62, 58, 51, 47],
color: '#f59e0b'
},
{
id: 'compQ3',
title: 'Standards met (count) by reporting year',
datasetLabel: 'Count',
labels: ['2023', '2024', '2025', '2026'],
data: [88, 92, 95, 97],
color: '#22c55e'
}
];
let comparisonBarCharts = [];
function destroyComparisonBarCharts() {
comparisonBarCharts.forEach(function (ch) {
ch.destroy();
});
comparisonBarCharts = [];
}
function colClassForChartCount(n) {
if (n <= 1) return 'col-12';
if (n === 2) return 'col-12 col-md-6';
return 'col-12 col-md-4';
}
function renderComparisonReportCharts() {
const root = document.getElementById('comparisonChartsRoot');
const emptyEl = document.getElementById('comparisonChartsEmpty');
const selected = comparisonReportConfigs.filter(function (cfg) {
const el = document.getElementById(cfg.id);
return el && el.checked;
});
destroyComparisonBarCharts();
root.innerHTML = '';
if (selected.length === 0) {
emptyEl.classList.remove('d-none');
root.classList.add('d-none');
return;
}
emptyEl.classList.add('d-none');
root.classList.remove('d-none');
const colCls = colClassForChartCount(selected.length);
selected.forEach(function (cfg, i) {
const col = document.createElement('div');
col.className = colCls;
const wrap = document.createElement('div');
wrap.className = 'comparison-chart-wrap border rounded-3 p-3 bg-body-secondary bg-opacity-10';
const title = document.createElement('h6');
title.className = 'small text-muted mb-2 fw-semibold';
title.textContent = cfg.title;
const canvas = document.createElement('canvas');
canvas.setAttribute('role', 'img');
canvas.setAttribute('aria-label', cfg.title);
wrap.appendChild(title);
wrap.appendChild(canvas);
col.appendChild(wrap);
root.appendChild(col);
const ctx = canvas.getContext('2d');
const gridColor = document.documentElement.getAttribute('data-bs-theme') === 'dark' ?
'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)';
const ch = new Chart(ctx, {
type: 'bar',
plugins: [{
id: 'comparisonBarValueLabels',
afterDatasetsDraw: function (chart) {
const meta = chart.getDatasetMeta(0);
if (!meta || !meta.data || !meta.data.length) return;
const values = chart.data.datasets[0].data;
const c = chart.ctx;
const isDark = document.documentElement.getAttribute('data-bs-theme') === 'dark';
c.save();
c.font = '600 13px ' + (Chart.defaults.font.family || 'sans-serif');
c.fillStyle = isDark ? '#f1f5f9' : '#1e293b';
c.textAlign = 'center';
c.textBaseline = 'bottom';
meta.data.forEach(function (bar, index) {
const val = values[index];
if (val === undefined || val === null) return;
const x = bar.x;
const y = bar.y;
const top = Math.min(y, bar.base);
c.fillText(String(val), x, top - 6);
});
c.restore();
}
}],
data: {
labels: cfg.labels,
datasets: [{
label: cfg.datasetLabel,
data: cfg.data,
backgroundColor: cfg.color,
borderRadius: 6,
maxBarThickness: 48
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
title: function (items) {
return items.length ? 'Year ' + items[0].label : '';
}
}
}
},
scales: {
y: {
beginAtZero: true,
grid: {
color: gridColor
},
ticks: {
precision: 0
}
},
x: {
grid: {
display: false
}
}
}
}
});
comparisonBarCharts.push(ch);
});
}
document.getElementById('comparisonReportGenerateChartsBtn').addEventListener('click',
renderComparisonReportCharts);
</script>