PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / admin / layouts / statistics / widgets / packages / items / chart.php
vikappointments / admin / layouts / statistics / widgets / packages / items Last commit date
chart.php 6 days ago count.php 6 days ago index.html 6 days ago
chart.php
290 lines
1 <?php
2 /**
3 * @package VikAppointments
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * Layout variables
16 * -----------------
17 * @var VAPStatisticsWidget $widget The instance of the widget to be displayed.
18 * @var mixed $data The chart data to immediately display.
19 * @var boolean $legend True to display the legend, false otherwise.
20 */
21 extract($displayData);
22
23 // include chart JS dependencies
24 JHtml::fetch('vaphtml.assets.chartjs');
25
26 JText::script('VAP_N_ORDERS');
27 JText::script('VAP_N_ORDERS_1');
28
29 ?>
30
31 <div class="canvas-align-bottom packages-items-chart" id="packages-items-chart-<?php echo $widget->getID(); ?>">
32 <canvas></canvas>
33 </div>
34
35 <script>
36
37 /**
38 * Defines a pool of charts, if undefined.
39 *
40 * @var object
41 */
42 if (typeof PACKAGES_ITEMS_CHARTS === 'undefined') {
43 var PACKAGES_ITEMS_CHARTS = {};
44 }
45
46 // init callbacks pool in case the caller didn't declare it
47 if (typeof WIDGET_CALLBACKS === 'undefined') {
48 var WIDGET_CALLBACKS = {};
49 }
50
51 (function($) {
52 'use strict';
53
54 /**
55 * Register callback to be executed after
56 * completing the update request.
57 *
58 * @param mixed widget The widget selector.
59 * @param mixed data The AJAX response.
60 * @param object config The widget configuration.
61 *
62 * @return void
63 */
64 WIDGET_CALLBACKS[<?php echo $widget->getID(); ?>] = (widget, data, config) => {
65 // get widget ID
66 var id = $(widget).attr('id');
67
68 /**
69 * Create callback used to format the values displayed
70 * on the Y axis, according to the saved configuration.
71 *
72 * @param mixed value The value passed by the chart.
73 *
74 * @return string The formatted value.
75 */
76 const formatAxisY = (value) => {
77 // format as currency in case of earning
78 if (config.valuetype == 'total') {
79 // do not display decimal values on Y axis
80 return VAPCurrency.getInstance().format(value, 0);
81 }
82
83 return value;
84 }
85
86 /**
87 * Create callback used to format the values displayed
88 * with the tooltips of the hovered points, according
89 * to the saved configuration.
90 *
91 * @param mixed item The item to display.
92 * @param mixed data The chart data.
93 *
94 * @return string The formatted value.
95 */
96 const formatPointTooltip = (item, data) => {
97 var label = '';
98
99 // extract package name from dataset
100 let packName = data.datasets[item.datasetIndex].label;
101
102 // format as currency in case of earning
103 if (config.valuetype == 'total') {
104 label = VAPCurrency.getInstance().format(item.value);
105 }
106 // format number of packages
107 else
108 {
109 var count = parseInt(item.value);
110 var langk = 'VAP_N_ORDERS';
111
112 // format label by fetching singular/plural form
113 if (count == 1) {
114 label = Joomla.JText._(langk + '_1');
115 } else {
116 label = Joomla.JText._(langk).replace(/%d/, count);
117 }
118 }
119
120 if (packName) {
121 label = packName + ': ' + label;
122 }
123
124 return ' ' + label;
125 }
126
127 // in case the chart already exists and the config type doesn't match, unset the cached chart, because
128 // we have to manually recreate it, as the chart type have been changed from line to bar or viceversa
129 if (PACKAGES_ITEMS_CHARTS.hasOwnProperty(id) && PACKAGES_ITEMS_CHARTS[id].config.type != config.chart) {
130 PACKAGES_ITEMS_CHARTS[id].destroy();
131 delete PACKAGES_ITEMS_CHARTS[id];
132 }
133
134 // prepare chart data
135 var chartData = {
136 labels: Object.keys(data),
137 datasets: {},
138 };
139
140 // iterate all dates
141 $.each(data, (lbl, packages) => {
142 // iterate all packages
143 $.each(packages, (id_package, pack) => {
144 // check whether we already created the dataset for this package
145 if (!chartData.datasets.hasOwnProperty(id_package)) {
146 if (config.chart == 'bar') {
147 // nope, init data set
148 chartData.datasets[id_package] = {
149 // the label string that appears when hovering the mouse above the lines intersection points
150 label: pack.name,
151 // the background color drawn behind the line (99 = 60% opacity)
152 backgroundColor: "#" + pack.color + "99",
153 // the fill color of the line
154 borderColor: "#" + pack.color,
155 // the line dataset
156 data: [],
157 };
158 } else {
159 // nope, init data set
160 chartData.datasets[id_package] = {
161 // the label string that appears when hovering the mouse above the lines intersection points
162 label: pack.name,
163 // the background color drawn behind the line (33 = 20% opacity)
164 backgroundColor: "#" + pack.color + "33",
165 // the fill color of the line
166 borderColor: "#" + pack.color,
167 // the fill color of the points
168 pointBackgroundColor: "#" + pack.color,
169 // the border color of the points
170 pointBorderColor: "#fff",
171 // the radius of the points (in pixel)
172 pointRadius: 4,
173 // the fill color of the points when hovered
174 pointHoverBackgroundColor: "#fff",
175 // the border color of the points when hovered
176 pointHoverBorderColor: "#" + pack.color,
177 // the radius of the points (in pixel) when hovered
178 pointHoverRadius: 5,
179 // the line dataset
180 data: [],
181 };
182 }
183 }
184
185 // include value within the dataset
186 chartData.datasets[id_package].data.push(pack[config.valuetype]);
187 });
188 });
189
190 // convert datasets into a linear array
191 chartData.datasets = Object.values(chartData.datasets);
192
193 // init chart from scratch if NULL
194 if (!PACKAGES_ITEMS_CHARTS.hasOwnProperty(id)) {
195 // prepare chart configuration
196 var options = {
197 legend: {
198 // draw legend only if explicitly requested
199 display: <?php echo !empty($legend) ? 'true' : 'false'; ?>,
200 },
201 // axes handling
202 scales: {
203 // Y Axis properties
204 yAxes: [{
205 // make sure the chart starts at 0
206 ticks: {
207 // format value as currency
208 callback: formatAxisY,
209 beginAtZero: true,
210 },
211 }],
212 },
213 // tooltip handling
214 tooltips: {
215 // tooltip callbacks are used to customize default texts
216 callbacks: {
217 // format the tooltip text displayed when hovering a point
218 label: formatPointTooltip,
219 // change label colors because, by default, the legend background is blank
220 labelColor: (tooltipItem, chart) => {
221 // get tooltip item meta data
222 var meta = chart.data.datasets[tooltipItem.datasetIndex];
223
224 return {
225 // use white border
226 borderColor: 'rgb(0,0,0)',
227 // use same item background color
228 backgroundColor: meta.borderColor,
229 };
230 },
231 },
232 },
233 animation: {
234 // unset duration in case we are exporting the chart
235 duration: <?php echo isset($data) ? 0 : 1000; ?>,
236 },
237 };
238
239 // get 2D canvas for LINE chart
240 var canvas = $(widget).find('canvas')[0];
241 var ctx = canvas.getContext('2d');
242
243 // init chart from scratch if undefined
244 PACKAGES_ITEMS_CHARTS[id] = new Chart(ctx, {
245 type: config.chart,
246 data: chartData,
247 options: options,
248 });
249 }
250 // otherwise update labels and values
251 else {
252 // update chart data
253 PACKAGES_ITEMS_CHARTS[id].data = chartData;
254
255 // update format callbacks
256 PACKAGES_ITEMS_CHARTS[id].options.scales.yAxes[0].ticks.callback = formatAxisY;
257 PACKAGES_ITEMS_CHARTS[id].options.tooltips.callbacks.label = formatPointTooltip;
258
259 // refresh chart
260 PACKAGES_ITEMS_CHARTS[id].update();
261 }
262 }
263
264 <?php
265
266 ////////////////////////
267 ///// EXPORT UTILS /////
268 ////////////////////////
269
270 if (isset($data))
271 {
272 ?>
273 $(function() {
274 // auto-invoke callback on page loading completion
275 WIDGET_CALLBACKS[<?php echo $widget->getID(); ?>](
276 // exported assume, there should be only one widget displayed
277 $('#packages-items-chart-<?php echo $widget->getID(); ?>'),
278 // JSON-encode the passed chart data
279 <?php echo json_encode($data); ?>,
280 // JSON-encode the widget configuration
281 <?php echo json_encode($widget->getOptions()); ?>
282 );
283 });
284 <?php
285 }
286 ?>
287 })(jQuery);
288
289 </script>
290