admin-dashboard.js
7 months ago
admin-post.js
7 months ago
admin-quick-edit.js
7 months ago
admin-settings.js
7 months ago
admin-widgets.js
7 months ago
block-editor.min.js
7 months ago
column-modal.js
7 months ago
counter.js
7 months ago
counter.min.js
7 months ago
dummy.js
7 months ago
frontend.js
7 months ago
frontend.min.js
7 months ago
column-modal.js
301 lines
| 1 | /** |
| 2 | * Post Views Counter - Column Modal Chart |
| 3 | */ |
| 4 | ( function( $ ) { |
| 5 | 'use strict'; |
| 6 | |
| 7 | // check if jQuery is available |
| 8 | if ( typeof $ === 'undefined' ) |
| 9 | return; |
| 10 | |
| 11 | let pvcModalChart = null; |
| 12 | let currentPostId = null; |
| 13 | |
| 14 | // check if localized data is available |
| 15 | if ( typeof pvcColumnModal === 'undefined' ) |
| 16 | return; |
| 17 | |
| 18 | /** |
| 19 | * Initialize Micromodal |
| 20 | */ |
| 21 | function initMicromodal() { |
| 22 | if ( typeof MicroModal === 'undefined' ) |
| 23 | return false; |
| 24 | |
| 25 | // initialize with basic config |
| 26 | MicroModal.init( { |
| 27 | disableScroll: true, |
| 28 | awaitCloseAnimation: true |
| 29 | } ); |
| 30 | |
| 31 | return true; |
| 32 | } |
| 33 | |
| 34 | /** |
| 35 | * Prepare modal for specific post |
| 36 | */ |
| 37 | function prepareModalForPost( postId, postTitle ) { |
| 38 | if ( ! postId ) |
| 39 | return false; |
| 40 | |
| 41 | currentPostId = postId; |
| 42 | |
| 43 | // set modal title |
| 44 | $( '#pvc-modal-title' ).text( postTitle ); |
| 45 | |
| 46 | // show loading state |
| 47 | const $container = $( '.pvc-modal-chart-container' ); |
| 48 | $container.addClass( 'loading' ); |
| 49 | $container.find( '.spinner' ).addClass( 'is-active' ); |
| 50 | |
| 51 | // clear previous content |
| 52 | $( '.pvc-modal-views-label' ).text( '' ); |
| 53 | $( '.pvc-modal-count' ).text( '' ); |
| 54 | $( '.pvc-modal-dates' ).html( '' ); |
| 55 | |
| 56 | return true; |
| 57 | } |
| 58 | |
| 59 | function resetModalContent() { |
| 60 | $( '#pvc-modal-title' ).text( '' ); |
| 61 | $( '.pvc-modal-views-label' ).text( '' ); |
| 62 | $( '.pvc-modal-count' ).text( '' ); |
| 63 | $( '.pvc-modal-dates' ).html( '' ); |
| 64 | |
| 65 | const $container = $( '.pvc-modal-chart-container' ); |
| 66 | $container.removeClass( 'loading' ); |
| 67 | $container.find( '.spinner' ).removeClass( 'is-active' ); |
| 68 | |
| 69 | // remove any error messages |
| 70 | $( '.pvc-modal-error' ).remove(); |
| 71 | } |
| 72 | |
| 73 | /** |
| 74 | * Load chart data via AJAX |
| 75 | */ |
| 76 | function loadChartData( postId, period ) { |
| 77 | const $container = $( '.pvc-modal-chart-container' ); |
| 78 | |
| 79 | // show loading |
| 80 | $container.addClass( 'loading' ); |
| 81 | $container.find( '.spinner' ).addClass( 'is-active' ); |
| 82 | |
| 83 | $.ajax( { |
| 84 | url: pvcColumnModal.ajaxURL, |
| 85 | type: 'POST', |
| 86 | dataType: 'json', |
| 87 | data: { |
| 88 | action: 'pvc_column_chart', |
| 89 | nonce: pvcColumnModal.nonce, |
| 90 | post_id: postId, |
| 91 | period: period |
| 92 | }, |
| 93 | success: function( response ) { |
| 94 | if ( response.success ) { |
| 95 | renderChart( response.data ); |
| 96 | } else { |
| 97 | showError( response.data.message || pvcColumnModal.i18n.error ); |
| 98 | } |
| 99 | }, |
| 100 | error: function( xhr, status, error ) { |
| 101 | showError( pvcColumnModal.i18n.error ); |
| 102 | }, |
| 103 | complete: function() { |
| 104 | $container.removeClass( 'loading' ); |
| 105 | $container.find( '.spinner' ).removeClass( 'is-active' ); |
| 106 | } |
| 107 | } ); |
| 108 | } |
| 109 | |
| 110 | /** |
| 111 | * Render chart with data |
| 112 | */ |
| 113 | function renderChart( data ) { |
| 114 | const ctx = document.getElementById( 'pvc-modal-chart' ); |
| 115 | |
| 116 | if ( ! ctx ) |
| 117 | return; |
| 118 | |
| 119 | // destroy existing chart |
| 120 | if ( pvcModalChart ) { |
| 121 | pvcModalChart.destroy(); |
| 122 | pvcModalChart = null; |
| 123 | } |
| 124 | |
| 125 | // remove any error messages and show canvas |
| 126 | $( '.pvc-modal-error' ).remove(); |
| 127 | $( ctx ).show(); |
| 128 | |
| 129 | // update stats |
| 130 | $( '.pvc-modal-views-label' ).text( pvcColumnModal.i18n.summary ); |
| 131 | $( '.pvc-modal-count' ).text( data.total_views.toLocaleString() ); |
| 132 | |
| 133 | // update date navigation |
| 134 | $( '.pvc-modal-dates' ).html( data.dates_html ); |
| 135 | |
| 136 | /* debug logging (admins only) - shows views keys returned from AJAX |
| 137 | if ( typeof console !== 'undefined' && data.debug_views_keys ) { |
| 138 | console.debug( 'PVC debug: views keys for post ' + data.post_id + ':', data.debug_views_keys ); |
| 139 | console.debug( 'PVC debug: views sample by keys:', data.debug_views ); |
| 140 | console.debug( 'PVC debug: example date_key used by client:', data.debug_date_key_example ); |
| 141 | } |
| 142 | |
| 143 | if ( typeof console !== 'undefined' && data.data ) { |
| 144 | console.debug( 'PVC debug: chart data object', data.data ); |
| 145 | console.debug( 'PVC debug: dataset length', data.data.datasets && data.data.datasets[0] ? data.data.datasets[0].data.length : 0 ); |
| 146 | } |
| 147 | */ |
| 148 | |
| 149 | // chart configuration |
| 150 | const config = { |
| 151 | type: 'line', |
| 152 | data: data.data, |
| 153 | options: { |
| 154 | maintainAspectRatio: false, |
| 155 | responsive: true, |
| 156 | plugins: { |
| 157 | legend: { |
| 158 | display: false |
| 159 | }, |
| 160 | tooltip: { |
| 161 | callbacks: { |
| 162 | title: function( context ) { |
| 163 | return data.data.dates[context[0].dataIndex]; |
| 164 | }, |
| 165 | label: function( context ) { |
| 166 | const count = context.parsed.y; |
| 167 | const viewText = count === 1 ? pvcColumnModal.i18n.view : pvcColumnModal.i18n.views; |
| 168 | return count.toLocaleString() + ' ' + viewText; |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | }, |
| 173 | scales: { |
| 174 | x: { |
| 175 | display: true, |
| 176 | grid: { |
| 177 | display: false |
| 178 | } |
| 179 | }, |
| 180 | y: { |
| 181 | display: true, |
| 182 | beginAtZero: true, |
| 183 | ticks: { |
| 184 | precision: 0 |
| 185 | }, |
| 186 | grid: { |
| 187 | color: 'rgba(0, 0, 0, 0.05)' |
| 188 | } |
| 189 | } |
| 190 | } |
| 191 | } |
| 192 | }; |
| 193 | |
| 194 | // apply design from server |
| 195 | if ( data.design ) { |
| 196 | config.data.datasets.forEach( function( dataset ) { |
| 197 | Object.assign( dataset, data.design ); |
| 198 | dataset.tension = 0.4; // match dashboard curve |
| 199 | } ); |
| 200 | } |
| 201 | |
| 202 | // create chart |
| 203 | pvcModalChart = new Chart( ctx.getContext( '2d' ), config ); |
| 204 | } |
| 205 | |
| 206 | /** |
| 207 | * Show error message without destroying modal structure |
| 208 | */ |
| 209 | function showError( message ) { |
| 210 | // destroy existing chart |
| 211 | if ( pvcModalChart ) { |
| 212 | pvcModalChart.destroy(); |
| 213 | pvcModalChart = null; |
| 214 | } |
| 215 | |
| 216 | // reset states |
| 217 | $( '.pvc-modal-summary' ).text( '' ); |
| 218 | $( '.pvc-modal-dates' ).html( '' ); |
| 219 | |
| 220 | const $container = $( '.pvc-modal-chart-container' ); |
| 221 | $container.removeClass( 'loading' ); |
| 222 | $container.find( '.spinner' ).removeClass( 'is-active' ); |
| 223 | |
| 224 | // remove any existing error message |
| 225 | $( '.pvc-modal-error' ).remove(); |
| 226 | |
| 227 | // inject error message before chart container |
| 228 | $container.before( '<p class="pvc-modal-error">' + message + '</p>' ); |
| 229 | |
| 230 | // hide canvas during error |
| 231 | $container.find( 'canvas' ).hide(); |
| 232 | } |
| 233 | |
| 234 | /** |
| 235 | * Document ready |
| 236 | */ |
| 237 | $( function() { |
| 238 | // check if modal HTML exists |
| 239 | if ( $( '#pvc-chart-modal' ).length === 0 ) |
| 240 | return; |
| 241 | |
| 242 | // initialize Micromodal |
| 243 | if ( ! initMicromodal() ) |
| 244 | return; |
| 245 | |
| 246 | // handle click on view chart link |
| 247 | $( document ).on( 'click', '.pvc-view-chart', function( e ) { |
| 248 | e.preventDefault(); |
| 249 | |
| 250 | const postId = $( this ).data( 'post-id' ); |
| 251 | const postTitle = $( this ).data( 'post-title' ); |
| 252 | |
| 253 | if ( ! postId ) |
| 254 | return; |
| 255 | |
| 256 | // prepare modal with post data |
| 257 | if ( prepareModalForPost( postId, postTitle ) ) { |
| 258 | // open modal using MicroModal with callbacks |
| 259 | if ( typeof MicroModal !== 'undefined' ) { |
| 260 | MicroModal.show( 'pvc-chart-modal', { |
| 261 | onShow: function( modal ) { |
| 262 | if ( currentPostId ) |
| 263 | loadChartData( currentPostId, '' ); |
| 264 | }, |
| 265 | onClose: function() { |
| 266 | // destroy chart when modal closes |
| 267 | if ( pvcModalChart ) { |
| 268 | pvcModalChart.destroy(); |
| 269 | pvcModalChart = null; |
| 270 | } |
| 271 | |
| 272 | currentPostId = null; |
| 273 | |
| 274 | // reset modal content |
| 275 | resetModalContent(); |
| 276 | }, |
| 277 | disableScroll: true, |
| 278 | awaitCloseAnimation: true |
| 279 | } ); |
| 280 | } |
| 281 | } |
| 282 | } ); |
| 283 | |
| 284 | // handle period navigation |
| 285 | $( document ).on( 'click', '.pvc-modal-nav-prev, .pvc-modal-nav-next', function( e ) { |
| 286 | e.preventDefault(); |
| 287 | |
| 288 | // check if disabled |
| 289 | if ( $( this ).hasClass( 'pvc-disabled' ) ) |
| 290 | return; |
| 291 | |
| 292 | const period = $( this ).data( 'period' ); |
| 293 | |
| 294 | if ( period && currentPostId ) { |
| 295 | loadChartData( currentPostId, period ); |
| 296 | } |
| 297 | } ); |
| 298 | } ); |
| 299 | |
| 300 | } )( jQuery ); |
| 301 |