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 / customers / appointments / chart.php
vikappointments / admin / layouts / statistics / widgets / customers / appointments Last commit date
chart.php 5 days ago index.html 5 days ago weekdays.php 5 days ago
chart.php
286 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 */
20 extract($displayData);
21
22 // include chart JS dependencies
23 JHtml::fetch('vaphtml.assets.chartjs');
24
25 // get list of preset colors
26 $colors = JHtml::fetch('vaphtml.color.preset', $list = true, $group = false);
27
28 JText::script('VAP_N_RESERVATIONS');
29 JText::script('VAP_N_RESERVATIONS_1');
30
31 ?>
32
33 <div class="canvas-align-bottom customers-appointments-chart">
34 <canvas></canvas>
35 </div>
36
37 <div class="no-results" style="display:none;">
38 <?php echo VAPApplication::getInstance()->alert(JText::translate('JGLOBAL_NO_MATCHING_RESULTS')); ?>
39 </div>
40
41 <script>
42
43 /**
44 * Defines a pool of charts, if undefined.
45 *
46 * @var object
47 */
48 if (typeof CUSTOMERS_APPOINTMENTS_CHARTS === 'undefined') {
49 var CUSTOMERS_APPOINTMENTS_CHARTS = {};
50 }
51
52 // init callbacks pool in case the caller didn't declare it
53 if (typeof WIDGET_CALLBACKS === 'undefined') {
54 var WIDGET_CALLBACKS = {};
55 }
56
57 (function($) {
58 'use strict';
59
60 // get default system preset of colors
61 const colorsPreset = <?php echo json_encode($colors); ?>;
62
63 /**
64 * Register callback to be executed after
65 * completing the update request.
66 *
67 * @param mixed widget The widget selector.
68 * @param mixed data The AJAX response.
69 * @param object config The widget configuration.
70 *
71 * @return void
72 */
73 WIDGET_CALLBACKS[<?php echo $widget->getID(); ?>] = (widget, data, config) => {
74 // get widget ID
75 var id = $(widget).attr('id');
76
77 if (!data) {
78 // show "no results" box in case of empty list
79 $(widget).find('.customers-appointments-chart').hide();
80 $(widget).find('.no-results').show();
81
82 // do not need to go ahead
83 return false;
84 }
85
86 // hide "no results" box if there is at least a status
87 $(widget).find('.no-results').hide();
88 $(widget).find('.customers-appointments-chart').show();
89
90 // start from first position of the preset
91 let colorIndex = 0;
92
93 // in case the chart already exists and the config type doesn't match, unset the cached chart, because
94 // we have to manually recreate it, as the chart type have been changed from line to bar or viceversa
95 if (CUSTOMERS_APPOINTMENTS_CHARTS.hasOwnProperty(id) && CUSTOMERS_APPOINTMENTS_CHARTS[id].config.type != config.chart) {
96 CUSTOMERS_APPOINTMENTS_CHARTS[id].destroy();
97 delete CUSTOMERS_APPOINTMENTS_CHARTS[id];
98 }
99
100 // prepare chart data
101 var chartData = {
102 labels: Object.keys(data),
103 datasets: {},
104 };
105
106 let suggestedMax = 0;
107
108 // iterate all dates
109 $.each(data, (lbl, customers) => {
110 // iterate all coupons
111 $.each(customers, (id_user, customer) => {
112 // check whether we already created the dataset for this coupon code
113 if (!chartData.datasets.hasOwnProperty(id_user)) {
114 // get progressive color
115 let color = colorsPreset[colorIndex++ % colorsPreset.length];
116
117 if (config.chart == 'bar') {
118 // nope, init data set
119 chartData.datasets[id_user] = {
120 // the label string that appears when hovering the mouse above the lines intersection points
121 label: customer.name,
122 // the background color drawn behind the line (99 = 60% opacity)
123 backgroundColor: "#" + color + "99",
124 // the fill color of the line
125 borderColor: "#" + color,
126 // the line dataset
127 data: [],
128 };
129 } else {
130 chartData.datasets[id_user] = {
131 // the label string that appears when hovering the mouse above the lines intersection points
132 label: customer.name,
133 // the background color drawn behind the line (33 = 20% opacity)
134 backgroundColor: "#" + color + "33",
135 // the fill color of the line
136 borderColor: "#" + color,
137 // the fill color of the points
138 pointBackgroundColor: "#" + color,
139 // the border color of the points
140 pointBorderColor: "#fff",
141 // the radius of the points (in pixel)
142 pointRadius: 4,
143 // the fill color of the points when hovered
144 pointHoverBackgroundColor: "#fff",
145 // the border color of the points when hovered
146 pointHoverBorderColor: "#" + color,
147 // the radius of the points (in pixel) when hovered
148 pointHoverRadius: 5,
149 // the line dataset
150 data: [],
151 };
152 }
153 }
154
155 // include value within the dataset
156 chartData.datasets[id_user].data.push(customer.total);
157
158 // take maximum amount
159 suggestedMax = Math.max(suggestedMax, parseInt(customer.total) + 1);
160 });
161 });
162
163 // convert datasets into a linear array
164 chartData.datasets = Object.values(chartData.datasets);
165
166 // init chart from scratch if NULL
167 if (!CUSTOMERS_APPOINTMENTS_CHARTS.hasOwnProperty(id)) {
168 // prepare chart configuration
169 var options = {
170 legend: {
171 // draw legend only if explicitly requested
172 display: <?php echo !empty($legend) ? 'true' : 'false'; ?>,
173 },
174 // axes handling
175 scales: {
176 // Y Axis properties
177 yAxes: [{
178 ticks: {
179 // make sure the chart starts at 0
180 beginAtZero: true,
181 // ignore real numbers
182 precision: 0,
183 // increase ceil by one for a better readability
184 suggestedMax: suggestedMax,
185 },
186 }],
187 },
188 // tooltip handling
189 tooltips: {
190 // tooltip callbacks are used to customize default texts
191 callbacks: {
192 // format the tooltip text displayed when hovering a point
193 label: (item, data) => {
194 // extract customer name from dataset
195 let customer = data.datasets[item.datasetIndex].label;
196
197 let label = '';
198
199 var count = parseInt(item.value);
200 var langk = 'VAP_N_RESERVATIONS';
201
202 // format label by fetching singular/plural form
203 if (count == 1) {
204 label = Joomla.JText._(langk + '_1');
205 } else {
206 label = Joomla.JText._(langk).replace(/%d/, count);
207 }
208
209 if (customer) {
210 label = customer + ': ' + label;
211 }
212
213 // create label in the form "COUPON: TOTAL"
214 return ' ' + label;
215 },
216 // change label colors because, by default, the legend background is blank
217 labelColor: (tooltipItem, chart) => {
218 // get tooltip item meta data
219 var meta = chart.data.datasets[tooltipItem.datasetIndex];
220
221 return {
222 // use white border
223 borderColor: 'rgb(0,0,0)',
224 // use same item background color
225 backgroundColor: meta.borderColor,
226 };
227 },
228 },
229 },
230 animation: {
231 // unset duration in case we are exporting the chart
232 duration: <?php echo isset($data) ? 0 : 1000; ?>,
233 },
234 };
235
236 // get 2D canvas for LINE chart
237 var canvas = $(widget).find('canvas')[0];
238 var ctx = canvas.getContext('2d');
239
240 // init chart from scratch if undefined
241 CUSTOMERS_APPOINTMENTS_CHARTS[id] = new Chart(ctx, {
242 type: config.chart,
243 data: chartData,
244 options: options,
245 });
246 }
247 // otherwise update labels and values
248 else {
249 // update chart data
250 CUSTOMERS_APPOINTMENTS_CHARTS[id].data = chartData;
251
252 // refresh suggested max
253 CUSTOMERS_APPOINTMENTS_CHARTS[id].options.scales.yAxes[0].ticks.suggestedMax = suggestedMax;
254
255 // refresh chart
256 CUSTOMERS_APPOINTMENTS_CHARTS[id].update();
257 }
258 }
259
260 <?php
261
262 ////////////////////////
263 ///// EXPORT UTILS /////
264 ////////////////////////
265
266 if (isset($data))
267 {
268 ?>
269 $(function() {
270 // auto-invoke callback on page loading completion
271 WIDGET_CALLBACKS[<?php echo $widget->getID(); ?>](
272 // exported assume, there should be only one widget displayed
273 $('.customers-appointments-chart'),
274 // JSON-encode the passed chart data
275 <?php echo json_encode($data); ?>,
276 // JSON-encode the widget configuration
277 <?php echo json_encode($widget->getOptions()); ?>
278 );
279 });
280 <?php
281 }
282 ?>
283 })(jQuery);
284
285 </script>
286