// Load summary statistics on page load loadSummaryStats(); totalSatQsos(); totalQsosPerYear(); // Needed for sattable header fix, will be squished without $("a[href='#satellite']").on('shown.bs.tab', function(e) { $(".sattable").DataTable().columns.adjust(); }); $("a[href='#bandtab']").on('shown.bs.tab', function(e) { if (!($('.bandtable').length > 0)) { totalBandQsos(); } }); $("a[href='#modetab']").on('shown.bs.tab', function(e) { if (!($('.modetable').length > 0)) { totalModeQsos(); } }); $("a[href='#qsotab']").on('shown.bs.tab', function(e) { if (!($('.qsotable').length > 0)) { totalQsos(); } }); $("a[href='#uniquetab']").on('shown.bs.tab', function(e) { if (!($('.uniquetable').length > 0)) { uniqueCallsigns(); } }); $("a[href='#trendstab']").on('shown.bs.tab', function(e) { if (!($('.trends-loaded').length > 0)) { loadTrends(); } }); $("a[href='#continentstab']").on('shown.bs.tab', function(e) { if (!($('.continents-loaded').length > 0)) { loadContinents(); } }); $("a[href='#mostworkedtab']").on('shown.bs.tab', function(e) { if (!($('.mostworked-loaded').length > 0)) { loadMostWorked(); } }); function loadSummaryStats() { $.ajax({ url: base_url+'index.php/statistics/get_summary_stats', type: 'post', data: getDateFilterParams(), success: function (data) { // Check if user has no QSOs if (data.total_qsos == 0) { $('#summaryCards').html('

No QSOs Found

You haven\'t logged any contacts yet. Start logging QSOs to see your statistics here!

