Skip to content

Commit b57ca78

Browse files
Polish course admin and pagination UI
1 parent 967d0cb commit b57ca78

22 files changed

Lines changed: 438 additions & 278 deletions

accounts/templates/accounts/login.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ <h1 class="text-2xl font-semibold app-heading">
1212
<p class="mt-1 text-sm app-muted">Choose your preferred login method</p>
1313
</div>
1414

15-
<div class="flex flex-col items-start gap-3 px-5 py-5">
15+
<div class="flex flex-col gap-3 px-5 py-5">
1616
{% for provider in providers %}
17-
<a href="{{ provider.login_url }}" class="primer-button primer-button-secondary">
17+
<a href="{{ provider.login_url }}" class="primer-button primer-button-secondary w-full justify-center">
1818
{% if 'Google' in provider.name %}
1919
<i class="fab fa-google text-danger"></i>
2020
{% elif 'GitHub' in provider.name %}

accounts/tests.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ def test_account_settings_shows_user_and_enrolled_courses(self):
8383
self.assertContains(response, "student@example.com")
8484
self.assertContains(response, "Data Course")
8585
self.assertContains(response, "Student One")
86+
self.assertNotContains(response, reverse("cadmin_course_list"))
87+
88+
def test_account_menu_shows_cadmin_for_staff(self):
89+
self.user.is_staff = True
90+
self.user.save()
91+
self.client.force_login(self.user)
92+
93+
response = self.client.get(reverse("account_settings"))
94+
95+
self.assertEqual(response.status_code, 200)
96+
self.assertContains(response, reverse("cadmin_course_list"))
97+
self.assertContains(response, "Course admin")
8698

8799
def test_account_settings_updates_profile(self):
88100
self.client.force_login(self.user)

add_data.py

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
)
1010
django.setup()
1111

12+
from allauth.socialaccount.models import SocialApp # noqa: E402
13+
from django.conf import settings # noqa: E402
1214
from django.contrib.auth import get_user_model # noqa: E402
15+
from django.contrib.sites.models import Site # noqa: E402
16+
from django.core.cache import cache # noqa: E402
1317

1418
# This will retrieve your 'CustomUser' model
1519
from courses.models import ( # noqa: E402
@@ -34,6 +38,39 @@
3438

3539
User = get_user_model()
3640

41+
42+
def configure_local_social_apps():
43+
site, _ = Site.objects.update_or_create(
44+
id=settings.SITE_ID,
45+
defaults={
46+
"domain": "localhost:8000",
47+
"name": "localhost:8000",
48+
},
49+
)
50+
51+
providers = [
52+
("google", "GoogleDTC"),
53+
("github", "GitHubDTC"),
54+
("slack", "SlackDTC"),
55+
]
56+
57+
for provider, name in providers:
58+
social_app, _ = SocialApp.objects.update_or_create(
59+
provider=provider,
60+
name=name,
61+
defaults={
62+
"client_id": f"fake-local-{provider}-client-id",
63+
"secret": f"fake-local-{provider}-secret",
64+
"key": "",
65+
},
66+
)
67+
social_app.sites.set([site])
68+
69+
cache.delete("available_providers")
70+
71+
72+
configure_local_social_apps()
73+
3774
admin_user, created = User.objects.get_or_create(
3875
username="admin", defaults={"email": "alexey@datatalks.club"}
3976
)
@@ -244,10 +281,10 @@ def join_possible_answers(answers: list) -> str:
244281
enrollment=admin_enrollment,
245282
github_link="https://github.com/DataTalksClub/data-engineering-zoomcamp",
246283
commit_id="8c45587",
247-
learning_in_public_links={
248-
"link1": "http://example.com",
249-
"link2": "http://example.org",
250-
},
284+
learning_in_public_links=[
285+
"http://example.com",
286+
"http://example.org",
287+
],
251288
faq_contribution="Contributed to the following FAQs...",
252289
time_spent=10.0,
253290
problems_comments="This is a test submission.",
@@ -396,4 +433,4 @@ def join_possible_answers(answers: list) -> str:
396433
slug="fake-course-2",
397434
finished=True,
398435
)
399-
course2.save()
436+
course2.save()

cadmin/templates/cadmin/course_admin.html

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,18 @@
88

