diff --git a/physics/templates/physics/partials/exam_take_overview_fragment.html b/physics/templates/physics/partials/exam_take_overview_fragment.html
new file mode 100644
index 00000000..79742fef
--- /dev/null
+++ b/physics/templates/physics/partials/exam_take_overview_fragment.html
@@ -0,0 +1,68 @@
+
+
Overview: {{ exam }}
+
{{ answer_count }} / {{ exam_length }} answered
+
+
+
+ {% for q, ans in question_answer_tuples %}
+ -
+
+
+
{{ q.stem|truncatechars:140|safe }}
+
+ {% if ans %}
+ {% if ans.a or ans.b or ans.c or ans.d or ans.e %}
+ Answered
+ {% else %}
+ Unanswered
+ {% endif %}
+ {% else %}
+ Unanswered
+ {% endif %}
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
diff --git a/physics/views.py b/physics/views.py
index 7f4c26db..b09ec09c 100644
--- a/physics/views.py
+++ b/physics/views.py
@@ -282,6 +282,42 @@ def exam_take(request, pk: int, sk: int, cid: str | None = None, passcode: str |
return exam_take_fragment(request, pk=pk, sk=pos - 1, cid=cid, passcode=passcode)
return redirect(take_url, sk=pos - 1, **kwargs)
elif "finish" in request.POST:
+ # For HTMX clients, return an overview fragment so the page can
+ # update in-place instead of performing a full-page redirect.
+ if is_htmx:
+ # Build the same context used by exam_take_overview
+ questions = exam.get_questions()
+ if cid is not None:
+ answers = UserAnswer.objects.filter(cid=cid, exam=exam)
+ else:
+ answers = UserAnswer.objects.filter(user=request.user, exam=exam)
+
+ answer_question_map = {ans.question: ans for ans in answers}
+
+ question_answer_tuples = []
+ answer_count = 0
+ for q in questions:
+ if q in answer_question_map:
+ question_answer_tuples.append((q, answer_question_map[q]))
+ answer_count += 1
+ else:
+ question_answer_tuples.append((q, None))
+
+ cid_user_exam = exam.get_or_create_cid_user_exam(cid=cid, user_user=request.user)
+
+ return render(
+ request,
+ "physics/partials/exam_take_overview_fragment.html",
+ {
+ "exam": exam,
+ "cid": cid,
+ "question_answer_tuples": question_answer_tuples,
+ "answer_count": answer_count,
+ "exam_length": len(questions),
+ "passcode": passcode,
+ "cid_user_exam": cid_user_exam,
+ },
+ )
return redirect(finish_url, **kwargs)
elif "save" in request.POST:
# Save action should not trigger a full-page load for HTMX clients.