from django.contrib import admin from django import forms from django.utils.translation import gettext_lazy as _ from unfold.admin import ModelAdmin, StackedInline from unfold.contrib.filters.admin import ( ChoicesDropdownFilter, MultipleRelatedDropdownFilter, RangeDateFilter, ) from utils.admin import project_admin_site from apps.course.models import ( CourseLiveSession, LiveSessionRecording, LiveSessionUser, USER_ROLE_CHOICES, RECORDING_TYPE_CHOICES, ) from django.contrib.auth import get_user_model User = get_user_model() # 🔔 CUSTOM FILTER: Only show active, non-guest users in the right sidebar filter class ActiveUserDropdownFilter(MultipleRelatedDropdownFilter): def field_choices(self, field, request, model_admin): return field.get_choices( include_blank=False, limit_choices_to={'is_active': True, 'email__isnull': False} ) # --- WIDTH ENFORCEMENT & PLACEHOLDER TEXT FOR DROPDOWNS --- class MinWidthInlineForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Target the dropdown fields to ensure they don't collapse target_dropdown_fields = ['user', 'role', 'recording_type'] for field_name, field in self.fields.items(): if field_name in target_dropdown_fields: if hasattr(field.widget, 'attrs'): existing_class = field.widget.attrs.get('class', '') field.widget.attrs['class'] = f"{existing_class} min-w-[250px] w-full" existing_style = field.widget.attrs.get('style', '') field.widget.attrs['style'] = f"{existing_style} min-width: 250px; width: 250px;" # 🔔 Add the custom placeholder text if hasattr(field, 'empty_label'): field.empty_label = _("Select a value") class LiveSessionUserInline(StackedInline): model = LiveSessionUser form = MinWidthInlineForm extra = 1 tab = True fieldsets = ( (None, { 'fields': (('user', 'role', 'is_online'),) # Grouped horizontally }), (_("Timing"), { 'fields': (('entered_at', 'exited_at'),) # Grouped horizontally }), ) show_change_link = True verbose_name = _("Session User") verbose_name_plural = _("Session Users") # 🔔 FILTER THE USER DROPDOWN IN THE FORM ITSELF def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "user": kwargs["queryset"] = User.objects.filter(is_active=True, email__isnull=False) return super().formfield_for_foreignkey(db_field, request, **kwargs) class LiveSessionRecordingInline(StackedInline): model = LiveSessionRecording form = MinWidthInlineForm extra = 1 tab = True fieldsets = ( (None, { 'fields': (('title', 'recording_type', 'is_active'),) # Grouped horizontally }), (_("Media"), { 'fields': ('file',) }), ) show_change_link = True verbose_name = _("Session Recording") verbose_name_plural = _("Session Recordings") class CourseLiveSessionAdmin(ModelAdmin): list_display = ("subject", "course", "started_at", "ended_at", "created_at") list_filter = ( ("course", MultipleRelatedDropdownFilter), ("started_at", RangeDateFilter), ) search_fields = ("subject", "course__title") ordering = ("-started_at",) autocomplete_fields = ("course",) readonly_fields = ("created_at", "updated_at") # Add the custom tabs here inlines = [LiveSessionUserInline, LiveSessionRecordingInline] fieldsets = ( (None, {"fields": ("course", "subject", "started_at", "ended_at")}), (_("Timestamps"), {"fields": ("created_at", "updated_at")}), ) class LiveSessionUserAdmin(ModelAdmin): list_display = ("user", "session", "role", "is_online", "entered_at", "exited_at") list_filter = ( ("session", MultipleRelatedDropdownFilter), ("user", ActiveUserDropdownFilter), # 🔔 APPLIED CUSTOM FILTER HERE! ("role", ChoicesDropdownFilter), ("entered_at", RangeDateFilter), ("is_online", admin.BooleanFieldListFilter), ) search_fields = ( "user__email", "user__fullname", "session__subject", ) autocomplete_fields = ("user", "session") readonly_fields = ("created_at", "updated_at") fieldsets = ( (None, {"fields": ("session", "user", "role")}), (_("Session Timing"), {"fields": ("entered_at", "exited_at", "is_online")}), (_("Timestamps"), {"fields": ("created_at", "updated_at")}), ) def get_role_choices(self, request): return USER_ROLE_CHOICES # FILTER THE STANDALONE ADMIN FORM AS WELL JUST IN CASE def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "user": kwargs["queryset"] = User.objects.filter(is_active=True, email__isnull=False) return super().formfield_for_foreignkey(db_field, request, **kwargs) class LiveSessionRecordingAdmin(ModelAdmin): list_display = ("title", "session", "recording_type", "is_active", "created_at") list_filter = ( ("session", MultipleRelatedDropdownFilter), ("recording_type", ChoicesDropdownFilter), ("created_at", RangeDateFilter), ("is_active", admin.BooleanFieldListFilter), ) search_fields = ("title", "session__subject", "session__course__title") autocomplete_fields = ("session",) readonly_fields = ("created_at", "updated_at") fieldsets = ( (None, {"fields": ("session", "title", "recording_type")}), (_("Files"), {"fields": ("file", "file_time", "thumbnail")}), (_("Status"), {"fields": ("is_active",)}), (_("Timestamps"), {"fields": ("created_at", "updated_at")}), ) def get_recording_type_choices(self, request): return RECORDING_TYPE_CHOICES project_admin_site.register(CourseLiveSession, CourseLiveSessionAdmin) project_admin_site.register(LiveSessionUser, LiveSessionUserAdmin) project_admin_site.register(LiveSessionRecording, LiveSessionRecordingAdmin)