Enhance case list item links and management actions: update hrefs for dynamic case numbering and improve button structure for better accessibility
This commit is contained in:
@@ -3,8 +3,12 @@
|
|||||||
<div class="flex-fill">
|
<div class="flex-fill">
|
||||||
<div class="d-flex justify-content-between align-items-start">
|
<div class="d-flex justify-content-between align-items-start">
|
||||||
<div>
|
<div>
|
||||||
<a class="h6 mb-0 text-decoration-none" href="{% url 'atlas:collection_case_view' pk=collection.pk case_number=forloop.counter0 %}">{{ casedetail.case.title }}</a>
|
<a class="h6 mb-0 text-decoration-none case-link"
|
||||||
<div class="small text-muted">Case {{ forloop.counter }} — <span class="text-truncate">{{ casedetail.case.description|default_if_none:""|truncatechars:80 }}</span></div>
|
href="{% url 'atlas:collection_case_view' pk=collection.pk case_number=forloop.counter0 %}"
|
||||||
|
data-case-base-href="{% url 'atlas:collection_case_view' pk=collection.pk case_number=0 %}">
|
||||||
|
{{ casedetail.case.title }}
|
||||||
|
</a>
|
||||||
|
<div class="small text-muted"><span class="case-number">Case {{ forloop.counter }}</span> — <span class="text-truncate">{{ casedetail.case.description|default_if_none:""|truncatechars:80 }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<span class="btn-group btn-group-sm me-2" role="group" aria-label="management-actions">
|
<div class="btn-group btn-group-sm me-2" role="group" aria-label="management-actions">
|
||||||
<a class="btn btn-outline-secondary" href="{% url 'atlas:collection_case_displaysetup' casedetail.collection.pk casedetail.case.pk %}" title="Setup default display">
|
<a class="btn btn-outline-secondary" href="{% url 'atlas:collection_case_displaysetup' casedetail.collection.pk casedetail.case.pk %}" title="Setup default display">
|
||||||
<i class="bi bi-display"></i>
|
<i class="bi bi-display"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -30,4 +30,13 @@
|
|||||||
<i class="bi bi-check"></i>
|
<i class="bi bi-check"></i>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</div>
|
||||||
|
|
||||||
|
{# Remove button (HTMX) - only shown when the surrounding template has `can_edit` true #}
|
||||||
|
{% if can_edit %}
|
||||||
|
<form class="d-inline ms-2 m-0" hx-post="{% url 'atlas:remove_case_from_collection' casedetail.case.pk casedetail.collection.pk %}" hx-target="closest li" hx-swap="outerHTML" hx-confirm="Remove this case from the collection?">
|
||||||
|
<button type="submit" class="btn btn-outline-danger btn-sm case-remove" title="Remove this case from the collection">
|
||||||
|
<i class="bi bi-trash"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
@@ -155,9 +155,29 @@
|
|||||||
for (var i = ul.children.length; i >= 0; i--) {
|
for (var i = ul.children.length; i >= 0; i--) {
|
||||||
ul.appendChild(ul.children[Math.random() * i | 0]);
|
ul.appendChild(ul.children[Math.random() * i | 0]);
|
||||||
}
|
}
|
||||||
|
renumber();
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
// helper to renumber visible cases and update links
|
||||||
|
function renumber() {
|
||||||
|
$("#full-question-list li").each((i, e) => {
|
||||||
|
// update Case N label
|
||||||
|
$(e).find('.case-number').text('Case ' + (i + 1));
|
||||||
|
|
||||||
|
// update link href based on data-case-base-href (replace trailing 0)
|
||||||
|
const a = $(e).find('.case-link');
|
||||||
|
if (a.length) {
|
||||||
|
const base = a.attr('data-case-base-href');
|
||||||
|
if (base) {
|
||||||
|
// replace trailing '/0' with new index
|
||||||
|
const newHref = base.replace(/\/0$/, '/' + i);
|
||||||
|
a.attr('href', newHref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$("#full-question-list li").each((n, el) => {
|
$("#full-question-list li").each((n, el) => {
|
||||||
// Add up/down arrows at the start of each <li>
|
// Add up/down arrows at the start of each <li>
|
||||||
const upBtn = $("<button class='move-up btn btn-sm' title='Move up'>↑</button>").click(function(e){
|
const upBtn = $("<button class='move-up btn btn-sm' title='Move up'>↑</button>").click(function(e){
|
||||||
@@ -166,6 +186,7 @@
|
|||||||
const prev = li.prev();
|
const prev = li.prev();
|
||||||
if (prev.length) {
|
if (prev.length) {
|
||||||
prev.before(li);
|
prev.before(li);
|
||||||
|
renumber();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const downBtn = $("<button class='move-down btn btn-sm' title='Move down'>↓</button>").click(function(e){
|
const downBtn = $("<button class='move-down btn btn-sm' title='Move down'>↓</button>").click(function(e){
|
||||||
@@ -174,16 +195,23 @@
|
|||||||
const next = li.next();
|
const next = li.next();
|
||||||
if (next.length) {
|
if (next.length) {
|
||||||
next.after(li);
|
next.after(li);
|
||||||
|
renumber();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$(el).prepend(downBtn).prepend(upBtn);
|
$(el).prepend(downBtn).prepend(upBtn);
|
||||||
|
|
||||||
// Existing delete button logic
|
// Prefer server-side HTMX remove button when present; otherwise keep a fallback
|
||||||
|
if ($(el).find('.case-remove').length === 0) {
|
||||||
|
// fallback: append an inline client-side remove button (for contexts without server-side remove)
|
||||||
$(el).append($(
|
$(el).append($(
|
||||||
"<span class='exam-question-delete flex-col'><button title='Remove this question from the exam/collection'>Remove</button></span>"
|
"<span class='exam-question-delete flex-col'><button title='Remove this question from the exam/collection'>Remove</button></span>"
|
||||||
).click(() => {
|
).click(() => {
|
||||||
el.remove();
|
el.remove();
|
||||||
|
renumber();
|
||||||
}));
|
}));
|
||||||
|
} else {
|
||||||
|
// If HTMX remove exists it will handle the server call and swap; renumber after swap via htmx:afterSwap
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#full-question-list").after($(
|
$("#full-question-list").after($(
|
||||||
@@ -208,6 +236,7 @@
|
|||||||
|
|
||||||
if (data.status == "success") {
|
if (data.status == "success") {
|
||||||
toastr.info('Exam order changed.')
|
toastr.info('Exam order changed.')
|
||||||
|
renumber();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.always(function () {
|
.always(function () {
|
||||||
@@ -220,6 +249,20 @@
|
|||||||
).click(() => {
|
).click(() => {
|
||||||
location.reload();
|
location.reload();
|
||||||
}));
|
}));
|
||||||
|
// Keep list numbering in sync after HTMX swaps that affect the case list
|
||||||
|
document.body.addEventListener('htmx:afterSwap', function(evt){
|
||||||
|
try {
|
||||||
|
const target = evt.detail && evt.detail.target ? evt.detail.target : null;
|
||||||
|
if (!target) return;
|
||||||
|
if (target.id === 'full-question-list' || (target.closest && target.closest('#full-question-list'))) {
|
||||||
|
setTimeout(function(){
|
||||||
|
if (typeof renumber === 'function') renumber();
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.error('htmx afterSwap renumber error', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user