From 556a107276ff7889578f3470037d3f90d9084dec Mon Sep 17 00:00:00 2001
From: Robert Lange <robert.lange@uni-leipzig.de>
Date: Tue, 15 Feb 2022 14:28:30 +0100
Subject: [PATCH] refs #21313 [finc] consolidate count items in menu

* GetAdditionalAccountInfo:
** dont fetch fines - sum up is already done by account_ajax
** use GET instead of POST request
* account_ajax: add finc override and itemCount; remove tooltips / titles
* remove obsolete script in menu.phtml
---
 .../AjaxHandler/GetAdditionalAccountInfo.php  |  10 +-
 themes/finc/js/account_ajax.js                | 280 ++++++++++++++++++
 themes/finc/templates/myresearch/menu.phtml   |  25 --
 3 files changed, 286 insertions(+), 29 deletions(-)
 create mode 100644 themes/finc/js/account_ajax.js

diff --git a/module/finc/src/finc/AjaxHandler/GetAdditionalAccountInfo.php b/module/finc/src/finc/AjaxHandler/GetAdditionalAccountInfo.php
index 5d6f7c179ba..0554c25de2d 100644
--- a/module/finc/src/finc/AjaxHandler/GetAdditionalAccountInfo.php
+++ b/module/finc/src/finc/AjaxHandler/GetAdditionalAccountInfo.php
@@ -30,7 +30,7 @@ namespace finc\AjaxHandler;
 use Zend\Mvc\Controller\Plugin\Params;
 
 /**
- * "Get Additional Account Info" AJAX handler
+ * AJAX handler to count items for view
  *
  * Get additional account infos
  *
@@ -56,7 +56,7 @@ class GetAdditionalAccountInfo extends \VuFind\AjaxHandler\AbstractIlsAndUserAct
     {
         $this->disableSessionWrites();  // avoid session write timing bug
         // collect data for views to be counted
-        $viewsToCount = $params->fromPost('views', $params->fromQuery('views'));
+        $viewsToCount = $params()->fromQuery('views');
 
         try {
             $patron = $this->ilsAuthenticator->storedCatalogLogin();
@@ -65,12 +65,14 @@ class GetAdditionalAccountInfo extends \VuFind\AjaxHandler\AbstractIlsAndUserAct
                     $viewsToCount,
                     $patron
                 );
-
+                
+                /* #21313 use vufind code of account_ajax.js for fines
                 $countFines = $this->ils->getFinesTotal(
                     $patron
                 );
+                */
 
