Refactor exam candidates view for improved layout and user experience
This commit is contained in:
@@ -1,160 +1,162 @@
|
||||
<div id="history-modal"
|
||||
class="modal modal-blur fade"
|
||||
style="display: none"
|
||||
aria-hidden="false"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||||
<h2>Exam user history:
|
||||
<span id="history-user"></span>
|
||||
</h2>
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% block content %}
|
||||
<div class="container py-3">
|
||||
<div class="row mb-3">
|
||||
<div class="col-12 d-flex justify-content-between align-items-center">
|
||||
<h3 class="mb-0">Candidates for {{ exam.get_exam_name }}</h3>
|
||||
<div>
|
||||
<a class="btn btn-sm btn-outline-light" href="{% url exam.app_name|add:':exam_cids_edit' exam.pk %}">Edit Exam CIDs</a>
|
||||
<a class="btn btn-sm btn-outline-light ms-2" href="{% url exam.app_name|add:':exam_users_edit' exam.pk %}">Edit Exam Users</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-white bg-dark">
|
||||
<h3>CID candidates</h3>
|
||||
{% if cid_users %}
|
||||
<p>{{cid_user_count}} CID candidates.</p>
|
||||
|
||||
<div id="htmx-info"></div>
|
||||
|
||||
<ol class="cid-candidate-list">
|
||||
{% for cid in cid_users %}
|
||||
|
||||
<li class="cid-user" data-cid={{cid.cid}}>
|
||||
<div class="cid-data d-flex align-items-center">
|
||||
<i class="bi bi-clock-history me-2"
|
||||
title="View user exam history"
|
||||
hx-get="{% url exam.app_name|add:':exam_user_status_cid' exam.id cid.cid %}"
|
||||
hx-target=".modal-content"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#history-modal"
|
||||
_="on htmx:afterOnLoad put {{cid.cid}} into #history-user.innerHTML "
|
||||
></i>
|
||||
<span class="d-inline-flex align-items-center flex-nowrap">
|
||||
<a class="cid-link me-1" href="{% url 'generic:update_cid' cid.pk %}">{{cid.cid}}</a>
|
||||
<span class="text-muted">[{{cid.passcode}}]</span>
|
||||
<a title="View CID exam details" class="ms-2 d-inline-block cid-detail-link" hx-get="{% url 'generic:cid_user_exam_partial' exam.app_name exam.id cid.cid %}" hx-target=".modal-content" data-bs-toggle="modal" data-bs-target="#history-modal" role="button"><i class="bi bi-box-arrow-up-right"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="cid-name">{{cid.name}}</div>
|
||||
<div class="cid-email">Email: {{cid.email}}</div>
|
||||
</li>
|
||||
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% else %}
|
||||
<p>This exam has no CID candidates. Add some with the below link.</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<a href="{% url exam.app_name|add:':exam_cids_edit' exam.pk %}">Edit Exam CIDs</a>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<div class="card text-white bg-dark">
|
||||
<h3>User candidates</h3>
|
||||
{% if user_users %}
|
||||
<p>{{user_user_count}} User candidates.</p>
|
||||
|
||||
<ol class="user-candidate-list">
|
||||
{% for user in user_users %}
|
||||
|
||||
<li class="user-user" data-user="{{user.username}}">
|
||||
|
||||
<div>
|
||||
<i class="bi bi-clock-history"
|
||||
title="View user exam history"
|
||||
hx-get="{% url exam.app_name|add:':exam_user_status_user' exam.id user.id %}"
|
||||
hx-target=".modal-content"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#history-modal"
|
||||
_="on htmx:afterOnLoad put {{cid.cid}} into #history-user.innerHTML "
|
||||
></i>
|
||||
{% if request.user.is_superuser %}
|
||||
<a href='{% url "account_profile" user.username %}'><i class="bi bi-link"></i></a>
|
||||
<div class="row">
|
||||
<div class="col-lg-6 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">CID candidates <small class="text-muted">({{ cid_user_count }})</small></h5>
|
||||
<div id="htmx-info"></div>
|
||||
{% if cid_users %}
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for cid in cid_users %}
|
||||
<li class="list-group-item">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-clock-history text-muted me-2" title="View user exam history"
|
||||
hx-get="{% url exam.app_name|add:':exam_user_status_cid' exam.id cid.cid %}"
|
||||
hx-target=".modal-content"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#history-modal"
|
||||
_="on htmx:afterOnLoad put {{cid.cid}} into #history-user.innerHTML "
|
||||
></i>
|
||||
<a class="fw-bold me-2 cid-link" href="{% url 'generic:update_cid' cid.pk %}">{{ cid.cid }}</a>
|
||||
<span class="text-muted">[{{ cid.passcode }}]</span>
|
||||
<a class="ms-3" title="View CID exam details" hx-get="{% url 'generic:cid_user_exam_partial' exam.app_name exam.id cid.cid %}" hx-target=".modal-content" data-bs-toggle="modal" data-bs-target="#history-modal"><i class="bi bi-box-arrow-up-right"></i></a>
|
||||
</div>
|
||||
<div class="small text-muted mt-1">{{ cid.name }}</div>
|
||||
<div class="small">Email: {{ cid.email }}</div>
|
||||
</div>
|
||||
<div class="text-end small">
|
||||
{% if cid.login_email_sent %}
|
||||
<span class="badge bg-success">Email sent</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Not sent</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted">This exam has no CID candidates. Add some with the edit link above.</p>
|
||||
{% endif %}
|
||||
{{user.username}}</div>
|
||||
<div>Email: {{user.email}}</div>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% else %}
|
||||
<p>This exam has no User candidates. Add some with the below link.</p>
|
||||
{% endif %}
|
||||
<a href="{% url exam.app_name|add:':exam_users_edit' exam.pk %}">Edit Exam Users</a>
|
||||
<div class="col-lg-6 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">User candidates <small class="text-muted">({{ user_user_count }})</small></h5>
|
||||
{% if user_users %}
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for user in user_users %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-clock-history text-muted me-2" title="View user exam history"
|
||||
hx-get="{% url exam.app_name|add:':exam_user_status_user' exam.id user.id %}"
|
||||
hx-target=".modal-content"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#history-modal"
|
||||
_="on htmx:afterOnLoad put {{cid.cid}} into #history-user.innerHTML "
|
||||
></i>
|
||||
{% if request.user.is_superuser %}
|
||||
<a class="me-2" href='{% url "account_profile" user.username %}'><i class="bi bi-link"></i></a>
|
||||
{% endif %}
|
||||
<span class="fw-bold">{{ user.username }}</span>
|
||||
</div>
|
||||
<div class="small">Email: {{ user.email }}</div>
|
||||
</div>
|
||||
<div class="small text-muted"> </div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted">This exam has no User candidates. Add some with the edit link above.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% if user_exam_data %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Submitted candidates</h5>
|
||||
<p class="small text-muted">The following candidate submissions have been received. Candidates with submissions are highlighted above.</p>
|
||||
<ul id="submitted-candidates" class="list-unstyled small">
|
||||
{% for user_data in user_exam_data %}
|
||||
<li data-cid="{{user_data.cid_user}}" data-user="{{user_data.user_user}}">CID: {{user_data.cid_user}} / User: {{user_data.user_user}} / Start: {{user_data.start_time}} / End: {{user_data.end_time}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<br/>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<button class="btn btn-sm btn-outline-secondary" title="View all user exam history" hx-get="{% url exam.app_name|add:':exam_user_status' exam.id %}" hx-target=".modal-content" data-bs-toggle="modal" data-bs-target="#history-modal">Exam history <i class="bi bi-clock-history"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user_exam_data %}
|
||||
<div class="card text-white bg-dark">
|
||||
<details>
|
||||
<summary>
|
||||
<h3>Submitted candidates</h3>
|
||||
</summary>
|
||||
The following candidate submissions have been received. Candidates with submissions will be shown in green above.
|
||||
<ul id="submitted-candidates">
|
||||
{% for user_data in user_exam_data %}
|
||||
<li data-cid="{{user_data.cid_user}}" data-user="{{user_data.user_user}}">
|
||||
CID: {{user_data.cid_user}} /
|
||||
User: {{user_data.user_user}} /
|
||||
Start time: {{user_data.start_time}} /
|
||||
End time: {{user_data.end_time}} /
|
||||
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
<!-- HTMX modal for history / partials -->
|
||||
<div id="history-modal" class="modal fade" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Exam user history: <span id="history-user"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
{% endif %}
|
||||
<span
|
||||
title="View all user exam history"
|
||||
hx-get="{% url exam.app_name|add:':exam_user_status' exam.id %}"
|
||||
hx-target=".modal-content"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#history-modal"
|
||||
_="on htmx:afterOnLoad put {{cid.cid}} into #history-user.innerHTML "
|
||||
>
|
||||
Exam history <i class="bi bi-clock-history"></i>
|
||||
</span>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Highlight users that have submitted answers
|
||||
$("#submitted-candidates li").each(function(n, el) {
|
||||
console.log(el);
|
||||
if (el.dataset.cid != "None") {
|
||||
$(`.cid-user[data-cid=${el.dataset.cid}]`).addClass("submitted")
|
||||
}
|
||||
if (el.dataset.user != "None") {
|
||||
$(`.user-user[data-user="${el.dataset.user}"]`).addClass("submitted")
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
// Highlight users that have submitted answers
|
||||
document.querySelectorAll('#submitted-candidates li').forEach(function(el){
|
||||
try{
|
||||
const cid = el.dataset.cid;
|
||||
const user = el.dataset.user;
|
||||
if(cid && cid !== 'None'){
|
||||
// highlight matching cid element(s)
|
||||
document.querySelectorAll(`.list-group-item`).forEach(function(li){ if(li.querySelector('.cid-link') && li.querySelector('.cid-link').textContent.trim() === cid.toString()) li.classList.add('submitted'); });
|
||||
}
|
||||
if(user && user !== 'None'){
|
||||
document.querySelectorAll('.list-group-item').forEach(function(li){ if(li.textContent.includes(user)) li.classList.add('submitted'); });
|
||||
}
|
||||
}catch(e){ console.error(e); }
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
/* Ensure action links are inline-block and don't overlap following content */
|
||||
.cid-data .cid-detail-link, .cid-data .cid-link { display: inline-block; }
|
||||
.cid-name { margin-top: 0.25rem; }
|
||||
</style>
|
||||
{% endblock js %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block css %}
|
||||
<style>
|
||||
.submitted {
|
||||
color: green;
|
||||
}
|
||||
.cid-name { margin-top: 0.25rem; }
|
||||
.cid-link { font-size: 1.05rem; }
|
||||
.submitted { color: green; }
|
||||
</style>
|
||||
{% endblock css %}
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user