Enhance user experience with visual indicators for HTMX actions and streamline button interactions in CID group view
This commit is contained in:
@@ -38,17 +38,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if group_type == 'cid' %}
|
{% if group_type == 'cid' %}
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<form method="post" hx-post="{% url 'generic:cid_group_generate_preview' group.pk %}" hx-target="#cid-generate-preview" hx-swap="innerHTML">
|
<form method="post" hx-post="{% url 'generic:cid_group_generate_preview' group.pk %}" hx-target="#cid-generate-preview" hx-swap="innerHTML">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<label class="small text-muted">Quick generate users (one per line: name,email)</label>
|
<label class="small text-muted">Quick generate users (one per line: name,email)</label>
|
||||||
<textarea name="rows" class="form-control form-control-sm mb-2" rows="5" placeholder="John Smith,j.smith@example.org\nJane Doe,jane@example.org"></textarea>
|
<textarea name="rows" class="form-control form-control-sm mb-2" rows="5" placeholder="John Smith,j.smith@example.org\nJane Doe,jane@example.org"></textarea>
|
||||||
<div>
|
<div>
|
||||||
<button type="submit" class="btn btn-sm btn-outline-primary">Preview</button>
|
<button type="submit" class="btn btn-sm btn-outline-primary">Preview</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div id="cid-generate-preview"></div>
|
<div id="cid-generate-preview"></div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div id="group-users-wrap">
|
<div id="group-users-wrap">
|
||||||
@@ -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 %}
|
||||||
Reference in New Issue
Block a user