Add user group archiving functionality with HTMX support
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
{% comment %}Partial for a single user group card. Rendered by HTMX after archive toggles.{% endcomment %}
|
||||
<div class="card mb-3" id="group-card-{{ group.pk }}">
|
||||
<div class="card-body d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="card-title mb-0"><a href="{% url 'generic:user_group_detail' group.pk %}">{{ group.name }}</a></h5>
|
||||
{% if group.description %}
|
||||
<p class="mb-0 text-muted small">{{ group.description }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="btn-group" role="group" aria-label="Group actions">
|
||||
<a class="btn btn-sm btn-outline-primary" href="{% url 'generic:user_group_update' pk=group.pk %}">Edit</a>
|
||||
<a class="btn btn-sm btn-outline-danger" href="{% url 'generic:user_group_delete' pk=group.pk %}">Delete</a>
|
||||
|
||||
<form hx-post="{% url 'generic:user_group_toggle_archive' pk=group.pk %}" hx-swap="outerHTML" method="post" class="d-inline-block ms-1">
|
||||
{% csrf_token %}
|
||||
{% if view_all %}
|
||||
<input type="hidden" name="view_all" value="1">
|
||||
{% endif %}
|
||||
{% if group.archive %}
|
||||
<input type="hidden" name="archive" value="false">
|
||||
<button class="btn btn-sm btn-outline-success">Unarchive</button>
|
||||
{% else %}
|
||||
<input type="hidden" name="archive" value="true">
|
||||
<button class="btn btn-sm btn-outline-secondary">Archive</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,38 +1,25 @@
|
||||
{% extends 'generic/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h2>User Groups</h2>
|
||||
<p>
|
||||
<a href="{% url 'generic:user_group_create' %}">Create new group</a>
|
||||
</p>
|
||||
{% for group in groups %}
|
||||
<p><a href="{% url 'generic:user_group_detail' group.pk %}"><h4>{{group.name}}</h4></a>
|
||||
{% comment %} <button class="small-url-button" data-url="{% url 'generic:group_email' pk=group.pk %}
|
||||
">Email candidate details</button>
|
||||
<button class="small-url-button" data-url="{% url 'generic:group_email_resend' pk=group.pk %}
|
||||
">Resend candidate details</button>
|
||||
<button class="small-url-button" data-url="{% url 'generic:group_email_results' pk=group.pk %}
|
||||
">Email candidate results</button>
|
||||
<button class="small-url-button" data-url="{% url 'generic:group_email_results_resend' pk=group.pk %}
|
||||
">Resend candidate results</button> {% endcomment %}
|
||||
<button class="small-url-button" data-url="{% url 'generic:user_group_update' pk=group.pk %}
|
||||
">Edit</button>
|
||||
<button class="small-url-button" data-url="{% url 'generic:user_group_delete' pk=group.pk %}
|
||||
">Delete</button>
|
||||
</p>
|
||||
{% endfor %}
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2 class="mb-0">User Groups</h2>
|
||||
<div>
|
||||
<a class="btn btn-sm btn-primary me-2" href="{% url 'generic:user_group_create' %}">Create new group</a>
|
||||
{% if view_all %}
|
||||
<a class="btn btn-sm btn-outline-secondary" href="{% url 'generic:user_group_view' %}">View active</a>
|
||||
{% else %}
|
||||
<a class="btn btn-sm btn-outline-secondary" href="{% url 'generic:user_group_view_all' %}">View all</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(".small-url-button").click((evt) => {
|
||||
window.location = evt.currentTarget.dataset.url;
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
{% if view_all %}
|
||||
<a href="{% url 'generic:user_group_view' %}">View active</a>
|
||||
{% else %}
|
||||
<a href="{% url 'generic:user_group_view_all' %}">View all</a>
|
||||
{% endif %}
|
||||
<div id="groups-list">
|
||||
{% for group in groups %}
|
||||
{% include 'generic/partials/user_group_card.html' with group=group view_all=view_all %}
|
||||
{% empty %}
|
||||
<div class="alert alert-info">No groups found.</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -201,6 +201,11 @@ urlpatterns = [
|
||||
views.UserUserGroupDelete.as_view(),
|
||||
name="user_group_delete",
|
||||
),
|
||||
path(
|
||||
"user/group/<int:pk>/archive",
|
||||
views.user_group_toggle_archive,
|
||||
name="user_group_toggle_archive",
|
||||
),
|
||||
path(
|
||||
"user/group/create",
|
||||
views.UserUserGroupCreate.as_view(),
|
||||
|
||||
@@ -4919,6 +4919,37 @@ def user_group_view_all(request):
|
||||
)
|
||||
|
||||
|
||||
@user_is_cid_user_manager
|
||||
def user_group_toggle_archive(request, pk):
|
||||
"""HTMX endpoint to archive or unarchive a UserUserGroup.
|
||||
|
||||
POST params:
|
||||
- archive: 'true' or 'false'
|
||||
- view_all: optional '1' to indicate the caller is viewing the all-groups list
|
||||
|
||||
Returns an HTML fragment for the updated group card, or an empty
|
||||
response when the card should be removed from the page (e.g. archiving
|
||||
from the active-only view).
|
||||
"""
|
||||
if request.method != "POST":
|
||||
return HttpResponse(status=405)
|
||||
|
||||
group = get_object_or_404(UserUserGroup, pk=pk)
|
||||
|
||||
archive_val = request.POST.get("archive", "true")
|
||||
group.archive = True if archive_val == "true" else False
|
||||
group.save()
|
||||
|
||||
view_all = True if request.POST.get("view_all") == "1" else False
|
||||
|
||||
# If the group is now archived and the caller is not viewing archived
|
||||
# groups, remove the card from the page by returning an empty fragment.
|
||||
if group.archive and not view_all:
|
||||
return HttpResponse("")
|
||||
|
||||
return render(request, "generic/partials/user_group_card.html", {"group": group, "view_all": view_all})
|
||||
|
||||
|
||||
@login_required
|
||||
def user_toggle_group(request, user_id, group_id):
|
||||
"""Toggle membership of a user in either a UserUserGroup or an auth Group.
|
||||
|
||||
Reference in New Issue
Block a user