Improve moderation dashboard UI and HTMX integration:

- Create partial templates for dashboard, edit submissions, and photo submissions
- Update views to properly handle HTMX requests
- Fix duplicate UI issues when navigating
- Improve overall UI design and transitions
This commit is contained in:
pacnpal
2024-11-13 17:04:42 +00:00
parent 96341bfd82
commit 177117f4d6
4 changed files with 109 additions and 26 deletions

View File

@@ -7,7 +7,7 @@ from django.template.loader import render_to_string
from django.db.models import Q from django.db.models import Q
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from typing import Optional, Any, cast from typing import Optional, Any, cast
from accounts.models import User # Import custom User model from accounts.models import User
from .models import EditSubmission, PhotoSubmission from .models import EditSubmission, PhotoSubmission
@@ -30,7 +30,10 @@ class ModeratorRequiredMixin(UserPassesTestMixin):
raise PermissionDenied("You do not have moderator permissions.") raise PermissionDenied("You do not have moderator permissions.")
class DashboardView(LoginRequiredMixin, ModeratorRequiredMixin, TemplateView): class DashboardView(LoginRequiredMixin, ModeratorRequiredMixin, TemplateView):
template_name = 'moderation/dashboard.html' def get_template_names(self):
if self.request.headers.get('HX-Request'):
return ['moderation/partials/dashboard_content.html']
return ['moderation/dashboard.html']
def get_context_data(self, **kwargs: Any) -> dict[str, Any]: def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@@ -60,9 +63,13 @@ class EditSubmissionListView(LoginRequiredMixin, ModeratorRequiredMixin, ListVie
return queryset return queryset
class PhotoSubmissionListView(LoginRequiredMixin, ModeratorRequiredMixin, ListView): class PhotoSubmissionListView(LoginRequiredMixin, ModeratorRequiredMixin, ListView):
template_name = 'moderation/photo_submission_list.html'
context_object_name = 'submissions' context_object_name = 'submissions'
def get_template_names(self):
if self.request.headers.get('HX-Request'):
return ['moderation/partials/photo_submission_content.html']
return ['moderation/photo_submission_list.html']
def get_queryset(self): def get_queryset(self):
queryset = PhotoSubmission.objects.all().order_by('-created_at') queryset = PhotoSubmission.objects.all().order_by('-created_at')
@@ -71,18 +78,6 @@ class PhotoSubmissionListView(LoginRequiredMixin, ModeratorRequiredMixin, ListVi
return queryset return queryset
def _update_submission_notes(submission: EditSubmission, notes: Optional[str]) -> None:
"""Update submission notes if provided."""
if notes:
submission.notes = notes
submission.save()
def _render_submission_response(template: str, submission: Any, request: HttpRequest) -> HttpResponse:
"""Render submission template response."""
context = {'submission': submission}
html = render_to_string(template, context, request=request)
return HttpResponse(html)
@login_required @login_required
def submission_list(request: HttpRequest) -> HttpResponse: def submission_list(request: HttpRequest) -> HttpResponse:
"""HTMX endpoint for filtered submission list""" """HTMX endpoint for filtered submission list"""
@@ -174,3 +169,15 @@ def reject_photo(request: HttpRequest, submission_id: int) -> HttpResponse:
submission.reject(user, request.POST.get('notes', '')) submission.reject(user, request.POST.get('notes', ''))
return _render_submission_response('moderation/partials/photo_submission.html', submission, request) return _render_submission_response('moderation/partials/photo_submission.html', submission, request)
def _update_submission_notes(submission: EditSubmission, notes: Optional[str]) -> None:
"""Update submission notes if provided."""
if notes:
submission.notes = notes
submission.save()
def _render_submission_response(template: str, submission: Any, request: HttpRequest) -> HttpResponse:
"""Render submission template response."""
context = {'submission': submission}
html = render_to_string(template, context, request=request)
return HttpResponse(html)

View File

@@ -0,0 +1,62 @@
{% load static %}
<div class="space-y-6">
<div class="grid grid-cols-1 gap-6 md:grid-cols-3">
<div class="p-6 transition-shadow duration-200 bg-white border rounded-lg shadow-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50 hover:shadow-xl">
<div class="flex items-center justify-between">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Pending Reviews</h3>
<span class="px-3 py-1 text-sm font-medium text-yellow-800 bg-yellow-100 rounded-full dark:bg-yellow-900/50 dark:text-yellow-200">
{{ submissions|length }}
</span>
</div>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">Submissions awaiting moderation</p>
</div>
<div class="p-6 transition-shadow duration-200 bg-white border rounded-lg shadow-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50 hover:shadow-xl">
<div class="flex items-center justify-between">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Recent Activity</h3>
<i class="text-gray-400 fas fa-history"></i>
</div>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">Latest moderation actions</p>
</div>
<div class="p-6 transition-shadow duration-200 bg-white border rounded-lg shadow-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50 hover:shadow-xl">
<div class="flex items-center justify-between">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Quick Actions</h3>
<i class="text-gray-400 fas fa-bolt"></i>
</div>
<div class="mt-4 space-y-2">
<a href="{% url 'moderation:edit_submissions' %}"
class="block px-4 py-2 text-sm text-gray-700 rounded-lg hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700"
hx-get="{% url 'moderation:edit_submissions' %}"
hx-target="#submissions-content"
hx-push-url="true">
<i class="mr-2 fas fa-edit"></i> Review Edit Submissions
</a>
<a href="{% url 'moderation:photo_submissions' %}"
class="block px-4 py-2 text-sm text-gray-700 rounded-lg hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700"
hx-get="{% url 'moderation:photo_submissions' %}"
hx-target="#submissions-content"
hx-push-url="true">
<i class="mr-2 fas fa-image"></i> Review Photo Submissions
</a>
</div>
</div>
</div>
<div class="bg-white border rounded-lg shadow-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50">
<div class="p-6">
<h3 class="mb-4 text-lg font-medium text-gray-900 dark:text-white">Recent Submissions</h3>
<div class="space-y-4">
{% for submission in submissions %}
{% include "moderation/partials/submission_list.html" %}
{% empty %}
<div class="py-8 text-center">
<i class="mb-3 text-4xl text-green-500 fas fa-check-circle"></i>
<p class="text-gray-600 dark:text-gray-400">No pending submissions</p>
</div>
{% endfor %}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,23 @@
{% load static %}
<div class="space-y-6">
<div class="p-6 bg-white border rounded-lg shadow-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50">
<div class="flex items-center justify-between mb-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Photo Submissions</h3>
<span class="px-3 py-1 text-sm font-medium text-yellow-800 bg-yellow-100 rounded-full dark:bg-yellow-900/50 dark:text-yellow-200">
{{ submissions|length }}
</span>
</div>
<div class="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{% for submission in submissions %}
{% include "moderation/partials/photo_submission.html" %}
{% empty %}
<div class="py-8 text-center col-span-full">
<i class="mb-3 text-4xl text-gray-400 fas fa-camera"></i>
<p class="text-gray-600 dark:text-gray-400">No photo submissions found</p>
</div>
{% endfor %}
</div>
</div>
</div>

View File

@@ -1,16 +1,7 @@
{% extends "moderation/dashboard.html" %} {% extends "moderation/dashboard.html" %}
{% block moderation_content %} {% block moderation_content %}
<div id="submissions-content" class="submission-list"> <div id="submissions-content">
{% for submission in submissions %} {% include "moderation/partials/photo_submission_content.html" %}
{% include "moderation/partials/photo_submission.html" %}
{% empty %}
<div class="p-8 text-center bg-white border rounded-lg shadow-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50">
<div class="text-gray-500 dark:text-gray-400">
<i class="mb-3 text-4xl fas fa-camera"></i>
<p>No photo submissions found.</p>
</div>
</div>
{% endfor %}
</div> </div>
{% endblock %} {% endblock %}