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
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)
|