Spaces:
Sleeping
Sleeping
| {% extends "base.html" %} | |
| {% block title %}Edit Classified Questions{% endblock %} | |
| {% block content %} | |
| <div class="container mt-5"> | |
| <div class="card bg-dark text-white"> | |
| <div class="card-header d-flex justify-content-between align-items-center"> | |
| <h2>Edit Classified Questions</h2> | |
| <a href="/neetprep" class="btn btn-outline-light">Back to NeetPrep Home</a> | |
| </div> | |
| <div class="card-body"> | |
| <!-- Filter Form --> | |
| <div class="row mb-3"> | |
| <div class="col-md-6"> | |
| <input type="text" id="search-input" class="form-control bg-dark text-white" placeholder="Search questions..."> | |
| </div> | |
| <div class="col-md-4"> | |
| <select id="chapter-filter" class="form-select bg-dark text-white"> | |
| <option value="all">All Chapters</option> | |
| {% for chapter in chapters %} | |
| <option value="{{ chapter }}">{{ chapter }}</option> | |
| {% endfor %} | |
| </select> | |
| </div> | |
| </div> | |
| <!-- Tag Filter --> | |
| <div class="mb-3"> | |
| <strong>Filter by Tag:</strong> | |
| <span id="tag-filter-chips"> | |
| <button class="btn btn-sm btn-outline-light active" data-tag="all">All</button> | |
| {% for tag in all_tags %} | |
| <button class="btn btn-sm btn-outline-info" data-tag="{{ tag }}">{{ tag }}</button> | |
| {% endfor %} | |
| </span> | |
| </div> | |
| <!-- Bulk Actions --> | |
| <div class="mb-3"> | |
| <button class="btn btn-danger" id="delete-selected-btn">Delete Selected</button> | |
| </div> | |
| <!-- Questions Table --> | |
| <div class="table-responsive"> | |
| <table class="table table-dark table-hover"> | |
| <thead> | |
| <tr> | |
| <th><input type="checkbox" id="select-all-checkbox"></th> | |
| <th>Question Text</th> | |
| <th>Chapter</th> | |
| <th>Subject</th> | |
| <th>Tags</th> | |
| <th>Action</th> | |
| </tr> | |
| </thead> | |
| <tbody id="questions-table-body"> | |
| {% for q in questions %} | |
| <tr data-chapter="{{ q.chapter }}" data-tags="{{ q.tags or '' }}"> | |
| <td><input type="checkbox" class="question-checkbox" data-id="{{ q.id }}"></td> | |
| <td>{{ q.question_text_plain }}</td> | |
| <td class="chapter-cell">{{ q.chapter }}</td> | |
| <td class="subject-cell">{{ q.subject }}</td> | |
| <td>{{ q.tags }}</td> | |
| <td> | |
| <button class="btn btn-sm btn-primary edit-btn" | |
| data-id="{{ q.id }}" | |
| data-chapter="{{ q.chapter }}" | |
| data-subject="{{ q.subject }}" | |
| data-bs-toggle="modal" | |
| data-bs-target="#editModal"> | |
| Edit | |
| </button> | |
| <button class="btn btn-sm btn-danger delete-btn" | |
| data-id="{{ q.id }}"> | |
| Delete | |
| </button> | |
| </td> | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Edit Modal --> | |
| <div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true"> | |
| <div class="modal-dialog"> | |
| <div class="modal-content bg-dark text-white"> | |
| <div class="modal-header"> | |
| <h5 class="modal-title" id="editModalLabel">Edit Question Metadata</h5> | |
| <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button> | |
| </div> | |
| <div class="modal-body"> | |
| <form id="edit-form"> | |
| <input type="hidden" id="edit-question-id"> | |
| <div class="mb-3"> | |
| <label for="edit-chapter" class="form-label">Chapter</label> | |
| <input type="text" class="form-control bg-secondary text-white" id="edit-chapter" required> | |
| </div> | |
| <div class="mb-3"> | |
| <label for="edit-subject" class="form-label">Subject</label> | |
| <select class="form-select bg-secondary text-white" id="edit-subject" required> | |
| {% for subject in available_subjects %} | |
| <option value="{{ subject }}">{{ subject }}</option> | |
| {% endfor %} | |
| </select> | |
| </div> | |
| </form> | |
| </div> | |
| <div class="modal-footer"> | |
| <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> | |
| <button type="button" class="btn btn-primary" id="save-changes-btn">Save changes</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {% endblock %} | |
| {% block scripts %} | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function () { | |
| const editModal = new bootstrap.Modal(document.getElementById('editModal')); | |
| const editQuestionId = document.getElementById('edit-question-id'); | |
| const editChapter = document.getElementById('edit-chapter'); | |
| const editSubject = document.getElementById('edit-subject'); | |
| document.querySelectorAll('.edit-btn').forEach(button => { | |
| button.addEventListener('click', () => { | |
| editQuestionId.value = button.dataset.id; | |
| editChapter.value = button.dataset.chapter; | |
| editSubject.value = button.dataset.subject; | |
| }); | |
| }); | |
| document.getElementById('save-changes-btn').addEventListener('click', async () => { | |
| const id = editQuestionId.value; | |
| const chapter = editChapter.value; | |
| const subject = editSubject.value; | |
| try { | |
| const response = await fetch(`/classified/update_question/${id}`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ chapter: chapter, subject: subject }) | |
| }); | |
| const result = await response.json(); | |
| if (result.success) { | |
| // Update the table row | |
| const row = document.querySelector(`button[data-id='${id}']`).closest('tr'); | |
| row.querySelector('.chapter-cell').textContent = chapter; | |
| row.querySelector('.subject-cell').textContent = subject; | |
| // Update the button's data attributes as well | |
| const editBtn = row.querySelector('.edit-btn'); | |
| editBtn.dataset.chapter = chapter; | |
| editBtn.dataset.subject = subject; | |
| editModal.hide(); | |
| } else { | |
| alert(`Error: ${result.error}`); | |
| } | |
| } catch (error) { | |
| alert(`Request failed: ${error}`); | |
| } | |
| }); | |
| // --- Filtering Logic --- | |
| const searchInput = document.getElementById('search-input'); | |
| const chapterFilter = document.getElementById('chapter-filter'); | |
| const tagFilterChips = document.getElementById('tag-filter-chips'); | |
| const tableBody = document.getElementById('questions-table-body'); | |
| const rows = tableBody.getElementsByTagName('tr'); | |
| let activeTag = 'all'; | |
| function filterTable() { | |
| const searchText = searchInput.value.toLowerCase(); | |
| const selectedChapter = chapterFilter.value; | |
| for (let i = 0; i < rows.length; i++) { | |
| const row = rows[i]; | |
| const chapter = row.dataset.chapter; | |
| const tags = (row.dataset.tags || '').split(',').map(t => t.trim()); | |
| const text = row.textContent || row.innerText; | |
| const chapterMatch = (selectedChapter === 'all' || chapter === selectedChapter); | |
| const searchMatch = (text.toLowerCase().indexOf(searchText) > -1); | |
| const tagMatch = (activeTag === 'all' || tags.includes(activeTag)); | |
| if (chapterMatch && searchMatch && tagMatch) { | |
| row.style.display = ""; | |
| } else { | |
| row.style.display = "none"; | |
| } | |
| } | |
| } | |
| searchInput.addEventListener('keyup', filterTable); | |
| chapterFilter.addEventListener('change', filterTable); | |
| tagFilterChips.addEventListener('click', (e) => { | |
| if (e.target.tagName === 'BUTTON') { | |
| tagFilterChips.querySelectorAll('button').forEach(btn => btn.classList.remove('active')); | |
| e.target.classList.add('active'); | |
| activeTag = e.target.dataset.tag; | |
| filterTable(); | |
| } | |
| }); | |
| // --- Bulk Actions --- | |
| const selectAllCheckbox = document.getElementById('select-all-checkbox'); | |
| const questionCheckboxes = document.querySelectorAll('.question-checkbox'); | |
| const deleteSelectedBtn = document.getElementById('delete-selected-btn'); | |
| selectAllCheckbox.addEventListener('change', (e) => { | |
| questionCheckboxes.forEach(checkbox => { | |
| checkbox.checked = e.target.checked; | |
| }); | |
| }); | |
| deleteSelectedBtn.addEventListener('click', async () => { | |
| const selectedIds = Array.from(questionCheckboxes) | |
| .filter(checkbox => checkbox.checked) | |
| .map(checkbox => checkbox.dataset.id); | |
| if (selectedIds.length === 0) { | |
| alert('Please select at least one question to delete.'); | |
| return; | |
| } | |
| if (!confirm(`Are you sure you want to delete ${selectedIds.length} questions?`)) return; | |
| try { | |
| const response = await fetch('/classified/delete_many', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ ids: selectedIds }) | |
| }); | |
| const result = await response.json(); | |
| if (result.success) { | |
| selectedIds.forEach(id => { | |
| document.querySelector(`.question-checkbox[data-id='${id}']`).closest('tr').remove(); | |
| }); | |
| } else { | |
| alert(`Error: ${result.error}`); | |
| } | |
| } catch (error) { | |
| alert(`Request failed: ${error}`); | |
| } | |
| }); | |
| document.querySelectorAll('.delete-btn').forEach(button => { | |
| button.addEventListener('click', async () => { | |
| const id = button.dataset.id; | |
| if (!confirm('Are you sure you want to delete this question?')) return; | |
| try { | |
| const response = await fetch(`/classified/delete_question/${id}`, { | |
| method: 'DELETE' | |
| }); | |
| const result = await response.json(); | |
| if (result.success) { | |
| button.closest('tr').remove(); | |
| } else { | |
| alert(`Error: ${result.error}`); | |
| } | |
| } catch (error) { | |
| alert(`Request failed: ${error}`); | |
| } | |
| }); | |
| }); | |
| }); | |
| </script> | |
| {% endblock %} | |