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