Add functionality to delete all leave requests for a worker with permission checks

This commit is contained in:
Ross
2025-12-15 21:25:21 +00:00
parent 638fe17c60
commit c5850eb1b2
5 changed files with 73 additions and 2 deletions
@@ -14,7 +14,14 @@
<p style="margin-top:0.5rem"><a class="button is-light" hx-get="{% url 'rota:worker_settings_token' token %}" hx-target="#modal" hx-swap="innerHTML">Update your settings</a></p>
{% endif %}
</div>
{# Navigation back to the first rota this worker belongs to (if available) #}
{% if worker %}
{% with back_rota=worker.rotas.all.0 %}
{% if back_rota %}
<p style="margin-top:0.5rem"><a class="button" href="{% url 'rota:rota_detail' back_rota.id %}">&larr; Back to rota: {{ back_rota.name }}</a></p>
{% endif %}
{% endwith %}
{% endif %}
<form method="post">
{% csrf_token %}
{% if token %}
@@ -7,11 +7,14 @@
<!-- Rely on the server to return an out-of-band (OOB) swap that updates
the #leave-list. Prevent htmx from attempting the main swap by
disabling the default swap for this request. -->
<form method="post" hx-post="{% url 'rota:leave_delete' l.id %}" hx-swap="none" style="display:inline;margin-left:0.5rem">
{% comment %}Only show delete control when the current request is allowed to delete:{% endcomment %}
{% if request.user.is_staff or request.user.is_authenticated and request.user.email and request.user.email == l.worker.email or token %}
<form method="post" action="{% url 'rota:leave_delete' l.id %}" hx-post="{% url 'rota:leave_delete' l.id %}" hx-swap="none" style="display:inline;margin-left:0.5rem">
{% csrf_token %}
{% if token %}<input type="hidden" name="token" value="{{ token }}">{% endif %}
<button class="button is-small is-danger" type="submit" onclick="return confirm('Delete this leave request?');">Delete</button>
</form>
{% endif %}
</li>
{% empty %}
<li>No leave recorded</li>
@@ -18,6 +18,13 @@
<h1 class="title">{{ worker.name }}</h1>
<p class="subtitle">{{ worker.email }} - {{ worker.site }}</p>
{# Navigation: back to an assigned rota (show first assigned rota if any) #}
{% with back_rota=worker.rotas.all.0 %}
{% if back_rota %}
<p style="margin-top:0.5rem"><a class="button is-light" href="{% url 'rota:rota_detail' back_rota.id %}">&larr; Back to rota: {{ back_rota.name }}</a></p>
{% endif %}
{% endwith %}
<div class="box">
<h2 class="subtitle">Request leave</h2>
<form method="post">
@@ -33,6 +40,13 @@
<h2 class="subtitle">Existing leave</h2>
<div class="content">
{# Delete-all control: only show to staff or the worker themselves #}
{% if request.user.is_staff or request.user.is_authenticated and request.user.email and request.user.email == worker.email %}
<form method="post" action="{% url 'rota:leave_delete_all' worker.id %}" hx-post="{% url 'rota:leave_delete_all' worker.id %}" hx-swap="none" style="display:inline;margin-bottom:0.5rem">
{% csrf_token %}
<button class="button is-small is-danger" type="submit" onclick="return confirm('Delete ALL leave requests for this worker?');">Delete all leave</button>
</form>
{% endif %}
{% include 'rota/partials/leave_list.html' with leaves=worker.leaves.all %}
</div>
</div>
+1
View File
@@ -21,6 +21,7 @@ urlpatterns = [
path("leave/request/token/<uuid:token>/", views.request_leave, name="request_leave_token"),
path("worker/settings/token/<uuid:token>/", views.worker_settings_token, name="worker_settings_token"),
path("leave/<int:leave_id>/delete/", views.leave_delete, name="leave_delete"),
path("worker/<int:worker_id>/leaves/delete_all/", views.leave_delete_all, name="leave_delete_all"),
path("worker/<int:worker_id>/regenerate_token/", views.regenerate_worker_token, name="regenerate_worker_token"),
path("run/<int:run_id>/", views.rota_run_detail, name="rota_run_detail"),
path("run/<int:run_id>/export/html/", views.rota_run_export_html, name="rota_run_export_html"),
+46
View File
@@ -1062,6 +1062,52 @@ def leave_delete(request, leave_id):
return response
@require_POST
def leave_delete_all(request, worker_id):
"""Delete all Leave records for a worker. Permissions same as `leave_delete`.
Returns an OOB swap updating `#leave-list` and triggers `leaveUpdated`.
"""
worker = get_object_or_404(Worker, pk=worker_id)
# permission checks (same as leave_delete)
allowed = False
token = request.POST.get('token') or request.GET.get('token')
try:
if request.user and request.user.is_authenticated and request.user.is_staff:
allowed = True
elif request.user and request.user.is_authenticated and getattr(request.user, 'email', None) and request.user.email == (worker.email or ''):
allowed = True
elif token:
try:
import uuid
token_uuid = uuid.UUID(token)
if worker.request_token == token_uuid:
allowed = True
except Exception:
allowed = False
except Exception:
allowed = False
if not allowed:
return HttpResponseBadRequest('Permission denied')
# delete all leaves for this worker
worker.leaves.all().delete()
# render updated leave list for the worker
leave_list_html = render_to_string(
"rota/partials/leave_list.html",
{"leaves": worker.leaves.all(), "token": token},
request=request,
)
resp = f'<div id="leave-list" hx-swap-oob="true">{leave_list_html}</div>'
response = HttpResponse(resp)
response["HX-Trigger"] = "leaveUpdated"
return response
@require_POST
def rota_run_export_regenerate(request, run_id):
"""Regenerate the HTML export for a completed run and persist it on the RotaRun.