This commit is contained in:
Ross
2025-12-14 21:52:31 +00:00
parent ff2b2ebb95
commit f0ebc0d01a
9 changed files with 259 additions and 105 deletions
+50
View File
@@ -407,6 +407,56 @@ class RotaScheduleForm(forms.ModelForm):
self.fields["end_date"].initial = end_date
return cleaned
class WorkerSelfServiceForm(forms.ModelForm):
"""Small form exposed to workers via tokenized links so they can set
their FTE and pick non-working days.
"""
WEEKDAYS = [
('0', 'Monday'),
('1', 'Tuesday'),
('2', 'Wednesday'),
('3', 'Thursday'),
('4', 'Friday'),
('5', 'Saturday'),
('6', 'Sunday'),
]
nwds = forms.MultipleChoiceField(choices=WEEKDAYS, required=False, widget=forms.CheckboxSelectMultiple, label="Non-working days")
class Meta:
model = Worker
fields = ["fte"]
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance')
initial = kwargs.get('initial', {})
if instance is not None:
try:
opts = instance.options or {}
nwds = opts.get('nwds', [])
# store as strings for the MultipleChoiceField
initial['nwds'] = [str(x) for x in (nwds or [])]
initial['fte'] = instance.fte
except Exception:
pass
kwargs['initial'] = initial
super().__init__(*args, **kwargs)
def save(self, commit=True):
instance = super().save(commit=False)
# persist non-working days into instance.options['nwds'] as list of ints
opts = instance.options or {}
nwds = self.cleaned_data.get('nwds') or []
try:
opts['nwds'] = [int(x) for x in nwds]
except Exception:
opts['nwds'] = nwds
instance.options = opts
if commit:
instance.save()
return instance
def save(self, commit=True):
instance = super().save(commit=False)
end_date = self.cleaned_data.get("end_date")
+1
View File
@@ -19,6 +19,7 @@ urlpatterns = [
path("worker/<int:worker_id>/leaves.json", views.leaves_json, name="worker_leaves_json"),
path("leave/request/", views.request_leave, name="request_leave"),
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>/regenerate_token/", views.regenerate_worker_token, name="regenerate_worker_token"),
path("run/<int:run_id>/", views.rota_run_detail, name="rota_run_detail"),
+46
View File
@@ -3,6 +3,7 @@ from django.urls import reverse
from .models import RotaSchedule, Worker
from .forms import WorkerForm, LeaveForm, RotaScheduleForm
from .forms import WorkerSelfServiceForm
from .services import run_rota_async
from .forms import ShiftForm
import json
@@ -568,6 +569,51 @@ def request_leave(request, token=None):
return render(request, "rota/leave_request.html", {"form": form, "worker": worker, "token": token})
@require_http_methods(["GET", "POST"])
def worker_settings_token(request, token):
"""Allow a worker identified by a request token to update simple settings
such as FTE and non-working days without requiring authentication.
"""
worker = None
if token:
try:
worker = get_object_or_404(Worker, request_token=token)
except Exception:
worker = None
if worker is None:
return HttpResponseBadRequest("Invalid token")
is_hx = request.headers.get("HX-Request", "false").lower() == "true"
if request.method == "POST":
form = WorkerSelfServiceForm(request.POST, instance=worker)
if form.is_valid():
form.save()
if is_hx:
# Return minimal fragment confirming success and trigger a leaveUpdated
# so any calendars can refresh (if they rely on worker settings).
resp = render_to_string("rota/partials/worker_settings_saved.html", {"worker": worker}, request=request)
response = HttpResponse(resp)
response["HX-Trigger"] = "closeModal,leaveUpdated"
return response
# Full page: redirect back to tokenized request page
return redirect(reverse('rota:request_leave_token', args=[token]))
else:
form = WorkerSelfServiceForm(instance=worker)
# HTMX modal request: return partial modal
if is_hx:
modal_html = render_to_string(
"rota/partials/worker_selfservice_modal.html",
{"form": form, "form_action": request.get_full_path(), "worker": worker},
request=request,
)
return HttpResponse(modal_html)
return render(request, "rota/worker_settings.html", {"form": form, "worker": worker, "token": token})
def rota_options(request, rota_id):
"""HTMX-aware modal edit for rota.options JSON.