Add functionality to delete all leave requests for a worker with permission checks
This commit is contained in:
@@ -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 %}">← 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 %}">← 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>
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user