Files
thrillwiki_django_no_react/moderation/views.py
pacnpal 6a9154ce69 Improve moderation dashboard UI and functionality
- Add status tabs (Pending, Approved, Rejected, Escalated)
- Implement HTMX for smooth tab switching and status changes
- Add proper permissions for escalated submissions
- Change Status filter to Submission Type (Text/Photo)
- Move navigation into dashboard content
- Fix tab menu visibility and transitions
- Add contextual loading indicator
- Update styling to match dark theme
- Ensure consistent styling across components
2024-11-13 18:37:36 +00:00

235 lines
9.4 KiB
Python

from django.views.generic import ListView, TemplateView
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse, JsonResponse, HttpRequest
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.decorators import login_required
from django.template.loader import render_to_string
from django.db.models import Q
from django.core.exceptions import PermissionDenied
from typing import Optional, Any, cast
from accounts.models import User
from .models import EditSubmission, PhotoSubmission
MODERATOR_ROLES = ['MODERATOR', 'ADMIN', 'SUPERUSER']
class ModeratorRequiredMixin(UserPassesTestMixin):
request: HttpRequest
def test_func(self) -> bool:
"""Check if user has moderator permissions."""
user = cast(User, self.request.user)
return (
user.is_authenticated and
(getattr(user, 'role', None) in MODERATOR_ROLES or user.is_superuser)
)
def handle_no_permission(self) -> HttpResponse:
if not self.request.user.is_authenticated:
return super().handle_no_permission()
raise PermissionDenied("You do not have moderator permissions.")
class DashboardView(LoginRequiredMixin, ModeratorRequiredMixin, ListView):
template_name = 'moderation/dashboard.html'
context_object_name = 'submissions'
paginate_by = 10
def get_template_names(self):
if self.request.headers.get('HX-Request'):
return ['moderation/partials/dashboard_content.html']
return [self.template_name]
def get_queryset(self):
status = self.request.GET.get('status', 'NEW')
submission_type = self.request.GET.get('submission_type', '')
if submission_type == 'photo':
queryset = PhotoSubmission.objects.filter(status=status).order_by('-created_at')
else:
queryset = EditSubmission.objects.filter(status=status).order_by('-created_at')
if type_filter := self.request.GET.get('type'):
queryset = queryset.filter(submission_type=type_filter)
if content_type := self.request.GET.get('content_type'):
queryset = queryset.filter(content_type__model=content_type)
return queryset
@login_required
def submission_list(request: HttpRequest) -> HttpResponse:
"""View for submission list with filters"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
status = request.GET.get('status', 'NEW')
submission_type = request.GET.get('submission_type', '')
if submission_type == 'photo':
queryset = PhotoSubmission.objects.filter(status=status).order_by('-created_at')
else:
queryset = EditSubmission.objects.filter(status=status).order_by('-created_at')
if type_filter := request.GET.get('type'):
queryset = queryset.filter(submission_type=type_filter)
if content_type := request.GET.get('content_type'):
queryset = queryset.filter(content_type__model=content_type)
context = {
'submissions': queryset,
'user': request.user,
}
# If it's an HTMX request, return just the content
if request.headers.get('HX-Request'):
return render(request, 'moderation/partials/dashboard_content.html', context)
# For direct URL access, return the full dashboard template
return render(request, 'moderation/dashboard.html', context)
@login_required
def approve_submission(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for approving a submission"""
user = cast(User, request.user)
submission = get_object_or_404(EditSubmission, id=submission_id)
if submission.status == 'ESCALATED' and not (user.role in ['ADMIN', 'SUPERUSER'] or user.is_superuser):
return HttpResponse("Only admins can handle escalated submissions", status=403)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
try:
submission.approve(user)
_update_submission_notes(submission, request.POST.get('notes'))
# Get updated queryset with filters
status = request.GET.get('status', 'NEW')
submission_type = request.GET.get('submission_type', '')
if submission_type == 'photo':
queryset = PhotoSubmission.objects.filter(status=status).order_by('-created_at')
else:
queryset = EditSubmission.objects.filter(status=status).order_by('-created_at')
if type_filter := request.GET.get('type'):
queryset = queryset.filter(submission_type=type_filter)
if content_type := request.GET.get('content_type'):
queryset = queryset.filter(content_type__model=content_type)
context = {
'submissions': queryset,
'user': request.user,
}
return render(request, 'moderation/partials/dashboard_content.html', context)
except ValueError as e:
return HttpResponse(str(e), status=400)
@login_required
def reject_submission(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for rejecting a submission"""
user = cast(User, request.user)
submission = get_object_or_404(EditSubmission, id=submission_id)
if submission.status == 'ESCALATED' and not (user.role in ['ADMIN', 'SUPERUSER'] or user.is_superuser):
return HttpResponse("Only admins can handle escalated submissions", status=403)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
submission.reject(user)
_update_submission_notes(submission, request.POST.get('notes'))
# Get updated queryset with filters
status = request.GET.get('status', 'NEW')
submission_type = request.GET.get('submission_type', '')
if submission_type == 'photo':
queryset = PhotoSubmission.objects.filter(status=status).order_by('-created_at')
else:
queryset = EditSubmission.objects.filter(status=status).order_by('-created_at')
if type_filter := request.GET.get('type'):
queryset = queryset.filter(submission_type=type_filter)
if content_type := request.GET.get('content_type'):
queryset = queryset.filter(content_type__model=content_type)
context = {
'submissions': queryset,
'user': request.user,
}
return render(request, 'moderation/partials/dashboard_content.html', context)
@login_required
def escalate_submission(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for escalating a submission"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
submission = get_object_or_404(EditSubmission, id=submission_id)
if submission.status == 'ESCALATED':
return HttpResponse("Submission is already escalated", status=400)
submission.escalate(user)
_update_submission_notes(submission, request.POST.get('notes'))
# Get updated queryset with filters
status = request.GET.get('status', 'NEW')
submission_type = request.GET.get('submission_type', '')
if submission_type == 'photo':
queryset = PhotoSubmission.objects.filter(status=status).order_by('-created_at')
else:
queryset = EditSubmission.objects.filter(status=status).order_by('-created_at')
if type_filter := request.GET.get('type'):
queryset = queryset.filter(submission_type=type_filter)
if content_type := request.GET.get('content_type'):
queryset = queryset.filter(content_type__model=content_type)
context = {
'submissions': queryset,
'user': request.user,
}
return render(request, 'moderation/partials/dashboard_content.html', context)
@login_required
def approve_photo(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for approving a photo submission"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
submission = get_object_or_404(PhotoSubmission, id=submission_id)
try:
submission.approve(user, request.POST.get('notes', ''))
return render(request, 'moderation/partials/photo_submission.html', {'submission': submission})
except Exception as e:
return HttpResponse(str(e), status=400)
@login_required
def reject_photo(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for rejecting a photo submission"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
submission = get_object_or_404(PhotoSubmission, id=submission_id)
submission.reject(user, request.POST.get('notes', ''))
return render(request, 'moderation/partials/photo_submission.html', {'submission': submission})
def _update_submission_notes(submission: EditSubmission, notes: Optional[str]) -> None:
"""Update submission notes if provided."""
if notes:
submission.notes = notes
submission.save()