These actions will act on all the selected trainees. Refresh the page to cancel / clear.
@@ -101,15 +105,25 @@
hx-include="[name='selection']"
hx-target="#htmx-options"
title="Changes the selected users grade."
-
>Edit grade
+
+
@@ -203,6 +217,27 @@
lastSorted = {idx, asc};
});
});
+
+ // Update selection count
+ const updateSelectedCount = () => {
+ const checkedCount = table.querySelectorAll('.trainee-checkbox:checked').length;
+ const countEl = document.getElementById('selected-count');
+ if (countEl) {
+ countEl.textContent = checkedCount;
+ }
+ };
+
+ // Listen for checkbox changes directly
+ table.addEventListener('change', function(e) {
+ if (e.target.classList.contains('trainee-checkbox')) {
+ updateSelectedCount();
+ }
+ });
+
+ // Expose to window so hyperscript can call it
+ window.updateSelectedCount = updateSelectedCount;
+
+ updateSelectedCount();
})();
{% endblock %}
diff --git a/generic/tests/test_trainees.py b/generic/tests/test_trainees.py
index 1bb17491..1d775853 100644
--- a/generic/tests/test_trainees.py
+++ b/generic/tests/test_trainees.py
@@ -68,3 +68,71 @@ def test_trainee_stuff(client, create_cid_manager, create_users):
assert (
len(trainee_rows) == UserProfile.objects.filter(peninsula_trainee=True).count()
)
+
+
+def test_bulk_edit_groups(client, create_cid_manager, create_users):
+ from generic.models import UserUserGroup
+ from django.contrib.auth import get_user_model
+ User = get_user_model()
+
+ client.login(username="cidmanager", password="password")
+
+ group = UserUserGroup.objects.create(name="Test Group")
+
+ user1 = User.objects.get(username="user1")
+ user2 = User.objects.get(username="user2")
+
+ # Bulk add to group
+ response = client.post(
+ reverse("generic:users_bulk_edit"),
+ data={"selection": [user1.pk, user2.pk]},
+ HTTP_HX_REQUEST="true",
+ HTTP_HX_TRIGGER=f"add-group--{group.id}"
+ )
+
+ assert response.status_code == 200
+ assert "Group added" in response.content.decode()
+ assert "alert alert-success" in response.content.decode()
+
+ user1.refresh_from_db()
+ user2.refresh_from_db()
+ assert group in user1.user_groups.all()
+ assert group in user2.user_groups.all()
+
+ # Bulk remove from group
+ response = client.post(
+ reverse("generic:users_bulk_edit"),
+ data={"selection": [user1.pk, user2.pk]},
+ HTTP_HX_REQUEST="true",
+ HTTP_HX_TRIGGER=f"remove-group--{group.id}"
+ )
+
+ assert response.status_code == 200
+ assert "Group removed" in response.content.decode()
+ assert "alert alert-success" in response.content.decode()
+
+ user1.refresh_from_db()
+ user2.refresh_from_db()
+ assert group not in user1.user_groups.all()
+ assert group not in user2.user_groups.all()
+
+
+def test_user_not_trainee_htmx(client, create_cid_manager, create_users):
+ from django.contrib.auth import get_user_model
+ User = get_user_model()
+
+ client.login(username="cidmanager", password="password")
+ user2 = User.objects.get(username="user2")
+ assert user2.userprofile.peninsula_trainee is True
+
+ # Call view via HTMX
+ response = client.get(
+ reverse("generic:user_not_trainee", kwargs={"user_id": user2.pk}),
+ HTTP_HX_REQUEST="true"
+ )
+
+ assert response.status_code == 200
+ assert response.content == b""
+
+ user2.refresh_from_db()
+ assert user2.userprofile.peninsula_trainee is False
diff --git a/generic/views.py b/generic/views.py
index c55d86c9..d9f76aea 100644
--- a/generic/views.py
+++ b/generic/views.py
@@ -4916,15 +4916,28 @@ def users_bulk_delete_supervisors(request):
u.userprofile.supervisor = None
u.save()
- modified_users = ",".join([user.username for user in user_models])
+ modified_users = ", ".join([f"{user.first_name} {user.last_name}" for user in user_models])
return HttpResponse(
- f"Supervisors removed from {modified_users}. Refresh page to see update",
- content_type="text/plain",
+ f"""
+
+ Success! Supervisors removed from: {modified_users}. Refresh page to see update.
+
+
+ """,
+ content_type="text/html",
+ )
class NoUsersSelected(Exception):
@@ -4947,6 +4960,8 @@ def user_not_trainee(request, user_id):
user.userprofile.peninsula_trainee = False
user.userprofile.save()
+ if request.htmx:
+ return HttpResponse("")
return HttpResponse(
f"{user.username} is no longer a trainee", content_type="text/plain"
)
@@ -4968,7 +4983,12 @@ def users_bulk_edit(request):
u.is_active = False
u.save()
- html = "Users deactivated"
+ html = """
+
+ Success! Selected users have been deactivated.
+
+
+ """
case "bulk-edit-grade-button":
user_grades = UserGrades.objects.all()
@@ -5013,7 +5033,12 @@ def users_bulk_edit(request):
for u in user_models:
u.userprofile.grade_id = grade
u.save()
- html = "Grades updated"
+ html = """
+
+ Success! Grades updated successfully.
+
+
+ """
case "bulk-add-group-button":
user_groups = UserUserGroup.objects.all()
@@ -5064,7 +5089,12 @@ def users_bulk_edit(request):
pass
u.user_groups.add(r.split("--")[-1])
u.save()
- html = "Group added"
+ html = """
+
+ Success! Group added to all selected users.
+
+
+ """
case r if r.startswith("remove-group"):
user_models = get_user_selection_from_request(request)
@@ -5072,12 +5102,25 @@ def users_bulk_edit(request):
for u in user_models:
group_id = r.split("--")[-1]
u.user_groups.remove(group_id)
- html = "Group removed"
+ html = """
+
+ Success! Group removed from all selected users.
+
+