You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

201 lines
7.5 KiB

from django.contrib import admin
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from django.db.models import Count
from unfold.admin import ModelAdmin, TabularInline
from unfold.contrib.filters.admin import RangeNumericFilter, RangeDateTimeFilter
from apps.chat.models import RoomMessage, ChatMessage, MessageReadStatus
from utils.admin import project_admin_site
class ChatMessageInline(TabularInline):
model = ChatMessage
extra = 0
fields = ('sender', 'content', 'content_type', 'sent_at', 'is_deleted')
readonly_fields = ('sent_at',)
can_delete = False
show_change_link = True
classes = ['collapse']
verbose_name = _("Message")
verbose_name_plural = _("Messages")
class MessageReadStatusAdmin(ModelAdmin):
list_display = (
'user', 'message', 'is_read_status', 'read_at',
)
list_filter = (
('read_at', RangeDateTimeFilter),
'is_read',
)
search_fields = ('user__username', 'user__email', 'message__content')
readonly_fields = ('read_at',)
def is_read_status(self, obj):
if obj.is_read:
return format_html('<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">Read</span>')
return format_html('<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">Unread</span>')
is_read_status.short_description = _("Read Status")
class RoomMessageAdmin(ModelAdmin):
list_display = (
'name', 'room_type_badge', 'course', 'initiator',
'messages_count', 'view_messages_button'
)
list_filter = (
'room_type',
('created_at', RangeDateTimeFilter),
('updated_at', RangeDateTimeFilter),
'course'
)
search_fields = ('name', 'description', 'course__title', 'initiator__username', 'recipient__username')
ordering = ('-created_at',)
readonly_fields = ('created_at', 'updated_at', 'messages_count')
inlines = [ChatMessageInline]
fieldsets = (
(_("Room Information"), {
'fields': ('name', 'description', 'room_type', 'messages_count'),
'classes': ('grid-col-2',),
}),
(_("Relations"), {
'fields': ('course', 'initiator', 'recipient'),
'classes': ('grid-col-2',),
}),
(_("Timestamps"), {
'fields': ('created_at', 'updated_at'),
'classes': ('grid-col-2',),
}),
)
def messages_count(self, obj):
count = obj.messages.count()
return format_html('<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">{}</span>', count)
messages_count.short_description = _("Messages Count")
def room_type_badge(self, obj):
if obj.room_type == 'group':
return format_html('<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800">Group</span>')
return format_html('<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800">Private</span>')
room_type_badge.short_description = _("Room Type")
def get_queryset(self, request):
queryset = super().get_queryset(request)
queryset = queryset.annotate(
total_messages=Count('messages')
)
return queryset
def view_messages_button(self, obj):
from django.urls import reverse
url = f"{reverse('admin:chat_chatmessage_changelist')}?room__id__exact={obj.id}"
return format_html(
'<a href="{}" class="inline-flex items-center px-3 py-1.5 rounded text-xs font-medium bg-blue-500 text-white hover:bg-blue-600">'
'<span class="material-icons-outlined mr-1" style="font-size: 14px;">chat</span> {}</a>',
url, _("View Messages")
)
view_messages_button.short_description = _("Messages")
class MessageReadStatusInline(TabularInline):
model = MessageReadStatus
extra = 0
fields = ('user', 'is_read', 'read_at')
readonly_fields = ('read_at',)
can_delete = False
show_change_link = True
classes = ['collapse']
verbose_name = _("Read Status")
verbose_name_plural = _("Read Statuses")
class ChatMessageAdmin(ModelAdmin):
change_list_template = 'admin/chat/chatmessage/change_list.html'
list_display = (
'id', 'room', 'sender', 'content_type_badge', 'content_preview',
'content_size_display', 'sent_at', 'is_deleted_status'
)
list_filter = (
'room',
'content_type',
'is_deleted',
('sent_at', RangeDateTimeFilter),
('updated_at', RangeDateTimeFilter),
('content_size', RangeNumericFilter)
)
search_fields = ('room__name', 'sender__username', 'content')
ordering = ('-sent_at',)
readonly_fields = ('sent_at', 'updated_at', 'content_size')
inlines = [MessageReadStatusInline]
fieldsets = (
(_("Message Information"), {
'fields': ('room', 'sender', 'content', 'content_type'),
'classes': ('grid-col-2',),
}),
(_("Additional Info"), {
'fields': ('content_size',),
'classes': ('grid-col-1',),
}),
(_("Status"), {
'fields': ('is_deleted', 'deleted_at'),
'classes': ('grid-col-2',),
}),
(_("Timestamps"), {
'fields': ('sent_at', 'updated_at'),
'classes': ('grid-col-2',),
}),
)
def content_preview(self, obj):
if obj.content_type == 'text':
preview = obj.content[:50] + '...' if len(obj.content) > 50 else obj.content
return preview
return f"{obj.content_type} content"
content_preview.short_description = _("Content Preview")
def content_type_badge(self, obj):
badges = {
'text': ('blue', 'Text'),
'file': ('yellow', 'File'),
'audio': ('green', 'Audio'),
'image': ('pink', 'Image'),
}
color, label = badges.get(obj.content_type, ('gray', obj.content_type))
return format_html(
'<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-{}-100 text-{}-800">{}</span>',
color, color, label
)
content_type_badge.short_description = _("Type")
def is_deleted_status(self, obj):
if obj.is_deleted:
return format_html('<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">Deleted</span>')
return format_html('<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">Active</span>')
is_deleted_status.short_description = _("Status")
def content_size_display(self, obj):
if obj.content_size:
# Format size in KB if larger than 1024 bytes
if obj.content_size > 1024:
size_kb = obj.content_size / 1024
return f"{size_kb:.1f} KB"
return f"{obj.content_size} bytes"
return "-"
content_size_display.short_description = _("Size")
# Register models with the custom admin site
project_admin_site.register(RoomMessage, RoomMessageAdmin)
project_admin_site.register(ChatMessage, ChatMessageAdmin)
project_admin_site.register(MessageReadStatus, MessageReadStatusAdmin)