From c1d0f0a3464c7a9644088d911ca45fcb637963b7 Mon Sep 17 00:00:00 2001 From: Stephan Kerkman Date: Sun, 31 May 2026 15:17:36 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20add=20area=20management=20=E2=80=94=20l?= =?UTF-8?q?ist,=20add,=20edit,=20delete=20locations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds /locations/ page with full CRUD for plant areas. Edit redirects to dedicated form; delete unlinks plants and shows confirm dialog if area has plants. Gear icon in plant list filter bar links to management. Co-Authored-By: Claude Sonnet 4.6 --- plants/templates/plants/location_edit.html | 21 +++++++++++++ plants/templates/plants/locations.html | 36 ++++++++++++++++++++++ plants/templates/plants/plant_list.html | 13 +++++--- plants/urls.py | 5 ++- plants/views/locations.py | 34 ++++++++++++++++++++ 5 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 plants/templates/plants/location_edit.html create mode 100644 plants/templates/plants/locations.html create mode 100644 plants/views/locations.py diff --git a/plants/templates/plants/location_edit.html b/plants/templates/plants/location_edit.html new file mode 100644 index 0000000..2409ab0 --- /dev/null +++ b/plants/templates/plants/location_edit.html @@ -0,0 +1,21 @@ +{% extends "plants/base.html" %} +{% block title %}Edit Area — BloomBase{% endblock %} +{% block content %} + +
+ +
Edit area
+
+ +
+ {% csrf_token %} +
+ +
+
+ + Cancel +
+
+ +{% endblock %} diff --git a/plants/templates/plants/locations.html b/plants/templates/plants/locations.html new file mode 100644 index 0000000..8ccfbc2 --- /dev/null +++ b/plants/templates/plants/locations.html @@ -0,0 +1,36 @@ +{% extends "plants/base.html" %} +{% block title %}Areas — BloomBase{% endblock %} +{% block content %} + +
+ +
Areas
+
+ +
+{% for loc in locations %} +
+ {{ loc.name }} {{ loc.plant_count }} plant{{ loc.plant_count|pluralize }} +
+ ✏️ +
+ {% csrf_token %} + +
+
+
+{% empty %} +
No areas yet.
+{% endfor %} +
+ +
+ {% csrf_token %} +
+ + +
+
+ +{% endblock %} diff --git a/plants/templates/plants/plant_list.html b/plants/templates/plants/plant_list.html index b4b5783..7bd399e 100644 --- a/plants/templates/plants/plant_list.html +++ b/plants/templates/plants/plant_list.html @@ -18,11 +18,14 @@ > -
- All - {% for loc in locations %} - {{ loc.name }} - {% endfor %} +
+
+ All + {% for loc in locations %} + {{ loc.name }} + {% endfor %} +
+ ⚙️
diff --git a/plants/urls.py b/plants/urls.py index b01fb1e..63808cb 100644 --- a/plants/urls.py +++ b/plants/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from plants.views import dashboard, plants, pruning, species, identify +from plants.views import dashboard, plants, pruning, species, identify, locations urlpatterns = [ path('', dashboard.dashboard, name='dashboard'), @@ -22,6 +22,9 @@ urlpatterns = [ path('photos//set-thumbnail/', plants.set_thumbnail, name='set_thumbnail'), path('photos//delete/', plants.delete_photo, name='delete_photo'), path('pruning/', pruning.pruning_calendar, name='pruning_calendar'), + path('locations/', locations.location_list, name='location_list'), + path('locations//edit/', locations.location_edit, name='location_edit'), + path('locations//delete/', locations.location_delete, name='location_delete'), path('species/search/', species.species_search, name='species_search'), path('species/select/', species.species_select, name='species_select'), path('species/select/vpc/', species.vpc_select, name='vpc_select'), diff --git a/plants/views/locations.py b/plants/views/locations.py new file mode 100644 index 0000000..e557c37 --- /dev/null +++ b/plants/views/locations.py @@ -0,0 +1,34 @@ +from django.shortcuts import render, get_object_or_404, redirect +from django.views.decorators.http import require_POST +from django.db.models import Count +from plants.models import Location, Plant + + +def location_list(request): + if request.method == 'POST': + name = request.POST.get('name', '').strip() + if name: + Location.objects.get_or_create(name=name) + return redirect('location_list') + + locations = Location.objects.annotate(plant_count=Count('plants')) + return render(request, 'plants/locations.html', {'locations': locations}) + + +def location_edit(request, pk): + location = get_object_or_404(Location, pk=pk) + if request.method == 'POST': + name = request.POST.get('name', '').strip() + if name: + location.name = name + location.save() + return redirect('location_list') + return render(request, 'plants/location_edit.html', {'location': location}) + + +@require_POST +def location_delete(request, pk): + location = get_object_or_404(Location, pk=pk) + Plant.objects.filter(location=location).update(location=None) + location.delete() + return redirect('location_list')