from django.contrib import admin from django.utils.translation import gettext_lazy as _ from django.http import JsonResponse from django.urls import path from django.db import models from django.contrib import messages from django.http import HttpResponseRedirect from django.urls import reverse from unfold.admin import ModelAdmin from unfold.decorators import display from dj_category.admin import BaseCategoryAdmin from ajaxdatatable.admin import AjaxDatatable from django.db.models import Case, When, Value from django.utils.html import format_html from apps.hadis.models import HadisCategory from utils.admin import project_admin_site @admin.register(HadisCategory) class HadisCategoryAdmin(BaseCategoryAdmin, ModelAdmin): change_form_template = 'admin/hadiscategory/change_form.html' change_list_template = 'admin/category_index.html' fieldsets = ( (None, { 'fields': ('name', 'source_type', 'category_type', 'parent', 'is_active', 'order'), 'classes': ('unfold-fieldset',), }), ) search_fields = ['name'] list_display = ['name', 'source_type_badge', 'category_type', 'parent', 'is_active', 'order'] list_filter = ['source_type', 'category_type', 'is_active'] @display(description=_("Source Type")) def source_type_badge(self, obj): badge_classes = { 'quran': 'unfold-badge unfold-badge--success', 'hadith': 'unfold-badge unfold-badge--info', 'book': 'unfold-badge unfold-badge--warning', # Add more source types as needed } badge_class = badge_classes.get(obj.source_type, 'unfold-badge') return format_html('{}', badge_class, obj.get_source_type_display()) def get_form(self, request, obj=None, **kwargs): form = super().get_form(request, obj, **kwargs) return form def get_urls(self): urls = super().get_urls() custom_urls = [ path('categories-ajax/hadiscategory/', self.admin_site.admin_view(self.ajax_categories), name='hadiscategory_ajax_categories'), ] return custom_urls + urls def get_categories_groupby_language(self, request=None, selected_values=(), is_multiple=False): return super().get_categories(request, selected_values, is_multiple) def ajax_update(self, request): data = request.POST src_node = self.model.objects.get(pk=int(data['srcNode'])) other_node = self.model.objects.get(pk=int(data['otherNode'])) if src_node.slug in self.base_categories or other_node.slug in self.base_categories: return JsonResponse({'data': _('This item can not be modified')}, status=401) mode = data['hitMode'] if mode == 'over': src_node.move_to(other_node, 'first-child') elif mode == 'after': src_node.move_to(other_node, 'right') elif mode == 'before': src_node.move_to(other_node, 'left') return JsonResponse({'data': 'ok'}, safe=False) def get_categories(self, request=None, selected_values=(), is_multiple=False): """ Override the get_categories method to filter by source_type if provided in the request """ categories = super().get_categories(request, selected_values, is_multiple) # If request has source_type parameter, filter the categories if request and request.GET.get('source_type'): source_type = request.GET.get('source_type') # Filter the categories by source_type filtered_categories = [] for category in categories: # If it's a dictionary (serialized category) if isinstance(category, dict) and category.get('source_type') == source_type: filtered_categories.append(category) # If it's a model instance elif hasattr(category, 'source_type') and getattr(category, 'source_type') == source_type: filtered_categories.append(category) return filtered_categories return categories def ajax_categories(self, request): """ Handle AJAX request for categories with source_type filtering and search """ # Get source_type from request source_type = request.GET.get('source_type') # Get node_id if provided (for single node data) node_id = request.GET.get('node_id') # Get search term if provided search = request.GET.get('search') # Get parent level filter if provided parent_level = request.GET.get('parent_level') if node_id: # Return data for a specific node try: node = self.model.objects.get(pk=int(node_id)) return JsonResponse({ 'id': node.id, 'source_type': node.source_type, 'category_type': node.category_type, 'parent': node.parent_id, 'level': node.level_p # Add the level_p property }) except self.model.DoesNotExist: return JsonResponse({'error': 'Node not found'}, status=404) # Get all categories queryset = self.model.objects.all() # Annotate queryset with level_p queryset = queryset.annotate( level_pp=Case( When(parent=None, then=Value(1)), When(parent__isnull=False, parent__parent=None, then=Value(2)), default=Value(3), output_field=models.IntegerField() ) ) # Filter by source_type if provided if source_type: queryset = queryset.filter(source_type=source_type) # Filter by search term if provided if search: queryset = queryset.filter(name__icontains=search) # Filter by parent_level if provided if parent_level and parent_level.isdigit(): # Convert to integer level = int(parent_level) # Filter categories by level_p queryset = queryset.filter(level_pp=level) # Convert queryset to list of dictionaries for JSON response categories = [] for category in queryset: categories.append({ 'key': category.id, 'title': category.name, 'parent': category.parent_id, 'source_type': category.source_type, 'category_type': category.category_type, 'level': category.level_p, # Add data property to store additional information 'data': { 'parent': category.parent_id, 'level': category.level_p } }) return JsonResponse(categories, safe=False) def save_model(self, request, obj, form, change): # Get the level choice from the form data level_choice = request.POST.get('level_choice_hidden') # Get the parent from AJAX selection if provided ajax_parent = request.POST.get('ajax_parent') if ajax_parent and ajax_parent.isdigit(): # Set the parent for the object try: parent_category = self.model.objects.get(pk=int(ajax_parent)) obj.parent = parent_category except self.model.DoesNotExist: pass # Let the parent class handle the save super().save_model(request, obj, form, change) # Add a message to trigger tree reload via JavaScript messages.success(request, _("Category saved successfully. Tree will be reloaded."), extra_tags='unfold-message unfold-message--success') # Set a flag in the request to redirect back to the category index page request._category_saved = True def response_add(self, request, obj, post_url_continue=None): """ Override to redirect back to the category index page after adding a new category """ if hasattr(request, '_category_saved') and request._category_saved: return HttpResponseRedirect(reverse('admin:hadis_hadiscategory_changelist')) return super().response_add(request, obj, post_url_continue) def response_change(self, request, obj): """ Override to redirect back to the category index page after editing a category """ if hasattr(request, '_category_saved') and request._category_saved: return HttpResponseRedirect(reverse('admin:hadis_hadiscategory_changelist')) return super().response_change(request, obj) # Register with project_admin_site if needed # project_admin_site.register(HadisCategory, HadisCategoryAdmin)