Enhance user experience with visual indicators for HTMX actions and streamline button interactions in CID group view

This commit is contained in:
Ross
2025-12-22 10:44:50 +00:00
parent d3d0e865c3
commit a02f274a4e
@@ -104,15 +104,15 @@
<form class="m-0 d-inline"> <form class="m-0 d-inline">
<input type="hidden" name="exam_id" value="{{ exam.pk }}"> <input type="hidden" name="exam_id" value="{{ exam.pk }}">
<input type="hidden" name="exam_type" value="{{ exam.app_name }}"> <input type="hidden" name="exam_type" value="{{ exam.app_name }}">
<button class="btn btn-sm btn-outline-success" hx-post="{% url 'generic:cid_group_add_candidates_to_exams' group.pk %}" name="action" value="add">Add</button> <button class="btn btn-sm btn-outline-success hx-action-btn" hx-post="{% url 'generic:cid_group_add_candidates_to_exams' group.pk %}" name="action" value="add">Add</button>
<button class="btn btn-sm btn-outline-danger" hx-post="{% url 'generic:cid_group_add_candidates_to_exams' group.pk %}" name="action" value="remove">Remove</button> <button class="btn btn-sm btn-outline-danger hx-action-btn" hx-post="{% url 'generic:cid_group_add_candidates_to_exams' group.pk %}" name="action" value="remove">Remove</button>
</form> </form>
{% else %} {% else %}
<form class="m-0 d-inline"> <form class="m-0 d-inline">
<input type="hidden" name="exam_id" value="{{ exam.pk }}"> <input type="hidden" name="exam_id" value="{{ exam.pk }}">
<input type="hidden" name="exam_type" value="{{ exam.app_name }}"> <input type="hidden" name="exam_type" value="{{ exam.app_name }}">
<button class="btn btn-sm btn-outline-success" hx-post="{% url 'generic:user_group_add_candidates_to_exams' group.pk %}" name="action" value="add">Add</button> <button class="btn btn-sm btn-outline-success hx-action-btn" hx-post="{% url 'generic:user_group_add_candidates_to_exams' group.pk %}" name="action" value="add">Add</button>
<button class="btn btn-sm btn-outline-danger" hx-post="{% url 'generic:user_group_add_candidates_to_exams' group.pk %}" name="action" value="remove">Remove</button> <button class="btn btn-sm btn-outline-danger hx-action-btn" hx-post="{% url 'generic:user_group_add_candidates_to_exams' group.pk %}" name="action" value="remove">Remove</button>
</form> </form>
{% endif %} {% endif %}
</div> </div>
@@ -205,5 +205,47 @@
.finally(()=>{ if(btn){ const s = btn.querySelector('.spinner-border'); if(s) s.remove(); btn.disabled = false; } }); .finally(()=>{ if(btn){ const s = btn.querySelector('.spinner-border'); if(s) s.remove(); btn.disabled = false; } });
}); });
}); });
// Visual indicators for HTMX add/remove actions
document.addEventListener('DOMContentLoaded', function(){
document.body.addEventListener('htmx:beforeRequest', function(evt){
try{
const elt = evt.detail.elt;
if(!elt) return;
const btn = elt.closest && elt.closest('.hx-action-btn') ? elt.closest('.hx-action-btn') : (elt.classList && elt.classList.contains('hx-action-btn') ? elt : null);
if(!btn) return;
// stash original HTML for restore
btn.dataset._origHtml = btn.innerHTML;
btn.disabled = true;
const spinner = document.createElement('span');
spinner.className = 'spinner-border spinner-border-sm ms-2';
spinner.setAttribute('role','status'); spinner.setAttribute('aria-hidden','true');
spinner.dataset._hx_spinner = '1';
btn.appendChild(spinner);
}catch(e){ console.error(e); }
});
document.body.addEventListener('htmx:afterRequest', function(evt){
try{
const elt = evt.detail.elt;
if(!elt) return;
const btn = elt.closest && elt.closest('.hx-action-btn') ? elt.closest('.hx-action-btn') : (elt.classList && elt.classList.contains('hx-action-btn') ? elt : null);
if(!btn) return;
// remove spinner(s)
const sp = btn.querySelectorAll('span[data-_hx_spinner]');
sp.forEach(s => s.remove());
// show transient status
const status = document.createElement('span');
status.className = 'badge bg-success ms-2 hx-action-status';
status.textContent = 'Done';
btn.appendChild(status);
// restore after short delay
setTimeout(function(){
if(btn.dataset._origHtml){ btn.innerHTML = btn.dataset._origHtml; delete btn.dataset._origHtml; }
btn.disabled = false;
}, 1500);
}catch(e){ console.error(e); }
});
});
</script> </script>
{% endblock %} {% endblock %}