default.php
4 days ago
default_calendar.php
4 days ago
default_filters.php
4 days ago
default_reservations.php
4 days ago
default_timeline.php
4 days ago
index.html
4 days ago
default_timeline.php
349 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 | $vik = VAPApplication::getInstance(); |
| 15 | |
| 16 | ?> |
| 17 | |
| 18 | <div class="vaptimeline" id="vaptimeline"> |
| 19 | |
| 20 | </div> |
| 21 | |
| 22 | <div class="vaptimeline-hover-tip" style="display:none;"> |
| 23 | <i class="fas fa-life-ring"></i> |
| 24 | <?php echo JText::translate('VAPTIMELINEHOVERTIP'); ?> |
| 25 | </div> |
| 26 | |
| 27 | <?php |
| 28 | JText::script('VAPCONNECTIONLOSTERROR'); |
| 29 | JText::script('VAP_N_PEOPLE_1'); |
| 30 | JText::script('VAP_N_PEOPLE'); |
| 31 | JText::script('VAPSTATUSCLOSURE'); |
| 32 | ?> |
| 33 | |
| 34 | <script> |
| 35 | |
| 36 | /* GROUP INFO EVENTS */ |
| 37 | |
| 38 | var SLOT_HOVER_TIMEOUT = null; |
| 39 | var SLOT_CURRENT_TARGET = null; |
| 40 | var SLOT_AJAX_HANDLE = null; |
| 41 | |
| 42 | jQuery(function($) { |
| 43 | // attempt to refresh timeline |
| 44 | vapGetTimeline(); |
| 45 | |
| 46 | // iterate each RED block to support view details action |
| 47 | $('#vaptimeline').on('click', '.vaptlblock0', function() { |
| 48 | vapViewDetails($(this).data('hour'), $(this).data('min'), this); |
| 49 | }); |
| 50 | |
| 51 | // implement hover/leave events for time slots with a number of seats |
| 52 | $('#vaptimeline').on('mouseenter mouseleave', '.vap-timeline-block[data-seats]', function(event) { |
| 53 | let seats = $(this).data('seats'); |
| 54 | |
| 55 | if (isNaN(seats) || !seats) { |
| 56 | return false; |
| 57 | } |
| 58 | |
| 59 | if (event.type == 'mouseenter') { |
| 60 | // register timer to load time slots |
| 61 | SLOT_HOVER_TIMEOUT = setTimeout(() => { |
| 62 | if (this == SLOT_CURRENT_TARGET) { |
| 63 | return; |
| 64 | } |
| 65 | |
| 66 | openSmartOverlay(this); |
| 67 | }, 1000); |
| 68 | } else { |
| 69 | // reset slot loader |
| 70 | clearTimeout(SLOT_HOVER_TIMEOUT); |
| 71 | } |
| 72 | }); |
| 73 | |
| 74 | $(window).on('beforeunload click', () => { |
| 75 | // close overlay in order to safely abort any pending |
| 76 | // AJAX request before leaving the page |
| 77 | closeSmartOverlay(); |
| 78 | }); |
| 79 | |
| 80 | $(window).on('resize', function() { |
| 81 | calculateOverlayPosition($('.smart-overlay')); |
| 82 | }); |
| 83 | }); |
| 84 | |
| 85 | function vapGetTimeline(date) { |
| 86 | if (typeof date === 'undefined') { |
| 87 | // recover stored date |
| 88 | date = jQuery('#vapdayselected').val(); |
| 89 | |
| 90 | if (!date) { |
| 91 | // no selected date |
| 92 | return false; |
| 93 | } |
| 94 | |
| 95 | // auto-select clicked cell |
| 96 | jQuery('.vaptdday[data-day="' + date + '"]').addClass('vaptdselected'); |
| 97 | } else { |
| 98 | // register selected date |
| 99 | jQuery('#vapdayselected').val(date); |
| 100 | } |
| 101 | |
| 102 | // prepare timeline data |
| 103 | let data = { |
| 104 | day: date, |
| 105 | id_emp: <?php echo (int) $this->filters['id_emp']; ?>, |
| 106 | id_ser: <?php echo (int) $this->filters['id_ser']; ?>, |
| 107 | }; |
| 108 | |
| 109 | new Promise((resolve, reject) => { |
| 110 | UIAjax.do( |
| 111 | '<?php echo $vik->ajaxUrl('index.php?option=com_vikappointments&task=findreservation.timelineajax'); ?>', |
| 112 | data, |
| 113 | (resp) => { |
| 114 | resolve(resp); |
| 115 | }, |
| 116 | (err) => { |
| 117 | reject(err); |
| 118 | } |
| 119 | ); |
| 120 | }).then((resp) => { |
| 121 | // file timeline with fetched HTML |
| 122 | jQuery('#vaptimeline').html(resp.html); |
| 123 | // load appointments list on the selected date |
| 124 | getReservationsOnDay(date); |
| 125 | }).catch((err) => { |
| 126 | // display response error message |
| 127 | jQuery('#vaptimeline').html(err.responseText); |
| 128 | // unset selected date |
| 129 | jQuery('#vapdayselected').val(''); |
| 130 | }).finally(() => { |
| 131 | // animate only in case the timeline is not visible |
| 132 | var px_to_scroll = isBoxOutOfMonitor(jQuery('#vaptimeline'), 60); |
| 133 | |
| 134 | if (px_to_scroll !== false) { |
| 135 | jQuery('html,body').animate({scrollTop: "+=" + px_to_scroll}, {duration:'normal'}); |
| 136 | } |
| 137 | |
| 138 | // count slots with number of seats |
| 139 | let count = jQuery('.vap-timeline-block[data-seats]'); |
| 140 | |
| 141 | if (count.length) { |
| 142 | // display "hover" label |
| 143 | jQuery('.vaptimeline-hover-tip').show(); |
| 144 | } else { |
| 145 | // hide "hover" label |
| 146 | jQuery('.vaptimeline-hover-tip').hide(); |
| 147 | } |
| 148 | }); |
| 149 | } |
| 150 | |
| 151 | function vapTimeClicked(hour, min, slot) |
| 152 | { |
| 153 | // inject selected hours and minutes |
| 154 | document.adminForm.hour.value = hour; |
| 155 | document.adminForm.min.value = min; |
| 156 | |
| 157 | // check whether the employee ID is selected |
| 158 | if (!jQuery('select[name="id_emp"]').val()) { |
| 159 | // attempt to find parent employee ID |
| 160 | const parent = jQuery(slot).closest('.vaptimeline-empblock[data-id]'); |
| 161 | |
| 162 | if (parent.length) { |
| 163 | // overwrite employee ID with the clicked one |
| 164 | jQuery('#adminForm').append('<input type="hidden" name="id_emp" value="' + parent.data('id') + '" />'); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | // submit task |
| 169 | Joomla.submitbutton('reservation.add'); |
| 170 | } |
| 171 | |
| 172 | function vapViewDetails(hour, min, slot) { |
| 173 | let id_emp = parseInt(jQuery('select[name="id_emp"]').val()); |
| 174 | |
| 175 | // check whether the employee ID is selected |
| 176 | if (isNaN(id_emp) || !id_emp) { |
| 177 | // attempt to find parent employee ID |
| 178 | id_emp = jQuery(slot).closest('.vaptimeline-empblock[data-id]').data('id'); |
| 179 | } |
| 180 | |
| 181 | let date = jQuery('#vapdayselected').val(); |
| 182 | |
| 183 | UIAjax.do( |
| 184 | '<?php echo $vik->ajaxUrl('index.php?option=com_vikappointments&task=findreservation.appointmentsajax'); ?>', |
| 185 | { |
| 186 | id_emp: id_emp, |
| 187 | date: date, |
| 188 | hour: hour, |
| 189 | min: min, |
| 190 | }, |
| 191 | (resp) => { |
| 192 | // map records to obtain only a list of reservations ID |
| 193 | let ids = resp.map((elem) => { return elem.id; }); |
| 194 | |
| 195 | // display modal |
| 196 | displayDetailsView(ids); |
| 197 | }, |
| 198 | (err) => { |
| 199 | alert(err.responseText || Joomla.JText._('VAPCONNECTIONLOSTERROR')); |
| 200 | } |
| 201 | ); |
| 202 | } |
| 203 | |
| 204 | function openSmartOverlay(slot) { |
| 205 | closeSmartOverlay(); |
| 206 | |
| 207 | SLOT_CURRENT_TARGET = slot; |
| 208 | |
| 209 | var html = '<div class="smart-overlay-loading">\n'+ |
| 210 | '<i class="fas fa-circle-notch fa-spin fa-3x" style="font-size: 32px;"></i>\n'+ |
| 211 | '</div>'; |
| 212 | |
| 213 | jQuery('#adminForm').append('<div class="smart-overlay">' + html + '</div>'); |
| 214 | |
| 215 | var overlay = jQuery('.smart-overlay'); |
| 216 | |
| 217 | calculateOverlayPosition(overlay); |
| 218 | |
| 219 | overlay.on('click', (event) => { |
| 220 | event.preventDefault(); |
| 221 | event.stopPropagation(); |
| 222 | }); |
| 223 | |
| 224 | loadSmartOverlayData(overlay); |
| 225 | } |
| 226 | |
| 227 | function calculateOverlayPosition(overlay) { |
| 228 | if (!SLOT_CURRENT_TARGET) { |
| 229 | return; |
| 230 | } |
| 231 | |
| 232 | var offset = jQuery(SLOT_CURRENT_TARGET).offset(); |
| 233 | |
| 234 | var left = offset.left; |
| 235 | |
| 236 | if (left + overlay.width() >= jQuery(window).width() - 5) { |
| 237 | left = jQuery(window).width() - overlay.width() - 5; |
| 238 | } |
| 239 | |
| 240 | overlay.css('top', (offset.top - overlay.height() - 5) + 'px'); |
| 241 | overlay.css('left', left + 'px'); |
| 242 | } |
| 243 | |
| 244 | function loadSmartOverlayData(overlay) { |
| 245 | var id_emp = <?php echo (int) $this->filters['id_emp']; ?>; |
| 246 | |
| 247 | if (!id_emp) { |
| 248 | // fallback to obtain the employee ID from the timeline |
| 249 | id_emp = jQuery(SLOT_CURRENT_TARGET).closest('.vaptimeline-empblock').data('id'); |
| 250 | } |
| 251 | |
| 252 | var date = jQuery('#vapdayselected').val(); |
| 253 | var hour = jQuery(SLOT_CURRENT_TARGET).data('hour'); |
| 254 | var min = jQuery(SLOT_CURRENT_TARGET).data('min'); |
| 255 | |
| 256 | // get cached record, if any |
| 257 | var cache = VAPTempCache.get([id_emp, date, hour, min]); |
| 258 | |
| 259 | if (cache) { |
| 260 | // fill smart overlay with cached data |
| 261 | fillSmartOverlay(cache, overlay); |
| 262 | return; |
| 263 | } |
| 264 | |
| 265 | SLOT_AJAX_HANDLE = UIAjax.do( |
| 266 | '<?php echo $vik->ajaxUrl('index.php?option=com_vikappointments&task=findreservation.appointmentsajax'); ?>', |
| 267 | { |
| 268 | id_emp: id_emp, |
| 269 | date: date, |
| 270 | hour: hour, |
| 271 | min: min, |
| 272 | }, |
| 273 | (resp) => { |
| 274 | fillSmartOverlay(resp, overlay); |
| 275 | // cache result for later use |
| 276 | VAPTempCache.set([id_emp, date, hour, min], resp); |
| 277 | }, |
| 278 | (err) => { |
| 279 | if (err.statusText != 'abort') { |
| 280 | console.log(err, err.responseText); |
| 281 | alert(Joomla.JText._('VAPCONNECTIONLOSTERROR')); |
| 282 | } |
| 283 | |
| 284 | // close overlay on error |
| 285 | closeSmartOverlay(); |
| 286 | } |
| 287 | ); |
| 288 | } |
| 289 | |
| 290 | function fillSmartOverlay(obj, overlay) { |
| 291 | // reset overlay HTML |
| 292 | overlay.html(''); |
| 293 | |
| 294 | var _guest = Joomla.JText._('VAP_N_PEOPLE_1'); |
| 295 | var _guests = Joomla.JText._('VAP_N_PEOPLE'); |
| 296 | |
| 297 | obj.forEach((app) => { |
| 298 | // create overlay record |
| 299 | let record = jQuery('<div class="overlay-record"></div>'); |
| 300 | |
| 301 | // create record ID column |
| 302 | let recordId = jQuery('<div class="record-column col-id"></div>').html('#' + app.id); |
| 303 | record.append(recordId); |
| 304 | |
| 305 | // create custom name column |
| 306 | let recordCust = jQuery('<div class="record-column col-name"></div>'); |
| 307 | if (app.closure == 1) { |
| 308 | recordCust.html(Joomla.JText._('VAPSTATUSCLOSURE').toUpperCase()); |
| 309 | } else { |
| 310 | recordCust.html(app.purchaser_nominative || app.purchaser_phone || app.purchaser_mail); |
| 311 | } |
| 312 | record.append(recordCust); |
| 313 | |
| 314 | // create last column |
| 315 | let recordThird = jQuery('<div class="record-column col-count"></div>'); |
| 316 | |
| 317 | if (app.id_service == <?php echo $this->filters['id_ser']; ?>) { |
| 318 | // same service, display number of people |
| 319 | if (app.people > 1) { |
| 320 | recordThird.html(_guests.replace(/%d/, app.people)); |
| 321 | } else { |
| 322 | recordThird.html(_guest); |
| 323 | } |
| 324 | } else { |
| 325 | // display service name |
| 326 | recordThird.html(app.service_name); |
| 327 | } |
| 328 | |
| 329 | record.append(recordThird); |
| 330 | |
| 331 | // append record to averlay |
| 332 | overlay.append(record); |
| 333 | }); |
| 334 | |
| 335 | calculateOverlayPosition(overlay); |
| 336 | } |
| 337 | |
| 338 | function closeSmartOverlay() { |
| 339 | jQuery('.smart-overlay').remove(); |
| 340 | SLOT_CURRENT_TARGET = null; |
| 341 | |
| 342 | if (SLOT_AJAX_HANDLE) { |
| 343 | SLOT_AJAX_HANDLE.abort(); |
| 344 | SLOT_AJAX_HANDLE = null; |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | </script> |
| 349 |