From 761f52eafaf132390e9a5dc4db796aba5e9645e6 Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 15 Dec 2025 22:16:35 +0000 Subject: [PATCH] . --- .../rota/partials/select_workers_modal.html | 93 +++++++++++++++++++ djangorota/rota/urls.py | 2 + djangorota/rota/views.py | 53 +++++++++++ static/js/calendar.js | 20 +++- 4 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 djangorota/djangorota/templates/rota/partials/select_workers_modal.html diff --git a/djangorota/djangorota/templates/rota/partials/select_workers_modal.html b/djangorota/djangorota/templates/rota/partials/select_workers_modal.html new file mode 100644 index 0000000..527307d --- /dev/null +++ b/djangorota/djangorota/templates/rota/partials/select_workers_modal.html @@ -0,0 +1,93 @@ + diff --git a/djangorota/rota/urls.py b/djangorota/rota/urls.py index 0797bb8..4029fb5 100644 --- a/djangorota/rota/urls.py +++ b/djangorota/rota/urls.py @@ -22,6 +22,8 @@ urlpatterns = [ path("worker/settings/token//", views.worker_settings_token, name="worker_settings_token"), path("leaves/all/", views.leaves_global, name="leaves_global"), path("leaves/all.json", views.leaves_global_json, name="leaves_global_json"), + path("leave/select_workers/", views.select_workers_modal, name="select_workers_modal"), + path("leave/apply_multi/", views.apply_leave_multi, name="apply_leave_multi"), path("leave//delete/", views.leave_delete, name="leave_delete"), path("worker//leaves/delete_all/", views.leave_delete_all, name="leave_delete_all"), path("worker//regenerate_token/", views.regenerate_worker_token, name="regenerate_worker_token"), diff --git a/djangorota/rota/views.py b/djangorota/rota/views.py index defb184..1528e5a 100644 --- a/djangorota/rota/views.py +++ b/djangorota/rota/views.py @@ -1085,6 +1085,59 @@ def leaves_global(request): return render(request, 'rota/leaves_global.html', {'leaves_json': leaves_json}) +@require_http_methods(["GET"]) +def select_workers_modal(request): + """Return an HTMX modal partial that lists workers to apply a selected leave range to. + + Query params: start_date, end_date, rota_id (optional) + """ + start_date = request.GET.get('start_date') + end_date = request.GET.get('end_date') + rota_id = request.GET.get('rota_id') + workers = Worker.objects.all() + if rota_id: + try: + workers = workers.filter(rotas__id=int(rota_id)) + except Exception: + pass + workers = workers.order_by('name') + modal_html = render_to_string( + 'rota/partials/select_workers_modal.html', + {'workers': workers, 'start_date': start_date, 'end_date': end_date}, + request=request, + ) + return HttpResponse(modal_html) + + +@require_http_methods(["POST"]) +def apply_leave_multi(request): + """Apply a leave range to multiple workers. Expects POST with worker_ids (list), start_date and end_date.""" + from .models import Leave + + worker_ids = request.POST.getlist('worker_id') + start_date = request.POST.get('start_date') + end_date = request.POST.get('end_date') + if not worker_ids or not start_date: + return HttpResponseBadRequest('Missing parameters') + created = [] + for wid in worker_ids: + try: + w = Worker.objects.get(pk=int(wid)) + except Exception: + continue + try: + leave = Leave(worker=w, start_date=start_date, end_date=end_date) + leave.save() + created.append(leave) + except Exception: + continue + + # Return simple HTMX response: close modal and trigger calendar refresh + response = HttpResponse('') + response['HX-Trigger'] = 'closeModal,leaveUpdated' + return response + + @require_POST def leave_delete(request, leave_id): """Delete a Leave record. Allowed if the requester is staff, or if a valid token matches the worker, or the authenticated user's email matches the worker email.""" diff --git a/static/js/calendar.js b/static/js/calendar.js index 45fc27f..4f66906 100644 --- a/static/js/calendar.js +++ b/static/js/calendar.js @@ -168,10 +168,22 @@ previewEnd = null; render(); if(selStart && selEnd){ - const url = requestUrl + '?worker_id=' + encodeURIComponent(workerId) + '&start_date=' + selStart + '&end_date=' + selEnd; - setTimeout(function(){ - try{ if(window.htmx && typeof window.htmx.ajax === 'function'){ window.htmx.ajax('GET', url, {target:'#modal', swap:'innerHTML'}); } else { htmx.ajax('GET', url, {target:'#modal', swap:'innerHTML'}); } }catch(e){ window.location = url; } - }, 80); + // If a specific workerId is supplied, use existing single-worker modal flow. + if(workerId){ + const url = requestUrl + '?worker_id=' + encodeURIComponent(workerId) + '&start_date=' + selStart + '&end_date=' + selEnd; + setTimeout(function(){ + try{ if(window.htmx && typeof window.htmx.ajax === 'function'){ window.htmx.ajax('GET', url, {target:'#modal', swap:'innerHTML'}); } else { htmx.ajax('GET', url, {target:'#modal', swap:'innerHTML'}); } }catch(e){ window.location = url; } + }, 80); + } else { + // Global calendar: prompt for workers to apply this leave to. + // Use an HTMX GET to render the select-workers modal. + let url = '/leave/select_workers/?start_date=' + encodeURIComponent(selStart) + '&end_date=' + encodeURIComponent(selEnd); + // Include rota_id if present in the page URL + try{ const rotaParam = new URLSearchParams(window.location.search).get('rota_id'); if(rotaParam){ url += '&rota_id=' + encodeURIComponent(rotaParam); } }catch(e){} + setTimeout(function(){ + try{ if(window.htmx && typeof window.htmx.ajax === 'function'){ window.htmx.ajax('GET', url, {target:'#modal', swap:'innerHTML'}); } else { htmx.ajax('GET', url, {target:'#modal', swap:'innerHTML'}); } }catch(e){ window.location = url; } + }, 80); + } } }catch(e){ console.error('calendar pointer handler', e); } });