Implement timed-out submission handling and AJAX response for collection case view
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
<div id="question-timer-progress-inner" style="width:100%; height:100%; background:var(--timer-color, #28a745);"></div>
|
<div id="question-timer-progress-inner" style="width:100%; height:100%; background:var(--timer-color, #28a745);"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="timer-htmx-target" style="display:none;"></div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@@ -414,15 +415,35 @@
|
|||||||
var $next = $form.find('button[name="next"]');
|
var $next = $form.find('button[name="next"]');
|
||||||
var $finish = $form.find('button[name="finish"]');
|
var $finish = $form.find('button[name="finish"]');
|
||||||
|
|
||||||
if ($next.length) {
|
// Instead of navigating away, submit via fetch with timed_out flag
|
||||||
$next.prop('disabled', false);
|
// Use HTMX to POST the timed_out flag and then lock the UI
|
||||||
$next.click();
|
var onAfter = function (evt) {
|
||||||
} else if ($finish.length) {
|
try {
|
||||||
$finish.prop('disabled', false);
|
// lock the form UI in-place
|
||||||
$finish.click();
|
$form.find('input, textarea, select, button').prop('disabled', true);
|
||||||
} else {
|
$form.addClass('timed-out-locked');
|
||||||
$form.submit();
|
$('#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 {
|
} else {
|
||||||
$timer.text(formatTime(remaining));
|
$timer.text(formatTime(remaining));
|
||||||
updateProgress();
|
updateProgress();
|
||||||
|
|||||||
@@ -2786,11 +2786,33 @@ def collection_case_view_take(
|
|||||||
raise Http404("Self review not enabled")
|
raise Http404("Self review not enabled")
|
||||||
answer.completed = True
|
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()
|
answer.save()
|
||||||
|
|
||||||
cid_user_exam.end_time = timezone.now()
|
cid_user_exam.end_time = timezone.now()
|
||||||
cid_user_exam.save()
|
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:
|
if cid is not None:
|
||||||
kwargs = {"pk": pk, "cid": cid, "passcode": passcode}
|
kwargs = {"pk": pk, "cid": cid, "passcode": passcode}
|
||||||
redirect_url = "atlas:collection_case_view_take"
|
redirect_url = "atlas:collection_case_view_take"
|
||||||
|
|||||||
Reference in New Issue
Block a user