import json from django import forms from django.contrib import admin from django.contrib.admin import SimpleListFilter from django.http import JsonResponse from django.urls import path from django.utils.html import format_html from django.views.decorators.http import require_POST from .models import Box, BoxType, Facet, Tag, Thing, ThingFile, ThingLink class BoxFilter(SimpleListFilter): """Custom filter for boxes using pk to avoid spaces in aliases.""" title = 'box' parameter_name = 'box__pk' def lookups(self, request, model_admin): boxes = Box.objects.select_related('box_type').order_by('sort_order') return [(box.pk, str(box)) for box in boxes] def queryset(self, request, queryset): if self.value(): return queryset.filter(box__pk=self.value()) return queryset class TagsFilter(SimpleListFilter): """Custom filter for tags using pk to avoid spaces in aliases.""" title = 'tags' parameter_name = 'tags__pk' def lookups(self, request, model_admin): tags = Tag.objects.select_related('facet').order_by('facet__name', 'name') return [(tag.pk, str(tag)) for tag in tags] def queryset(self, request, queryset): if self.value(): return queryset.filter(tags__pk=self.value()) return queryset @admin.register(BoxType) class BoxTypeAdmin(admin.ModelAdmin): """Admin configuration for BoxType model.""" list_display = ('name', 'width', 'height', 'length') search_fields = ('name',) @admin.register(Box) class BoxAdmin(admin.ModelAdmin): """Admin configuration for Box model.""" ordering = ['sort_order'] list_display = ('id', 'box_type', 'sort_order') list_filter = ('box_type',) search_fields = ('id',) change_list_template = 'admin/boxes/box/change_list.html' def get_urls(self): urls = super().get_urls() custom_urls = [ path('reorder/', self.admin_site.admin_view(self.reorder_view), name='boxes_box_reorder'), ] return custom_urls + urls def reorder_view(self, request): """Handle AJAX reorder requests.""" if request.method != 'POST': return JsonResponse({'error': 'POST required'}, status=405) try: data = json.loads(request.body) order = data.get('order', []) for index, pk in enumerate(order): Box.objects.filter(pk=pk).update(sort_order=index) return JsonResponse({'status': 'ok'}) except (json.JSONDecodeError, KeyError) as e: return JsonResponse({'error': str(e)}, status=400) class ThingFileInline(admin.TabularInline): """Inline admin for Thing files.""" model = ThingFile extra = 1 fields = ('title', 'file') class ThingLinkInline(admin.TabularInline): """Inline admin for Thing links.""" model = ThingLink extra = 1 fields = ('title', 'url') class ThingAdmin(admin.ModelAdmin): """Admin configuration for Thing model.""" list_display = ('name', 'box') list_filter = (BoxFilter, TagsFilter) search_fields = ('name', 'description') filter_horizontal = ('tags',) inlines = [ThingFileInline, ThingLinkInline] admin.site.register(Thing, ThingAdmin) @admin.register(ThingFile) class ThingFileAdmin(admin.ModelAdmin): """Admin configuration for ThingFile model.""" list_display = ('thing', 'title', 'uploaded_at') list_filter = ('thing',) search_fields = ('title',) class ColorInput(forms.TextInput): """Color picker widget using HTML5 color input.""" input_type = 'color' class FacetAdminForm(forms.ModelForm): """Form for Facet model with color picker widget.""" class Meta: model = Facet fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['color'].widget = ColorInput(attrs={'type': 'color', 'style': 'height: 50px; width: 100px;'}) @admin.register(Facet) class FacetAdmin(admin.ModelAdmin): """Admin configuration for Facet model.""" form = FacetAdminForm list_display = ('name', 'color_preview', 'cardinality') search_fields = ('name',) prepopulated_fields = {'slug': ('name',)} list_filter = ('cardinality',) def color_preview(self, obj): return format_html('■ {}', obj.color, obj.color) @admin.register(Tag) class TagAdmin(admin.ModelAdmin): """Admin configuration for Tag model.""" list_display = ('__str__', 'facet_with_color') list_filter = ('facet',) search_fields = ('name', 'facet__name') def facet_with_color(self, obj): if obj.facet: return format_html('{}', obj.facet.color, obj.facet.name) return '-' facet_with_color.short_description = 'Facet' @admin.register(ThingLink) class ThingLinkAdmin(admin.ModelAdmin): """Admin configuration for ThingLink model.""" list_display = ('thing', 'title', 'url', 'uploaded_at') list_filter = ('thing',) search_fields = ('title', 'url')