2025-11-14 14:36:31 +00:00
// Load summary statistics on page load
loadSummaryStats ( ) ;
2022-09-18 16:48:16 +02:00
totalSatQsos ( ) ;
totalQsosPerYear ( ) ;
// Needed for sattable header fix, will be squished without
$ ( "a[href='#satellite']" ) . on ( 'shown.bs.tab' , function ( e ) {
$ ( ".sattable" ) . DataTable ( ) . columns . adjust ( ) ;
} ) ;
2022-09-25 20:39:55 +02:00
$ ( "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 ( ) ;
}
} ) ;
2025-11-14 14:36:31 +00:00
$ ( "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 ) {
2025-11-14 14:43:07 +00:00
// Check if user has no QSOs
if ( data . total _qsos == 0 ) {
$ ( '#summaryCards' ) . html ( '<div class="alert alert-info text-center"><h4><i class="fas fa-info-circle"></i> No QSOs Found</h4><p>You haven\'t logged any contacts yet. Start logging QSOs to see your statistics here!</p></div>' ) ;
// Hide the tabs since there's no data
$ ( '.nav-pills' ) . hide ( ) ;
$ ( '.tab-content' ) . html ( '<div class="alert alert-info text-center mt-3">Log your first QSO to start building your statistics.</div>' ) ;
return ;
}
2025-11-14 14:36:31 +00:00
var html = '' ;
html += '<div class="stats-card">' ;
html += '<div class="stats-card-value">' + Number ( data . total _qsos ) . toLocaleString ( ) + '</div>' ;
html += '<div class="stats-card-label">Total QSOs</div>' ;
html += '</div>' ;
html += '<div class="stats-card">' ;
html += '<div class="stats-card-value">' + Number ( data . unique _callsigns ) . toLocaleString ( ) + '</div>' ;
html += '<div class="stats-card-label">Unique Callsigns</div>' ;
html += '</div>' ;
html += '<div class="stats-card">' ;
html += '<div class="stats-card-value">' + Number ( data . total _countries ) . toLocaleString ( ) + '</div>' ;
html += '<div class="stats-card-label">Countries Worked</div>' ;
html += '</div>' ;
html += '<div class="stats-card">' ;
html += '<div class="stats-card-value">' + data . total _bands + '</div>' ;
html += '<div class="stats-card-label">Bands Worked</div>' ;
html += '</div>' ;
html += '<div class="stats-card">' ;
html += '<div class="stats-card-value">' + data . total _modes + '</div>' ;
html += '<div class="stats-card-label">Modes Used</div>' ;
html += '</div>' ;
$ ( '#summaryCards' ) . html ( html ) ;
2025-11-14 14:43:07 +00:00
// Make sure tabs are visible
$ ( '.nav-pills' ) . show ( ) ;
2025-11-14 14:36:31 +00:00
} ,
error : function ( ) {
$ ( '#summaryCards' ) . html ( '<div class="alert alert-danger">Failed to load summary statistics</div>' ) ;
}
} ) ;
}
2022-09-25 20:39:55 +02:00
function uniqueCallsigns ( ) {
$ . ajax ( {
url : base _url + 'index.php/statistics/get_unique_callsigns' ,
type : 'post' ,
success : function ( data ) {
if ( data . length > 0 ) {
$ ( ".unique" ) . html ( data ) ;
}
2025-11-14 14:36:31 +00:00
} ,
error : function ( ) {
$ ( '.unique' ) . html ( '<div class="alert alert-danger">Failed to load unique callsigns</div>' ) ;
2022-09-25 20:39:55 +02:00
}
} ) ;
}
function totalQsos ( ) {
$ . ajax ( {
url : base _url + 'index.php/statistics/get_total_qsos' ,
type : 'post' ,
success : function ( data ) {
if ( data . length > 0 ) {
$ ( ".qsos" ) . html ( data ) ;
}
2025-11-14 14:36:31 +00:00
} ,
error : function ( ) {
$ ( '.qsos' ) . html ( '<div class="alert alert-danger">Failed to load QSO statistics</div>' ) ;
2022-09-25 20:39:55 +02:00
}
} ) ;
}
2022-09-18 16:48:16 +02:00
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' ,
2025-11-14 14:36:31 +00:00
data : getDateFilterParams ( ) ,
2022-09-18 16:48:16 +02:00
success : function ( data ) {
if ( data . length > 0 ) {
2025-11-14 14:36:31 +00:00
$ ( ".years" ) . html ( '<h2>' + lang _statistics _years + '</h2><div id="yearContainer"></div><div id="yearTable"></div>' ) ;
2022-09-18 16:48:16 +02:00
$ ( "#yearContainer" ) . append ( "<canvas id=\"yearChart\" width=\"400\" height=\"100\"></canvas>" ) ;
// appending table to hold the data
$ ( "#yearTable" ) . append ( '<table style="width:100%" class="yeartable table table-sm table-bordered table-hover table-striped table-condensed text-center"><thead>' +
'<tr>' +
'<td>#</td>' +
2023-11-05 16:58:03 +01:00
'<td>' + lang _statistics _year + '</td>' +
'<td>' + lang _statistics _number _of _qso _worked + ' </td>' +
2022-09-18 16:48:16 +02:00
'</tr>' +
'</thead>' +
'<tbody></tbody></table>' ) ;
var labels = [ ] ;
var dataQso = [ ] ;
var $myTable = $ ( '.yeartable' ) ;
var i = 1 ;
// building the rows in the table
var rowElements = data . map ( function ( row ) {
var $row = $ ( '<tr></tr>' ) ;
var $iterator = $ ( '<td></td>' ) . html ( i ++ ) ;
var $type = $ ( '<td></td>' ) . html ( row . year ) ;
var $content = $ ( '<td></td>' ) . 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 ) ;
} ) ;
2023-01-05 08:36:29 +01:00
labels . reverse ( ) ;
dataQso . reverse ( ) ;
2022-09-18 16:48:16 +02:00
var ctx = document . getElementById ( "yearChart" ) . getContext ( '2d' ) ;
var myChart = new Chart ( ctx , {
type : 'bar' ,
data : {
labels : labels ,
datasets : [ {
2023-11-05 16:58:03 +01:00
label : lang _statistics _number _of _qso _worked _each _year ,
2022-09-18 16:48:16 +02:00
data : dataQso ,
backgroundColor : 'rgba(54, 162, 235, 0.2)' ,
borderColor : 'rgba(54, 162, 235, 1)' ,
borderWidth : 2 ,
color : color
} ]
} ,
options : {
2025-11-14 14:36:31 +00:00
responsive : true ,
maintainAspectRatio : true ,
2022-09-18 16:48:16 +02:00
scales : {
y : {
ticks : {
beginAtZero : true ,
color : color
}
} ,
x : {
ticks : {
color : color
}
}
} ,
plugins : {
legend : {
labels : {
color : color
}
2025-11-14 14:36:31 +00:00
} ,
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 ;
2022-09-18 16:48:16 +02:00
}
}
}
} ) ;
$ ( '.yeartable' ) . DataTable ( {
responsive : false ,
ordering : false ,
2022-09-25 20:39:55 +02:00
"scrollY" : "320px" ,
2022-09-18 16:48:16 +02:00
"scrollCollapse" : true ,
"paging" : false ,
"scrollX" : true ,
2024-01-03 22:03:11 -05:00
"language" : {
2024-01-03 22:18:20 -05:00
url : getDataTablesLanguageUrl ( ) ,
2024-01-03 22:03:11 -05:00
} ,
2022-09-18 16:48:16 +02:00
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" ) ;
}
}
2025-11-14 14:36:31 +00:00
} ,
error : function ( ) {
$ ( '.years' ) . html ( '<div class="alert alert-danger">Failed to load year statistics</div>' ) ;
2022-09-18 16:48:16 +02:00
}
} ) ;
}
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 ;
2025-11-14 14:36:31 +00:00
$ ( ".mode" ) . html ( '<br /><div style="display: flex;" id="modeContainer"><h2>' + lang _statistics _modes + '</h2><div style="flex: 1;"><canvas id="modeChart" width="500" height="500"></canvas></div><div style="flex: 1;" id="modeTable"></div></div><br />' ) ;
2022-09-18 16:48:16 +02:00
// appending table to hold the data
$ ( "#modeTable" ) . append ( '<table style="width:100%" class=\"modetable table table-sm table-bordered table-hover table-striped table-condensed text-center"><thead>' +
'<tr>' +
'<td>#</td>' +
2023-11-05 16:58:03 +01:00
'<td>' + lang _gen _hamradio _mode + ' </td>' +
'<td>' + lang _statistics _number _of _qso _worked + ' </td>' +
2022-09-18 16:48:16 +02:00
'</tr>' +
'</thead>' +
'<tbody></tbody></table>' ) ;
var $myTable = $ ( '.modetable' ) ;
var i = 1 ;
// building the rows in the table
var rowElements = data . map ( function ( row ) {
var $row = $ ( '<tr></tr>' ) ;
var $iterator = $ ( '<td></td>' ) . html ( i ++ ) ;
var $type = $ ( '<td></td>' ) . html ( row . mode . toUpperCase ( ) ) ;
var $content = $ ( '<td></td>' ) . 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"
} ,
2025-11-14 14:36:31 +00:00
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 + '%)' ;
}
}
} ,
2022-09-18 16:48:16 +02:00
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" ) ;
}
}
2025-11-14 14:36:31 +00:00
} ,
error : function ( ) {
$ ( '.mode' ) . html ( '<div class="alert alert-danger">Failed to load mode statistics</div>' ) ;
2022-09-18 16:48:16 +02:00
}
} ) ;
}
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 ) {
2025-11-14 14:36:31 +00:00
$ ( ".band" ) . html ( '<br /><div style="display: flex;" id="bandContainer"><h2>' + lang _statistics _bands + '</h2><div style="flex: 1;"><canvas id="bandChart" width="500" height="500"></canvas></div><div style="flex: 1;" id="bandTable"></div></div><br />' ) ;
2022-09-18 16:48:16 +02:00
// appending table to hold the data
$ ( "#bandTable" ) . append ( '<table style="width:100%" class="bandtable table table-sm table-bordered table-hover table-striped table-condensed text-center"><thead>' +
'<tr>' +
'<td>#</td>' +
2023-11-05 16:58:03 +01:00
'<td>' + lang _gen _hamradio _band + '</td>' +
'<td>' + lang _statistics _number _of _qso _worked + ' </td>' +
2022-09-18 16:48:16 +02:00
'</tr>' +
'</thead>' +
'<tbody></tbody></table>' ) ;
var labels = [ ] ;
var dataQso = [ ] ;
2022-09-19 09:21:40 +02:00
var totalQso = Number ( 0 ) ;
2022-09-18 16:48:16 +02:00
var $myTable = $ ( '.bandtable' ) ;
var i = 1 ;
// building the rows in the table
var rowElements = data . map ( function ( row ) {
var $row = $ ( '<tr></tr>' ) ;
var $iterator = $ ( '<td></td>' ) . html ( i ++ ) ;
var $type = $ ( '<td></td>' ) . html ( row . band ) ;
var $content = $ ( '<td></td>' ) . 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 ) ;
2022-09-19 09:21:40 +02:00
totalQso = Number ( totalQso ) + Number ( this . count ) ;
2022-09-18 16:48:16 +02:00
} ) ;
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 : {
2022-09-19 09:55:44 +02:00
padding : 100
2022-09-18 16:48:16 +02:00
} ,
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"
} ,
2025-11-14 14:36:31 +00:00
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 + '%)' ;
}
}
} ,
2022-09-18 16:48:16 +02:00
outlabels : {
2022-09-19 09:21:40 +02:00
display : function ( context ) { // Hide labels with low percentage
return ( ( context . dataset . data [ context . dataIndex ] / totalQso * 100 ) > 1 )
} ,
2022-09-18 16:48:16 +02:00
backgroundColor : COLORS ,
borderColor : COLORS ,
borderRadius : 2 , // Border radius of Label
borderWidth : 2 , // Thickness of border
color : 'white' ,
2022-09-18 17:59:57 +02:00
stretch : 10 ,
2022-09-18 16:48:16 +02:00
padding : 0 ,
font : {
resizable : true ,
2022-09-18 17:59:57 +02:00
minSize : 12 ,
2022-09-18 16:48:16 +02:00
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 ,
2022-09-18 17:59:57 +02:00
"scrollY" : "330px" ,
2022-09-18 16:48:16 +02:00
"scrollCollapse" : true ,
"paging" : false ,
"scrollX" : true ,
2024-01-03 22:03:11 -05:00
"language" : {
2024-01-03 22:18:20 -05:00
url : getDataTablesLanguageUrl ( ) ,
2024-01-03 22:03:11 -05:00
} ,
2022-09-18 16:48:16 +02:00
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" ) ;
}
}
2025-11-14 14:36:31 +00:00
} ,
error : function ( ) {
$ ( '.band' ) . html ( '<div class="alert alert-danger">Failed to load band statistics</div>' ) ;
2022-09-18 16:48:16 +02:00
}
} ) ;
}
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 = [ ] ;
2022-09-19 09:21:40 +02:00
var totalQso = Number ( 0 ) ;
2022-09-18 16:48:16 +02:00
var $myTable = $ ( '.sattable' ) ;
var i = 1 ;
// building the rows in the table
var rowElements = data . map ( function ( row ) {
var $row = $ ( '<tr></tr>' ) ;
var $iterator = $ ( '<td></td>' ) . html ( i ++ ) ;
var $type = $ ( '<td></td>' ) . html ( row . sat ) ;
var $content = $ ( '<td></td>' ) . 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 ) ;
2022-09-19 09:21:40 +02:00
totalQso = Number ( totalQso ) + Number ( this . count ) ;
2022-09-18 16:48:16 +02:00
} ) ;
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 : {
2022-09-19 09:55:44 +02:00
padding : 100
2022-09-18 16:48:16 +02:00
} ,
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"
} ,
2025-11-14 14:36:31 +00:00
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 + '%)' ;
}
}
} ,
2022-09-18 16:48:16 +02:00
outlabels : {
2022-09-19 09:21:40 +02:00
display : function ( context ) { // Hide labels with low percentage
return ( ( context . dataset . data [ context . dataIndex ] / totalQso * 100 ) > 1 )
} ,
2022-09-18 16:48:16 +02:00
backgroundColor : COLORS ,
borderColor : COLORS ,
borderRadius : 2 , // Border radius of Label
borderWidth : 2 , // Thickness of border
color : 'white' ,
2022-09-18 17:59:57 +02:00
stretch : 10 ,
2022-09-18 16:48:16 +02:00
padding : 0 ,
font : {
resizable : true ,
2022-09-18 17:59:57 +02:00
minSize : 12 ,
2022-09-18 16:48:16 +02:00
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 ,
2022-09-18 17:59:57 +02:00
"scrollY" : "330px" ,
2022-09-18 16:48:16 +02:00
"scrollX" : true ,
2024-01-03 22:03:11 -05:00
"language" : {
2024-01-03 22:18:20 -05:00
url : getDataTablesLanguageUrl ( ) ,
2024-01-03 22:03:11 -05:00
} ,
2022-09-18 16:48:16 +02:00
"ScrollCollapse" : true ,
"paging" : false ,
bFilter : false ,
bInfo : false ,
} ) ;
}
}
} ) ;
2025-11-14 14:36:31 +00:00
}
function loadTrends ( ) {
var color = ifDarkModeThemeReturn ( 'white' , 'grey' ) ;
$ . ajax ( {
url : base _url + 'index.php/statistics/get_trends' ,
type : 'post' ,
success : function ( data ) {
var html = '<div class="trends-loaded">' ;
html += '<h2>Activity Trends</h2>' ;
// Summary cards for trends
html += '<div class="row mb-4">' ;
html += '<div class="col-md-4">' ;
html += '<div class="stats-card">' ;
html += '<div class="stats-card-icon"><i class="fas fa-calendar-day text-primary"></i></div>' ;
html += '<div class="stats-card-value">' + data . this _month + '</div>' ;
html += '<div class="stats-card-label">QSOs This Month</div>' ;
html += '</div></div>' ;
html += '<div class="col-md-4">' ;
html += '<div class="stats-card">' ;
html += '<div class="stats-card-icon"><i class="fas fa-calendar-alt text-success"></i></div>' ;
html += '<div class="stats-card-value">' + data . this _year + '</div>' ;
html += '<div class="stats-card-label">QSOs This Year</div>' ;
html += '</div></div>' ;
html += '<div class="col-md-4">' ;
html += '<div class="stats-card">' ;
html += '<div class="stats-card-icon"><i class="fas fa-calendar-week text-info"></i></div>' ;
html += '<div class="stats-card-value">' + data . last _30 _days + '</div>' ;
html += '<div class="stats-card-label">Last 30 Days</div>' ;
html += '</div></div>' ;
html += '</div>' ;
// Monthly trend chart
html += '<div class="row">' ;
html += '<div class="col-12">' ;
html += '<canvas id="trendsChart" width="400" height="100"></canvas>' ;
html += '</div></div></div>' ;
$ ( '.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 ( '<div class="alert alert-danger">Failed to load trends</div>' ) ;
}
} ) ;
}
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 = '<div class="continents-loaded">' ;
html += '<br /><div style="display: flex;" id="continentContainer">' ;
html += '<h2>Continents</h2>' ;
html += '<div style="flex: 1;"><canvas id="continentChart" width="500" height="500"></canvas></div>' ;
html += '<div style="flex: 1;" id="continentTable"></div></div><br /></div>' ;
$ ( '.continents' ) . html ( html ) ;
// Build table
var tableHtml = '<table style="width:100%" class="continenttable table table-sm table-bordered table-hover table-striped table-condensed text-center"><thead>' ;
tableHtml += '<tr><td>#</td><td>Continent</td><td># of QSOs</td></tr></thead><tbody>' ;
var labels = [ ] ;
var counts = [ ] ;
data . forEach ( ( item , index ) => {
tableHtml += '<tr><td>' + ( index + 1 ) + '</td><td>' + item . continent + '</td><td>' + item . count + '</td></tr>' ;
labels . push ( item . continent ) ;
counts . push ( item . count ) ;
} ) ;
tableHtml += '</tbody></table>' ;
$ ( '#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 ( '<div class="alert alert-danger">Failed to load continent statistics</div>' ) ;
}
} ) ;
}
function loadMostWorked ( ) {
$ . ajax ( {
url : base _url + 'index.php/statistics/get_most_worked' ,
type : 'post' ,
success : function ( data ) {
var html = '<div class="mostworked-loaded"><h2>Most Worked</h2>' ;
html += '<div class="row">' ;
// Most worked callsigns
html += '<div class="col-md-6">' ;
html += '<h4><i class="fas fa-user text-primary"></i> Top 10 Callsigns</h4>' ;
html += '<table class="table table-sm table-bordered table-hover table-striped">' ;
html += '<thead><tr><th>#</th><th>Callsign</th><th>Country</th><th>QSOs</th></tr></thead><tbody>' ;
data . callsigns . forEach ( ( item , index ) => {
html += '<tr>' ;
html += '<td>' + ( index + 1 ) + '</td>' ;
html += '<td><strong>' + item . callsign + '</strong></td>' ;
html += '<td>' + ( item . country || '-' ) + '</td>' ;
html += '<td><span class="badge bg-primary">' + item . count + '</span></td>' ;
html += '</tr>' ;
} ) ;
html += '</tbody></table></div>' ;
// Most worked countries
html += '<div class="col-md-6">' ;
html += '<h4><i class="fas fa-flag text-success"></i> Top 10 Countries</h4>' ;
html += '<table class="table table-sm table-bordered table-hover table-striped">' ;
html += '<thead><tr><th>#</th><th>Country</th><th>QSOs</th></tr></thead><tbody>' ;
data . countries . forEach ( ( item , index ) => {
html += '<tr>' ;
html += '<td>' + ( index + 1 ) + '</td>' ;
html += '<td><strong>' + item . country + '</strong></td>' ;
html += '<td><span class="badge bg-success">' + item . count + '</span></td>' ;
html += '</tr>' ;
} ) ;
html += '</tbody></table></div>' ;
html += '</div></div>' ;
$ ( '.mostworked' ) . html ( html ) ;
} ,
error : function ( ) {
$ ( '.mostworked' ) . html ( '<div class="alert alert-danger">Failed to load most worked statistics</div>' ) ;
}
} ) ;
}
// 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 ( '<div class="loading-indicator"><div class="loading-spinner"></div><p>Loading filtered data...</p></div>' ) ;
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 ( '<div class="loading-indicator"><div class="loading-spinner"></div><p>Loading data...</p></div>' ) ;
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 ;
2022-09-18 16:48:16 +02:00
}