Implement timed-out submission handling and AJAX response for collection case view

This commit is contained in:
Ross
2025-10-13 10:32:25 +01:00
parent 1cf18c6f0d
commit fa08f9cf76
2 changed files with 52 additions and 9 deletions
@@ -25,6 +25,7 @@
<div id="question-timer-progress-inner" style="width:100%; height:100%; background:var(--timer-color, #28a745);"></div>
</div>
</div>
<div id="timer-htmx-target" style="display:none;"></div>
</div>
{% endif %}
@@ -414,15 +415,35 @@
var $next = $form.find('button[name="next"]');
var $finish = $form.find('button[name="finish"]');
if ($next.length) {
$next.prop('disabled', false);
$next.click();
} else if ($finish.length) {
$finish.prop('disabled', false);
$finish.click();
} else {
$form.submit();
}
// Instead of navigating away, submit via fetch with timed_out flag
// Use HTMX to POST the timed_out flag and then lock the UI
var onAfter = function (evt) {
try {
// lock the form UI in-place
$form.find('input, textarea, select, button').prop('disabled', true);
$form.addClass('timed-out-locked');
$('#question-timer').text('Locked');
// hide the progress bar
$('#question-timer-progress, #question-timer-progress-outer, #question-timer-progress-inner').hide();
// ensure inner shows empty state if something relies on it
$('#question-timer-progress-inner').css({ width: '0%', background: '#dc3545' });
} catch (e) {
console.error('Error locking UI after htmx request', e);
} finally {
htmx.off('htmx:afterRequest', onAfter);
}
};
htmx.on('htmx:afterRequest', onAfter);
htmx.ajax('POST', window.location.href, {
values: { timed_out: '1' },
swap: 'none',
headers: {
'X-CSRFToken': document.querySelector('input[name="csrfmiddlewaretoken"]').value,
},
target: "#timer-htmx-target",
});
} else {
$timer.text(formatTime(remaining));
updateProgress();
+22
View File
@@ -2786,11 +2786,33 @@ def collection_case_view_take(
raise Http404("Self review not enabled")
answer.completed = True
# If this was a timed-out submission, mark as completed
if request.POST.get('timed_out') == '1':
answer.completed = True
answer.save()
cid_user_exam.end_time = timezone.now()
cid_user_exam.save()
# If this was an AJAX/HTMX request, return JSON so the client
# can remain on the same page and update the UI without redirect.
# Detect HTMX/XHR requests robustly. HTMX sets the HX-Request header.
is_ajax = (
request.headers.get('HX-Request', '').lower() == 'true'
or request.headers.get('x-requested-with') == 'XMLHttpRequest'
or getattr(request, 'htmx', False)
)
if is_ajax:
from django.http import JsonResponse
return JsonResponse(
{
'status': 'ok',
'locked': answer.completed,
'submitted_at': answer.submitted_at.isoformat() if getattr(answer, 'submitted_at', None) else None,
}
)
if cid is not None:
kwargs = {"pk": pk, "cid": cid, "passcode": passcode}
redirect_url = "atlas:collection_case_view_take"