Add case jump functionality with HTMX support and corresponding template
This commit is contained in:
@@ -3,19 +3,34 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<span class="collection-name-blend">Collection: {{collection}}</span>
|
<span class="collection-name-blend">Collection: {{collection}}</span>
|
||||||
<h2>Case {{case_number|add:1}}
|
<div class="d-flex justify-content-between align-items-start">
|
||||||
|
<h2 class="mb-0">Case {{case_number|add:1}}
|
||||||
|
|
||||||
{% if show_title %}
|
{% if show_title %}
|
||||||
: {{case.title}}
|
: {{case.title}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if question_completed %}
|
{% if question_completed %}
|
||||||
<span class="stamp-white">REVIEW</span>
|
<span class="stamp-white">REVIEW</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
</h2>
|
||||||
|
|
||||||
</h2>
|
<!-- Case jump dropdown (loads via HTMX) - placed to the right of the header -->
|
||||||
|
<div class="dropdown ms-3">
|
||||||
|
<a class="btn btn-outline-secondary btn-sm dropdown-toggle" href="#" role="button" id="caseJump" data-bs-toggle="dropdown" aria-expanded="false"
|
||||||
|
hx-get="{% url 'atlas:collection_case_jump_partial' pk=collection.pk %}?current={{case_number}}{% if cid %}&cid={{cid}}&passcode={{passcode}}{% endif %}"
|
||||||
|
hx-target="#case-jump-menu"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-trigger="click">
|
||||||
|
Jump
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-menu dropdown-menu-end" id="case-jump-menu">
|
||||||
|
<div class="px-3 py-2 text-muted">Loading...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if not question_completed and collection.question_time_limit is not None %}
|
{% if not question_completed and collection.question_time_limit is not None %}
|
||||||
<div id="question-timer-block" style="margin-top:8px; margin-bottom:8px;" title="This question has a time limit of {{ collection.question_time_limit }} seconds. The timer will start when the page loads.">
|
<div id="question-timer-block" style="margin-top:8px; margin-bottom:8px;" title="This question has a time limit of {{ collection.question_time_limit }} seconds. The timer will start when the page loads.">
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{% for cd in casedetails %}
|
||||||
|
{% with idx=forloop.counter0 %}
|
||||||
|
{% if cid %}
|
||||||
|
<a class="dropdown-item {% if idx == current %}active{% endif %}" href="{% url 'atlas:collection_case_view_take' pk=collection.pk case_number=idx cid=cid passcode=passcode %}">
|
||||||
|
{% else %}
|
||||||
|
<a class="dropdown-item {% if idx == current %}active{% endif %}" href="{% url 'atlas:collection_case_view_take_user' pk=collection.pk case_number=idx %}">
|
||||||
|
{% endif %}
|
||||||
|
Case {{ forloop.counter }}: {{ cd.case.title|default:cd.case.pk|truncatechars:60 }}
|
||||||
|
</a>
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
@@ -112,6 +112,11 @@ urlpatterns = [
|
|||||||
views.CollectionCaseUpdate.as_view(),
|
views.CollectionCaseUpdate.as_view(),
|
||||||
name="collection_case_update",
|
name="collection_case_update",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"collection/<int:pk>/case_jump",
|
||||||
|
views.collection_case_jump_partial,
|
||||||
|
name="collection_case_jump_partial",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"collection/add_cases",
|
"collection/add_cases",
|
||||||
views.add_cases_to_collection,
|
views.add_cases_to_collection,
|
||||||
|
|||||||
@@ -1134,6 +1134,46 @@ def collection_options(request):
|
|||||||
html = render_to_string("atlas/partials/collection_options.html", {"collections": collections}, request=request)
|
html = render_to_string("atlas/partials/collection_options.html", {"collections": collections}, request=request)
|
||||||
return HttpResponse(html)
|
return HttpResponse(html)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def collection_case_jump_partial(request, pk: int):
|
||||||
|
"""Return an HTMX partial containing a jump list for cases in the collection.
|
||||||
|
|
||||||
|
Expects optional GET params:
|
||||||
|
- current: the current case index (0-based) to mark active
|
||||||
|
- cid, passcode: optional candidate identifiers so links include them when present
|
||||||
|
"""
|
||||||
|
# Allow non-HTMX callers to still get the fragment (useful for testing)
|
||||||
|
collection = get_object_or_404(CaseCollection, pk=pk)
|
||||||
|
|
||||||
|
casedetails = (
|
||||||
|
CaseDetail.objects.filter(collection=collection)
|
||||||
|
.select_related('case')
|
||||||
|
.order_by('sort_order')
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
current = int(request.GET.get('current', 0) or 0)
|
||||||
|
except Exception:
|
||||||
|
current = 0
|
||||||
|
|
||||||
|
cid = request.GET.get('cid')
|
||||||
|
passcode = request.GET.get('passcode')
|
||||||
|
|
||||||
|
html = render_to_string(
|
||||||
|
'atlas/partials/_collection_case_jump.html',
|
||||||
|
{
|
||||||
|
'collection': collection,
|
||||||
|
'casedetails': casedetails,
|
||||||
|
'current': current,
|
||||||
|
'cid': cid,
|
||||||
|
'passcode': passcode,
|
||||||
|
},
|
||||||
|
request=request,
|
||||||
|
)
|
||||||
|
|
||||||
|
return HttpResponse(html)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def remove_cases_from_collection(request):
|
def remove_cases_from_collection(request):
|
||||||
if not request.htmx:
|
if not request.htmx:
|
||||||
|
|||||||
Reference in New Issue
Block a user