Bugzilla – Attachment 190459 Details for
Bug 41439
Column filtering on the circ rules table
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 41439: Add column visibility toggles to circulation rules table
594bf9e.patch (text/plain), 24.56 KB, created by
Martin Renvoize (ashimema)
on 2025-12-12 12:11:36 UTC
(
hide
)
Description:
Bug 41439: Add column visibility toggles to circulation rules table
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2025-12-12 12:11:36 UTC
Size:
24.56 KB
patch
obsolete
>From 594bf9e38f8a21718135df5473b05370e02a540f Mon Sep 17 00:00:00 2001 >From: Martin Renvoize <martin.renvoize@openfifth.co.uk> >Date: Fri, 12 Dec 2025 11:29:16 +0000 >Subject: [PATCH] Bug 41439: Add column visibility toggles to circulation rules > table > >The circulation rules table has become excessively wide with 40+ >columns, making it difficult to navigate and use. This patch adds >column visibility toggle buttons that allow users to focus on >specific rule categories. > >Changes: >- Add data-colname attributes to all table headers and footers > for robust column identification >- Add toggle buttons for: Checkouts, Due dates, Fines, Renewals, > Holds, Article requests, Recalls, and Show all >- Group columns into logical categories based on their function >- Use column name selectors instead of fragile index-based > selection for maintainability >- Default to showing only Checkout columns on page load for a > cleaner initial view >- Highlight the active button to indicate current view >- Disable search functionality (not appropriate for this table) >- Handle dynamic column visibility based on system preferences > (UnseenRenewals, ArticleRequests, UseRecalls) > >Test plan: >1. Navigate to Administration > Circulation and fine rules >2. Observe the table now shows only checkout-related columns by > default with the "Checkouts" button highlighted >3. Click different category buttons (Fines, Renewals, Holds, etc.) > and verify only relevant columns are shown >4. Verify the active button is highlighted in blue >5. Click "Show all" to display all columns >6. Verify the layout works with different system preference > combinations (enable/disable ArticleRequests, UseRecalls, > UnseenRenewals) >7. Verify column visibility persists correctly when system > preferences are changed >--- > .../prog/en/modules/admin/smart-rules.tt | 353 +++++++++++++----- > 1 file changed, 258 insertions(+), 95 deletions(-) > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt >index bd0526017da..b0ce725734d 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt >@@ -117,58 +117,58 @@ > <table id="default-circulation-rules"> > <thead> > <tr> >- <th> </th> >- <th class="fixed_sort">Patron category</th> >- <th> </th> >- <th class="fixed_sort">Item type</th> >- <th class="no-export">Actions</th> >- <th>Note</th> >- <th>Current checkouts allowed</th> >- <th>Current on-site checkouts allowed</th> >- <th>Loan period</th> >- <th>Days mode</th> >- <th>Unit</th> >- <th>Hard due date</th> >- <th>Decreased loan period for high holds (day)</th> >- <th>Fine amount</th> >- <th>Fine charging interval</th> >- <th>When to charge</th> >- <th>Fine/suspension grace period</th> >- <th>Overdue fines cap (amount)</th> >- <th>Cap fine at replacement price</th> >- <th>Expired hold charge</th> >- <th>Suspension in days (day)</th> >- <th>Max. suspension duration (day)</th> >- <th>Suspension charging interval</th> >- <th>Renewals allowed (count)</th> >+ <th data-colname="sort-patron"> </th> >+ <th data-colname="patron-category" class="fixed_sort">Patron category</th> >+ <th data-colname="sort-item"> </th> >+ <th data-colname="item-type" class="fixed_sort">Item type</th> >+ <th data-colname="actions-left" class="no-export">Actions</th> >+ <th data-colname="note">Note</th> >+ <th data-colname="maxissueqty">Current checkouts allowed</th> >+ <th data-colname="maxonsiteissueqty">Current on-site checkouts allowed</th> >+ <th data-colname="issuelength">Loan period</th> >+ <th data-colname="daysmode">Days mode</th> >+ <th data-colname="lengthunit">Unit</th> >+ <th data-colname="hardduedate">Hard due date</th> >+ <th data-colname="decreaseloanholds">Decreased loan period for high holds (day)</th> >+ <th data-colname="fine">Fine amount</th> >+ <th data-colname="chargeperiod">Fine charging interval</th> >+ <th data-colname="chargeperiod_charge_at">When to charge</th> >+ <th data-colname="firstremind">Fine/suspension grace period</th> >+ <th data-colname="overduefinescap">Overdue fines cap (amount)</th> >+ <th data-colname="cap_fine_to_replacement_price">Cap fine at replacement price</th> >+ <th data-colname="expire_reserves_charge">Expired hold charge</th> >+ <th data-colname="finedays">Suspension in days (day)</th> >+ <th data-colname="maxsuspensiondays">Max. suspension duration (day)</th> >+ <th data-colname="suspension_chargeperiod">Suspension charging interval</th> >+ <th data-colname="renewalsallowed">Renewals allowed (count)</th> > [% IF Koha.Preference('UnseenRenewals') %] >- <th>Unseen renewals allowed (count)</th> >+ <th data-colname="unseen_renewals_allowed">Unseen renewals allowed (count)</th> > [% END %] >- <th>Renewal period</th> >- <th>No renewal before</th> >- <th>No automatic renewal before</th> >- <th>Automatic renewal</th> >- <th>No automatic renewal after</th> >- <th>No automatic renewal after (hard limit)</th> >- <th>Holds allowed (total)</th> >- <th>Holds allowed (daily)</th> >- <th>Holds per record (count)</th> >- <th>On shelf holds allowed</th> >- <th>OPAC item level holds</th> >- <th>Holds pickup period (day)</th> >+ <th data-colname="renewalperiod">Renewal period</th> >+ <th data-colname="norenewalbefore">No renewal before</th> >+ <th data-colname="noautorenewalbefore">No automatic renewal before</th> >+ <th data-colname="auto_renew">Automatic renewal</th> >+ <th data-colname="no_auto_renewal_after">No automatic renewal after</th> >+ <th data-colname="no_auto_renewal_after_hard_limit">No automatic renewal after (hard limit)</th> >+ <th data-colname="reservesallowed">Holds allowed (total)</th> >+ <th data-colname="holds_per_day">Holds allowed (daily)</th> >+ <th data-colname="holds_per_record">Holds per record (count)</th> >+ <th data-colname="onshelfholds">On shelf holds allowed</th> >+ <th data-colname="opacitemholds">OPAC item level holds</th> >+ <th data-colname="holds_pickup_period">Holds pickup period (day)</th> > [% IF Koha.Preference('ArticleRequests') %] >- <th>Article requests</th> >+ <th data-colname="article_requests">Article requests</th> > [% END %] >- <th>Rental discount (%)</th> >+ <th data-colname="rentaldiscount">Rental discount (%)</th> > [% IF Koha.Preference('UseRecalls') %] >- <th>Recalls allowed (total)</th> >- <th>Recalls per record (count)</th> >- <th>On shelf recalls allowed</th> >- <th>Recall due date interval (day)</th> >- <th>Recall overdue fine amount</th> >- <th>Recall pickup period (day)</th> >+ <th data-colname="recalls_allowed">Recalls allowed (total)</th> >+ <th data-colname="recalls_per_record">Recalls per record (count)</th> >+ <th data-colname="on_shelf_recalls">On shelf recalls allowed</th> >+ <th data-colname="recall_due_date_interval">Recall due date interval (day)</th> >+ <th data-colname="recall_overdue_fine">Recall overdue fine amount</th> >+ <th data-colname="recall_shelf_time">Recall pickup period (day)</th> > [% END %] >- <th class="no-export">Actions</th> >+ <th data-colname="actions-right" class="no-export">Actions</th> > </tr> > </thead> > <tbody> >@@ -598,58 +598,58 @@ > </tbody> > <tfoot> > <tr> >- <th> </th> >- <th>Patron category</th> >- <th> </th> >- <th>Item type</th> >- <th> </th> >- <th>Note</th> >- <th>Current checkouts allowed</th> >- <th>Current on-site checkouts allowed</th> >- <th>Loan period</th> >- <th>Days mode</th> >- <th>Unit</th> >- <th>Hard due date</th> >- <th>Decreased loan period for high holds (day)</th> >- <th>Fine amount</th> >- <th>Fine charging interval</th> >- <th>Charge when?</th> >- <th>Fine/suspension grace period</th> >- <th>Overdue fines cap (amount)</th> >- <th>Cap fine at replacement price</th> >- <th>Expired hold charge</th> >- <th>Suspension in days (day)</th> >- <th>Max. suspension duration (day)</th> >- <th>Suspension charging interval</th> >- <th>Renewals allowed (count)</th> >+ <th data-colname="sort-patron"> </th> >+ <th data-colname="patron-category">Patron category</th> >+ <th data-colname="sort-item"> </th> >+ <th data-colname="item-type">Item type</th> >+ <th data-colname="actions-left"> </th> >+ <th data-colname="note">Note</th> >+ <th data-colname="maxissueqty">Current checkouts allowed</th> >+ <th data-colname="maxonsiteissueqty">Current on-site checkouts allowed</th> >+ <th data-colname="issuelength">Loan period</th> >+ <th data-colname="daysmode">Days mode</th> >+ <th data-colname="lengthunit">Unit</th> >+ <th data-colname="hardduedate">Hard due date</th> >+ <th data-colname="decreaseloanholds">Decreased loan period for high holds (day)</th> >+ <th data-colname="fine">Fine amount</th> >+ <th data-colname="chargeperiod">Fine charging interval</th> >+ <th data-colname="chargeperiod_charge_at">Charge when?</th> >+ <th data-colname="firstremind">Fine/suspension grace period</th> >+ <th data-colname="overduefinescap">Overdue fines cap (amount)</th> >+ <th data-colname="cap_fine_to_replacement_price">Cap fine at replacement price</th> >+ <th data-colname="expire_reserves_charge">Expired hold charge</th> >+ <th data-colname="finedays">Suspension in days (day)</th> >+ <th data-colname="maxsuspensiondays">Max. suspension duration (day)</th> >+ <th data-colname="suspension_chargeperiod">Suspension charging interval</th> >+ <th data-colname="renewalsallowed">Renewals allowed (count)</th> > [% IF Koha.Preference('UnseenRenewals') %] >- <th>Unseen renewals allowed (count)</th> >+ <th data-colname="unseen_renewals_allowed">Unseen renewals allowed (count)</th> > [% END %] >- <th>Renewal period</th> >- <th>No renewal before</th> >- <th>No automatic renewal before</th> >- <th>Automatic renewal</th> >- <th>No automatic renewal after</th> >- <th>No automatic renewal after (hard limit)</th> >- <th>Holds allowed (total)</th> >- <th>Holds allowed (daily)</th> >- <th>Holds per record (count)</th> >- <th>On shelf holds allowed</th> >- <th>OPAC item level holds</th> >- <th>Holds pickup period (day)</th> >+ <th data-colname="renewalperiod">Renewal period</th> >+ <th data-colname="norenewalbefore">No renewal before</th> >+ <th data-colname="noautorenewalbefore">No automatic renewal before</th> >+ <th data-colname="auto_renew">Automatic renewal</th> >+ <th data-colname="no_auto_renewal_after">No automatic renewal after</th> >+ <th data-colname="no_auto_renewal_after_hard_limit">No automatic renewal after (hard limit)</th> >+ <th data-colname="reservesallowed">Holds allowed (total)</th> >+ <th data-colname="holds_per_day">Holds allowed (daily)</th> >+ <th data-colname="holds_per_record">Holds per record (count)</th> >+ <th data-colname="onshelfholds">On shelf holds allowed</th> >+ <th data-colname="opacitemholds">OPAC item level holds</th> >+ <th data-colname="holds_pickup_period">Holds pickup period (day)</th> > [% IF Koha.Preference('ArticleRequests') %] >- <th>Article requests</th> >+ <th data-colname="article_requests">Article requests</th> > [% END %] >- <th>Rental discount (%)</th> >+ <th data-colname="rentaldiscount">Rental discount (%)</th> > [% IF Koha.Preference('UseRecalls') %] >- <th>Recalls allowed (total)</th> >- <th>Recalls per record (count)</th> >- <th>On shelf recalls allowed</th> >- <th>Recall due date interval (day)</th> >- <th>Recall overdue fine amount</th> >- <th>Recall pickup period (day)</th> >+ <th data-colname="recalls_allowed">Recalls allowed (total)</th> >+ <th data-colname="recalls_per_record">Recalls per record (count)</th> >+ <th data-colname="on_shelf_recalls">On shelf recalls allowed</th> >+ <th data-colname="recall_due_date_interval">Recall due date interval (day)</th> >+ <th data-colname="recall_overdue_fine">Recall overdue fine amount</th> >+ <th data-colname="recall_shelf_time">Recall pickup period (day)</th> > [% END %] >- <th> </th> >+ <th data-colname="actions-right"> </th> > </tr> > </tfoot> > </table> >@@ -1441,11 +1441,68 @@ > [% INCLUDE 'datatables.inc' %] > [% INCLUDE 'calendar.inc' %] > [% INCLUDE 'format_price.inc' %] >+ <style> >+ #default-circulation-rules_wrapper .top { >+ margin-bottom: 1em; >+ display: flex; >+ gap: 1em; >+ align-items: center; >+ flex-wrap: wrap; >+ } >+ #default-circulation-rules_wrapper .dt-buttons { >+ display: flex; >+ flex-wrap: wrap; >+ gap: 0.5em; >+ align-items: center; >+ padding: 0.5em; >+ background-color: #f8f9fa; >+ border: 1px solid #dee2e6; >+ border-radius: 4px; >+ order: -1; /* Place on the left */ >+ } >+ #default-circulation-rules_wrapper .dt-buttons button { >+ margin: 0 !important; >+ white-space: nowrap; >+ } >+ /* Hide the clear filter button since we disabled search */ >+ #default-circulation-rules_wrapper .dt_button_clear_filter { >+ display: none !important; >+ } >+ </style> > <script> > $(document).ready(function () { >- $("#default-circulation-rules").kohaTable({ >+ // Define column groups by their data-colname attributes >+ const columnGroups = { >+ core: ['patron-category', 'item-type', 'actions-left', 'note', 'actions-right'], >+ checkout: ['maxissueqty', 'maxonsiteissueqty', 'issuelength', 'daysmode', 'lengthunit'], >+ dueDate: ['hardduedate', 'decreaseloanholds'], >+ fines: ['fine', 'chargeperiod', 'chargeperiod_charge_at', 'firstremind', 'overduefinescap', >+ 'cap_fine_to_replacement_price', 'expire_reserves_charge', 'finedays', >+ 'maxsuspensiondays', 'suspension_chargeperiod'], >+ renewals: ['renewalsallowed', [% IF Koha.Preference('UnseenRenewals') %]'unseen_renewals_allowed',[% END %] >+ 'renewalperiod', 'norenewalbefore', 'noautorenewalbefore', 'auto_renew', >+ 'no_auto_renewal_after', 'no_auto_renewal_after_hard_limit'].filter(Boolean), >+ holds: ['reservesallowed', 'holds_per_day', 'holds_per_record', 'onshelfholds', >+ 'opacitemholds', 'holds_pickup_period'], >+ [% IF Koha.Preference('ArticleRequests') %] >+ articleRequests: ['article_requests'], >+ [% END %] >+ rental: ['rentaldiscount'], >+ [% IF Koha.Preference('UseRecalls') %] >+ recalls: ['recalls_allowed', 'recalls_per_record', 'on_shelf_recalls', >+ 'recall_due_date_interval', 'recall_overdue_fine', 'recall_shelf_time'] >+ [% END %] >+ }; >+ >+ // Helper function to get column selector for a group >+ function getColumnSelector(groupNames) { >+ return groupNames.map(name => 'th[data-colname="' + name + '"]').join(','); >+ } >+ >+ // Initialize the table first with kohaTable >+ var table = $("#default-circulation-rules").kohaTable({ > columnDefs: [ >- { visible: false, targets: [0, 2] }, >+ { visible: false, targets: getColumnSelector(['sort-patron', 'sort-item']) }, > { orderable: false, targets: ["_all"] }, > ], > orderFixed: [ >@@ -1455,8 +1512,114 @@ > [3, "asc"], > ], > paging: false, >- autoWidth: false, >+ searching: false, >+ autoWidth: false >+ }); >+ >+ // Get the DataTables API instance >+ var dtApi = table.DataTable(); >+ >+ // Add our custom column visibility buttons >+ const visibilityButtons = [ >+ { >+ text: 'Checkouts', >+ className: 'btn btn-sm btn-default', >+ action: function (e, dt, node, config) { >+ dt.columns().visible(false); >+ dt.columns(getColumnSelector(columnGroups.core.concat(columnGroups.checkout))).visible(true); >+ } >+ }, >+ { >+ text: 'Due dates', >+ className: 'btn btn-sm btn-default', >+ action: function (e, dt, node, config) { >+ dt.columns().visible(false); >+ dt.columns(getColumnSelector(columnGroups.core.concat(columnGroups.dueDate))).visible(true); >+ } >+ }, >+ { >+ text: 'Fines', >+ className: 'btn btn-sm btn-default', >+ action: function (e, dt, node, config) { >+ dt.columns().visible(false); >+ dt.columns(getColumnSelector(columnGroups.core.concat(columnGroups.fines))).visible(true); >+ } >+ }, >+ { >+ text: 'Renewals', >+ className: 'btn btn-sm btn-default', >+ action: function (e, dt, node, config) { >+ dt.columns().visible(false); >+ dt.columns(getColumnSelector(columnGroups.core.concat(columnGroups.renewals))).visible(true); >+ } >+ }, >+ { >+ text: 'Holds', >+ className: 'btn btn-sm btn-default', >+ action: function (e, dt, node, config) { >+ dt.columns().visible(false); >+ dt.columns(getColumnSelector(columnGroups.core.concat(columnGroups.holds))).visible(true); >+ } >+ } >+ ]; >+ >+ [% IF Koha.Preference('ArticleRequests') %] >+ visibilityButtons.push({ >+ text: 'Article requests', >+ className: 'btn btn-sm btn-default', >+ action: function (e, dt, node, config) { >+ dt.columns().visible(false); >+ dt.columns(getColumnSelector(columnGroups.core.concat(columnGroups.articleRequests))).visible(true); >+ } >+ }); >+ [% END %] >+ >+ [% IF Koha.Preference('UseRecalls') %] >+ visibilityButtons.push({ >+ text: 'Recalls', >+ className: 'btn btn-sm btn-default', >+ action: function (e, dt, node, config) { >+ dt.columns().visible(false); >+ dt.columns(getColumnSelector(columnGroups.core.concat(columnGroups.recalls))).visible(true); >+ } > }); >+ [% END %] >+ >+ // Add 'Show all' button at the end >+ visibilityButtons.push({ >+ text: 'Show all', >+ className: 'btn btn-sm btn-default', >+ action: function (e, dt, node, config) { >+ dt.columns().visible(true); >+ dt.columns(getColumnSelector(['sort-patron', 'sort-item'])).visible(false); // Keep sorting columns hidden >+ } >+ }); >+ >+ // Create a button container >+ var buttonContainer = $('<div class="dt-buttons"></div>'); >+ >+ // Add each button manually >+ visibilityButtons.forEach(function(buttonConfig) { >+ var button = $('<button type="button"></button>') >+ .addClass(buttonConfig.className) >+ .text(buttonConfig.text) >+ .on('click', function() { >+ // Remove active class from all buttons >+ buttonContainer.find('button').removeClass('btn-primary').addClass('btn-default'); >+ // Add active class to clicked button >+ $(this).removeClass('btn-default').addClass('btn-primary'); >+ // Execute the button action >+ buttonConfig.action(null, dtApi, $(this), buttonConfig); >+ }); >+ buttonContainer.append(button); >+ }); >+ >+ // Insert into the top area >+ var wrapper = $('#default-circulation-rules_wrapper'); >+ wrapper.find('.top').append(buttonContainer); >+ >+ // Default to Checkouts view on page load >+ buttonContainer.find('button:first').trigger('click'); > }); > > function clear_edit() { >-- >2.52.0 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 41439
:
190455
|
190456
| 190459