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
This commit is contained in:
pacnpal
2024-11-13 18:37:36 +00:00
parent 15e56c9770
commit 6a9154ce69
5 changed files with 440 additions and 433 deletions

View File

@@ -5,108 +5,24 @@
{% block extra_css %}
<style>
.moderation-nav {
@apply bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 shadow-sm sticky top-0 z-10;
/* Form Elements */
.form-select {
@apply rounded-lg border-gray-700 focus:border-blue-500 focus:ring-2 focus:ring-blue-500 bg-gray-800 text-gray-300 transition-colors duration-200;
}
.moderation-nav-container {
@apply container px-4 mx-auto;
/* Transitions */
[x-cloak] {
display: none !important;
}
.moderation-nav ul {
@apply flex items-center h-16 space-x-6;
.fade-enter-active,
.fade-leave-active {
@apply transition-all duration-200;
}
.moderation-nav li a {
@apply flex items-center px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors duration-200;
}
.moderation-nav li a.active {
@apply bg-blue-50 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300;
}
.moderation-nav li a i {
@apply mr-2;
}
.moderation-content {
@apply container px-4 py-6 mx-auto max-w-6xl;
}
.submission-list {
@apply space-y-6;
}
.submission-card {
@apply bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200/50 dark:border-gray-700/50 transition-all duration-200;
}
.submission-card:hover {
@apply shadow-xl border-gray-300 dark:border-gray-600;
}
.submission-header {
@apply flex justify-between items-start;
}
.submission-meta {
@apply text-sm text-gray-600 dark:text-gray-400;
}
.submission-changes {
@apply mt-4 space-y-2;
}
.change-item {
@apply rounded-lg bg-gray-50 dark:bg-gray-700/50 transition-colors duration-200;
}
.change-item:hover {
@apply bg-gray-100 dark:bg-gray-700;
}
.change-field {
@apply font-medium text-gray-900 dark:text-gray-100;
}
.change-value {
@apply text-gray-700 dark:text-gray-300;
}
.action-buttons {
@apply flex justify-end space-x-4 mt-4;
}
.btn-approve {
@apply px-4 py-2 text-white bg-green-600 rounded-lg hover:bg-green-700 dark:bg-green-500 dark:hover:bg-green-600 transition-colors duration-200 flex items-center;
}
.btn-reject {
@apply px-4 py-2 text-white bg-red-600 rounded-lg hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600 transition-colors duration-200 flex items-center;
}
.btn-escalate {
@apply px-4 py-2 text-white bg-yellow-600 rounded-lg hover:bg-yellow-700 dark:bg-yellow-500 dark:hover:bg-yellow-600 transition-colors duration-200 flex items-center;
}
.status-badge {
@apply px-3 py-1 text-sm rounded-full font-medium inline-flex items-center;
}
.status-pending {
@apply bg-yellow-100 text-yellow-800 dark:bg-yellow-900/50 dark:text-yellow-200;
}
.status-approved, .status-auto_approved {
@apply bg-green-100 text-green-800 dark:bg-green-900/50 dark:text-green-200;
}
.status-rejected {
@apply bg-red-100 text-red-800 dark:bg-red-900/50 dark:text-red-200;
}
.status-escalated {
@apply bg-orange-100 text-orange-800 dark:bg-orange-900/50 dark:text-orange-200;
.fade-enter-from,
.fade-leave-to {
@apply opacity-0;
}
/* HTMX Loading States */
@@ -122,34 +38,6 @@
@apply opacity-0 transition-opacity duration-200;
}
/* Transitions */
[x-cloak] {
display: none !important;
}
.fade-enter-active,
.fade-leave-active {
@apply transition-opacity duration-200;
}
.fade-enter-from,
.fade-leave-to {
@apply opacity-0;
}
/* Form Elements */
.form-select {
@apply rounded-lg border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white transition-colors duration-200;
}
.filters {
@apply bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200/50 dark:border-gray-700/50 p-6 transition-all duration-200;
}
.filters:hover {
@apply shadow-xl border-gray-300 dark:border-gray-600;
}
/* Animations */
@keyframes spin {
to {
@@ -164,21 +52,19 @@
{% endblock %}
{% block content %}
{% include "moderation/partials/moderation_nav.html" %}
<div class="moderation-content">
<div id="submissions-content">
<div class="container max-w-6xl px-4 py-6 mx-auto">
<div id="dashboard-content" class="relative transition-opacity duration-200">
{% block moderation_content %}
{% include "moderation/partials/dashboard_content.html" %}
{% endblock %}
</div>
</div>
<div id="loading-indicator"
class="fixed inset-0 flex items-center justify-center htmx-indicator bg-black/20 dark:bg-black/40">
<div class="flex items-center p-6 space-x-4 bg-white rounded-lg shadow-xl dark:bg-gray-800">
<div class="w-8 h-8 border-4 border-blue-500 rounded-full animate-spin border-t-transparent"></div>
<span class="text-gray-700 dark:text-gray-300">Processing...</span>
<div id="loading-indicator"
class="absolute inset-0 flex items-center justify-center rounded-lg htmx-indicator bg-gray-900/80">
<div class="flex items-center p-6 space-x-4">
<div class="w-8 h-8 border-4 border-blue-500 rounded-full animate-spin border-t-transparent"></div>
<span class="text-gray-300">Loading...</span>
</div>
</div>
</div>
</div>
{% endblock %}
@@ -190,22 +76,15 @@ document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
});
document.body.addEventListener('htmx:beforeSwap', function(evt) {
if (evt.detail.target.id === 'submissions-content') {
evt.detail.shouldSwap = true;
evt.detail.target.classList.add('fade-leave-active');
evt.detail.target.classList.add('opacity-0');
document.body.addEventListener('htmx:beforeRequest', function(evt) {
if (evt.detail.target.id === 'dashboard-content') {
evt.detail.target.style.opacity = '0.5';
}
});
document.body.addEventListener('htmx:afterSwap', function(evt) {
if (evt.detail.target.id === 'submissions-content') {
evt.detail.target.classList.remove('fade-leave-active');
evt.detail.target.classList.remove('opacity-0');
evt.detail.target.classList.add('fade-enter-active');
requestAnimationFrame(() => {
evt.detail.target.classList.remove('fade-enter-active');
});
document.body.addEventListener('htmx:afterOnLoad', function(evt) {
if (evt.detail.target.id === 'dashboard-content') {
evt.detail.target.style.opacity = '1';
}
});
</script>