99
{% block cadmin_content %}
1010
<section class="border-b app-border pb-5">
11-
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
12-
<div>
13-
<p class="text-sm font-semibold uppercase tracking-wide app-muted">Admin Panel</p>
14-
<h1 class="mt-2 text-2xl font-semibold app-heading md:text-3xl">{{ course.title }}</h1>
15-
<p class="mt-1 text-sm app-muted">Start with operational tasks, then use the full lists for reference and maintenance.</p>
16-
</div>
17-
<div class="grid grid-cols-2 gap-2 sm:flex sm:flex-wrap lg:justify-end">
18-
<a href="{% url 'course' course.slug %}" class="primer-button primer-button-secondary">Course Page</a>
19-
<a href="{% url 'dashboard' course.slug %}" class="primer-button primer-button-secondary">Dashboard</a>
20-
<a href="{% url 'leaderboard' course.slug %}" class="primer-button">Leaderboard</a>
21-
<a href="/admin/courses/course/{{ course.id }}/change/" class="primer-button primer-button-secondary">
22-
<i class="fas fa-edit"></i> Django Admin
23-
</a>
11+
<div>
12+
<p class="text-sm font-semibold uppercase tracking-wide app-muted">Admin Panel</p>
13+
<div class="mt-2 flex flex-wrap items-center gap-3">
14+
<h1 class="text-2xl font-semibold app-heading md:text-3xl">{{ course.title }}</h1>
15+
<div class="dense-utility-links flex flex-wrap gap-x-3 gap-y-1 text-xs">
16+
<a href="{% url 'course' course.slug %}" title="View public course page" class="app-link">
17+
<i class="fas fa-external-link-alt"></i> Public
18+
</a>
19+
<a href="/admin/courses/course/{{ course.id }}/change/" title="Edit in Django Admin" class="app-link">
20+
<i class="fas fa-edit"></i> Django
21+
</a>
22+
</div>
2423
</div>
2524
</div>
2625
</section>

cadmin/templates/cadmin/course_list.html

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
{% extends 'cadmin/base.html' %}
2-
{% block breadcrumbs %}
3-
<li><a href="{% url 'cadmin_course_list' %}">Course Admin</a></li>
4-
{% endblock %}
2+
{% block breadcrumbs_nav %}{% endblock %}
53
{% block cadmin_content %}
6-
<section class="border-b app-border pb-5 ">
7-
<p class="text-sm font-semibold uppercase tracking-wide app-muted">Cadmin</p>
8-
<div class="mt-2 flex flex-col gap-2 md:flex-row md:items-end md:justify-between">
9-
<div>
10-
<h1 class="text-2xl font-semibold app-heading md:text-3xl">Course Administration</h1>
11-
<p class="mt-1 max-w-2xl text-sm app-muted">Manage course operations, public pages, and team-only workflows from one compact overview.</p>
12-
</div>
13-
<span class="inline-flex w-fit items-center rounded-full border app-border app-surface-muted px-3 py-1 text-sm font-medium app-text ">{{ courses|length }} courses</span>
14-
</div>
4+
<section class="border-b app-border pb-5">
5+
<h1 class="text-2xl font-semibold app-heading md:text-3xl">Course admin</h1>
156
</section>
167
<section class="mt-6 overflow-hidden rounded-md border app-border app-surface ">
178
<div class="divide-y app-divide md:hidden">
189
{% for course in courses %}
1910
<article class="px-4 py-4">
11+
<div class="flex items-center gap-2">
2012
<a href="{% url 'cadmin_course' course.slug %}" class="font-medium app-link hover:underline ">{{ course.title }}</a>
13+
<a href="{% url 'course' course.slug %}" class="app-link text-sm" title="Open student view" aria-label="Open student view">
14+
<i class="fas fa-external-link-alt"></i>
15+
<span class="sr-only">Student view</span>
16+
</a>
17+
<a href="/admin/courses/course/{{ course.id }}/change/" class="app-link text-sm" title="Edit in Django Admin" aria-label="Edit in Django Admin">
18+
<i class="fas fa-edit"></i>
19+
<span class="sr-only">Django admin</span>
20+
</a>
21+
</div>
2122
<p class="mt-1 text-xs app-muted">{{ course.slug }}</p>
2223
<div class="mt-3 flex flex-wrap gap-2">
2324
{% if course.visible %}
@@ -31,10 +32,6 @@ <h1 class="text-2xl font-semibold app-heading md:text-3xl">Course Administration
3132
<span class="inline-flex rounded-full app-alert-warning px-2 py-0.5 text-xs font-medium ">In progress</span>
3233
{% endif %}
3334
</div>
34-
<div class="mt-3 flex flex-wrap gap-2">
35-
<a href="{% url 'cadmin_course' course.slug %}" class="primer-button">Manage</a>
36-
<a href="{% url 'course' course.slug %}" class="primer-button primer-button-secondary">View</a>
37-
</div>
3835
</article>
3936
{% empty %}
4037
<p class="px-4 py-8 text-center text-sm app-muted">No courses found</p>
@@ -47,14 +44,23 @@ <h1 class="text-2xl font-semibold app-heading md:text-3xl">Course Administration
4744
<th class="px-4 py-3">Course</th>
4845
<th class="px-4 py-3">Visible</th>
4946
<th class="px-4 py-3">Finished</th>
50-
<th class="px-4 py-3 text-right">Actions</th>
5147
</tr>
5248
</thead>
5349
<tbody class="divide-y app-divide">
5450
{% for course in courses %}
5551
<tr class="align-top">
5652
<td class="px-4 py-3">
53+
<div class="flex items-center gap-2">
5754
<a href="{% url 'cadmin_course' course.slug %}" class="font-medium app-link hover:underline ">{{ course.title }}</a>
55+
<a href="{% url 'course' course.slug %}" class="app-link text-xs" title="Open student view" aria-label="Open student view">
56+
<i class="fas fa-external-link-alt"></i>
57+
<span class="sr-only">Student view</span>
58+
</a>
59+
<a href="/admin/courses/course/{{ course.id }}/change/" class="app-link text-xs" title="Edit in Django Admin" aria-label="Edit in Django Admin">
60+
<i class="fas fa-edit"></i>
61+
<span class="sr-only">Django admin</span>
62+
</a>
63+
</div>
5864
<div class="mt-1 text-xs app-muted">{{ course.slug }}</div>
5965
</td>
6066
<td class="px-4 py-3">
@@ -71,16 +77,10 @@ <h1 class="text-2xl font-semibold app-heading md:text-3xl">Course Administration
7177
<span class="inline-flex rounded-full app-alert-warning px-2 py-0.5 text-xs font-medium ">No</span>
7278
{% endif %}
7379
</td>
74-
<td class="px-4 py-3">
75-
<div class="flex justify-end gap-2">
76-
<a href="{% url 'cadmin_course' course.slug %}" class="primer-button"> Manage </a>
77-
<a href="{% url 'course' course.slug %}" class="primer-button primer-button-secondary"> View </a>
78-
</div>
79-
</td>
8080
</tr>
8181
{% empty %}
8282
<tr>
83-
<td colspan="4" class="px-4 py-8 text-center text-sm app-muted">No courses found</td>
83+
<td colspan="3" class="px-4 py-8 text-center text-sm app-muted">No courses found</td>
8484
</tr>
8585
{% endfor %}
8686
</tbody>

cadmin/templates/cadmin/enrollment_edit.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ <h1 class="mt-2 text-2xl font-semibold app-heading md:text-3xl">Edit Enrollment<
1717
<p class="mt-1 text-sm app-muted">Review a student record, inspect submissions, and manage learning-in-public eligibility.</p>
1818
</div>
1919
<div class="flex flex-wrap gap-2">
20+
<form method="post" action="{% url 'loginas-user-login' enrollment.student.id %}">
21+
{% csrf_token %}
22+
<input type="hidden" name="next" value="{% url 'course' course.slug %}">
23+
<button type="submit" class="primer-button primer-button-secondary">
24+
<i class="fas fa-sign-in-alt"></i> Log in as student
25+
</button>
26+
</form>
2027
<a href="{% url 'leaderboard_score_breakdown' course_slug=course.slug enrollment_id=enrollment.id %}" class="primer-button primer-button-secondary">View Score Breakdown</a>
2128
<a href="{% url 'leaderboard' course.slug %}" class="primer-button primer-button-secondary">View Leaderboard</a>
2229
<a href="{% url 'cadmin_enrollments' course.slug %}" class="primer-button">Back to Enrollments</a>

cadmin/templates/cadmin/homework_submission_edit.html

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,31 @@ <h1 class="mt-2 text-2xl font-semibold app-heading md:text-3xl">Edit Homework Su
1717
<a href="{% url 'cadmin_homework_submissions' course.slug homework.slug %}" class="primer-button primer-button-secondary w-fit">Back to Submissions</a>
1818
</div>
1919
</section>
20-
<section class="cadmin-meta-grid mt-6 grid grid-cols-2 gap-2 lg:grid-cols-4">
21-
<div class="rounded-md border app-border app-surface p-3 ">
22-
<p class="text-xs font-semibold uppercase app-muted">Student</p>
23-
<p class="mt-1 font-semibold app-heading">{{ submission.student.username }}</p>
20+
<section class="mt-6 rounded-md border app-border app-surface px-4 py-3 text-sm">
21+
<dl class="grid gap-x-6 gap-y-3 md:grid-cols-[minmax(0,1fr)_10rem_7rem] md:items-start">
22+
<div class="min-w-0">
23+
<dt class="text-xs font-semibold uppercase app-muted">Student</dt>
24+
<dd class="mt-1 min-w-0">
25+
<div class="flex flex-wrap items-center gap-x-3 gap-y-1">
26+
<span class="font-semibold app-heading">{{ submission.student.username }}</span>
27+
<a href="{% url 'cadmin_enrollment_edit' course.slug submission.enrollment.id %}" class="app-link">
28+
<i class="fas fa-user-edit"></i> Manage enrollment
29+
</a>
30+
</div>
2431
{% if submission.student.email %}
25-
<p class="mt-1 text-xs app-muted">{{ submission.student.email }}</p>
32+
<div class="mt-1 truncate text-xs app-muted">{{ submission.student.email }}</div>
2633
{% endif %}
34+
</dd>
2735
</div>
28-
<div class="rounded-md border app-border app-surface p-3 ">
29-
<p class="text-xs font-semibold uppercase app-muted">Homework</p>
30-
<p class="mt-1 font-semibold app-heading">{{ homework.title }}</p>
31-
</div>
32-
<div class="rounded-md border app-border app-surface p-3 ">
33-
<p class="text-xs font-semibold uppercase app-muted">Submitted</p>
34-
<p class="mt-1 font-semibold app-heading">{{ submission.submitted_at|date:"Y-m-d H:i" }}</p>
36+
<div>
37+
<dt class="text-xs font-semibold uppercase app-muted">Submitted</dt>
38+
<dd class="mt-1 font-medium app-heading">{{ submission.submitted_at|date:"Y-m-d H:i" }}</dd>
3539
</div>
36-
<div class="rounded-md border app-border app-surface p-3 ">
37-
<p class="text-xs font-semibold uppercase app-muted">Current Total Score</p>
38-
<p class="mt-1 text-2xl font-semibold app-heading">{{ submission.total_score }}</p>
40+
<div>
41+
<dt class="text-xs font-semibold uppercase app-muted">Total score</dt>
42+
<dd class="mt-1 text-lg font-semibold app-heading">{{ submission.total_score }}</dd>
3943
</div>
44+
</dl>
4045
</section>
4146
<form class="mt-6 space-y-6" method="post">
4247
{% csrf_token %}
@@ -104,10 +109,11 @@ <h3 class="font-medium app-heading">Question {{ forloop.counter }}: {{ item.ques
104109
<h2 class="text-base font-semibold app-heading">Learning in Public Links</h2>
105110
</div>
106111
<div class="p-4">
107-
<label class="mb-2 block text-sm font-semibold app-heading" for="learning_in_public_links">Links (comma-separated)</label>
108-
<textarea class="form-control" id="learning_in_public_links" name="learning_in_public_links" rows="3" placeholder="https://example.com/post1, https://example.com/post2">
112+
<label class="mb-2 block text-sm font-semibold app-heading" for="learning_in_public_links">Links (one per line)</label>
113+
<textarea class="form-control" id="learning_in_public_links" name="learning_in_public_links" rows="4" placeholder="https://example.com/post1&#10;https://example.com/post2">
109114
{% if submission.learning_in_public_links %}
110-
{{ submission.learning_in_public_links|join:", " }}
115+
{{ submission.learning_in_public_links|join:"
116+
" }}
111117
{% endif %}
112118
</textarea>
113119
<p class="mt-2 text-sm app-muted">Each link contributes to the learning in public score, capped at {{ homework.learning_in_public_cap }}.</p>

cadmin/templates/cadmin/project_submissions.html

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -343,17 +343,5 @@ <h2 class="text-base font-semibold app-heading">Submissions</h2>
343343

344344
updateArrows();
345345
</script>
346-
{% if submissions_page.paginator.num_pages > 1 %}
347-
<nav class="mt-4 flex flex-wrap items-center justify-between gap-3 text-sm app-text" aria-label="Project submission pages">
348-
<span class="app-muted">Page {{ submissions_page.number }} of {{ submissions_page.paginator.num_pages }}</span>
349-
<div class="flex flex-wrap gap-2">
350-
{% if submissions_page.has_previous %}
351-
<a class="primer-button primer-button-secondary" href="?page={{ submissions_page.previous_page_number }}{{ pagination_querystring }}">Previous</a>
352-
{% endif %}
353-
{% if submissions_page.has_next %}
354-
<a class="primer-button" href="?page={{ submissions_page.next_page_number }}{{ pagination_querystring }}">Next</a>
355-
{% endif %}
356-
</div>
357-
</nav>
358-
{% endif %}
346+
{% include 'include/pagination.html' with pagination_page=submissions_page pagination_range=page_range pagination_querystring=pagination_querystring pagination_label='Project submission pages' %}
359347
{% endblock %}

0 commit comments

Comments
 (0)