overview
1 year ago
campaign_builder_controller.js
1 year ago
chart_controller.js
2 months ago
chart_interval_controller.js
2 years ago
clipboard_controller.js
1 year ago
copy_report_controller.js
2 years ago
create_report_controller.js
1 year ago
delete_data_controller.js
1 year ago
delete_report_controller.js
2 years ago
easepick_controller.js
2 years ago
examiner_controller.js
2 months ago
examiner_header_controller.js
11 months ago
export_overview_controller.js
9 months ago
export_reports_controller.js
2 years ago
filters_controller.js
6 months ago
group_controller.js
2 years ago
import_reports_controller.js
2 years ago
journey_controller.js
6 months ago
map_controller.js
9 months ago
migration_redirect_controller.js
2 years ago
modal_controller.js
2 years ago
pause_emails_controller.js
6 months ago
pie_chart_controller.js
6 months ago
plugin_group_options_controller.js
1 year ago
pruner_controller.js
5 months ago
quick_stats_controller.js
1 year ago
real_time_controller.js
6 months ago
refresh_overview_controller.js
1 year ago
rename_report_controller.js
2 years ago
report_controller.js
2 months ago
reset_analytics_controller.js
1 year ago
reset_overview_controller.js
1 year ago
save_report_controller.js
2 years ago
select_input_controller.js
2 years ago
set_favorite_report_controller.js
2 years ago
sort_controller.js
2 years ago
sortable_reports_controller.js
2 years ago
table_columns_controller.js
2 years ago
tooltip_controller.js
6 months ago
woocommerce_settings_controller.js
1 year ago
real_time_controller.js
303 lines
| 1 | import {Controller} from "@hotwired/stimulus" |
| 2 | import {Chart, registerables} from 'chart.js' |
| 3 | import Isotope from "isotope-layout" |
| 4 | import htmlLegendPlugin from "../chart_plugins/html_legend_plugin"; |
| 5 | import corsairPlugin from "../chart_plugins/corsair_plugin"; |
| 6 | import {isDarkMode} from "../utils/appearance"; |
| 7 | |
| 8 | Chart.register(...registerables); |
| 9 | Chart.defaults.font.family = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"'; |
| 10 | |
| 11 | export default class extends Controller { |
| 12 | static targets = [ |
| 13 | 'minuteChart', |
| 14 | 'secondChart', |
| 15 | 'visitorMessage', |
| 16 | 'pageMessage', |
| 17 | 'referrerMessage', |
| 18 | 'countryMessage', |
| 19 | 'pagesList', |
| 20 | 'referrersList', |
| 21 | 'countriesList', |
| 22 | 'campaignsList', |
| 23 | 'device_typesList' |
| 24 | ] |
| 25 | static values = { |
| 26 | chartData: Object, |
| 27 | nonce: String, |
| 28 | visible: { |
| 29 | type: Boolean, |
| 30 | default: true |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | connect() { |
| 35 | document.addEventListener('visibilitychange', this.tabVisibilityChanged) |
| 36 | this.initializeChart({ |
| 37 | element: this.minuteChartTarget, |
| 38 | views: this.chartDataValue['minute_interval_views'], |
| 39 | labelsShort: this.chartDataValue['minute_interval_labels_short'], |
| 40 | labelsFull: this.chartDataValue['minute_interval_labels_full'] |
| 41 | }) |
| 42 | this.initializeChart({ |
| 43 | element: this.secondChartTarget, |
| 44 | views: this.chartDataValue['second_interval_views'], |
| 45 | labelsShort: this.chartDataValue['second_interval_labels_short'], |
| 46 | labelsFull: this.chartDataValue['second_interval_labels_full'] |
| 47 | }) |
| 48 | this.startServerPolling() |
| 49 | } |
| 50 | |
| 51 | disconnect() { |
| 52 | document.removeEventListener('visibilitychange', this.tabVisibilityChanged) |
| 53 | } |
| 54 | |
| 55 | tabVisibilityChanged = () => { |
| 56 | this.visibleValue = !document.hidden |
| 57 | |
| 58 | if (this.visibleValue) { |
| 59 | this.refresh() |
| 60 | this.startServerPolling() |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | startServerPolling() { |
| 65 | clearInterval(this.interval) |
| 66 | this.interval = setInterval(() => { |
| 67 | if (this.visibleValue) { |
| 68 | this.refresh() |
| 69 | } else { |
| 70 | clearInterval(this.interval) |
| 71 | } |
| 72 | }, 10000) |
| 73 | } |
| 74 | |
| 75 | refresh() { |
| 76 | const data = { |
| 77 | ...iawpActions.real_time_data |
| 78 | }; |
| 79 | |
| 80 | jQuery.post(ajaxurl, data, (response, a, b) => { |
| 81 | document.getElementById('real-time-dashboard').classList.remove('refreshed') |
| 82 | void document.getElementById('real-time-dashboard').offsetWidth |
| 83 | document.getElementById('real-time-dashboard').classList.add('refreshed') |
| 84 | if (response.success) { |
| 85 | this.rerender(response.data) |
| 86 | } |
| 87 | }); |
| 88 | } |
| 89 | |
| 90 | rerender(data) { |
| 91 | this.visitorMessageTarget.textContent = data.visitor_message |
| 92 | this.pageMessageTarget.textContent = data.page_message |
| 93 | this.referrerMessageTarget.textContent = data.referrer_message |
| 94 | this.countryMessageTarget.textContent = data.country_message |
| 95 | this.updateChart({ |
| 96 | element: this.minuteChartTarget, |
| 97 | visitors: data.chart_data.minute_interval_visitors, |
| 98 | views: data.chart_data.minute_interval_views, |
| 99 | }) |
| 100 | this.updateChart({ |
| 101 | element: this.secondChartTarget, |
| 102 | visitors: data.chart_data.second_interval_visitors, |
| 103 | views: data.chart_data.second_interval_views |
| 104 | }) |
| 105 | this.updateList({ |
| 106 | element: this.pagesListTarget, |
| 107 | entries: data.lists.pages.entries |
| 108 | }) |
| 109 | this.updateList({ |
| 110 | element: this.referrersListTarget, |
| 111 | entries: data.lists.referrers.entries |
| 112 | }) |
| 113 | this.updateList({ |
| 114 | element: this.countriesListTarget, |
| 115 | entries: data.lists.countries.entries |
| 116 | }) |
| 117 | this.updateList({ |
| 118 | element: this.campaignsListTarget, |
| 119 | entries: data.lists.campaigns.entries |
| 120 | }) |
| 121 | this.updateList({ |
| 122 | element: this.device_typesListTarget, |
| 123 | entries: data.lists.device_types.entries |
| 124 | }) |
| 125 | } |
| 126 | |
| 127 | existingElements() { |
| 128 | return Array.from(this.element.querySelectorAll('li:not(.exiting)')) |
| 129 | } |
| 130 | |
| 131 | existingIds() { |
| 132 | return this.existingElements().map((existingElement) => existingElement.dataset.id) |
| 133 | } |
| 134 | |
| 135 | updateList({ |
| 136 | element, |
| 137 | entries |
| 138 | }) { |
| 139 | let iso = element.iso |
| 140 | |
| 141 | if (!iso) { |
| 142 | iso = element.iso = new Isotope(element, { |
| 143 | itemSelector: "li", |
| 144 | layoutMode: "vertical", |
| 145 | sortColumn: "position", |
| 146 | getSortData: { |
| 147 | position: (element) => { |
| 148 | return parseInt(element.dataset.position); |
| 149 | } |
| 150 | } |
| 151 | }) |
| 152 | } |
| 153 | |
| 154 | // Remove existing elements |
| 155 | this.existingElements().forEach((existingElement) => { |
| 156 | iso.remove(existingElement) |
| 157 | }) |
| 158 | |
| 159 | // Add new elements |
| 160 | entries.forEach((entry, index) => { |
| 161 | const entryElement = this.elementFromEntry(entry) |
| 162 | iso.insert(entryElement) |
| 163 | }) |
| 164 | |
| 165 | iso.arrange() |
| 166 | |
| 167 | const totalElements = iso.items.length |
| 168 | const emptyMessage = element.parentNode.querySelector('.most-popular-empty-message') |
| 169 | |
| 170 | if (totalElements > 0) { |
| 171 | emptyMessage.classList.add('hide') |
| 172 | } else { |
| 173 | emptyMessage.classList.remove('hide') |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | elementFromEntry(entry) { |
| 178 | const id = entry['id'] |
| 179 | const position = entry['position'] |
| 180 | const title = entry['title'] |
| 181 | const subtitle = entry['subtitle'] |
| 182 | const views = entry['views'] |
| 183 | const flag = entry['flag'] ? entry['flag'] : '' |
| 184 | const subtitleHTML = subtitle ? `<span class="real-time-subtitle">${subtitle}</span>` : ''; |
| 185 | |
| 186 | const li = ` |
| 187 | <li data-id="${id}" data-position="${position}"> |
| 188 | <span class="real-time-position">${position}.</span> |
| 189 | ${flag} |
| 190 | <span class="real-time-resource">${title} ${subtitleHTML}</span> |
| 191 | <span class="real-time-stat">${views}</span> |
| 192 | </li> |
| 193 | ` |
| 194 | const el = document.createElement('div'); |
| 195 | el.innerHTML = li; |
| 196 | return el.firstElementChild; |
| 197 | } |
| 198 | |
| 199 | initializeChart({ |
| 200 | element, |
| 201 | views, |
| 202 | labelsShort, |
| 203 | labelsFull |
| 204 | }) { |
| 205 | const data = { |
| 206 | labels: labelsFull, |
| 207 | datasets: [{ |
| 208 | id: 'views', |
| 209 | label: iawpText.views, |
| 210 | data: views, |
| 211 | backgroundColor: 'rgba(108,70,174,0.2)', |
| 212 | borderColor: 'rgba(108,70,174,1)', |
| 213 | borderWidth: { |
| 214 | bottom: 0, |
| 215 | top: 3, |
| 216 | left: 0, |
| 217 | right: 0 |
| 218 | } |
| 219 | },] |
| 220 | } |
| 221 | |
| 222 | const config = { |
| 223 | type: 'bar', |
| 224 | data, |
| 225 | options: { |
| 226 | interaction: { |
| 227 | intersect: false, |
| 228 | mode: 'index' |
| 229 | }, |
| 230 | responsive: true, |
| 231 | scales: { |
| 232 | y: { |
| 233 | border: { |
| 234 | color: '#DEDAE6', |
| 235 | dash: [2, 4] |
| 236 | }, |
| 237 | grid: { |
| 238 | tickColor: '#DEDAE6', |
| 239 | display: true, |
| 240 | drawOnChartArea: true, |
| 241 | }, |
| 242 | beginAtZero: true, |
| 243 | suggestedMax: 5, |
| 244 | ticks: { |
| 245 | color: isDarkMode() ? '#ffffff' : '#6D6A73', |
| 246 | precision: 0 |
| 247 | } |
| 248 | }, |
| 249 | x: { |
| 250 | stacked: true, |
| 251 | border: { |
| 252 | color: '#DEDAE6', |
| 253 | }, |
| 254 | grid: { |
| 255 | tickColor: '#DEDAE6', |
| 256 | display: true, |
| 257 | drawOnChartArea: false, |
| 258 | }, |
| 259 | ticks: { |
| 260 | color: isDarkMode() ? '#ffffff' : '#6D6A73', |
| 261 | beginAtZero: true, |
| 262 | callback: function (value, index, values) { |
| 263 | return labelsShort[index]; |
| 264 | } |
| 265 | } |
| 266 | } |
| 267 | }, |
| 268 | plugins: { |
| 269 | mode: String, // 'light' or 'dark' |
| 270 | htmlLegend: { |
| 271 | container: element.parentNode.querySelector('.legend') |
| 272 | }, |
| 273 | legend: { |
| 274 | display: false |
| 275 | }, |
| 276 | corsair: { |
| 277 | dash: [2, 4], |
| 278 | color: '#777', |
| 279 | width: 1 |
| 280 | }, |
| 281 | }, |
| 282 | }, |
| 283 | plugins: [ |
| 284 | corsairPlugin, |
| 285 | htmlLegendPlugin |
| 286 | ], |
| 287 | }; |
| 288 | |
| 289 | new Chart(element, config); |
| 290 | } |
| 291 | |
| 292 | updateChart({ |
| 293 | element, |
| 294 | views |
| 295 | }) { |
| 296 | const chart = Chart.getChart(element); |
| 297 | chart.data.datasets.forEach((dataset, index) => { |
| 298 | dataset.data.splice(0, dataset.data.length, ...views) |
| 299 | }); |
| 300 | chart.update('none'); |
| 301 | } |
| 302 | } |
| 303 |