'); // Hide the tabs since there's no data $('.nav-pills').hide(); $('.tab-content').html('
Log your first QSO to start building your statistics.
'); return; } var html = ''; html += '
'; html += '
' + Number(data.total_qsos).toLocaleString() + '
'; html += '
Total QSOs
'; html += '
'; html += '
'; html += '
' + Number(data.unique_callsigns).toLocaleString() + '
'; html += '
Unique Callsigns
'; html += '
'; html += '
'; html += '
' + Number(data.total_countries).toLocaleString() + '
'; html += '
Countries Worked
'; html += '
'; html += '
'; html += '
' + data.total_bands + '
'; html += '
Bands Worked
'; html += '
'; html += '
'; html += '
' + data.total_modes + '
'; html += '
Modes Used
'; html += '
'; $('#summaryCards').html(html); // Make sure tabs are visible $('.nav-pills').show(); }, error: function() { $('#summaryCards').html('
Failed to load summary statistics
'); } }); } function uniqueCallsigns() { $.ajax({ url: base_url+'index.php/statistics/get_unique_callsigns', type: 'post', success: function (data) { if (data.length > 0) { $(".unique").html(data); } }, error: function() { $('.unique').html('
Failed to load unique callsigns
'); } }); } function totalQsos() { $.ajax({ url: base_url+'index.php/statistics/get_total_qsos', type: 'post', success: function (data) { if (data.length > 0) { $(".qsos").html(data); } }, error: function() { $('.qsos').html('
Failed to load QSO statistics
'); } }); } function totalQsosPerYear() { // using this to change color of legend and label according to background color var color = ifDarkModeThemeReturn('white', 'grey'); $.ajax({ url: base_url+'index.php/statistics/get_year', type: 'post', data: getDateFilterParams(), success: function (data) { if (data.length > 0) { $(".years").html('

' + lang_statistics_years + '

'); $("#yearContainer").append(""); // appending table to hold the data $("#yearTable").append('' + '' + '' + '' + '' + '' + '' + '
#' + lang_statistics_year +'' + lang_statistics_number_of_qso_worked + '
'); var labels = []; var dataQso = []; var $myTable = $('.yeartable'); var i = 1; // building the rows in the table var rowElements = data.map(function (row) { var $row = $(''); var $iterator = $('').html(i++); var $type = $('').html(row.year); var $content = $('').html(row.total); $row.append($iterator, $type, $content); return $row; }); // finally inserting the rows $myTable.append(rowElements); $.each(data, function () { labels.push(this.year); dataQso.push(this.total); }); labels.reverse(); dataQso.reverse(); var ctx = document.getElementById("yearChart").getContext('2d'); var myChart = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: lang_statistics_number_of_qso_worked_each_year, data: dataQso, backgroundColor: 'rgba(54, 162, 235, 0.2)', borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 2, color: color }] }, options: { responsive: true, maintainAspectRatio: true, scales: { y: { ticks: { beginAtZero: true, color: color } }, x: { ticks: { color: color } } }, plugins: { legend: { labels: { color: color } }, tooltip: { callbacks: { label: function(context) { return context.dataset.label + ': ' + context.parsed.y.toLocaleString() + ' QSOs'; } } } }, onClick: function(evt, activeElements) { if (activeElements.length > 0) { var year = labels[activeElements[0].index]; window.location.href = base_url + 'index.php/logbook?year=' + year; } } } }); $('.yeartable').DataTable({ responsive: false, ordering: false, "scrollY": "320px", "scrollCollapse": true, "paging": false, "scrollX": true, "language": { url: getDataTablesLanguageUrl(), }, bFilter: false, bInfo: false }); // using this to change color of csv-button if dark mode is chosen var background = $('body').css("background-color"); if (background != ('rgb(255, 255, 255)')) { $(".buttons-csv").css("color", "white"); } } }, error: function() { $('.years').html('
Failed to load year statistics
'); } }); } function totalModeQsos() { // using this to change color of legend and label according to background color var color = ifDarkModeThemeReturn('white', 'grey'); $.ajax({ url: base_url+'index.php/statistics/get_mode', type: 'post', success: function (data) { if (data.length > 0) { var labels = []; var dataQso = []; $.each(data, function () { labels.push(this.mode.toUpperCase()); dataQso.push(this.total); }); if (dataQso[0] === null && dataQso[1] === null && dataQso[2] === null && dataQso[3] === null) return; $(".mode").html('

' + lang_statistics_modes + '


'); // appending table to hold the data $("#modeTable").append('' + '' + '' + '' + '' + '' + '' + '
#' + lang_gen_hamradio_mode + ' ' + lang_statistics_number_of_qso_worked + '
'); var $myTable = $('.modetable'); var i = 1; // building the rows in the table var rowElements = data.map(function (row) { var $row = $(''); var $iterator = $('').html(i++); var $type = $('').html(row.mode.toUpperCase()); var $content = $('').html(row.total); $row.append($iterator, $type, $content); return $row; }); // finally inserting the rows $myTable.append(rowElements); const COLORS = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499"] var ctx = document.getElementById("modeChart").getContext('2d'); var myChart = new Chart(ctx, { type: 'pie', plugins: [ChartPieChartOutlabels], data: { labels: labels, datasets: [{ label: 'Number of QSO\'s worked', data: dataQso, backgroundColor: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499"], borderWidth: 1, borderColor: 'rgba(54, 162, 235, 1)', }] }, options: { layout: { padding: 100 }, title: { color: color, fullSize: true, }, responsive: false, maintainAspectRatio: false, plugins: { legend: { display: false, labels: { boxWidth: 15, color: color, font: { size: 14, } }, position: 'right', align: "middle" }, tooltip: { callbacks: { label: function(context) { var label = context.label || ''; var value = context.parsed || 0; var total = context.dataset.data.reduce((a, b) => a + b, 0); var percentage = ((value / total) * 100).toFixed(1); return label + ': ' + value.toLocaleString() + ' QSOs (' + percentage + '%)'; } } }, outlabels: { backgroundColor: COLORS, borderColor: COLORS, borderRadius: 2, // Border radius of Label borderWidth: 2, // Thickness of border color: 'white', stretch: 45, padding: 0, font: { resizable: true, minSize: 15, maxSize: 25, family: Chart.defaults.font.family, size: Chart.defaults.font.size, style: Chart.defaults.font.style, lineHeight: Chart.defaults.font.lineHeight, }, zoomOutPercentage: 100, textAlign: 'start', backgroundColor: COLORS, } } } }); // using this to change color of csv-button if dark mode is chosen var background = $('body').css("background-color"); if (background != ('rgb(255, 255, 255)')) { $(".buttons-csv").css("color", "white"); } } }, error: function() { $('.mode').html('
Failed to load mode statistics
'); } }); } function totalBandQsos() { // using this to change color of legend and label according to background color var color = ifDarkModeThemeReturn('white', 'grey'); $.ajax({ url: base_url+'index.php/statistics/get_band', type: 'post', success: function (data) { if (data.length > 0) { $(".band").html('

' + lang_statistics_bands + '


'); // appending table to hold the data $("#bandTable").append('' + '' + '' + '' + '' + '' + '' + '
#' + lang_gen_hamradio_band + '' + lang_statistics_number_of_qso_worked + '
'); var labels = []; var dataQso = []; var totalQso = Number(0); var $myTable = $('.bandtable'); var i = 1; // building the rows in the table var rowElements = data.map(function (row) { var $row = $(''); var $iterator = $('').html(i++); var $type = $('').html(row.band); var $content = $('').html(row.count); $row.append($iterator, $type, $content); return $row; }); // finally inserting the rows $myTable.append(rowElements); $.each(data, function () { labels.push(this.band); dataQso.push(this.count); totalQso = Number(totalQso) + Number(this.count); }); const COLORS = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499"] var ctx = document.getElementById("bandChart").getContext('2d'); var myChart = new Chart(ctx, { plugins: [ChartPieChartOutlabels], type: 'doughnut', data: { labels: labels, datasets: [{ label: 'Number of QSO\'s worked', data: dataQso, borderColor: 'rgba(54, 162, 235, 1)', backgroundColor: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499"], borderWidth: 1, }] }, options: { layout: { padding: 100 }, title: { fontColor: color, fullSize: true, }, responsive: true, maintainAspectRatio: true, plugins: { legend: { display: false, labels: { boxWidth: 15, color: color, font: { size: 14, } }, position: 'right', align: "middle" }, tooltip: { callbacks: { label: function(context) { var label = context.label || ''; var value = context.parsed || 0; var percentage = ((value / totalQso) * 100).toFixed(1); return label + ': ' + value.toLocaleString() + ' QSOs (' + percentage + '%)'; } } }, outlabels: { display: function(context) { // Hide labels with low percentage return ((context.dataset.data[context.dataIndex] / totalQso * 100) > 1) }, backgroundColor: COLORS, borderColor: COLORS, borderRadius: 2, // Border radius of Label borderWidth: 2, // Thickness of border color: 'white', stretch: 10, padding: 0, font: { resizable: true, minSize: 12, maxSize: 25, family: Chart.defaults.font.family, size: Chart.defaults.font.size, style: Chart.defaults.font.style, lineHeight: Chart.defaults.font.lineHeight, }, zoomOutPercentage: 100, textAlign: 'start', backgroundColor: COLORS, } } } }); $('.bandtable').DataTable({ responsive: false, ordering: false, "scrollY": "330px", "scrollCollapse": true, "paging": false, "scrollX": true, "language": { url: getDataTablesLanguageUrl(), }, bFilter: false, bInfo: false, }); // using this to change color of csv-button if dark mode is chosen var background = $('body').css("background-color"); if (background != ('rgb(255, 255, 255)')) { $(".buttons-csv").css("color", "white"); } } }, error: function() { $('.band').html('
Failed to load band statistics
'); } }); } function totalSatQsos() { // using this to change color of legend and label according to background color var color = ifDarkModeThemeReturn('white', 'grey'); $.ajax({ url: base_url+'index.php/statistics/get_sat', type: 'post', success: function (data) { if (data.length > 0) { $('.tabs').removeAttr('hidden'); var labels = []; var dataQso = []; var totalQso = Number(0); var $myTable = $('.sattable'); var i = 1; // building the rows in the table var rowElements = data.map(function (row) { var $row = $(''); var $iterator = $('').html(i++); var $type = $('').html(row.sat); var $content = $('').html(row.count); $row.append($iterator, $type, $content); return $row; }); // finally inserting the rows $myTable.append(rowElements); $.each(data, function () { labels.push(this.sat); dataQso.push(this.count); totalQso = Number(totalQso) + Number(this.count); }); const COLORS = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499"] var ctx = document.getElementById("satChart").getContext('2d'); var myChart = new Chart(ctx, { plugins: [ChartPieChartOutlabels], type: 'doughnut', data: { labels: labels, datasets: [{ borderColor: 'rgba(54, 162, 235, 1)', label: 'Number of QSO\'s worked', data: dataQso, backgroundColor: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499"], borderWidth: 1, labels: labels, }] }, options: { layout: { padding: 100 }, title: { fontColor: color, fullSize: true, }, responsive: false, maintainAspectRatio: true, plugins: { legend: { display: false, labels: { boxWidth: 15, color: color, font: { size: 14, } }, position: 'right', align: "middle" }, tooltip: { callbacks: { label: function(context) { var label = context.label || ''; var value = context.parsed || 0; var percentage = ((value / totalQso) * 100).toFixed(1); return label + ': ' + value.toLocaleString() + ' QSOs (' + percentage + '%)'; } } }, outlabels: { display: function(context) { // Hide labels with low percentage return ((context.dataset.data[context.dataIndex] / totalQso * 100) > 1) }, backgroundColor: COLORS, borderColor: COLORS, borderRadius: 2, // Border radius of Label borderWidth: 2, // Thickness of border color: 'white', stretch: 10, padding: 0, font: { resizable: true, minSize: 12, maxSize: 25, family: Chart.defaults.font.family, size: Chart.defaults.font.size, style: Chart.defaults.font.style, lineHeight: Chart.defaults.font.lineHeight, }, zoomOutPercentage: 100, textAlign: 'start', backgroundColor: COLORS, } } } }); // using this to change color of csv-button if dark mode is chosen var background = $('body').css("background-color"); if (background != ('rgb(255, 255, 255)')) { $(".buttons-csv").css("color", "white"); } $('.sattable').DataTable({ responsive: false, ordering: false, "scrollY": "330px", "scrollX": true, "language": { url: getDataTablesLanguageUrl(), }, "ScrollCollapse": true, "paging": false, bFilter: false, bInfo: false, }); } } }); } function loadTrends() { var color = ifDarkModeThemeReturn('white', 'grey'); $.ajax({ url: base_url+'index.php/statistics/get_trends', type: 'post', success: function (data) { var html = ''; $('.trends').html(html); // Create chart var labels = data.monthly.map(m => m.month); var counts = data.monthly.map(m => m.count); var ctx = document.getElementById("trendsChart").getContext('2d'); new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: 'QSOs per Month', data: counts, backgroundColor: 'rgba(54, 162, 235, 0.2)', borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 2, fill: true, tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: true, scales: { y: { beginAtZero: true, ticks: { color: color } }, x: { ticks: { color: color } } }, plugins: { legend: { labels: { color: color } } } } }); }, error: function() { $('.trends').html('
Failed to load trends
'); } }); } function loadContinents() { var color = ifDarkModeThemeReturn('white', 'grey'); $.ajax({ url: base_url+'index.php/statistics/get_continents', type: 'post', success: function (data) { if (data.length > 0) { var html = '
'; html += '
'; html += '

Continents

'; html += '
'; html += '

'; $('.continents').html(html); // Build table var tableHtml = ''; tableHtml += ''; var labels = []; var counts = []; data.forEach((item, index) => { tableHtml += ''; labels.push(item.continent); counts.push(item.count); }); tableHtml += '
#Continent# of QSOs
' + (index + 1) + '' + item.continent + '' + item.count + '
'; $('#continentTable').html(tableHtml); // Create chart const COLORS = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477"]; var ctx = document.getElementById("continentChart").getContext('2d'); new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'QSOs per Continent', data: counts, backgroundColor: COLORS, borderColor: COLORS, borderWidth: 1 }] }, options: { responsive: false, maintainAspectRatio: false, scales: { y: { beginAtZero: true, ticks: { color: color } }, x: { ticks: { color: color } } }, plugins: { legend: { display: false } } } }); } }, error: function() { $('.continents').html('
Failed to load continent statistics
'); } }); } function loadMostWorked() { $.ajax({ url: base_url+'index.php/statistics/get_most_worked', type: 'post', success: function (data) { var html = '

Most Worked

'; html += '
'; // Most worked callsigns html += '
'; html += '

Top 10 Callsigns

'; html += ''; html += ''; data.callsigns.forEach((item, index) => { html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; }); html += '
#CallsignCountryQSOs
' + (index + 1) + '' + item.callsign + '' + (item.country || '-') + '' + item.count + '
'; // Most worked countries html += '
'; html += '

Top 10 Countries

'; html += ''; html += ''; data.countries.forEach((item, index) => { html += ''; html += ''; html += ''; html += ''; html += ''; }); html += '
#CountryQSOs
' + (index + 1) + '' + item.country + '' + item.count + '
'; html += '
'; $('.mostworked').html(html); }, error: function() { $('.mostworked').html('
Failed to load most worked statistics
'); } }); } // Date filter functions function toggleDateFilter() { $('#dateFilterCard').slideToggle(); } function applyDateFilter() { var startDate = $('#filterStartDate').val(); var endDate = $('#filterEndDate').val(); if (!startDate || !endDate) { alert('Please select both start and end dates'); return; } if (new Date(startDate) > new Date(endDate)) { alert('Start date must be before end date'); return; } // Store filter in session/local storage localStorage.setItem('stats_filter_start', startDate); localStorage.setItem('stats_filter_end', endDate); // Reload all statistics with filter loadSummaryStats(); // Clear and reload active tab $('.years').html('

Loading filtered data...

'); totalQsosPerYear(); // Show filter is active $('#dateFilterCard').addClass('border-primary'); } function clearDateFilter() { $('#filterStartDate').val(''); $('#filterEndDate').val(''); localStorage.removeItem('stats_filter_start'); localStorage.removeItem('stats_filter_end'); $('#dateFilterCard').removeClass('border-primary'); // Reload data without filter loadSummaryStats(); $('.years').html('

Loading data...

'); totalQsosPerYear(); } // Get date filter parameters for AJAX calls function getDateFilterParams() { var params = {}; var startDate = localStorage.getItem('stats_filter_start'); var endDate = localStorage.getItem('stats_filter_end'); if (startDate && endDate) { params.start_date = startDate; params.end_date = endDate; } return params; }