-                return $this->formatResponse(compact('countViewItems','countFines'));
+                return $this->formatResponse(compact('countViewItems'));
             }
         } catch (\Exception $e) {
             // Do nothing -- just fail through to the error message below.
diff --git a/themes/finc/js/account_ajax.js b/themes/finc/js/account_ajax.js
new file mode 100644
index 00000000000..c789c5f3586
--- /dev/null
+++ b/themes/finc/js/account_ajax.js
@@ -0,0 +1,280 @@
+/*global userIsLoggedIn, VuFind */
+VuFind.register('account', function Account() {
+  // Retrieved statuses
+  var LOADING = -1 * Math.PI; // waiting for request
+  var MISSING = -2 * Math.PI; // no data available
+  var INACTIVE = -3 * Math.PI; // status element missing
+  var _statuses = {};
+
+  // Account Icons
+  var ICON_LEVELS = {
+    "NONE": 0,
+    "GOOD": 1,
+    "WARNING": 2,
+    "DANGER": 3
+  };
+  var _accountIcons = {};
+  _accountIcons[ICON_LEVELS.NONE] = "fa fa-user-circle";
+  _accountIcons[ICON_LEVELS.GOOD] = "fa fa-bell text-success";
+  _accountIcons[ICON_LEVELS.WARNING] = "fa fa-bell text-warning";
+  _accountIcons[ICON_LEVELS.DANGER] = "fa fa-exclamation-triangle text-danger";
+
+  var _submodules = [];
+
+  var _sessionDataPrefix = "vf-account-status-";
+  var _save = function _save(module) {
+    sessionStorage.setItem(
+      _sessionDataPrefix + module,
+      JSON.stringify(_statuses[module])
+    );
+  };
+
+  // Clearing save forces AJAX update next page load
+  var clearCache = function clearCache(name) {
+    if (typeof name === "undefined") {
+      for (var sub in _submodules) {
+        if (Object.prototype.hasOwnProperty.call(_submodules, sub)) {
+          clearCache(sub);
+        }
+      }
+    } else {
+      sessionStorage.removeItem(_sessionDataPrefix + name);
+    }
+  };
+
+  var _getStatus = function _getStatus(module) {
+    return (typeof _statuses[module] === "undefined") ? LOADING : _statuses[module];
+  };
+
+  var _render = function _render() {
+    var accountStatus = ICON_LEVELS.NONE;
+    for (var sub in _submodules) {
+      if (Object.prototype.hasOwnProperty.call(_submodules, sub)) {
+        var $element = $(_submodules[sub].selector);
+        if (!$element) {
+          _statuses[sub] = INACTIVE;
+          continue;
+        }
+        var status = _getStatus(sub);
+        if (status === MISSING) {
+          $element.addClass('hidden');
+        } else {
+          $element.removeClass('hidden');
+          if (status === LOADING) {
+            $element.html('<i class="fa fa-spin fa-spinner"></i>');
+          } else {
+            var moduleStatus = _submodules[sub].render($element, _statuses[sub], ICON_LEVELS);
+            if (moduleStatus > accountStatus) {
+              accountStatus = moduleStatus;
+            }
+          }
+        }
+      }
+    }
+    $("#account-icon").attr("class", _accountIcons[accountStatus]);
+    if (accountStatus > ICON_LEVELS.NONE) {
+      $("#account-icon")
+        .attr("data-toggle", "tooltip")
+        .attr("data-placement", "bottom")
+        .attr("title", VuFind.translate("account_has_alerts"))
+        .tooltip();
+    } else {
+      $("#account-icon").tooltip("destroy");
+    }
+  };
+  var _ajaxLookup = function _ajaxLookup(module) {
+    $.ajax({
+      url: VuFind.path + '/AJAX/JSON?method=' + _submodules[module].ajaxMethod,
+      dataType: 'json',
+      data: _submodules[module].data || [] /* finc specific */
+    })
+      .done(function ajaxLookupDone(response) {
+        _statuses[module] = response.data;
+      })
+      .fail(function ajaxLookupFail() {
+        _statuses[module] = MISSING;
+      })
+      .always(function ajaxLookupAlways() {
+        _save(module);
+        _render();
+      });
+  };
+
+  var _load = function _load(module) {
+    var $element = $(_submodules[module].selector);
+    if (!$element) {
+      _statuses[module] = INACTIVE;
+    } else {
+      var json = sessionStorage.getItem(_sessionDataPrefix + module);
+      var session = typeof json === "undefined" ? null : JSON.parse(json);
+      if (
+        session === null ||
+        session === LOADING ||
+        session === MISSING
+      ) {
+        _statuses[module] = LOADING;
+        _ajaxLookup(module);
+      } else {
+        _statuses[module] = session;
+      }
+      _render();
+    }
+  };
+
+  var init = function init() {
+    // Update information when certain actions are performed
+    $("#renewals").submit(function clearCheckedOut() {
+      clearCache("checkedOut");
+    });
+    $('#cancelHold, [name="placeHold"]').submit(function clearHolds() {
+      clearCache("holds");
+    });
+    $('#ILLRequestForm, #cancelILLRequest').submit(function clearHolds() {
+      clearCache("illRequests");
+    });
+    $('[name="placeStorageRetrievalRequest"], #cancelStorageRetrievalRequest').submit(function clearStorageRetrievals() {
+      clearCache("storageRetrievalRequests");
+    });
+    $("#library_card").change(function clearChangeLibraryCard() {
+      clearCache(/* all */);
+    });
+  };
+
+  var register = function register(name, module) {
+    if (typeof _submodules[name] === "undefined") {
+      _submodules[name] = typeof module == 'function' ? module() : module;
+    }
+    var $el = $(_submodules[name].selector);
+    if ($el.length > 0) {
+      $el.removeClass("hidden");
+      _statuses[name] = LOADING;
+      _load(name);
+    } else {
+      _statuses[name] = INACTIVE;
+    }
+  };
+
+  return {
+    init: init,
+    clearCache: clearCache,
+    // if user is logged out, clear cache instead of register
+    register: userIsLoggedIn ? register : clearCache
+  };
+});
+
+$(document).ready(function registerAccountAjax() {
+
+  VuFind.account.register("fines", {
+    selector: ".fines-status",
+    ajaxMethod: "getUserFines",
+    render: function render($element, status, ICON_LEVELS) {
+      if (status.value === 0) {
+        $element.addClass("hidden");
+        return ICON_LEVELS.NONE;
+      }
+      $element.html('<span class="badge overdue">' + status.display + '</span>');
+      return ICON_LEVELS.DANGER;
+    }
+  });
+
+  VuFind.account.register("checkedOut", {
+    selector: ".checkedout-status",
+    ajaxMethod: "getUserTransactions",
+    render: function render($element, status, ICON_LEVELS) {
+      var html = '';
+      var level = ICON_LEVELS.NONE;
+      if (status.ok > 0) {
+        html += '<span class="badge ok">' + status.ok + '</span>';
+      }
+      if (status.warn > 0) {
+        html += '<span class="badge warn">' + status.warn + '</span>';
+        level = ICON_LEVELS.WARNING;
+      }
+      if (status.overdue > 0) {
+        html += '<span class="badge overdue">' + status.overdue + '</span>';
+        level = ICON_LEVELS.DANGER;
+      }
+      $element.html(html);
+      return level;
+    }
+  });
+
+  VuFind.account.register("holds", {
+    selector: ".holds-status",
+    ajaxMethod: "getUserHolds",
+    render: function render($element, status, ICON_LEVELS) {
+      var level = ICON_LEVELS.NONE;
+      if (status.available > 0) {
+        $element.html('<i class="fa fa-bell text-success" title="' + VuFind.translate('hold_available') + '"></i>');
+        level = ICON_LEVELS.GOOD;
+      } else if (status.in_transit > 0) {
+        $element.html('<i class="fa fa-clock-o text-warning" title="' + VuFind.translate('request_in_transit') + '"></i>');
+      } else {
+        $element.addClass("holds-status hidden");
+      }
+      return level;
+    }
+  });
+
+  VuFind.account.register("illRequests", {
+    selector: ".illrequests-status",
+    ajaxMethod: "getUserILLRequests",
+    render: function render($element, status, ICON_LEVELS) {
+      var level = ICON_LEVELS.NONE;
+      if (status.available > 0) {
+        $element.html('<i class="fa fa-bell text-success" title="' + VuFind.translate('ill_request_available') + '"></i>');
+        level = ICON_LEVELS.GOOD;
+      } else if (status.in_transit > 0) {
+        $element.html('<i class="fa fa-clock-o text-warning" title="' + VuFind.translate('request_in_transit') + '"></i>');
+      } else {
+        $element.addClass("holds-status hidden");
+      }
+      return level;
+    }
+  });
+
+  VuFind.account.register("storageRetrievalRequests", {
+    selector: ".storageretrievalrequests-status",
+    ajaxMethod: "getUserStorageRetrievalRequests",
+    render: function render($element, status, ICON_LEVELS) {
+      var level = ICON_LEVELS.NONE;
+      if (status.available > 0) {
+        $element.html('<i class="fa fa-bell text-success" title="' + VuFind.translate('storage_retrieval_request_available') + '"></i>');
+        level = ICON_LEVELS.GOOD;
+      } else if (status.in_transit > 0) {
+        $element.html('<i class="fa fa-clock-o text-warning" title="' + VuFind.translate('request_in_transit') + '"></i>');
+      } else {
+        $element.addClass("holds-status hidden");
+      }
+      return level;
+    }
+  });
+
+  /* finc specific */
+  $('.itemCount').each(function() {
+    let selector = $(this).attr('id');
+    if(!selector)
+      return;
+
+    VuFind.account.register("itemCount", {
+      selector: "#" + selector,
+      ajaxMethod: "getAdditionalAccountInfo",
+      data: {'views':[selector]},
+      render: function render($element, status, ICON_LEVELS) {
+        var level = ICON_LEVELS.NONE;
+        var html = '';
+        $.each(status, function (i, result) {
+          $.each(result, function (id, itemCount) {
+            if ($element.attr("id") === id && itemCount > 0) {
+              level = ICON_LEVELS.GOOD;
+              html += '<span class="badge ok">' + itemCount + '</span>';
+            }
+          });
+        });
+        $element.html(html);
+        return level;
+      }
+    });
+  });
+  /* finc specific - END */
+});
diff --git a/themes/finc/templates/myresearch/menu.phtml b/themes/finc/templates/myresearch/menu.phtml
index 5178f89f595..f639faf74d5 100644
--- a/themes/finc/templates/myresearch/menu.phtml
+++ b/themes/finc/templates/myresearch/menu.phtml
@@ -165,29 +165,4 @@ $capabilityParams = $patron ? ['patron' => $patron] : [];
 
   </ul>
 <?php endif ?>
-<?php /* finc: This script is finc-specific - CK */ ?>
-<?php $script = <<<JS
-$(document).ready(function () {
-    countables = new Array;
-    $('.itemCount').each(function() {
-        countables.push($(this).attr('id'));
-    });
-
-    $.ajax({
-        dataType: 'json',
-        method: 'POST',
-        url: VuFind.path + '/AJAX/JSON?method=getAdditionalAccountInfo',
-        data: {'views':countables}
-    })
-    .done(function(response) {
-        $.each(response.data, function (i, result) {
-            $.each(result, function (id, itemCount) {
-                $('#'+id).text('('+itemCount+')');
-            });
-        });
-    });
-});
-JS;
-?>
-<?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET');?>
 <!-- finc: myresearch - menu - END -->
-- 
GitLab