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 / views / analytics / tmpl / default_customers_toolbar.php
vikappointments / admin / views / analytics / tmpl Last commit date
default.php 2 days ago default_customers_toolbar.php 2 days ago index.html 2 days ago
default_customers_toolbar.php
253 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 JHtml::fetch('vaphtml.assets.select2');
15
16 $vik = VAPApplication::getInstance();
17
18 JText::script('VAP_ANALYTICS_CUSTOMERS_PLACEHOLDER');
19
20 ?>
21
22 <div class="btn-toolbar" style="height: 32px; font-size: 13px;">
23
24 <input type="hidden" id="vap-users-select" value="" />
25
26 </div>
27
28 <script>
29
30 (function($) {
31 'use strict';
32
33 /**
34 * A queue of requests.
35 *
36 * @var array
37 */
38 let CUSTOMERS_UPDATE_QUEUE = [];
39
40 /**
41 * Create private callback to fetch the users previously stored within
42 * the session storage of the browser.
43 *
44 * @return object A lookup of users.
45 */
46 const fetchSelectedUsers = () => {
47 let json = null;
48
49 if (typeof sessionStorage !== 'undefined') {
50 // get previously saved users from session storage
51 json = sessionStorage.getItem('analytics.customers.records');
52 }
53
54 if (json) {
55 try {
56 return JSON.parse(json);
57 } catch (err) {
58 // do nothing
59 }
60 }
61
62 return {};
63 }
64
65 /**
66 * Create private callback to store the selected users within the
67 * session storage of the browser.
68 *
69 * @param mixed users Either an array or a comma-separated list.
70 *
71 * @return void
72 */
73 const storeSelectedUsers = (users) => {
74 if (typeof users === 'string') {
75 users = users.split(',');
76 }
77
78 let map = {};
79
80 users.forEach((u) => {
81 if (USERS_LOOKUP.hasOwnProperty(u)) {
82 map[u] = USERS_LOOKUP[u];
83 }
84 });
85
86 if (typeof sessionStorage !== 'undefined') {
87 // save selected users within the session storage
88 sessionStorage.setItem('analytics.customers.records', JSON.stringify(map));
89 }
90 }
91
92 /**
93 * Flag used to check whether the update thread is running or not.
94 *
95 * @var boolean
96 */
97 let isUpdateQueueRunning = false;
98
99 /**
100 * Create private callback to process the internal queue, needed to save
101 * the configuration of the widgets one by one to avoid session conflicts.
102 *
103 * @return void
104 */
105 const processCustomersUpdateQueue = () => {
106 if (CUSTOMERS_UPDATE_QUEUE.length == 0) {
107 // nothing else to process
108 isUpdateQueueRunning = false;
109 return;
110 }
111
112 // register running flag
113 isUpdateQueueRunning = true;
114
115 // remove first element from queue
116 let id = CUSTOMERS_UPDATE_QUEUE.shift();
117
118 $.vapWidgetDo('save', id).then(() => {
119 // widget saved successfully
120 }).catch((err) => {
121 // an error occurred
122 console.error(err);
123 }).finally(() => {
124 // recursively process the next element of the list
125 // once the current one completed its request
126 processCustomersUpdateQueue();
127 });
128 }
129
130 // init with saved users
131 const USERS_LOOKUP = fetchSelectedUsers();
132
133 // render select
134 $(function() {
135 // update select values
136 $('#vap-users-select').val(Object.keys(USERS_LOOKUP));
137
138 $('#vap-users-select').select2({
139 placeholder: Joomla.JText._('VAP_ANALYTICS_CUSTOMERS_PLACEHOLDER'),
140 allowClear: true,
141 width: '100%',
142 minimumInputLength: 2,
143 multiple: true,
144 ajax: {
145 url: '<?php echo $vik->ajaxUrl('index.php?option=com_vikappointments&task=customer.users'); ?>',
146 dataType: 'json',
147 type: 'POST',
148 quietMillis: 50,
149 data: (term) => {
150 return {
151 term: term
152 };
153 },
154 results: (data) => {
155 return {
156 results: $.map(data, (item) => {
157 // always refresh lookup
158 USERS_LOOKUP[item.id] = item.billing_name;
159
160 return {
161 text: item.text || item.billing_name,
162 id: item.id,
163 };
164 }),
165 };
166 },
167 },
168 initSelection: (element, callback) => {
169 var data = [];
170
171 // init selection with stored users
172 $.each(USERS_LOOKUP, (id, text) => {
173 data.push({id: id, text: text});
174 });
175
176 callback(data);
177 },
178 formatSelection: (data) => {
179 if ($.isEmptyObject(data.billing_name)) {
180 // display data returned from ajax parsing
181 return data.text;
182 }
183 // display pre-selected value
184 return data.billing_name;
185 },
186 });
187
188 // save selected users on change and notify all the widgets
189 $('#vap-users-select').on('change', function() {
190 // get users from select
191 const users = $(this).val();
192
193 // store users
194 storeSelectedUsers(users);
195
196 // iterate all widget forms
197 $('.inspector-fieldset').each(function() {
198 let id = $(this).data('id');
199 let widget = $(this).data('widget');
200
201 // make sure the input exists
202 const input = $(this).find('input[name="' + widget + '_' + id + '_customers"]');
203
204 if (input.length) {
205 // update "customers" parameter with selected users
206 input.val(users);
207
208 // Refresh widget contents without saving the configuration, because
209 // we noticed a strange behavior while refreshing the contents of several
210 // widgets simultaneously. Maybe the session update faced a conflict with
211 // other widgets that were saving the state at the same time.
212 $.vapWidgetDo('refresh', id);
213
214 // For this reason, we need to create a queue able to save the widgets one
215 // by one in background.
216 CUSTOMERS_UPDATE_QUEUE.push(id);
217 }
218 });
219
220 // Process update query once all the widgets have been refreshed.
221 // Do not call the queue in case the thread is already running.
222 if (!isUpdateQueueRunning) {
223 processCustomersUpdateQueue();
224 }
225 });
226
227 // When the document is ready, look for any widgets with "customers" field
228 // different than the globally selected ones.
229 // This might occur while creating a new widget with the global customers
230 // field already filled in.
231 let keys = Object.keys(USERS_LOOKUP).join(',');
232
233 // iterate all widget forms
234 $('.inspector-fieldset').each(function() {
235 let id = $(this).data('id');
236 let widget = $(this).data('widget');
237
238 // make sure the input exists
239 const input = $(this).find('input[name="' + widget + '_' + id + '_customers"]');
240
241 // check whether the value is different
242 if (input.length && keys != input.val()) {
243 // update "customers" parameter with selected users
244 input.val(keys);
245
246 // NOTE: we do not need to manually refresh the widget contents because this
247 // block of code should be executed before loading data of all the widgets.
248 }
249 });
250 });
251 })(jQuery);
252
253 </script>