Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Minimalistic MCQ Generator - Multiple Tests</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| .section-tab.active { background-color: #3B82F6; color: white; } | |
| .section-content { display: none; } | |
| .section-content.active { display: block; } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen py-8"> | |
| <div class="container mx-auto max-w-2xl bg-white p-6 rounded-lg shadow-md"> | |
| <h1 class="text-3xl font-bold text-center mb-6 text-gray-800">MCQ Generator</h1> | |
| <!-- Topics input area --> | |
| <div id="topics-container" class="space-y-4 mb-6"> | |
| <div class="topic-row flex items-center space-x-2"> | |
| <input type="text" name="topic" placeholder="Topic Name" class="flex-grow p-2 border rounded" required> | |
| <input type="number" name="num" placeholder="# of Questions" class="w-32 p-2 border rounded" required min="1"> | |
| <!-- Status indicator for this topic row --> | |
| <span class="row-status w-6"></span> | |
| <button class="remove-topic bg-red-500 text-white p-2 rounded hover:bg-red-600 transition"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" /> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Controls: Add Topic, Number of Tests & Generate MCQs --> | |
| <div class="flex justify-center items-center space-x-4 mb-8"> | |
| <button id="add-topic" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition">Add Topic</button> | |
| <input type="number" id="num-tests" placeholder="# of Tests" class="w-32 p-2 border rounded" required min="1"> | |
| <button id="submit-topics" class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600 transition">Generate MCQs</button> | |
| </div> | |
| <!-- Results area for tests --> | |
| <div id="tests-results" class="mt-8"> | |
| <h2 class="text-2xl font-semibold text-center mb-4 text-gray-700">Results:</h2> | |
| <!-- Test containers will be appended here --> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const topicsContainer = document.getElementById('topics-container'); | |
| const addTopicBtn = document.getElementById('add-topic'); | |
| const submitTopicsBtn = document.getElementById('submit-topics'); | |
| const testsResults = document.getElementById('tests-results'); | |
| addTopicBtn.addEventListener('click', addTopicRow); | |
| submitTopicsBtn.addEventListener('click', submitTopics); | |
| topicsContainer.addEventListener('click', handleRemoveTopic); | |
| // Add a new topic row | |
| function addTopicRow() { | |
| const newRow = document.createElement('div'); | |
| newRow.className = 'topic-row flex items-center space-x-2'; | |
| newRow.innerHTML = ` | |
| <input type="text" name="topic" placeholder="Topic Name" class="flex-grow p-2 border rounded" required> | |
| <input type="number" name="num" placeholder="# of Questions" class="w-32 p-2 border rounded" required min="1"> | |
| <span class="row-status w-6"></span> | |
| <button class="remove-topic bg-red-500 text-white p-2 rounded hover:bg-red-600 transition"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" /> | |
| </svg> | |
| </button> | |
| `; | |
| topicsContainer.appendChild(newRow); | |
| } | |
| // Remove a topic row | |
| function handleRemoveTopic(event) { | |
| if (event.target.closest('.remove-topic')) { | |
| event.target.closest('.topic-row').remove(); | |
| } | |
| } | |
| // Generate tests and call backend for each test & topic | |
| function submitTopics() { | |
| // Clear previous test results | |
| testsResults.innerHTML = `<h2 class="text-2xl font-semibold text-center mb-4 text-gray-700">Results:</h2>`; | |
| const numTests = parseInt(document.getElementById('num-tests').value); | |
| const rows = document.querySelectorAll('.topic-row'); | |
| // Reset dataset counter for each topic row (to track completions across tests) | |
| rows.forEach(row => { | |
| row.dataset.completed = "0"; | |
| }); | |
| // For each test (e.g. Test 1 to Test numTests) | |
| for (let testIndex = 1; testIndex <= numTests; testIndex++) { | |
| // Create a container for each test | |
| const testContainer = document.createElement('div'); | |
| testContainer.className = 'test-container mb-8 border p-4 rounded'; | |
| testContainer.innerHTML = `<h2 class="text-2xl font-semibold text-center mb-4 text-gray-700">Test ${testIndex}</h2>`; | |
| // Create containers for tab headers and tab contents for this test | |
| const tabHeaders = document.createElement('div'); | |
| tabHeaders.className = 'tab-headers flex flex-wrap gap-2 mb-4'; | |
| const tabContents = document.createElement('div'); | |
| tabContents.className = 'tab-contents'; | |
| testContainer.appendChild(tabHeaders); | |
| testContainer.appendChild(tabContents); | |
| testsResults.appendChild(testContainer); | |
| // For each topic row, create a tab for that topic in this test | |
| rows.forEach((row, topicIndex) => { | |
| const topic = row.querySelector('input[name="topic"]').value; | |
| const num = parseInt(row.querySelector('input[name="num"]').value); | |
| // Create tab header for current test & topic with spinner status | |
| const tabHeader = document.createElement('button'); | |
| tabHeader.className = 'tab-header px-4 py-2 rounded-t-lg bg-gray-200 hover:bg-gray-300 transition'; | |
| tabHeader.id = `tab-header-${testIndex}-${topicIndex}`; | |
| tabHeader.setAttribute('data-tab', `${testIndex}-${topicIndex}`); | |
| tabHeader.innerHTML = `${topic} <span class="status ml-2" id="status-${testIndex}-${topicIndex}"> | |
| <div class="animate-spin rounded-full h-3 w-3 border-b-2 border-blue-500 inline-block"></div> | |
| </span>`; | |
| tabHeader.addEventListener('click', () => activateTab(testIndex, topicIndex)); | |
| tabHeaders.appendChild(tabHeader); | |
| // Create tab content container for current test & topic | |
| const tabContent = document.createElement('div'); | |
| tabContent.className = 'tab-content hidden p-4 bg-gray-100 rounded-b-lg'; | |
| tabContent.id = `tab-content-${testIndex}-${topicIndex}`; | |
| tabContent.innerHTML = 'Loading...'; | |
| tabContents.appendChild(tabContent); | |
| // Call the backend API for this test and topic | |
| callBackend(topic, num, testIndex, (responseText) => { | |
| updateTabContent(testIndex, topicIndex, responseText); | |
| // Update the tab header status to tick (✓) | |
| document.getElementById(`status-${testIndex}-${topicIndex}`).innerHTML = '✓'; | |
| // Update the topic row's status counter | |
| let completed = parseInt(row.dataset.completed) || 0; | |
| completed++; | |
| row.dataset.completed = completed; | |
| // When all tests for a topic are complete, update its row status with tick | |
| if (completed === numTests) { | |
| row.querySelector('.row-status').innerHTML = '✓'; | |
| } | |
| }); | |
| }); | |
| // Activate the first tab for this test by default (if available) | |
| if (rows.length > 0) { | |
| activateTab(testIndex, 0); | |
| } | |
| } | |
| } | |
| // Update the tab content for given test & topic | |
| function updateTabContent(testIndex, topicIndex, content) { | |
| document.getElementById(`tab-content-${testIndex}-${topicIndex}`).innerHTML = `<pre class="whitespace-pre-wrap">${content}</pre>`; | |
| } | |
| // Activate a tab for a specific test | |
| function activateTab(testIndex, topicIndex) { | |
| // Find the test container by getting any element inside it | |
| const testContainer = document.getElementById(`tab-header-${testIndex}-${topicIndex}`).closest('.test-container'); | |
| const tabHeaders = testContainer.querySelectorAll('.tab-header'); | |
| const tabContents = testContainer.querySelectorAll('.tab-content'); | |
| tabHeaders.forEach(header => header.classList.remove('bg-blue-500', 'text-white')); | |
| tabContents.forEach(content => content.classList.add('hidden')); | |
| document.getElementById(`tab-header-${testIndex}-${topicIndex}`).classList.add('bg-blue-500', 'text-white'); | |
| document.getElementById(`tab-content-${testIndex}-${topicIndex}`).classList.remove('hidden'); | |
| } | |
| // Real backend API call for MCQ generation | |
| function callBackend(topic, num, testNumber, callback) { | |
| fetch('/generate_mcq', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ topic: topic, num: num, test: testNumber }) | |
| }) | |
| .then(response => { | |
| if (!response.ok) { | |
| throw new Error('Network response was not ok'); | |
| } | |
| return response.json(); | |
| }) | |
| .then(data => { | |
| callback(data.mcqs); | |
| }) | |
| .catch(error => { | |
| callback("Error: " + error.message); | |
| }); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |