Refactor exam overview template for improved readability and consistency in HTML structure

This commit is contained in:
Ross
2026-01-05 14:22:33 +00:00
parent 42d209af5a
commit b837c406cf
@@ -2,11 +2,11 @@
{% block content %} {% block content %}
<span id="user-id"> <span id="user-id">
{% if not cid %} {% if not cid %}
User: {{request.user}} User: {{request.user}}
{% else %} {% else %}
CID: {{cid}} CID: {{cid}}
{% endif %} {% endif %}
</span> </span>
{% include "exam_clock.html" %} {% include "exam_clock.html" %}
@@ -14,7 +14,7 @@
{% if exam.publish_results %} {% if exam.publish_results %}
<div class="alert alert-primary review-mode-alert" role="alert"> <div class="alert alert-primary review-mode-alert" role="alert">
Exam is in review mode. Score are available Exam is in review mode. Score are available
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<a href="{% url 'physics:exam_scores_user' pk=exam.id %}">here</a> <a href="{% url 'physics:exam_scores_user' pk=exam.id %}">here</a>
@@ -24,7 +24,7 @@
</div> </div>
{% elif cid_user_exam.completed %} {% elif cid_user_exam.completed %}
<div class="alert alert-primary review-mode-alert" role="alert"> <div class="alert alert-primary review-mode-alert" role="alert">
Exam completed. Answers are available Exam completed. Answers are available
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<a href="{% url 'physics:exam_scores_user' pk=exam.id %}">here</a> <a href="{% url 'physics:exam_scores_user' pk=exam.id %}">here</a>
@@ -37,19 +37,20 @@
You have unanswered questions. You have unanswered questions.
</div> </div>
{% endif %} {% endif %}
<div class="overview-text">{{answer_count}} out of {{exam_length}} questions answered. Unanswered questions are shown in red. <span class="d-block">Click any tile to jump to that question.</span></div> <div class="overview-text">{{answer_count}} out of {{exam_length}} questions answered. Unanswered questions are shown in red. <span class="d-block">Click any tile to jump to that question.</span></div>
<div class="physics-finish-list" role="list"> <div class="physics-finish-list" role="list">
{% for question, answer, flagged in question_answer_tuples %} {% for question, answer, flagged in question_answer_tuples %}
{% comment %} Use an anchor styled as a button rather than nesting button inside anchor (invalid HTML). Keep link targets unchanged. {% endcomment %} {% comment %} Use an anchor styled as a button rather than nesting button inside anchor (invalid HTML). Keep link targets unchanged. {% endcomment %}
{% if not cid %} {% if not cid %}
<a role="listitem" href="{% url 'physics:exam_take_user' pk=exam.id sk=forloop.counter0 %}" <a role="listitem" href="{% url 'physics:exam_take_user' pk=exam.id sk=forloop.counter0 %}"
class="overview-question-btn btn btn-outline-secondary {% if flagged %}flagged{% endif %}" class="overview-question-btn btn btn-outline-secondary {% if flagged %}flagged{% endif %}"
aria-label="Go to question {{ forloop.counter }}" aria-label="Go to question {{ forloop.counter }}"
{% if not answer %}title="You have not answered this question" aria-current="false"{% endif %}> {% if not answer %}title="You have not answered this question" aria-current="false"{% endif %}>
{{forloop.counter}}{% if answer and answer.answer %}: {{answer.answer}}{% endif %} <span class="qn-num">{{forloop.counter}}</span>{% if answer and answer.answer %}<span class="qn-ans">: {{answer.answer}}</span>{% endif %}
{% if flagged %}<span class="question-flag" aria-hidden="true"></span>{% endif %}
</a> </a>
{% else %} {% else %}
@@ -57,15 +58,16 @@
class="overview-question-btn btn btn-outline-secondary {% if flagged %}flagged{% endif %}" class="overview-question-btn btn btn-outline-secondary {% if flagged %}flagged{% endif %}"
aria-label="Go to question {{ forloop.counter }}" aria-label="Go to question {{ forloop.counter }}"
{% if not answer %}title="You have not answered this question" aria-current="false"{% endif %}> {% if not answer %}title="You have not answered this question" aria-current="false"{% endif %}>
{{forloop.counter}}{% if answer and answer.answer %}: {{answer.answer}}{% endif %} <span class="qn-num">{{forloop.counter}}</span>{% if answer and answer.answer %}<span class="qn-ans">: {{answer.answer}}</span>{% endif %}
{% if flagged %}<span class="question-flag" aria-hidden="true"></span>{% endif %}
</a> </a>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</div> </div>
<div id="time-details"> <div id="time-details">
Start time: {{cid_user_exam.start_time}}<br/> Start time: {{cid_user_exam.start_time}}<br/>
Last change time: {{cid_user_exam.end_time}}<br/> Last change time: {{cid_user_exam.end_time}}<br/>
Completed: {{cid_user_exam.completed}} Completed: {{cid_user_exam.completed}}
</div> </div>
{% if not cid_user_exam.completed %} {% if not cid_user_exam.completed %}
@@ -97,69 +99,71 @@
{% endblock %} {% endblock %}
{% block css %} {% block css %}
<style> <style>
/* Overview page specific styles */ /* Overview page specific styles */
.overview-text { .overview-text {
margin-bottom: 1rem; margin-bottom: 1rem;
font-weight: 500; font-weight: 500;
} }
.physics-finish-list { .physics-finish-list {
display: grid; display: grid;
/* Limit the maximum width of each tile so buttons don't stretch too wide on large screens. /* Limit the maximum width of each tile so buttons don't stretch too wide on large screens.
Each tile will be between 80px and 140px. The grid will wrap responsively. */ Each tile will be between 80px and 140px. The grid will wrap responsively. */
grid-template-columns: repeat(auto-fit, minmax(80px, 140px)); grid-template-columns: repeat(auto-fit, minmax(80px, 140px));
justify-content: start; justify-content: start;
gap: .5rem; gap: .5rem;
align-items: stretch; align-items: stretch;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.overview-question-btn { .overview-question-btn {
/* Fill the grid cell but the cell itself is capped by the grid-template-columns above. */ /* Fill the grid cell but the cell itself is capped by the grid-template-columns above. */
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
text-align: left; text-align: left;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
padding: .5rem .75rem; padding: .5rem .75rem;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
} }
/* Flagged question indicator: use border colour to signal a flagged item */ /* Flagged questions show only the flag icon; do not change border color */
.overview-question-btn.flagged {
border-color: #ffc107 !important; .overview-question-btn .question-flag {
box-shadow: 0 1px 2px rgba(255,193,7,0.06); margin-left: auto;
background: rgba(255,193,7,0.03); font-size: 0.95rem;
} color: #ffc107;
line-height: 1;
}
/* Unanswered questions should draw attention */ /* Unanswered questions should draw attention */
.overview-question-btn[title] { .overview-question-btn[title] {
border-color: #dc3545 !important; border-color: #dc3545 !important;
color: #dc3545 !important; color: #dc3545 !important;
background: rgba(220,53,69,0.03); background: rgba(220,53,69,0.03);
} }
#time-details { #time-details {
font-size: .95rem; font-size: .95rem;
color: #9aa0a6; color: #9aa0a6;
margin-top: .5rem; margin-top: .5rem;
} }
/* Make the finish button stand out on small screens */ /* Make the finish button stand out on small screens */
.btn-primary.mt-3 { .btn-primary.mt-3 {
min-width: 160px; min-width: 160px;
} }
@media (max-width: 575.98px) { @media (max-width: 575.98px) {
.physics-finish-list { .physics-finish-list {
/* On very small screens allow slightly narrower tiles but still wrap */ /* On very small screens allow slightly narrower tiles but still wrap */
grid-template-columns: repeat(auto-fit, minmax(70px, 110px)); grid-template-columns: repeat(auto-fit, minmax(70px, 110px));
}
.overview-question-btn { font-size: .95rem }
} }
</style> .overview-question-btn { font-size: .95rem }
}
</style>
{% endblock %} {% endblock %}