From fbd3c9bee570eea39fa207def7d455005c463752 Mon Sep 17 00:00:00 2001 From: "Adrian A. Baumann" Date: Sun, 28 Dec 2025 23:33:47 +0100 Subject: [PATCH] Add thing type detail page and move functionality - Add thing type detail page showing hierarchical structure and things - Add move to box functionality on thing detail page - Fix add things form to only show items in current box - Update thumbnails to 50x50 on box detail page - Make thing names linkable on box detail page Bump container version to 0.026 --- argocd/deployment.yaml | 2 +- boxes/templates/boxes/thing_detail.html | 54 ++++++ boxes/templates/boxes/thing_type_detail.html | 183 +++++++++++++++++++ boxes/views.py | 42 ++++- labhelper/urls.py | 3 +- 5 files changed, 277 insertions(+), 7 deletions(-) create mode 100644 boxes/templates/boxes/thing_type_detail.html diff --git a/argocd/deployment.yaml b/argocd/deployment.yaml index 7263556..c757ea7 100644 --- a/argocd/deployment.yaml +++ b/argocd/deployment.yaml @@ -27,7 +27,7 @@ spec: mountPath: /data containers: - name: web - image: git.baumann.gr/adebaumann/labhelper:0.025 + image: git.baumann.gr/adebaumann/labhelper:0.026 imagePullPolicy: Always ports: - containerPort: 8000 diff --git a/boxes/templates/boxes/thing_detail.html b/boxes/templates/boxes/thing_detail.html index f87347f..a8c7825 100644 --- a/boxes/templates/boxes/thing_detail.html +++ b/boxes/templates/boxes/thing_detail.html @@ -83,6 +83,45 @@ .nav-links a { margin-right: 15px; } + .move-form { + background: white; + padding: 20px; + border-radius: 8px; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + margin-top: 20px; + } + .move-form label { + font-weight: 600; + color: #666; + font-size: 14px; + margin-bottom: 8px; + display: block; + } + .move-form select { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 14px; + background-color: white; + margin-right: 10px; + } + .move-form select:focus { + outline: none; + border-color: #4a90a4; + } + .btn { + background-color: #4a90a4; + color: white; + padding: 8px 16px; + border: none; + border-radius: 4px; + font-size: 14px; + cursor: pointer; + font-weight: 600; + } + .btn:hover { + background-color: #3d7a96; + } @@ -125,7 +164,22 @@
{{ thing.description }}
{% endif %} + + +
+ {% csrf_token %} + + + +
diff --git a/boxes/templates/boxes/thing_type_detail.html b/boxes/templates/boxes/thing_type_detail.html new file mode 100644 index 0000000..7652710 --- /dev/null +++ b/boxes/templates/boxes/thing_type_detail.html @@ -0,0 +1,183 @@ +{% load thumbnail %} + + + + + + {{ thing_type.name }} - LabHelper + + + + ← Home + +

{{ thing_type.name }}

+ +
+ {% if thing_type.parent %} + + {% endif %} + {% if thing_type.children.exists %} +
+ Subtypes: + {% for child in thing_type.children.all %} + {{ child.name }} + {% endfor %} +
+ {% endif %} +
+ + {% if things_by_type %} + {% for subtype, things in things_by_type.items %} +
+

{{ subtype.name }}

+ {% if things %} + + + + + + + + + + + {% for thing in things %} + + + + + + + {% endfor %} + +
PictureNameBoxDescription
+ {% if thing.picture %} + {% thumbnail thing.picture "50x50" crop="center" as thumb %} + {{ thing.name }} + {% endthumbnail %} + {% else %} +
No image
+ {% endif %} +
+ {{ thing.name }} + + Box {{ thing.box.id }} +
{{ thing.box.box_type.name }} +
{{ thing.description|default:"-" }}
+ {% else %} +
No things in this category
+ {% endif %} +
+ {% endfor %} + {% else %} +
+
No things found in this category or its subcategories
+
+ {% endif %} + + \ No newline at end of file diff --git a/boxes/views.py b/boxes/views.py index ad6b6e4..a9792f1 100644 --- a/boxes/views.py +++ b/boxes/views.py @@ -2,7 +2,7 @@ from django.http import HttpResponse, JsonResponse from django.shortcuts import get_object_or_404, redirect, render from .forms import ThingFormSet -from .models import Box, Thing +from .models import Box, Thing, ThingType def index(request): @@ -27,7 +27,21 @@ def thing_detail(request, thing_id): Thing.objects.select_related('thing_type', 'box', 'box__box_type'), pk=thing_id ) - return render(request, 'boxes/thing_detail.html', {'thing': thing}) + + boxes = Box.objects.select_related('box_type').all().order_by('id') + + if request.method == 'POST': + new_box_id = request.POST.get('new_box') + if new_box_id: + new_box = get_object_or_404(Box, pk=new_box_id) + thing.box = new_box + thing.save() + return redirect('thing_detail', thing_id=thing.id) + + return render(request, 'boxes/thing_detail.html', { + 'thing': thing, + 'boxes': boxes, + }) def search(request): @@ -65,7 +79,7 @@ def add_things(request, box_id): success_message = None if request.method == 'POST': - formset = ThingFormSet(request.POST) + formset = ThingFormSet(request.POST, queryset=Thing.objects.filter(box=box)) if formset.is_valid(): things = formset.save(commit=False) @@ -77,12 +91,30 @@ def add_things(request, box_id): created_count += 1 if created_count > 0: success_message = f'Added {created_count} thing{"s" if created_count > 1 else ""} successfully.' - formset = ThingFormSet() + formset = ThingFormSet(queryset=Thing.objects.filter(box=box)) else: - formset = ThingFormSet() + formset = ThingFormSet(queryset=Thing.objects.filter(box=box)) return render(request, 'boxes/add_things.html', { 'box': box, 'formset': formset, 'success_message': success_message, }) + + +def thing_type_detail(request, type_id): + """Display details of a thing type with its hierarchy and things.""" + thing_type = get_object_or_404(ThingType, pk=type_id) + + descendants = thing_type.get_descendants(include_self=True) + things_by_type = {} + + for descendant in descendants: + things = descendant.things.select_related('box', 'box__box_type').all() + if things: + things_by_type[descendant] = things + + return render(request, 'boxes/thing_type_detail.html', { + 'thing_type': thing_type, + 'things_by_type': things_by_type, + }) diff --git a/labhelper/urls.py b/labhelper/urls.py index 03bad63..5032423 100644 --- a/labhelper/urls.py +++ b/labhelper/urls.py @@ -19,12 +19,13 @@ from django.conf.urls.static import static from django.contrib import admin from django.urls import path -from boxes.views import add_things, box_detail, index, search, search_api, thing_detail +from boxes.views import add_things, box_detail, index, search, search_api, thing_detail, thing_type_detail urlpatterns = [ path('', index, name='index'), path('box//', box_detail, name='box_detail'), path('thing//', thing_detail, name='thing_detail'), + path('thing-type//', thing_type_detail, name='thing_type_detail'), path('box//add/', add_things, name='add_things'), path('search/', search, name='search'), path('search/api/', search_api, name='search_api'),