Skip to content

Commit efc0086

Browse files
Improve Cadmin UI/UX: streamline navigation, reduce clutter, add filtering (#137)
* Initial plan * Update Cadmin UI/UX - course admin, homework/project submissions Co-authored-by: alexeygrigorev <875246+alexeygrigorev@users.noreply.github.com> * Address code review feedback - fix auto-uncheck and add constants Co-authored-by: alexeygrigorev <875246+alexeygrigorev@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: alexeygrigorev <875246+alexeygrigorev@users.noreply.github.com>
1 parent 10c3f07 commit efc0086

4 files changed

Lines changed: 218 additions & 77 deletions

File tree

cadmin/templates/cadmin/course_admin.html

Lines changed: 63 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ <h3>Homework</h3>
4949
{% for hw in homeworks %}
5050
<tr>
5151
<td>
52-
<a href="{% url 'homework' course.slug hw.slug %}">{{ hw.title }}</a>
53-
| <a href="/admin/courses/homework/{{ hw.id }}/change/" title="Edit in Django Admin">
54-
<i class="fas fa-edit"></i>
52+
<a href="{% url 'cadmin_homework_submissions' course.slug hw.slug %}">{{ hw.title }}</a>
53+
<a href="{% url 'homework' course.slug hw.slug %}" title="View Public Homework Page" class="ml-2">
54+
<i class="fas fa-external-link-alt"></i>
5555
</a>
5656
</td>
57-
<td>{{ hw.due_date|date:"Y-m-d H:i" }}</td>
57+
<td>{{ hw.due_date|date:"Y-m-d" }}</td>
5858
<td>
5959
{% if hw.state == 'OP' %}
6060
<span class="badge badge-success">Open</span>
@@ -66,32 +66,20 @@ <h3>Homework</h3>
6666
<span class="badge badge-warning">{{ hw.get_state_display }}</span>
6767
{% endif %}
6868
</td>
69+
<td>{{ hw.submissions_count }}</td>
6970
<td>
70-
<a href="{% url 'cadmin_homework_submissions' course.slug hw.slug %}">
71-
{{ hw.submissions_count }}
72-
</a>
73-
</td>
74-
<td>
75-
<div class="btn-group" role="group">
76-
<a href="{% url 'cadmin_homework_set_correct_answers' course.slug hw.slug %}"
77-
class="btn btn-sm btn-warning"
78-
onclick="return confirm('Set correct answers to most popular for {{ hw.title }}?')">
79-
Set Correct
80-
</a>
81-
<a href="{% url 'cadmin_homework_score' course.slug hw.slug %}"
82-
class="btn btn-sm btn-primary"
83-
onclick="return confirm('Score homework {{ hw.title }}?')">
84-
Score
85-
</a>
86-
<a href="{% url 'homework_statistics' course.slug hw.slug %}"
87-
class="btn btn-sm btn-info">
88-
Stats
89-
</a>
90-
<a href="{% url 'cadmin_homework_submissions' course.slug hw.slug %}"
91-
class="btn btn-sm btn-secondary">
92-
Submissions
93-
</a>
94-
</div>
71+
<form method="post" style="display: inline;">
72+
{% csrf_token %}
73+
<select class="form-control form-control-sm d-inline-block" style="width: auto;" name="action" id="hw-action-{{ hw.id }}">
74+
<option value="">Select action...</option>
75+
{% if hw.state == 'CL' %}
76+
<option value="{% url 'cadmin_homework_set_correct_answers' course.slug hw.slug %}">Set Correct Answers</option>
77+
<option value="{% url 'cadmin_homework_score' course.slug hw.slug %}">Score</option>
78+
{% endif %}
79+
<option value="{% url 'homework_statistics' course.slug hw.slug %}">View Statistics</option>
80+
</select>
81+
<button type="button" class="btn btn-sm btn-primary" onclick="applyAction('hw-action-{{ hw.id }}')">Apply</button>
82+
</form>
9583
</td>
9684
</tr>
9785
{% endfor %}
@@ -116,8 +104,7 @@ <h3>Projects</h3>
116104
<thead>
117105
<tr>
118106
<th>Title</th>
119-
<th>Submission Due</th>
120-
<th>Review Due</th>
107+
<th>Due Dates</th>
121108
<th>State</th>
122109
<th>Submissions</th>
123110
<th>Actions</th>
@@ -127,13 +114,15 @@ <h3>Projects</h3>
127114
{% for proj in projects %}
128115
<tr>
129116
<td>
130-
<a href="{% url 'project' course.slug proj.slug %}">{{ proj.title }}</a>
131-
| <a href="/admin/courses/project/{{ proj.id }}/change/" title="Edit in Django Admin">
132-
<i class="fas fa-edit"></i>
117+
<a href="{% url 'cadmin_project_submissions' course.slug proj.slug %}">{{ proj.title }}</a>
118+
<a href="{% url 'project' course.slug proj.slug %}" title="View Public Project Page" class="ml-2">
119+
<i class="fas fa-external-link-alt"></i>
133120
</a>
134121
</td>
135-
<td>{{ proj.submission_due_date|date:"Y-m-d H:i" }}</td>
136-
<td>{{ proj.peer_review_due_date|date:"Y-m-d H:i" }}</td>
122+
<td>
123+
Sub: {{ proj.submission_due_date|date:"Y-m-d" }}<br>
124+
Rev: {{ proj.peer_review_due_date|date:"Y-m-d" }}
125+
</td>
137126
<td>
138127
{% if proj.state == 'CL' %}
139128
<span class="badge badge-secondary">Closed</span>
@@ -147,32 +136,22 @@ <h3>Projects</h3>
147136
<span class="badge badge-info">{{ proj.get_state_display }}</span>
148137
{% endif %}
149138
</td>
139+
<td>{{ proj.submissions_count }}</td>
150140
<td>
151-
<a href="{% url 'cadmin_project_submissions' course.slug proj.slug %}">
152-
{{ proj.submissions_count }}
153-
</a>
154-
</td>
155-
<td>
156-
<div class="btn-group" role="group">
157-
<a href="{% url 'cadmin_project_assign_reviews' course.slug proj.slug %}"
158-
class="btn btn-sm btn-warning"
159-
onclick="return confirm('Assign peer reviews for {{ proj.title }}?')">
160-
Assign Reviews
161-
</a>
162-
<a href="{% url 'cadmin_project_score' course.slug proj.slug %}"
163-
class="btn btn-sm btn-primary"
164-
onclick="return confirm('Score project {{ proj.title }}?')">
165-
Score
166-
</a>
167-
<a href="{% url 'project_statistics' course.slug proj.slug %}"
168-
class="btn btn-sm btn-info">
169-
Stats
170-
</a>
171-
<a href="{% url 'cadmin_project_submissions' course.slug proj.slug %}"
172-
class="btn btn-sm btn-secondary">
173-
Submissions
174-
</a>
175-
</div>
141+
<form method="post" style="display: inline;">
142+
{% csrf_token %}
143+
<select class="form-control form-control-sm d-inline-block" style="width: auto;" name="action" id="proj-action-{{ proj.id }}">
144+
<option value="">Select action...</option>
145+
{% if proj.state == 'CS' %}
146+
<option value="{% url 'cadmin_project_assign_reviews' course.slug proj.slug %}">Assign Reviews</option>
147+
{% endif %}
148+
{% if proj.state == 'PR' %}
149+
<option value="{% url 'cadmin_project_score' course.slug proj.slug %}">Score</option>
150+
{% endif %}
151+
<option value="{% url 'project_statistics' course.slug proj.slug %}">View Statistics</option>
152+
</select>
153+
<button type="button" class="btn btn-sm btn-primary" onclick="applyAction('proj-action-{{ proj.id }}')">Apply</button>
154+
</form>
176155
</td>
177156
</tr>
178157
{% endfor %}
@@ -184,4 +163,27 @@ <h3>Projects</h3>
184163
{% endif %}
185164
</div>
186165
</div>
166+
167+
<script>
168+
function applyAction(selectId) {
169+
const select = document.getElementById(selectId);
170+
const url = select.value;
171+
172+
if (!url) {
173+
alert('Please select an action');
174+
return;
175+
}
176+
177+
// For statistics, open in same tab
178+
if (url.includes('statistics')) {
179+
window.location.href = url;
180+
return;
181+
}
182+
183+
// For other actions, confirm first
184+
if (confirm('Are you sure you want to perform this action?')) {
185+
window.location.href = url;
186+
}
187+
}
188+
</script>
187189
{% endblock %}

cadmin/templates/cadmin/homework_submissions.html

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,34 @@ <h2>{{ homework.title }} - Submissions</h2>
2121
</div>
2222

2323
{% if submissions_data %}
24+
<div class="mb-3">
25+
<label for="sortBy">Sort by:</label>
26+
<select id="sortBy" class="form-control form-control-sm d-inline-block" style="width: auto;">
27+
<option value="email">Email</option>
28+
<option value="submitted" selected>Submitted At</option>
29+
<option value="score">Score</option>
30+
</select>
31+
<button onclick="sortTable()" class="btn btn-sm btn-primary">Sort</button>
32+
33+
<input type="text" id="searchBox" class="form-control form-control-sm d-inline-block ml-3" placeholder="Search by email..." style="width: 250px;" onkeyup="filterTable()">
34+
</div>
35+
2436
<div class="table-responsive">
25-
<table class="table table-striped table-sm">
37+
<table class="table table-striped table-sm" id="submissionsTable">
2638
<thead>
2739
<tr>
28-
<th>Student</th>
2940
<th>Email</th>
3041
<th>Submitted At</th>
3142
<th>Score</th>
3243
{% for question in questions %}
3344
<th>Q{{ forloop.counter }}</th>
3445
{% endfor %}
46+
<th>Actions</th>
3547
</tr>
3648
</thead>
3749
<tbody>
3850
{% for data in submissions_data %}
3951
<tr>
40-
<td>{{ data.submission.student.username }}</td>
4152
<td>{{ data.submission.student.email }}</td>
4253
<td>{{ data.submission.submitted_at|date:"Y-m-d H:i" }}</td>
4354
<td>
@@ -58,11 +69,67 @@ <h2>{{ homework.title }} - Submissions</h2>
5869
</span>
5970
</td>
6071
{% endfor %}
72+
<td>
73+
<a href="/admin/login/user/{{ data.submission.student.id }}/?next={% url 'cadmin_homework_submissions' course.slug homework.slug %}"
74+
class="btn btn-sm btn-info"
75+
title="Log in as {{ data.submission.student.email }}">
76+
<i class="fas fa-user-circle"></i> Log in as
77+
</a>
78+
</td>
6179
</tr>
6280
{% endfor %}
6381
</tbody>
6482
</table>
6583
</div>
84+
85+
<script>
86+
const MISSING_SCORE_VALUE = -999; // Used for sorting when score is missing
87+
88+
function sortTable() {
89+
const table = document.getElementById('submissionsTable');
90+
const tbody = table.querySelector('tbody');
91+
const rows = Array.from(tbody.querySelectorAll('tr'));
92+
const sortBy = document.getElementById('sortBy').value;
93+
94+
rows.sort((a, b) => {
95+
let aVal, bVal;
96+
if (sortBy === 'email') {
97+
aVal = a.cells[0].textContent.trim();
98+
bVal = b.cells[0].textContent.trim();
99+
return aVal.localeCompare(bVal);
100+
} else if (sortBy === 'submitted') {
101+
aVal = a.cells[1].textContent.trim();
102+
bVal = b.cells[1].textContent.trim();
103+
return bVal.localeCompare(aVal); // Descending
104+
} else if (sortBy === 'score') {
105+
aVal = a.cells[2].textContent.trim();
106+
bVal = b.cells[2].textContent.trim();
107+
aVal = aVal === '-' ? MISSING_SCORE_VALUE : parseFloat(aVal);
108+
bVal = bVal === '-' ? MISSING_SCORE_VALUE : parseFloat(bVal);
109+
return bVal - aVal; // Descending
110+
}
111+
return 0;
112+
});
113+
114+
rows.forEach(row => tbody.appendChild(row));
115+
}
116+
117+
function filterTable() {
118+
const searchBox = document.getElementById('searchBox');
119+
const filter = searchBox.value.toLowerCase();
120+
const table = document.getElementById('submissionsTable');
121+
const rows = table.querySelectorAll('tbody tr');
122+
123+
rows.forEach(row => {
124+
const email = row.cells[0].textContent.toLowerCase();
125+
if (email.includes(filter)) {
126+
row.style.display = '';
127+
} else {
128+
row.style.display = 'none';
129+
}
130+
});
131+
}
132+
</script>
66133
{% else %}
67134
<p>No submissions yet.</p>
68135
{% endif %}

cadmin/templates/cadmin/project_submission_edit.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ <h4>Overall Results</h4>
144144
const criteriaScoreFields = document.querySelectorAll('.criteria-score');
145145
const additionalScoreFields = document.querySelectorAll('.additional-score');
146146
const peerReviewScoreFields = document.querySelectorAll('.peer-review-score');
147+
const passedCheckbox = document.getElementById('passed');
148+
149+
// Get passing threshold from the project
150+
const passingThreshold = {{ project.points_to_pass }};
147151

148152
function calculateProjectScore() {
149153
// Calculate project score from criteria scores
@@ -182,6 +186,17 @@ <h4>Overall Results</h4>
182186
if (totalField) {
183187
totalField.value = total;
184188
}
189+
190+
// Auto-check or uncheck "passed" based on total score vs passing threshold
191+
if (passedCheckbox) {
192+
if (total >= passingThreshold) {
193+
passedCheckbox.checked = true;
194+
} else {
195+
passedCheckbox.checked = false;
196+
}
197+
}
198+
199+
return total;
185200
}
186201

187202
// Add event listeners to criteria score fields

0 commit comments

Comments
 (0)