Refactor exam review fragment inclusion to support app-specific templates and improve HTMX handling
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-2"><span class="badge bg-info">Anatomy</span></div>
|
||||||
|
<h5 class="card-title">Question</h5>
|
||||||
|
<p class="lead">{{ question }}</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary" href="{% url app_name|add:':question_detail' question.pk %}" target="_blank">View full question details</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between mt-3">
|
||||||
|
{% if prev_index is not None %}
|
||||||
|
<a class="btn btn-outline-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk prev_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">← Previous</a>
|
||||||
|
{% else %}
|
||||||
|
<span></span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if next_index is not None %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk next_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">Next →</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-success" href="{% url app_name|add:':exam_overview' exam.pk %}">Complete review</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-2"><span class="badge bg-secondary">Atlas</span></div>
|
||||||
|
<h5 class="card-title">Question</h5>
|
||||||
|
<p class="lead">{{ question }}</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary" href="{% url app_name|add:':question_detail' question.pk %}" target="_blank">View full question details</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between mt-3">
|
||||||
|
{% if prev_index is not None %}
|
||||||
|
<a class="btn btn-outline-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk prev_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">← Previous</a>
|
||||||
|
{% else %}
|
||||||
|
<span></span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if next_index is not None %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk next_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">Next →</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-success" href="{% url app_name|add:':exam_overview' exam.pk %}">Complete review</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -6,7 +6,10 @@
|
|||||||
<div id="review-root">
|
<div id="review-root">
|
||||||
{# The #review-content element is replaced by HTMX when navigating between questions. #}
|
{# The #review-content element is replaced by HTMX when navigating between questions. #}
|
||||||
<div id="review-content">
|
<div id="review-content">
|
||||||
{% include 'generic/partials/exam_review_question_fragment.html' %}
|
{# The view selects an app-specific fragment if present and passes its name
|
||||||
|
in `fragment_template`. Include that fragment here so the full-page
|
||||||
|
render and the HTMX partials both use the same template. #}
|
||||||
|
{% include fragment_template %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+25
-2
@@ -1216,8 +1216,31 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
"can_edit": self.check_user_edit_access(request.user, exam_id=pk),
|
"can_edit": self.check_user_edit_access(request.user, exam_id=pk),
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.htmx:
|
# Choose an app-specific fragment if present, otherwise fall back to
|
||||||
return render(request, "generic/partials/exam_review_question_fragment.html", context)
|
# the generic fragment. Expose the chosen fragment name in the
|
||||||
|
# context so the full-page render can include it; return the fragment
|
||||||
|
# directly for HTMX requests.
|
||||||
|
from django.template import loader
|
||||||
|
|
||||||
|
fragment_candidates = [
|
||||||
|
f"{self.app_name}/partials/exam_review_question_fragment.html",
|
||||||
|
"generic/partials/exam_review_question_fragment.html",
|
||||||
|
]
|
||||||
|
template = loader.select_template(fragment_candidates)
|
||||||
|
fragment_template_name = template.template.name
|
||||||
|
context["fragment_template"] = fragment_template_name
|
||||||
|
|
||||||
|
# Detect HTMX: prefer the request.htmx attribute when available, otherwise
|
||||||
|
# check the HX-Request header.
|
||||||
|
is_htmx = getattr(request, "htmx", False)
|
||||||
|
if not is_htmx:
|
||||||
|
try:
|
||||||
|
is_htmx = request.headers.get("HX-Request", "").lower() == "true"
|
||||||
|
except Exception:
|
||||||
|
is_htmx = request.META.get("HTTP_HX_REQUEST", "") == "true"
|
||||||
|
|
||||||
|
if is_htmx:
|
||||||
|
return render(request, fragment_template_name, context)
|
||||||
|
|
||||||
return render(request, "generic/exam_review_question.html", context)
|
return render(request, "generic/exam_review_question.html", context)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-2"><span class="badge bg-secondary">Longs</span></div>
|
||||||
|
<h5 class="card-title">Question</h5>
|
||||||
|
<p class="lead">{{ question }}</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary" href="{% url app_name|add:':question_detail' question.pk %}" target="_blank">View full question details</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between mt-3">
|
||||||
|
{% if prev_index is not None %}
|
||||||
|
<a class="btn btn-outline-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk prev_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">← Previous</a>
|
||||||
|
{% else %}
|
||||||
|
<span></span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if next_index is not None %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk next_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">Next →</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-success" href="{% url app_name|add:':exam_overview' exam.pk %}">Complete review</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-2"><span class="badge bg-primary">Physics</span></div>
|
||||||
|
<h5 class="card-title">Question</h5>
|
||||||
|
<p class="lead">{{ question }}</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary" href="{% url app_name|add:':question_detail' question.pk %}" target="_blank">View full question details</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between mt-3">
|
||||||
|
{% if prev_index is not None %}
|
||||||
|
<a class="btn btn-outline-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk prev_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">← Previous</a>
|
||||||
|
{% else %}
|
||||||
|
<span></span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if next_index is not None %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk next_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">Next →</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-success" href="{% url app_name|add:':exam_overview' exam.pk %}">Complete review</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-2"><span class="badge bg-warning text-dark">Rapids</span></div>
|
||||||
|
<h5 class="card-title">Question</h5>
|
||||||
|
<p class="lead">{{ question }}</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary" href="{% url app_name|add:':question_detail' question.pk %}" target="_blank">View full question details</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between mt-3">
|
||||||
|
{% if prev_index is not None %}
|
||||||
|
<a class="btn btn-outline-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk prev_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">← Previous</a>
|
||||||
|
{% else %}
|
||||||
|
<span></span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if next_index is not None %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk next_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">Next →</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-success" href="{% url app_name|add:':exam_overview' exam.pk %}">Complete review</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-2"><span class="badge bg-dark">SBAs</span></div>
|
||||||
|
<h5 class="card-title">Question</h5>
|
||||||
|
<p class="lead">{{ question }}</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary" href="{% url app_name|add:':question_detail' question.pk %}" target="_blank">View full question details</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between mt-3">
|
||||||
|
{% if prev_index is not None %}
|
||||||
|
<a class="btn btn-outline-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk prev_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">← Previous</a>
|
||||||
|
{% else %}
|
||||||
|
<span></span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if next_index is not None %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk next_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">Next →</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-success" href="{% url app_name|add:':exam_overview' exam.pk %}">Complete review</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-2"><span class="badge bg-success">Shorts</span></div>
|
||||||
|
<h5 class="card-title">Question</h5>
|
||||||
|
<p class="lead">{{ question }}</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary" href="{% url app_name|add:':question_detail' question.pk %}" target="_blank">View full question details</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between mt-3">
|
||||||
|
{% if prev_index is not None %}
|
||||||
|
<a class="btn btn-outline-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk prev_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">← Previous</a>
|
||||||
|
{% else %}
|
||||||
|
<span></span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if next_index is not None %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
hx-get="{% url app_name|add:':exam_review_question' exam.pk next_index %}"
|
||||||
|
hx-target="#review-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url="true">Next →</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-success" href="{% url app_name|add:':exam_overview' exam.pk %}">Complete review</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user