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