Spaces:
Running
Running
| {% extends "base.html" %} | |
| {% block title %}{{ collection.name or 'Collection' }}{% endblock %} | |
| {% block head %} | |
| <style> | |
| body, html { | |
| background-color: var(--bg-dark); | |
| } | |
| .collection-header { | |
| background: linear-gradient(135deg, var(--bg-dark), var(--bg-card)); | |
| border: 1px solid var(--bg-elevated); | |
| border-radius: 12px; | |
| padding: 20px; | |
| margin-bottom: 20px; | |
| } | |
| .topic-section { | |
| background-color: var(--bg-card); | |
| border-radius: 12px; | |
| margin-bottom: 15px; | |
| overflow: hidden; | |
| } | |
| .topic-header { | |
| background: linear-gradient(180deg, var(--bg-elevated), var(--bg-card)); | |
| padding: 12px 15px; | |
| cursor: pointer; | |
| transition: all var(--transition-fast); | |
| } | |
| .topic-header:hover { | |
| background-color: var(--bg-hover); | |
| } | |
| .topic-body { | |
| padding: 15px; | |
| } | |
| .question-card { | |
| background-color: var(--bg-elevated); | |
| border-radius: 8px; | |
| padding: 15px; | |
| margin-bottom: 10px; | |
| position: relative; | |
| transition: all var(--transition-fast); | |
| } | |
| .question-card:hover { | |
| border-color: var(--accent-primary); | |
| } | |
| .question-card:last-child { | |
| margin-bottom: 0; | |
| } | |
| .question-text { | |
| font-size: 0.95rem; | |
| color: var(--text-primary); | |
| margin-bottom: 10px; | |
| } | |
| .option-list { | |
| list-style-type: upper-alpha; | |
| padding-left: 25px; | |
| margin-bottom: 10px; | |
| } | |
| .option-list li { | |
| padding: 4px 0; | |
| color: var(--text-muted); | |
| } | |
| .option-list li.correct { | |
| color: var(--accent-success); | |
| font-weight: 600; | |
| } | |
| .question-meta { | |
| font-size: 0.8rem; | |
| color: var(--border-muted); | |
| } | |
| .remove-btn { | |
| position: absolute; | |
| top: 10px; | |
| right: 10px; | |
| opacity: 0.5; | |
| transition: opacity var(--transition-fast); | |
| } | |
| .remove-btn:hover { | |
| opacity: 1; | |
| } | |
| .empty-state { | |
| text-align: center; | |
| padding: 60px 20px; | |
| color: var(--border-muted); | |
| } | |
| .empty-state i { | |
| font-size: 4rem; | |
| margin-bottom: 20px; | |
| } | |
| </style> | |
| {% endblock %} | |
| {% block content %} | |
| <div class="container-fluid mt-4" style="width: 90%; margin: auto;"> | |
| <!-- Header --> | |
| <div class="collection-header"> | |
| <div class="d-flex justify-content-between align-items-start flex-wrap gap-2"> | |
| <div> | |
| <h2 class="mb-2"> | |
| <i class="bi bi-bookmark-fill text-warning me-2"></i> | |
| <span id="collection-name">{{ collection.name or 'Untitled Collection' }}</span> | |
| <button class="btn btn-link text-secondary btn-sm" id="edit-name-btn" title="Edit name"> | |
| <i class="bi bi-pencil"></i> | |
| </button> | |
| </h2> | |
| <p class="text-muted mb-0"> | |
| <span class="badge bg-secondary">{{ question_count }} questions</span> | |
| {% if collection.subject %} | |
| <span class="badge bg-info">{{ collection.subject }}</span> | |
| {% endif %} | |
| {% if collection.tags %} | |
| <span class="badge bg-primary">{{ collection.tags }}</span> | |
| {% endif %} | |
| </p> | |
| </div> | |
| <div class="d-flex gap-2"> | |
| {% if question_count > 0 %} | |
| <button class="btn btn-primary btn-pill" id="start-quiz-btn"> | |
| <i class="bi bi-play-fill me-1"></i>Start Quiz | |
| </button> | |
| {% endif %} | |
| <button class="btn btn-success btn-pill" id="generate-pdf-btn"> | |
| <i class="bi bi-file-pdf me-1"></i>Generate PDF | |
| </button> | |
| <a href="{{ url_for('dashboard.dashboard', filter='collections') }}" class="btn btn-outline-secondary btn-pill"> | |
| <i class="bi bi-arrow-left me-1"></i>Back | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Questions by Topic --> | |
| {% if topics %} | |
| {% for topic, questions_list in topics.items() %} | |
| <div class="topic-section"> | |
| <div class="topic-header d-flex justify-content-between align-items-center" data-bs-toggle="collapse" data-bs-target="#topic-{{ loop.index }}"> | |
| <div> | |
| <i class="bi bi-folder-fill text-warning me-2"></i> | |
| <strong>{{ topic }}</strong> | |
| <span class="badge bg-secondary ms-2">{{ questions_list|length }}</span> | |
| </div> | |
| <i class="bi bi-chevron-down"></i> | |
| </div> | |
| <div class="collapse show topic-body" id="topic-{{ loop.index }}"> | |
| {% for q in questions_list %} | |
| <div class="question-card" data-question-id="{{ q.id }}" data-question-type="{{ q.question_type }}"> | |
| <button class="btn btn-outline-danger btn-sm remove-btn" title="Remove from collection"> | |
| <i class="bi bi-x-lg"></i> | |
| </button> | |
| <!-- Question Number Badge --> | |
| <div class="d-flex justify-content-between align-items-start mb-2"> | |
| <span class="badge bg-dark">#{{ loop.index }}</span> | |
| {% if q.correct_answer_index is not none %} | |
| <span class="badge bg-success">Answer: {{ 'ABCDEFGH'[q.correct_answer_index|int] if q.correct_answer_index|int < 8 else q.correct_answer_index }}</span> | |
| {% endif %} | |
| </div> | |
| {% if q.image_filename %} | |
| <div class="question-image-container mb-2"> | |
| <img src="/processed/{{ q.image_filename }}" class="img-fluid rounded" alt="Question" style="max-height: 300px;"> | |
| </div> | |
| {% else %} | |
| <div class="question-text">{{ q.question_text or 'No question text available' }}</div> | |
| {% endif %} | |
| {% if q.options %} | |
| <ol class="option-list"> | |
| {% for opt in q.options|from_json %} | |
| <li class="{{ 'correct' if loop.index0 == q.correct_answer_index else '' }}">{{ opt }}</li> | |
| {% endfor %} | |
| </ol> | |
| {% endif %} | |
| <div class="question-meta d-flex flex-wrap gap-2 align-items-center"> | |
| {% if q.level %} | |
| <span><i class="bi bi-speedometer2 me-1"></i>{{ q.level }}</span> | |
| {% endif %} | |
| <span><i class="bi bi-book me-1"></i>{{ q.subject or 'Unknown' }}</span> | |
| <span class="badge {{ 'bg-info' if q.question_type == 'neetprep' else 'bg-success' }}">{{ q.question_type }}</span> | |
| {% if q.question_number %} | |
| <span><i class="bi bi-hash me-1"></i>Q{{ q.question_number }}</span> | |
| {% endif %} | |
| </div> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| </div> | |
| {% endfor %} | |
| {% else %} | |
| <div class="empty-state"> | |
| <i class="bi bi-bookmark"></i> | |
| <h4>No Questions Yet</h4> | |
| <p>Questions you bookmark during quizzes will appear here.</p> | |
| <a href="{{ url_for('neetprep_bp.index') }}" class="btn btn-primary"> | |
| <i class="bi bi-play-fill me-1"></i>Start a Quiz | |
| </a> | |
| </div> | |
| {% endif %} | |
| </div> | |
| <!-- Edit Name Modal --> | |
| <div class="modal fade" id="editNameModal" tabindex="-1"> | |
| <div class="modal-dialog modal-dialog-centered"> | |
| <div class="modal-content bg-dark text-white"> | |
| <div class="modal-header border-secondary"> | |
| <h5 class="modal-title">Edit Collection Name</h5> | |
| <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button> | |
| </div> | |
| <div class="modal-body"> | |
| <input type="text" id="new-name-input" class="form-control bg-secondary text-white border-secondary" | |
| value="{{ collection.name or '' }}" placeholder="Collection name..."> | |
| </div> | |
| <div class="modal-footer border-secondary"> | |
| <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> | |
| <button type="button" class="btn btn-primary" id="save-name-btn">Save</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {% endblock %} | |
| {% block scripts %} | |
| <script> | |
| const sessionId = '{{ collection.id }}'; | |
| // Edit name | |
| document.getElementById('edit-name-btn').onclick = () => { | |
| new bootstrap.Modal(document.getElementById('editNameModal')).show(); | |
| }; | |
| document.getElementById('save-name-btn').onclick = async () => { | |
| const newName = document.getElementById('new-name-input').value.trim(); | |
| if (!newName) return; | |
| try { | |
| const res = await fetch(`/neetprep/collections/${sessionId}/update`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ name: newName }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success) { | |
| document.getElementById('collection-name').textContent = newName; | |
| bootstrap.Modal.getInstance(document.getElementById('editNameModal')).hide(); | |
| } | |
| } catch(e) { | |
| alert('Error updating name'); | |
| } | |
| }; | |
| // Generate PDF | |
| document.getElementById('generate-pdf-btn').onclick = async function() { | |
| const btn = this; | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Generating...'; | |
| try { | |
| const res = await fetch(`/neetprep/collections/${sessionId}/generate`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({}) | |
| }); | |
| const data = await res.json(); | |
| btn.disabled = false; | |
| btn.innerHTML = '<i class="bi bi-file-pdf me-1"></i>Generate PDF'; | |
| if (data.success && data.pdf_url) { | |
| window.open(data.pdf_url, '_blank'); | |
| } else { | |
| alert('Error: ' + (data.error || 'Failed to generate PDF')); | |
| } | |
| } catch(e) { | |
| btn.disabled = false; | |
| btn.innerHTML = '<i class="bi bi-file-pdf me-1"></i>Generate PDF'; | |
| alert('Error generating PDF'); | |
| } | |
| }; | |
| // Start Quiz | |
| const quizBtn = document.getElementById('start-quiz-btn'); | |
| if (quizBtn) { | |
| quizBtn.onclick = function() { | |
| window.location.href = `/neetprep/collections/${sessionId}/quiz`; | |
| }; | |
| } | |
| // Remove from collection | |
| document.querySelectorAll('.remove-btn').forEach(btn => { | |
| btn.onclick = async function() { | |
| const card = this.closest('.question-card'); | |
| const questionId = card.dataset.questionId; | |
| const questionType = card.dataset.questionType; | |
| if (!confirm('Remove this question from the collection?')) return; | |
| try { | |
| const res = await fetch('/neetprep/bookmark', { | |
| method: 'DELETE', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ question_id: questionId, session_id: sessionId, question_type: questionType }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success) { | |
| card.remove(); | |
| // Update count in header | |
| const countBadge = document.querySelector('.collection-header .badge.bg-secondary'); | |
| if (countBadge) { | |
| const currentCount = parseInt(countBadge.textContent); | |
| countBadge.textContent = `${currentCount - 1} questions`; | |
| } | |
| } | |
| } catch(e) { | |
| alert('Error removing question'); | |
| } | |
| }; | |
| }); | |
| </script> | |
| {% endblock %} | |