Enhance CID and User candidate filtering with clear buttons and indicators for improved usability
This commit is contained in:
@@ -15,8 +15,10 @@
|
|||||||
<div class="card h-100">
|
<div class="card h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">CID candidates <small class="text-muted">({{ cid_user_count }})</small></h5>
|
<h5 class="card-title">CID candidates <small class="text-muted">({{ cid_user_count }})</small></h5>
|
||||||
<div class="mb-2">
|
<div class="mb-2 input-group input-group-sm">
|
||||||
<input id="cid-filter" class="form-control form-control-sm" placeholder="Filter CID candidates by CID, name, email or passcode">
|
<input id="cid-filter" class="form-control form-control-sm" placeholder="Filter CID candidates by CID, name, email or passcode">
|
||||||
|
<button type="button" id="cid-filter-clear" class="btn btn-outline-secondary d-none" title="Clear filter">×</button>
|
||||||
|
<span id="cid-filter-indicator" class="input-group-text d-none">Filtered</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="htmx-info"></div>
|
<div id="htmx-info"></div>
|
||||||
<div id="cid-list" hx-get="{% url exam.app_name|add:':exam_cids_cid_list' exam.id %}" hx-trigger="load" hx-swap="innerHTML">
|
<div id="cid-list" hx-get="{% url exam.app_name|add:':exam_cids_cid_list' exam.id %}" hx-trigger="load" hx-swap="innerHTML">
|
||||||
@@ -52,8 +54,10 @@
|
|||||||
<div class="card h-100">
|
<div class="card h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">User candidates <small class="text-muted">({{ user_user_count }})</small></h5>
|
<h5 class="card-title">User candidates <small class="text-muted">({{ user_user_count }})</small></h5>
|
||||||
<div class="mb-2">
|
<div class="mb-2 input-group input-group-sm">
|
||||||
<input id="user-filter" class="form-control form-control-sm" placeholder="Filter User candidates by username or email">
|
<input id="user-filter" class="form-control form-control-sm" placeholder="Filter User candidates by username or email">
|
||||||
|
<button type="button" id="user-filter-clear" class="btn btn-outline-secondary d-none" title="Clear filter">×</button>
|
||||||
|
<span id="user-filter-indicator" class="input-group-text d-none">Filtered</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="user-list" hx-get="{% url exam.app_name|add:':exam_cids_user_list' exam.id %}" hx-trigger="load" hx-swap="innerHTML">
|
<div id="user-list" hx-get="{% url exam.app_name|add:':exam_cids_user_list' exam.id %}" hx-trigger="load" hx-swap="innerHTML">
|
||||||
<ul class="list-group list-group-flush">
|
<ul class="list-group list-group-flush">
|
||||||
@@ -122,16 +126,26 @@
|
|||||||
|
|
||||||
function normalizeText(s){ return (s||'').toString().toLowerCase(); }
|
function normalizeText(s){ return (s||'').toString().toLowerCase(); }
|
||||||
|
|
||||||
function filterList(inputElem, listSelector){
|
function filterList(inputElem, listSelector, indicatorId, clearBtnId){
|
||||||
const q = normalizeText(inputElem.value.trim());
|
const q = normalizeText(inputElem.value.trim());
|
||||||
document.querySelectorAll(listSelector).forEach(function(li){
|
document.querySelectorAll(listSelector).forEach(function(li){
|
||||||
const text = normalizeText(li.textContent);
|
const text = normalizeText(li.textContent || '');
|
||||||
if(!q || text.indexOf(q) !== -1){
|
if(!q || text.indexOf(q) !== -1){
|
||||||
li.style.display = '';
|
try{ li.style.removeProperty('display'); }catch(e){}
|
||||||
|
li.classList.remove('d-none');
|
||||||
} else {
|
} else {
|
||||||
li.style.display = 'none';
|
try{ li.style.setProperty('display','none','important'); }catch(e){ li.style.display = 'none'; }
|
||||||
|
li.classList.add('d-none');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Toggle indicator & clear button
|
||||||
|
try{
|
||||||
|
const ind = document.getElementById(indicatorId);
|
||||||
|
const clearBtn = document.getElementById(clearBtnId);
|
||||||
|
if(ind) ind.classList.toggle('d-none', !q);
|
||||||
|
if(clearBtn) clearBtn.classList.toggle('d-none', !q);
|
||||||
|
}catch(e){}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initTooltips(){
|
function initTooltips(){
|
||||||
@@ -163,10 +177,29 @@
|
|||||||
// Wire filters
|
// Wire filters
|
||||||
document.addEventListener('DOMContentLoaded', function(){
|
document.addEventListener('DOMContentLoaded', function(){
|
||||||
const cidFilter = document.getElementById('cid-filter');
|
const cidFilter = document.getElementById('cid-filter');
|
||||||
if(cidFilter){ cidFilter.addEventListener('input', function(){ filterList(cidFilter, '.col-lg-6:first-of-type .list-group-item'); }); }
|
if(cidFilter && !cidFilter.dataset.bound){
|
||||||
|
cidFilter.addEventListener('input', function(){ filterList(cidFilter, '#cid-list .list-group-item', 'cid-filter-indicator', 'cid-filter-clear'); });
|
||||||
|
cidFilter.dataset.bound = '1';
|
||||||
|
}
|
||||||
|
|
||||||
const userFilter = document.getElementById('user-filter');
|
const userFilter = document.getElementById('user-filter');
|
||||||
if(userFilter){ userFilter.addEventListener('input', function(){ filterList(userFilter, '.col-lg-6:nth-of-type(2) .list-group-item, .col-lg-6:nth-of-type(2) .list-group-item.d-flex'); }); }
|
if(userFilter && !userFilter.dataset.bound){
|
||||||
|
userFilter.addEventListener('input', function(){ filterList(userFilter, '#user-list .list-group-item', 'user-filter-indicator', 'user-filter-clear'); });
|
||||||
|
userFilter.dataset.bound = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear buttons
|
||||||
|
const cidClear = document.getElementById('cid-filter-clear');
|
||||||
|
if(cidClear && !cidClear.dataset.bound){
|
||||||
|
cidClear.addEventListener('click', function(){ const f=document.getElementById('cid-filter'); if(f){ f.value=''; filterList(f, '#cid-list .list-group-item', 'cid-filter-indicator', 'cid-filter-clear'); }});
|
||||||
|
cidClear.dataset.bound = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
const userClear = document.getElementById('user-filter-clear');
|
||||||
|
if(userClear && !userClear.dataset.bound){
|
||||||
|
userClear.addEventListener('click', function(){ const f=document.getElementById('user-filter'); if(f){ f.value=''; filterList(f, '#user-list .list-group-item', 'user-filter-indicator', 'user-filter-clear'); }});
|
||||||
|
userClear.dataset.bound = '1';
|
||||||
|
}
|
||||||
|
|
||||||
// initial runs (may be no partials yet)
|
// initial runs (may be no partials yet)
|
||||||
initTooltips();
|
initTooltips();
|
||||||
@@ -184,6 +217,14 @@
|
|||||||
initTooltips();
|
initTooltips();
|
||||||
// submitted-list may have loaded now; re-run highlighting
|
// submitted-list may have loaded now; re-run highlighting
|
||||||
highlightSubmitted();
|
highlightSubmitted();
|
||||||
|
|
||||||
|
// Re-run current filters for newly-loaded partials
|
||||||
|
try{
|
||||||
|
const cf = document.getElementById('cid-filter');
|
||||||
|
if(cf) filterList(cf, '#cid-list .list-group-item');
|
||||||
|
const uf = document.getElementById('user-filter');
|
||||||
|
if(uf) filterList(uf, '#user-list .list-group-item');
|
||||||
|
}catch(e){ /* ignore */ }
|
||||||
}
|
}
|
||||||
}catch(e){ console.error(e); }
|
}catch(e){ console.error(e); }
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user