From dadc940993bec424aebb65ce3713832584a33a12 Mon Sep 17 00:00:00 2001 From: Ross Date: Tue, 9 Dec 2025 13:23:17 +0000 Subject: [PATCH] . --- djangorota/rota/urls.py | 3 + djangorota/rota/views.py | 141 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/djangorota/rota/urls.py b/djangorota/rota/urls.py index 9bc7f82..971035e 100644 --- a/djangorota/rota/urls.py +++ b/djangorota/rota/urls.py @@ -22,6 +22,9 @@ urlpatterns = [ path("rota//export/builder/", views.rota_export_builder, name="rota_export_builder"), path("rota//options/update/", views.rota_options_update, name="rota_options_update"), path("rota//options/", views.rota_options, name="rota_options"), + path("rota//option/add/", views.rota_option_add, name="rota_option_add"), + path("rota//option/edit/", views.rota_option_edit, name="rota_option_edit"), + path("rota//option/delete/", views.rota_option_delete, name="rota_option_delete"), path("rota//runs/clear/", views.rota_runs_clear, name="rota_runs_clear"), path("run//export/regenerate/", views.rota_run_export_regenerate, name="rota_run_export_regenerate"), ] diff --git a/djangorota/rota/views.py b/djangorota/rota/views.py index a4f6ad9..7739a59 100644 --- a/djangorota/rota/views.py +++ b/djangorota/rota/views.py @@ -335,6 +335,147 @@ def rota_options(request, rota_id): return HttpResponse(modal_html) +def _oob_update_options_display(request, rota): + """Helper: render options display and return HTMX OOB swap response.""" + options_pretty = json.dumps(rota.options or {}, indent=4) + options_html = render_to_string( + "rota/partials/rota_options_display.html", + {"rota": rota, "options_pretty": options_pretty}, + request=request, + ) + resp = '
' + options_html + '
' + '' + response = HttpResponse(resp) + response["HX-Trigger"] = "closeModal" + return response + + +def rota_option_add(request, rota_id): + rota = get_object_or_404(RotaSchedule, pk=rota_id) + is_hx = request.headers.get("HX-Request", "false").lower() == "true" + + if request.method == "POST": + key = request.POST.get("key", "").strip() + val = request.POST.get("value", "") + vtype = request.POST.get("type", "string") + if not key: + return HttpResponseBadRequest("Key is required") + # parse value according to type + try: + if vtype == "int": + parsed = int(val) + elif vtype == "float": + parsed = float(val) + elif vtype == "bool": + parsed = val.lower() in ("1", "true", "yes", "on") + elif vtype == "json": + parsed = json.loads(val) if val.strip() else None + else: + parsed = val + except Exception as exc: + return HttpResponseBadRequest(f"Invalid value: {exc}") + + opts = rota.options or {} + opts[key] = parsed + rota.options = opts + rota.save(update_fields=["options"]) + + if is_hx: + return _oob_update_options_display(request, rota) + + return redirect("rota:rota_detail", rota_id=rota.id) + + # GET: show modal partial + modal_html = render_to_string( + "rota/partials/rota_option_modal_single.html", + {"form_action": reverse("rota:rota_option_add", args=(rota.id,)), "initial": {"key": "", "value": "", "type": "string"}, "rota": rota}, + request=request, + ) + return HttpResponse(modal_html) + + +def rota_option_edit(request, rota_id): + rota = get_object_or_404(RotaSchedule, pk=rota_id) + is_hx = request.headers.get("HX-Request", "false").lower() == "true" + + if request.method == "POST": + key = request.POST.get("key", "").strip() + val = request.POST.get("value", "") + vtype = request.POST.get("type", "string") + if not key: + return HttpResponseBadRequest("Key is required") + try: + if vtype == "int": + parsed = int(val) + elif vtype == "float": + parsed = float(val) + elif vtype == "bool": + parsed = val.lower() in ("1", "true", "yes", "on") + elif vtype == "json": + parsed = json.loads(val) if val.strip() else None + else: + parsed = val + except Exception as exc: + return HttpResponseBadRequest(f"Invalid value: {exc}") + + opts = rota.options or {} + opts[key] = parsed + rota.options = opts + rota.save(update_fields=["options"]) + + if is_hx: + return _oob_update_options_display(request, rota) + + return redirect("rota:rota_detail", rota_id=rota.id) + + # GET: prepare edit modal with existing value + key = request.GET.get("key") + if not key: + return HttpResponseBadRequest("Missing key") + current = rota.options or {} + cur_val = current.get(key, "") + # choose type hint + if isinstance(cur_val, bool): + vtype = "bool" + value = "1" if cur_val else "0" + elif isinstance(cur_val, int): + vtype = "int" + value = str(cur_val) + elif isinstance(cur_val, float): + vtype = "float" + value = str(cur_val) + elif isinstance(cur_val, (list, dict)): + vtype = "json" + value = json.dumps(cur_val) + else: + vtype = "string" + value = str(cur_val) + + modal_html = render_to_string( + "rota/partials/rota_option_modal_single.html", + {"form_action": reverse("rota:rota_option_edit", args=(rota.id,)), "initial": {"key": key, "value": value, "type": vtype}, "rota": rota}, + request=request, + ) + return HttpResponse(modal_html) + + +@require_POST +def rota_option_delete(request, rota_id): + rota = get_object_or_404(RotaSchedule, pk=rota_id) + key = request.POST.get("key") + if not key: + return HttpResponseBadRequest("Missing key") + opts = rota.options or {} + if key in opts: + opts.pop(key) + rota.options = opts + rota.save(update_fields=["options"]) + + if request.headers.get("HX-Request", "false").lower() == "true": + return _oob_update_options_display(request, rota) + + return redirect("rota:rota_detail", rota_id=rota.id) + + def rota_run_export_download(request, run_id): from .models import RotaRun