Spaces:
Running
Running
File size: 11,619 Bytes
6849482 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | <!DOCTYPE html>
<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> |