From aa71bd5cc72a727f175e2f43c2942ad14b3e4130 Mon Sep 17 00:00:00 2001 From: Ere Maijala Date: Thu, 19 Dec 2024 12:42:21 +0200 Subject: [PATCH 1/9] Use templates to render account status notifications server-side. Requires less JS code and makes it easier to customize the notification badges. --- .../AbstractIlsUserAndRendererAction.php | 70 +++++++++++ ...bstractIlsUserAndRendererActionFactory.php | 78 ++++++++++++ .../AjaxHandler/AbstractUserRequestAction.php | 24 +++- .../src/VuFind/AjaxHandler/GetUserFines.php | 37 +++--- .../AjaxHandler/GetUserFinesFactory.php | 2 +- .../AjaxHandler/GetUserTransactions.php | 36 ++++-- .../src/VuFind/AjaxHandler/PluginManager.php | 9 +- .../ILS/Logic/AccountStatusLevelType.php | 47 ++++++++ .../templates/ajax/account/checkouts.phtml | 25 ++++ .../templates/ajax/account/fines.phtml | 3 + .../templates/ajax/account/requests.phtml | 25 ++++ .../templates/ajax/account/status-badge.phtml | 6 + themes/bootstrap5/js/account_ajax.js | 114 +++--------------- .../templates/ajax/account/checkouts.phtml | 25 ++++ .../templates/ajax/account/fines.phtml | 3 + .../templates/ajax/account/requests.phtml | 25 ++++ .../templates/ajax/account/status-badge.phtml | 6 + 17 files changed, 406 insertions(+), 129 deletions(-) create mode 100644 module/VuFind/src/VuFind/AjaxHandler/AbstractIlsUserAndRendererAction.php create mode 100644 module/VuFind/src/VuFind/AjaxHandler/AbstractIlsUserAndRendererActionFactory.php create mode 100644 module/VuFind/src/VuFind/ILS/Logic/AccountStatusLevelType.php create mode 100644 themes/bootstrap3/templates/ajax/account/checkouts.phtml create mode 100644 themes/bootstrap3/templates/ajax/account/fines.phtml create mode 100644 themes/bootstrap3/templates/ajax/account/requests.phtml create mode 100644 themes/bootstrap3/templates/ajax/account/status-badge.phtml create mode 100644 themes/bootstrap5/templates/ajax/account/checkouts.phtml create mode 100644 themes/bootstrap5/templates/ajax/account/fines.phtml create mode 100644 themes/bootstrap5/templates/ajax/account/requests.phtml create mode 100644 themes/bootstrap5/templates/ajax/account/status-badge.phtml diff --git a/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsUserAndRendererAction.php b/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsUserAndRendererAction.php new file mode 100644 index 00000000000..d57982460d2 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsUserAndRendererAction.php @@ -0,0 +1,70 @@ + + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ + +namespace VuFind\AjaxHandler; + +use Laminas\View\Renderer\PhpRenderer; +use VuFind\Auth\ILSAuthenticator; +use VuFind\Db\Entity\UserEntityInterface; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\ILS\Connection; +use VuFind\Session\Settings as SessionSettings; + +/** + * Abstract base class for handlers depending on the ILS and a logged-in user. + * + * @category VuFind + * @package AJAX + * @author Demian Katz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +abstract class AbstractIlsUserAndRendererAction extends AbstractBase implements TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param Connection $ils ILS connection + * @param ILSAuthenticator $ilsAuthenticator ILS authenticator + * @param ?UserEntityInterface $user Logged in user (or null) + * @param PhpRenderer $renderer View renderer + */ + public function __construct( + SessionSettings $ss, + protected Connection $ils, + protected ILSAuthenticator $ilsAuthenticator, + protected ?UserEntityInterface $user, + protected PhpRenderer $renderer + ) { + $this->sessionSettings = $ss; + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsUserAndRendererActionFactory.php b/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsUserAndRendererActionFactory.php new file mode 100644 index 00000000000..b3ecf9829b9 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsUserAndRendererActionFactory.php @@ -0,0 +1,78 @@ + + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ + +namespace VuFind\AjaxHandler; + +use Laminas\ServiceManager\Exception\ServiceNotCreatedException; +use Laminas\ServiceManager\Exception\ServiceNotFoundException; +use Psr\Container\ContainerExceptionInterface as ContainerException; +use Psr\Container\ContainerInterface; + +/** + * Factory for AbstractIlsAndUserAction AJAX handlers. + * + * @category VuFind + * @package AJAX + * @author Demian Katz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AbstractIlsUserAndRendererActionFactory implements \Laminas\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException&\Throwable if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + ?array $options = null + ) { + return new $requestedName( + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\ILS\Connection::class), + $container->get(\VuFind\Auth\ILSAuthenticator::class), + $container->get(\VuFind\Auth\Manager::class)->getUserObject(), + $container->get('ViewRenderer'), + ...($options ?: []) + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/AbstractUserRequestAction.php b/module/VuFind/src/VuFind/AjaxHandler/AbstractUserRequestAction.php index fcb957d5561..60449b07245 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/AbstractUserRequestAction.php +++ b/module/VuFind/src/VuFind/AjaxHandler/AbstractUserRequestAction.php @@ -30,6 +30,7 @@ namespace VuFind\AjaxHandler; use Laminas\Mvc\Controller\Plugin\Params; +use VuFind\ILS\Logic\AccountStatusLevelType; /** * Abstract base class for fetching information about user requests. @@ -40,7 +41,7 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -abstract class AbstractUserRequestAction extends AbstractIlsAndUserAction +abstract class AbstractUserRequestAction extends AbstractIlsUserAndRendererAction { use \VuFind\ILS\Logic\SummaryTrait; @@ -69,6 +70,25 @@ public function handleRequest(Params $params) return $this->formatResponse('', self::STATUS_HTTP_ERROR); } $requests = $this->ils->{$this->lookupMethod}($patron); - return $this->formatResponse($this->getRequestSummary($requests)); + $result = $this->getRequestSummary($requests); + $result['level'] = $this->getAccountStatusLevel($result); + $result['html'] = $this->renderer->render('ajax/account/requests.phtml', $result); + return $this->formatResponse($result); + } + + /** + * Get account status level for notification icon + * + * @param array $status Status information + * + * @return AccountStatusLevelType + */ + protected function getAccountStatusLevel(array $status): AccountStatusLevelType + { + if ($status['available']) { + // This is equivalent to the GOOD level in account_ajax.js, though e.g. ActionRequired could also make sense + return AccountStatusLevelType::Good; + } + return AccountStatusLevelType::Normal; } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetUserFines.php b/module/VuFind/src/VuFind/AjaxHandler/GetUserFines.php index 0a7515cf02d..5d689f5d029 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetUserFines.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetUserFines.php @@ -30,9 +30,11 @@ namespace VuFind\AjaxHandler; use Laminas\Mvc\Controller\Plugin\Params; +use Laminas\View\Renderer\PhpRenderer; use VuFind\Auth\ILSAuthenticator; use VuFind\Db\Entity\UserEntityInterface; use VuFind\ILS\Connection; +use VuFind\ILS\Logic\AccountStatusLevelType; use VuFind\Service\CurrencyFormatter; use VuFind\Session\Settings as SessionSettings; @@ -45,17 +47,10 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class GetUserFines extends AbstractIlsAndUserAction +class GetUserFines extends AbstractIlsUserAndRendererAction { use \VuFind\ILS\Logic\SummaryTrait; - /** - * Currency formatter - * - * @var CurrencyFormatter - */ - protected $currencyFormatter; - /** * Constructor * @@ -63,6 +58,7 @@ class GetUserFines extends AbstractIlsAndUserAction * @param Connection $ils ILS connection * @param ILSAuthenticator $ilsAuthenticator ILS authenticator * @param ?UserEntityInterface $user Logged in user (or false) + * @param PhpRenderer $renderer Renderer * @param CurrencyFormatter $currencyFormatter Currency formatter */ public function __construct( @@ -70,10 +66,10 @@ public function __construct( Connection $ils, ILSAuthenticator $ilsAuthenticator, ?UserEntityInterface $user, - CurrencyFormatter $currencyFormatter + PhpRenderer $renderer, + protected CurrencyFormatter $currencyFormatter, ) { - parent::__construct($ss, $ils, $ilsAuthenticator, $user); - $this->currencyFormatter = $currencyFormatter; + parent::__construct($ss, $ils, $ilsAuthenticator, $user, $renderer); } /** @@ -94,8 +90,21 @@ public function handleRequest(Params $params) return $this->formatResponse('', self::STATUS_HTTP_ERROR); } $fines = $this->ils->getMyFines($patron); - return $this->formatResponse( - $this->getFineSummary($fines, $this->currencyFormatter) - ); + $result = $this->getFineSummary($fines, $this->currencyFormatter); + $result['level'] = $this->getAccountStatusLevel($result); + $result['html'] = $this->renderer->render('ajax/account/fines.phtml', $result); + return $this->formatResponse($result); + } + + /** + * Get account status level for notification icon + * + * @param array $status Status information + * + * @return AccountStatusLevelType + */ + protected function getAccountStatusLevel(array $status): AccountStatusLevelType + { + return $status['total'] ? AccountStatusLevelType::ActionRequired : AccountStatusLevelType::Normal; } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetUserFinesFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetUserFinesFactory.php index 257b1c07bb0..38f24085ce4 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetUserFinesFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetUserFinesFactory.php @@ -44,7 +44,7 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class GetUserFinesFactory extends AbstractIlsAndUserActionFactory implements FactoryInterface +class GetUserFinesFactory extends AbstractIlsUserAndRendererActionFactory implements FactoryInterface { /** * Create an object diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetUserTransactions.php b/module/VuFind/src/VuFind/AjaxHandler/GetUserTransactions.php index f4328becaac..2a3ed055c45 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetUserTransactions.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetUserTransactions.php @@ -30,6 +30,7 @@ namespace VuFind\AjaxHandler; use Laminas\Mvc\Controller\Plugin\Params; +use VuFind\ILS\Logic\AccountStatusLevelType; /** * "Get User Transactions" AJAX handler @@ -40,7 +41,7 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class GetUserTransactions extends AbstractIlsAndUserAction +class GetUserTransactions extends AbstractIlsUserAndRendererAction { use \VuFind\ILS\Logic\SummaryTrait; @@ -69,27 +70,46 @@ public function handleRequest(Params $params) return $this->formatResponse('', self::STATUS_HTTP_ERROR); } - $counts = []; + $result = []; $functionConfig = $this->ils->checkFunction('getMyTransactions', $patron); $page = 1; do { // Try to use large page size, but take ILS limits into account $pageOptions = $this->getPaginationHelper() ->getOptions($page, null, 1000, $functionConfig); - $result = $this->ils - ->getMyTransactions($patron, $pageOptions['ilsParams']); + $transactions = $this->ils->getMyTransactions($patron, $pageOptions['ilsParams']); - $summary = $this->getTransactionSummary($result['records']); + $summary = $this->getTransactionSummary($transactions['records']); foreach ($summary as $key => $value) { - $counts[$key] = ($counts[$key] ?? 0) + $value; + $result[$key] = ($result[$key] ?? 0) + $value; } $pageEnd = $pageOptions['ilsPaging'] - ? ceil($result['count'] / $pageOptions['limit']) + ? ceil($transactions['count'] / $pageOptions['limit']) : 1; $page++; } while ($page <= $pageEnd); - return $this->formatResponse($counts); + $result['level'] = $this->getAccountStatusLevel($result); + $result['html'] = $this->renderer->render('ajax/account/checkouts.phtml', $result); + return $this->formatResponse($result); + } + + /** + * Get account status level for notification icon + * + * @param array $status Status information + * + * @return AccountStatusLevelType + */ + protected function getAccountStatusLevel(array $status): AccountStatusLevelType + { + if ($status['overdue']) { + return AccountStatusLevelType::ActionRequired; + } + if ($status['warn']) { + return AccountStatusLevelType::Attention; + } + return AccountStatusLevelType::Normal; } /** diff --git a/module/VuFind/src/VuFind/AjaxHandler/PluginManager.php b/module/VuFind/src/VuFind/AjaxHandler/PluginManager.php index 170f62d9b07..ffb4a367e0a 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/PluginManager.php +++ b/module/VuFind/src/VuFind/AjaxHandler/PluginManager.php @@ -109,11 +109,10 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager GetSearchResults::class => GetSearchResultsFactory::class, GetSideFacets::class => GetSideFacetsFactory::class, GetUserFines::class => GetUserFinesFactory::class, - GetUserHolds::class => AbstractIlsAndUserActionFactory::class, - GetUserILLRequests::class => AbstractIlsAndUserActionFactory::class, - GetUserStorageRetrievalRequests::class => - AbstractIlsAndUserActionFactory::class, - GetUserTransactions::class => AbstractIlsAndUserActionFactory::class, + GetUserHolds::class => AbstractIlsUserAndRendererActionFactory::class, + GetUserILLRequests::class => AbstractIlsUserAndRendererActionFactory::class, + GetUserStorageRetrievalRequests::class => AbstractIlsUserAndRendererActionFactory::class, + GetUserTransactions::class => AbstractIlsUserAndRendererActionFactory::class, GetVisData::class => GetVisDataFactory::class, KeepAlive::class => KeepAliveFactory::class, Recommend::class => RecommendFactory::class, diff --git a/module/VuFind/src/VuFind/ILS/Logic/AccountStatusLevelType.php b/module/VuFind/src/VuFind/ILS/Logic/AccountStatusLevelType.php new file mode 100644 index 00000000000..674520ec475 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Logic/AccountStatusLevelType.php @@ -0,0 +1,47 @@ + + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ + +namespace VuFind\ILS\Logic; + +/** + * Account status level enum + * + * @category VuFind + * @package ILS_Logic + * @author Ere Maijala + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +enum AccountStatusLevelType: int +{ + case Normal = 0; + case Good = 1; + case Attention = 2; + case ActionRequired = 3; +} diff --git a/themes/bootstrap3/templates/ajax/account/checkouts.phtml b/themes/bootstrap3/templates/ajax/account/checkouts.phtml new file mode 100644 index 00000000000..8bb9f24a5e5 --- /dev/null +++ b/themes/bootstrap3/templates/ajax/account/checkouts.phtml @@ -0,0 +1,25 @@ +render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_normal_checkouts', + 'count' => $this->ok, + 'class' => 'account-info', + ] +); +echo $this->render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_checkouts_due', + 'count' => $this->warn, + 'class' => 'account-warning', + ] +); +echo $this->render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_checkouts_overdue', + 'count' => $this->overdue, + 'class' => 'account-alert', + ] +); diff --git a/themes/bootstrap3/templates/ajax/account/fines.phtml b/themes/bootstrap3/templates/ajax/account/fines.phtml new file mode 100644 index 00000000000..135facaf95b --- /dev/null +++ b/themes/bootstrap3/templates/ajax/account/fines.phtml @@ -0,0 +1,3 @@ +total): ?> + +render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_requests_available', + 'count' => $this->available, + 'class' => 'account-info', + ] +); +echo $this->render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_requests_in_transit', + 'count' => $this->in_transit, + 'class' => 'account-warning', + ] +); +echo $this->render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_requests_other', + 'count' => $this->other, + 'class' => 'account-none', + ] +); diff --git a/themes/bootstrap3/templates/ajax/account/status-badge.phtml b/themes/bootstrap3/templates/ajax/account/status-badge.phtml new file mode 100644 index 00000000000..79192356fb7 --- /dev/null +++ b/themes/bootstrap3/templates/ajax/account/status-badge.phtml @@ -0,0 +1,6 @@ +count > 0): ?> + + transEsc($this->title)?>: escapeHtml($this->count)?> , + accountStatus) { accountStatus = moduleStatus; } + } else { + // Render with default method: + const subStatus = _statuses[sub]; + if (subStatus.html !== '') { + $element.html(subStatus.html); + } else { + $element.addClass("hidden"); + } + $('[data-toggle="tooltip"],[data-bs-toggle="tooltip"]', $element).tooltip(); + if (subStatus.level > accountStatus) { + accountStatus = subStatus.level; + } } } } @@ -201,25 +214,9 @@ VuFind.register('account', function Account() { }); $(function registerAccountAjax() { - - var renderStatusBadge = (titleKey, count, className = "") => { - return count > 0 - ? ` - ${VuFind.translate(titleKey)}: ${count} , ` - : ""; - }; - VuFind.account.register("fines", { selector: ".fines-status", ajaxMethod: "getUserFines", - render: function render($element, status, ICON_LEVELS) { - if (status.total === 0) { - $element.addClass("hidden"); - return ICON_LEVELS.NONE; - } - $element.html(''); - return ICON_LEVELS.DANGER; - }, updateNeeded: function updateNeeded(currentStatus, status) { return status.total !== currentStatus.total; } @@ -228,24 +225,6 @@ $(function registerAccountAjax() { 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 += renderStatusBadge('account_normal_checkouts', status.ok, 'account-info'); - } - if (status.warn > 0) { - html += renderStatusBadge('account_checkouts_due', status.warn, 'account-warning'); - level = ICON_LEVELS.WARNING; - } - if (status.overdue > 0) { - html += renderStatusBadge('account_checkouts_overdue', status.overdue, 'account-alert'); - level = ICON_LEVELS.DANGER; - } - $element.html(html); - $('[data-toggle="tooltip"]', $element).tooltip(); - return level; - }, updateNeeded: function updateNeeded(currentStatus, status) { return status.ok !== currentStatus.ok || status.warn !== currentStatus.warn || status.overdue !== currentStatus.overdue; } @@ -254,27 +233,6 @@ $(function registerAccountAjax() { VuFind.account.register("holds", { selector: ".holds-status", ajaxMethod: "getUserHolds", - render: function render($element, status, ICON_LEVELS) { - var html = ''; - var level = ICON_LEVELS.NONE; - if (status.available > 0) { - html += renderStatusBadge('account_requests_available', status.available, 'account-info'); - level = ICON_LEVELS.GOOD; - } - if (status.in_transit > 0) { - html += renderStatusBadge('account_requests_in_transit', status.in_transit, 'account-warning'); - } - if (status.other > 0) { - html += renderStatusBadge('account_requests_other', status.other, 'account-none'); - } - if (html !== '') { - $element.html(html); - } else { - $element.addClass("holds-status hidden"); - } - $('[data-toggle="tooltip"]', $element).tooltip(); - return level; - }, updateNeeded: function updateNeeded(currentStatus, status) { return status.available !== currentStatus.available || status.in_transit !== currentStatus.in_transit || status.other !== currentStatus.other; } @@ -283,27 +241,6 @@ $(function registerAccountAjax() { VuFind.account.register("illRequests", { selector: ".illrequests-status", ajaxMethod: "getUserILLRequests", - render: function render($element, status, ICON_LEVELS) { - var html = ''; - var level = ICON_LEVELS.NONE; - if (status.available > 0) { - html += renderStatusBadge('account_requests_available', status.available, 'account-info'); - level = ICON_LEVELS.GOOD; - } - if (status.in_transit > 0) { - html += renderStatusBadge('account_requests_in_transit', status.in_transit, 'account-warning'); - } - if (status.other > 0) { - html += renderStatusBadge('account_requests_other', status.other, 'account-none'); - } - if (html !== '') { - $element.html(html); - } else { - $element.addClass("holds-status hidden"); - } - $('[data-toggle="tooltip"]', $element).tooltip(); - return level; - }, updateNeeded: function updateNeeded(currentStatus, status) { return status.available !== currentStatus.available || status.in_transit !== currentStatus.in_transit || status.other !== currentStatus.other; } @@ -312,27 +249,6 @@ $(function registerAccountAjax() { VuFind.account.register("storageRetrievalRequests", { selector: ".storageretrievalrequests-status", ajaxMethod: "getUserStorageRetrievalRequests", - render: function render($element, status, ICON_LEVELS) { - var html = ''; - var level = ICON_LEVELS.NONE; - if (status.available > 0) { - html += renderStatusBadge('account_requests_available', status.available, 'account-info'); - level = ICON_LEVELS.GOOD; - } - if (status.in_transit > 0) { - html += renderStatusBadge('account_requests_in_transit', status.in_transit, 'account-warning'); - } - if (status.other > 0) { - html += renderStatusBadge('account_requests_other', status.other, 'account-none'); - } - if (html !== '') { - $element.html(html); - } else { - $element.addClass("holds-status hidden"); - } - $('[data-toggle="tooltip"]', $element).tooltip(); - return level; - }, updateNeeded: function updateNeeded(currentStatus, status) { return status.available !== currentStatus.available || status.in_transit !== currentStatus.in_transit || status.other !== currentStatus.other; } diff --git a/themes/bootstrap5/templates/ajax/account/checkouts.phtml b/themes/bootstrap5/templates/ajax/account/checkouts.phtml new file mode 100644 index 00000000000..8bb9f24a5e5 --- /dev/null +++ b/themes/bootstrap5/templates/ajax/account/checkouts.phtml @@ -0,0 +1,25 @@ +render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_normal_checkouts', + 'count' => $this->ok, + 'class' => 'account-info', + ] +); +echo $this->render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_checkouts_due', + 'count' => $this->warn, + 'class' => 'account-warning', + ] +); +echo $this->render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_checkouts_overdue', + 'count' => $this->overdue, + 'class' => 'account-alert', + ] +); diff --git a/themes/bootstrap5/templates/ajax/account/fines.phtml b/themes/bootstrap5/templates/ajax/account/fines.phtml new file mode 100644 index 00000000000..135facaf95b --- /dev/null +++ b/themes/bootstrap5/templates/ajax/account/fines.phtml @@ -0,0 +1,3 @@ +total): ?> + +render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_requests_available', + 'count' => $this->available, + 'class' => 'account-info', + ] +); +echo $this->render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_requests_in_transit', + 'count' => $this->in_transit, + 'class' => 'account-warning', + ] +); +echo $this->render( + 'ajax/account/status-badge.phtml', + [ + 'title' => 'account_requests_other', + 'count' => $this->other, + 'class' => 'account-none', + ] +); diff --git a/themes/bootstrap5/templates/ajax/account/status-badge.phtml b/themes/bootstrap5/templates/ajax/account/status-badge.phtml new file mode 100644 index 00000000000..79192356fb7 --- /dev/null +++ b/themes/bootstrap5/templates/ajax/account/status-badge.phtml @@ -0,0 +1,6 @@ +count > 0): ?> + + transEsc($this->title)?>: escapeHtml($this->count)?> , + Date: Thu, 19 Dec 2024 14:20:53 +0200 Subject: [PATCH 2/9] Fix tests. --- .../src/VuFindTest/Mink/AccountMenuTest.php | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountMenuTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountMenuTest.php index 5439112ea14..9c381e2e139 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountMenuTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountMenuTest.php @@ -385,11 +385,11 @@ public function testIconGood() $this->login(); $storage = [ // Holds available - ['holds' => ['in_transit' => 0, 'available' => 1]], + ['holds' => ['in_transit' => 0, 'available' => 1, 'level' => 1]], // ILL Requests available - ['illRequests' => ['in_transit' => 0, 'available' => 1]], + ['illRequests' => ['in_transit' => 0, 'available' => 1, 'level' => 1]], // Storage Retrievals available - ['storageRetrievalRequests' => ['in_transit' => 0, 'available' => 1]], + ['storageRetrievalRequests' => ['in_transit' => 0, 'available' => 1, 'level' => 1]], ]; $this->checkIcon($storage, '.account-status-good'); } @@ -404,7 +404,7 @@ public function testIconWarning() $this->login(); $storage = [ // Checked out due soon - ['checkedOut' => ['warn' => 1]], + ['checkedOut' => ['warn' => 1, 'level' => 2]], ]; $this->checkIcon($storage, '.account-status-warning'); } @@ -419,9 +419,9 @@ public function testIconDanger() $this->login(); $storage = [ // User has fines - ['fines' => ['value' => 1000000, 'display' => '$...yikes']], + ['fines' => ['value' => 1000000, 'display' => '$...yikes', 'level' => 3]], // Checkedout overdue - ['checkedOut' => ['overdue' => 1]], + ['checkedOut' => ['overdue' => 1, 'level' => 3]], ]; $this->checkIcon($storage, '.account-status-danger'); } @@ -438,15 +438,15 @@ public function testIconClashes() $this->login(); // Danger overrides warning $this->checkIcon( - [['checkedOut' => ['warn' => 2, 'overdue' => 1]]], + [['checkedOut' => ['warn' => 2, 'overdue' => 1, 'level' => 3]]], '.account-status-danger' ); // Danger overrides good $this->checkIcon( [ [ - 'checkedOut' => ['overdue' => 1], - 'holds' => ['available' => 1], + 'checkedOut' => ['overdue' => 1, 'level' => 3], + 'holds' => ['available' => 1, 'level' => 1], ], ], '.account-status-danger' @@ -455,8 +455,8 @@ public function testIconClashes() $this->checkIcon( [ [ - 'checkedOut' => ['warn' => 1], - 'holds' => ['available' => 1], + 'checkedOut' => ['warn' => 1, 'level' => 2], + 'holds' => ['available' => 1, 'level' => 1], ], ], '.account-status-warning' @@ -465,8 +465,8 @@ public function testIconClashes() $this->checkIcon( [ [ - 'holds' => ['available' => 1], - 'fines' => ['total' => 0, 'display' => 'none'], + 'holds' => ['available' => 1, 'level' => 1], + 'fines' => ['total' => 0, 'display' => 'none', 'level' => 0], ], ], '.account-status-good' From acefd7c9064d85ee094af56105a77ad5e32311f9 Mon Sep 17 00:00:00 2001 From: Ere Maijala Date: Thu, 19 Dec 2024 20:57:22 +0200 Subject: [PATCH 3/9] Fix templates, tweak JS. --- themes/bootstrap3/js/account_ajax.js | 114 +++--------------- .../templates/ajax/account/status-badge.phtml | 2 +- .../templates/myresearch/menu-item.phtml | 2 +- themes/bootstrap5/js/account_ajax.js | 2 +- .../templates/ajax/account/status-badge.phtml | 4 +- .../templates/myresearch/menu-item.phtml | 2 +- 6 files changed, 21 insertions(+), 105 deletions(-) diff --git a/themes/bootstrap3/js/account_ajax.js b/themes/bootstrap3/js/account_ajax.js index 1d5af183fcb..9c85aeadf8b 100644 --- a/themes/bootstrap3/js/account_ajax.js +++ b/themes/bootstrap3/js/account_ajax.js @@ -68,11 +68,24 @@ VuFind.register('account', function Account() { $element.removeClass('hidden'); if (status === LOADING) { $element.html(VuFind.spinner()); - } else { - var moduleStatus = _submodules[sub].render($element, _statuses[sub], ICON_LEVELS); + } else if (Object.prototype.hasOwnProperty.call(_submodules[sub], 'render')) { + // Render using render function: + let moduleStatus = _submodules[sub].render($element, _statuses[sub], ICON_LEVELS); if (moduleStatus > accountStatus) { accountStatus = moduleStatus; } + } else { + // Render with default method: + const subStatus = _statuses[sub]; + if (subStatus.html !== '') { + $element.html(subStatus.html); + } else { + $element.addClass("hidden"); + } + $('[data-toggle="tooltip"],[data-bs-toggle="tooltip"]', $element).tooltip(); + if (subStatus.level > accountStatus) { + accountStatus = subStatus.level; + } } } } @@ -200,25 +213,9 @@ VuFind.register('account', function Account() { }); $(function registerAccountAjax() { - - var renderStatusBadge = (titleKey, count, className = "") => { - return count > 0 - ? ` - ${VuFind.translate(titleKey)}: ${count} , ` - : ""; - }; - VuFind.account.register("fines", { selector: ".fines-status", ajaxMethod: "getUserFines", - render: function render($element, status, ICON_LEVELS) { - if (status.total === 0) { - $element.addClass("hidden"); - return ICON_LEVELS.NONE; - } - $element.html(''); - return ICON_LEVELS.DANGER; - }, updateNeeded: function updateNeeded(currentStatus, status) { return status.total !== currentStatus.total; } @@ -227,24 +224,6 @@ $(function registerAccountAjax() { 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 += renderStatusBadge('account_normal_checkouts', status.ok, 'account-info'); - } - if (status.warn > 0) { - html += renderStatusBadge('account_checkouts_due', status.warn, 'account-warning'); - level = ICON_LEVELS.WARNING; - } - if (status.overdue > 0) { - html += renderStatusBadge('account_checkouts_overdue', status.overdue, 'account-alert'); - level = ICON_LEVELS.DANGER; - } - $element.html(html); - $('[data-toggle="tooltip"]', $element).tooltip(); - return level; - }, updateNeeded: function updateNeeded(currentStatus, status) { return status.ok !== currentStatus.ok || status.warn !== currentStatus.warn || status.overdue !== currentStatus.overdue; } @@ -253,27 +232,6 @@ $(function registerAccountAjax() { VuFind.account.register("holds", { selector: ".holds-status", ajaxMethod: "getUserHolds", - render: function render($element, status, ICON_LEVELS) { - var html = ''; - var level = ICON_LEVELS.NONE; - if (status.available > 0) { - html += renderStatusBadge('account_requests_available', status.available, 'account-info'); - level = ICON_LEVELS.GOOD; - } - if (status.in_transit > 0) { - html += renderStatusBadge('account_requests_in_transit', status.in_transit, 'account-warning'); - } - if (status.other > 0) { - html += renderStatusBadge('account_requests_other', status.other, 'account-none'); - } - if (html !== '') { - $element.html(html); - } else { - $element.addClass("holds-status hidden"); - } - $('[data-toggle="tooltip"]', $element).tooltip(); - return level; - }, updateNeeded: function updateNeeded(currentStatus, status) { return status.available !== currentStatus.available || status.in_transit !== currentStatus.in_transit || status.other !== currentStatus.other; } @@ -282,27 +240,6 @@ $(function registerAccountAjax() { VuFind.account.register("illRequests", { selector: ".illrequests-status", ajaxMethod: "getUserILLRequests", - render: function render($element, status, ICON_LEVELS) { - var html = ''; - var level = ICON_LEVELS.NONE; - if (status.available > 0) { - html += renderStatusBadge('account_requests_available', status.available, 'account-info'); - level = ICON_LEVELS.GOOD; - } - if (status.in_transit > 0) { - html += renderStatusBadge('account_requests_in_transit', status.in_transit, 'account-warning'); - } - if (status.other > 0) { - html += renderStatusBadge('account_requests_other', status.other, 'account-none'); - } - if (html !== '') { - $element.html(html); - } else { - $element.addClass("holds-status hidden"); - } - $('[data-toggle="tooltip"]', $element).tooltip(); - return level; - }, updateNeeded: function updateNeeded(currentStatus, status) { return status.available !== currentStatus.available || status.in_transit !== currentStatus.in_transit || status.other !== currentStatus.other; } @@ -311,27 +248,6 @@ $(function registerAccountAjax() { VuFind.account.register("storageRetrievalRequests", { selector: ".storageretrievalrequests-status", ajaxMethod: "getUserStorageRetrievalRequests", - render: function render($element, status, ICON_LEVELS) { - var html = ''; - var level = ICON_LEVELS.NONE; - if (status.available > 0) { - html += renderStatusBadge('account_requests_available', status.available, 'account-info'); - level = ICON_LEVELS.GOOD; - } - if (status.in_transit > 0) { - html += renderStatusBadge('account_requests_in_transit', status.in_transit, 'account-warning'); - } - if (status.other > 0) { - html += renderStatusBadge('account_requests_other', status.other, 'account-none'); - } - if (html !== '') { - $element.html(html); - } else { - $element.addClass("holds-status hidden"); - } - $('[data-toggle="tooltip"]', $element).tooltip(); - return level; - }, updateNeeded: function updateNeeded(currentStatus, status) { return status.available !== currentStatus.available || status.in_transit !== currentStatus.in_transit || status.other !== currentStatus.other; } diff --git a/themes/bootstrap3/templates/ajax/account/status-badge.phtml b/themes/bootstrap3/templates/ajax/account/status-badge.phtml index 79192356fb7..895e0772fee 100644 --- a/themes/bootstrap3/templates/ajax/account/status-badge.phtml +++ b/themes/bootstrap3/templates/ajax/account/status-badge.phtml @@ -2,5 +2,5 @@ - transEsc($this->title)?>: escapeHtml($this->count)?> , + transEsc($this->title)?>: escapeHtml($this->count)?> , icon($this->icon, 'icon-link__icon') ?> transEsc($this->label)?> - status ?? false): ?> + status ?? false) && $this->auth()->getManager()->ajaxEnabled()): ?> diff --git a/themes/bootstrap5/js/account_ajax.js b/themes/bootstrap5/js/account_ajax.js index e04f8b9a638..8e995710b3b 100644 --- a/themes/bootstrap5/js/account_ajax.js +++ b/themes/bootstrap5/js/account_ajax.js @@ -82,7 +82,7 @@ VuFind.register('account', function Account() { } else { $element.addClass("hidden"); } - $('[data-toggle="tooltip"],[data-bs-toggle="tooltip"]', $element).tooltip(); + $element[0].querySelectorAll('[data-bs-toggle="tooltip"]').forEach((el) => bootstrap.Tooltip.getOrCreateInstance(el)); if (subStatus.level > accountStatus) { accountStatus = subStatus.level; } diff --git a/themes/bootstrap5/templates/ajax/account/status-badge.phtml b/themes/bootstrap5/templates/ajax/account/status-badge.phtml index 79192356fb7..a1054f93a61 100644 --- a/themes/bootstrap5/templates/ajax/account/status-badge.phtml +++ b/themes/bootstrap5/templates/ajax/account/status-badge.phtml @@ -1,6 +1,6 @@ count > 0): ?> -