Spaces:
Build error
Build error
| {% extends "base.html" %} | |
| {% block title %} | |
| Moderate Comments - {{ video.title }} | |
| {% endblock %} | |
| {% block content %} | |
| <div class="container mx-auto px-4 py-8"> | |
| <!-- Outer container with card styling --> | |
| <div class="bg-white shadow-xl rounded-lg overflow-visible"> | |
| <!-- Header section with video title and controls --> | |
| <div class="bg-gradient-to-r from-blue-500 to-purple-600 p-6"> | |
| <div class="flex justify-between items-center"> | |
| <h1 class="text-3xl font-bold text-white flex items-center"> | |
| <i class="fab fa-youtube mr-4 text-red-500"></i> | |
| {{ video.title }} | |
| </h1> | |
| <div class="flex space-x-4"> | |
| <button id="refreshCommentsBtn" class="bg-white text-blue-600 px-4 py-2 rounded-lg hover:bg-blue-50 transition duration-300 flex items-center shadow-md"> | |
| <i class="fas fa-sync mr-2"></i>Refresh Comments | |
| </button> | |
| <div x-data="{ showSettings: false, saveSettings() { | |
| // Add your save logic here, e.g., update a model or call an API | |
| alert('Settings saved!'); | |
| this.showSettings = false; // Optionally hide the settings box after saving | |
| } }" class="relative"> | |
| <button @click="showSettings = !showSettings" class="bg-white text-purple-600 px-4 py-2 rounded-lg hover:bg-purple-50 transition duration-300 flex items-center shadow-md"> | |
| <i class="fas fa-cog mr-2"></i>Moderation Settings | |
| </button> | |
| <div x-show="showSettings" x-transition class="absolute right-0 mt-2 w-72 bg-white rounded-lg shadow-2xl p-6 z-20 border border-gray-100"> | |
| <h3 class="text-xl font-semibold mb-4 text-gray-800">Moderation Settings</h3> | |
| <div class="space-y-4"> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="auto-delete" class="mr-3 rounded text-blue-500 focus:ring-blue-400"> | |
| <label for="auto-delete" class="text-gray-700">Auto-delete flagged comments</label> | |
| </div> | |
| <div> | |
| <label class="block mb-2 text-gray-700">Gambling Confidence Threshold</label> | |
| <input type="range" min="0" max="1" step="0.05" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer" x-model="threshold" value="0.55"> | |
| </div> | |
| <button @click="saveSettings" class="w-full bg-gradient-to-r from-blue-500 to-purple-600 text-white py-2 rounded-lg hover:opacity-90 transition duration-300"> | |
| Save Settings | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Comments Section --> | |
| <div class="grid md:grid-cols-2 gap-6 p-6"> | |
| <!-- Safe Comments Column --> | |
| <div> | |
| <h2 class="text-2xl font-semibold mb-4 flex items-center text-green-600"> | |
| <i class="fas fa-check-circle mr-3"></i>Safe Comments | |
| <span class="text-sm text-gray-500 ml-2">({{ safe_comments|length }})</span> | |
| </h2> | |
| <div class="space-y-4"> | |
| {% for comment in safe_comments %} | |
| <div class="bg-white p-4 rounded-lg shadow-md border border-gray-100"> | |
| <p class="mb-2 text-gray-800">{{ comment.text }}</p> | |
| <div class="text-sm text-gray-600 flex justify-between items-center"> | |
| <span class="font-medium">{{ comment.author }}</span> | |
| <span class="text-xs text-green-600">Safe</span> | |
| </div> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| </div> | |
| <!-- Flagged Comments Column --> | |
| <div> | |
| <h2 class="text-2xl font-semibold mb-4 flex items-center text-red-600"> | |
| <i class="fas fa-exclamation-triangle mr-3"></i>Flagged Comments | |
| <span id="flaggedCount" class="text-sm text-gray-500 ml-2">({{ flagged_comments|length }})</span> | |
| </h2> | |
| <div class="space-y-4"> | |
| {% for comment in flagged_comments %} | |
| <div class="comment-card bg-red-50 p-4 rounded-lg shadow-md border border-red-200 relative"> | |
| <p class="mb-2 text-gray-800">{{ comment.text }}</p> | |
| <div class="text-sm text-gray-600 flex justify-between items-center"> | |
| <span class="font-medium">{{ comment.author }}</span> | |
| <div class="flex space-x-2"> | |
| <button data-comment-id="{{ comment.id }}" data-video-id="{{ video.id }}" class="delete-comment-btn bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600 transition duration-300"> | |
| Delete | |
| </button> | |
| <button data-comment-id="{{ comment.id }}" data-video-id="{{ video.id }}"class="keep-comment-btn bg-green-500 text-white px-3 py-1 rounded hover:bg-green-600 transition duration-300"> | |
| Keep | |
| </button> | |
| </div> | |
| </div> | |
| <div class="mt-2 text-xs text-gray-500"> | |
| <strong>Gambling Confidence:</strong> {{ comment.metrics.confidence_score }} | |
| </div> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {% endblock %} | |
| {% block scripts %} | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Delete Comment Functionality | |
| document.querySelectorAll('.delete-comment-btn').forEach(button => { | |
| button.addEventListener('click', async function(e) { | |
| e.stopPropagation(); // Ensure this click doesn't affect other handlers | |
| const commentId = this.getAttribute('data-comment-id'); | |
| const videoId = this.getAttribute('data-video-id'); | |
| const commentCard = this.closest('.comment-card'); | |
| try { | |
| const response = await fetch(`/api/comments/${commentId}?video_id=${videoId}`, { | |
| method: 'DELETE' | |
| }); | |
| const data = await response.json(); | |
| if (data.success) { | |
| // Remove the comment from the DOM | |
| commentCard.remove(); | |
| // Optionally, update the flagged comments count | |
| const flaggedCommentsCount = document.querySelector('h2 span'); | |
| const currentCount = parseInt(flaggedCommentsCount.textContent.replace(/[()]/g, '')); | |
| flaggedCommentsCount.textContent = `(${currentCount - 1})`; | |
| } else { | |
| alert("Failed to delete comment. Please refresh and try again."); | |
| } | |
| } catch (err) { | |
| console.error(err); | |
| alert("Error deleting comment."); | |
| } | |
| }); | |
| }); | |
| // Keep Comment Functionality with added debug logging | |
| document.querySelectorAll('.keep-comment-btn').forEach(button => { | |
| button.addEventListener('click', async function(e) { | |
| e.stopPropagation(); | |
| const commentId = this.getAttribute('data-comment-id'); | |
| const videoId = this.getAttribute('data-video-id'); | |
| const commentCard = this.closest('.comment-card'); | |
| console.log(`Keep button clicked for commentId: ${commentId}, videoId: ${videoId}`); | |
| try { | |
| // Make the API call to keep the comment on YouTube | |
| const response = await fetch(`/api/comments/keep/${commentId}?video_id=${videoId}`, { | |
| method: 'POST' | |
| }); | |
| console.log("Response received from API:", response); | |
| const data = await response.json(); | |
| console.log("Parsed response data:", data); | |
| if (data.success) { | |
| // If successful, remove from DOM and update the flagged count | |
| commentCard.remove(); | |
| const flaggedCommentsCount = document.querySelector('#flaggedCount'); | |
| const currentCount = parseInt(flaggedCommentsCount.textContent); | |
| flaggedCommentsCount.textContent = currentCount - 1; | |
| console.log(`Comment ${commentId} removed, flagged count updated to ${currentCount - 1}`); | |
| } else { | |
| console.error(`API reported error for comment ${commentId}:`, data.error); | |
| alert("Failed to keep comment: " + (data.error || 'Unknown error')); | |
| } | |
| } catch (err) { | |
| console.error(`Error keeping comment ${commentId}:`, err); | |
| alert("Error keeping comment."); | |
| } | |
| }); | |
| }); | |
| // Refresh Comments Functionality | |
| document.getElementById('refreshCommentsBtn').addEventListener('click', function(e) { | |
| e.stopPropagation(); // Prevent any unwanted event bubbling | |
| const videoId = "{{ video.id }}"; | |
| window.location.href = `/video/${videoId}`; | |
| }); | |
| }); | |
| </script> | |
| {% endblock %} | |