Refactor self-review display logic and enhance user answer feedback in collection templates; improve form serialization for timeout handling

This commit is contained in:
Ross
2025-10-13 11:48:13 +01:00
parent 32d8430f94
commit ed4bd95955
3 changed files with 72 additions and 30 deletions
@@ -221,6 +221,8 @@
{{form}} {{form}}
</fieldset> </fieldset>
</div> </div>
{% if question_completed %}
<div> <div>
{% if collection.self_review %} {% if collection.self_review %}
<p> <p>
@@ -240,6 +242,7 @@
<br/> <br/>
{% endif %} {% endif %}
</div> </div>
{% endif %}
{% endif %} {% endif %}
{% if previous %} {% if previous %}
@@ -458,14 +461,54 @@
}; };
htmx.on('htmx:afterRequest', onAfter); htmx.on('htmx:afterRequest', onAfter);
// Serialize the entire form so the server receives the user's answers
// along with the timed_out flag. This ensures the form.save() path
// can validate and persist the submitted answers on timeout.
(function(){
var formValues = {};
try {
$.each($form.serializeArray(), function(i, field) {
if (formValues[field.name] !== undefined) {
if (!Array.isArray(formValues[field.name])) {
formValues[field.name] = [formValues[field.name]];
}
formValues[field.name].push(field.value);
} else {
formValues[field.name] = field.value;
}
});
} catch (e) {
console.debug('Failed to serialize form with jQuery, falling back to manual collection', e);
// Fallback: try to collect inputs manually
var inputs = $form.find('input, textarea, select').not(':disabled');
inputs.each(function () {
var $el = $(this);
var name = $el.attr('name');
if (!name) return;
var val = $el.val();
if (formValues[name] !== undefined) {
if (!Array.isArray(formValues[name])) {
formValues[name] = [formValues[name]];
}
formValues[name].push(val);
} else {
formValues[name] = val;
}
});
}
// Ensure the timed_out flag is included
formValues['timed_out'] = '1';
htmx.ajax('POST', window.location.href, { htmx.ajax('POST', window.location.href, {
values: { timed_out: '1' }, values: formValues,
swap: 'none', swap: 'none',
headers: { headers: {
'X-CSRFToken': document.querySelector('input[name="csrfmiddlewaretoken"]').value, 'X-CSRFToken': document.querySelector('input[name="csrfmiddlewaretoken"]').value,
}, },
target: "#timer-htmx-target", target: "#timer-htmx-target",
}); });
})();
} else { } else {
$timer.text(formatTime(remaining)); $timer.text(formatTime(remaining));
updateProgress(); updateProgress();
@@ -10,21 +10,20 @@
<li class="case"> <li class="case">
<h4>{{forloop.counter}} / Case: {{casedetail.case.title}}</h4> <h4>{{forloop.counter}} / Case: {{casedetail.case.title}}</h4>
{{user_answer.started_at}} - {{user_answer.submitted_at}} Question started: {{user_answer.started_at}} - Answer submitted: {{user_answer.submitted_at}}
{% if request.user.is_superuser and user_answer %} {% if request.user.is_superuser and user_answer %}
&nbsp;(<a href="{% url 'admin:atlas_userreportanswer_change' user_answer.pk %}" target="_blank">Edit in admin</a>) &nbsp;(<a href="{% url 'admin:atlas_userreportanswer_change' user_answer.pk %}" target="_blank">Edit in admin</a>)
{% endif %} {% endif %}
<br/> <br/>
<br/>
{% if not user_answer %} {% if not user_answer %}
<span class="case-not-answered">Case not answered.</span> <span class="case-not-answered">Case not answered.</span>
{% else %} {% else %}
<div class="answer-block"> <div class="answer-block">
User answer:
<div class="user-answer">
{% if user_answer.answer %} {% if user_answer.answer %}
{{user_answer.answer}} {{user_answer.answer}}
{% else %} {% else %}
{{user_answer.json_answer}} {{user_answer.json_answer}}
{% for value, user_answer, correct_answer, answer_is_correct, automark in user_answer.get_correct_json_answers %} {% for value, user_answer, correct_answer, answer_is_correct, automark in user_answer.get_correct_json_answers %}
@@ -58,6 +57,7 @@
{% endfor %} {% endfor %}
{% endif %} {% endif %}
</div> </div>
</div>
{% endif %} {% endif %}
</li> </li>
-1
View File
@@ -2833,7 +2833,6 @@ def collection_case_view_take(
or getattr(request, 'htmx', False) or getattr(request, 'htmx', False)
) )
if is_ajax: if is_ajax:
from django.http import JsonResponse
return JsonResponse( return JsonResponse(
{ {
'status': 'ok', 'status': 'ok',