From 8c56cdca32895d7f48893f6b94f5113ba38f1213 Mon Sep 17 00:00:00 2001 From: Advay Chandorkar Date: Wed, 2 Apr 2025 02:58:36 +0000 Subject: [PATCH] Add Sorting Functionality and UI Enhancements --- app/index.html | 21 ++-- app/index.js | 285 ++++++++++++++++++++++++++++++----------------- assets/style.css | 17 +++ 3 files changed, 210 insertions(+), 113 deletions(-) diff --git a/app/index.html b/app/index.html index ef14bda..0fb7b81 100644 --- a/app/index.html +++ b/app/index.html @@ -27,14 +27,19 @@ Loading, please wait...
-
-
- - - - -
-
+
+
+ + + + + + + + + +
+
diff --git a/app/index.js b/app/index.js index 8402f09..8484a42 100644 --- a/app/index.js +++ b/app/index.js @@ -3,8 +3,18 @@ window.onload = () => resetState(); document.addEventListener('DOMContentLoaded', () => { resetState(); jsonDataFetched = false; - }); +}); +function handleYearSort() { + const yearButton = document.getElementById('sort-by-year'); + if (yearButton.textContent === 'Year ▼') { + sortQuestionsByYear('asc'); + yearButton.textContent = 'Year ▲'; + } else { + sortQuestionsByYear('desc'); + yearButton.textContent = 'Year ▼'; + } +} function toggleModal(){document.getElementById("modal").classList.toggle("hidden")} function toggleMS(){document.getElementById("markscheme").classList.toggle("hidden")} @@ -12,7 +22,6 @@ function toggleR(){document.getElementById("report").classList.toggle("hidden")} function toggleHelp(){document.getElementById("helpmenu").classList.toggle("hidden")} function toggleDownAllQs(){document.getElementById("addalltoPDFbtn").classList.remove('hidden');document.getElementById("generatePDFbtn").classList.remove('hidden')} - function toggleDarkMode() { var body = document.body; var head = document.head; @@ -152,6 +161,8 @@ let jsonDataFetched = false; let jsonData = null; let currentFileName = null; let topics = []; +let subtopics = []; +let sortOrder = null; const domCache = { rightCol: document.getElementById("right-col"), @@ -312,36 +323,101 @@ async function loadJSON(filename) { } } +// Extract year from question ID (e.g., "23M.1A.SL.TZ2.39" -> 2023) +function extractYear(questionId) { + if (!questionId || typeof questionId !== 'string') return 0; + + // Extract the first two digits + const match = questionId.match(/^(\d{2})/); + if (!match) return 0; + + let year = parseInt(match[1], 10); + + // Convert to four-digit year + if (year >= 0 && year <= 99) { + if (year >= 0 && year <= 25) { + year += 2000; // 00-25 -> 2000-2025 + } else { + year += 1900; // 26-99 -> 1926-1999 + } + } + + return year; +} +// Function to sort questions by year +function sortQuestionsByYear(direction = 'asc') { + if (!jsonData || !Array.isArray(jsonData)) return; + + sortOrder = direction; + + const sortedData = [...jsonData].sort((a, b) => { + const yearA = extractYear(a.question_id); + const yearB = extractYear(b.question_id); + + return direction === 'asc' ? yearA - yearB : yearB - yearA; + }); + + // Clear the existing questions + clearDisplayedQuestions(); + + // Re-render with sorted data + renderQuestions(sortedData); + + // Maintain topic filter states + document.querySelectorAll('input[name="topic"]:checked').forEach(checkbox => { + document.querySelectorAll(`div[class*="${checkbox.value}"]`) + .forEach(div => div.classList.remove('hidden')); + }); +} -function processData(data, filename) { - jsonDataFetched = true; - currentFileName = filename; +// Function to reset sorting and show questions in original order +function resetSorting() { + if (!jsonData || !Array.isArray(jsonData)) return; + + sortOrder = null; + document.getElementById('sort-by-year').textContent = 'Year ▼'; + + // Clear the existing questions + clearDisplayedQuestions(); + + // Re-render with original data + renderQuestions(jsonData); + + // Maintain topic filter states + document.querySelectorAll('input[name="topic"]:checked').forEach(checkbox => { + document.querySelectorAll(`div[class*="${checkbox.value}"]`) + .forEach(div => div.classList.remove('hidden')); + }); +} - topics = [...new Set(data.flatMap(item => item.topics))].sort(); - subtopics = [...new Set(data.flatMap(item => item.subtopics))].sort(); - renderTopics(); +// Function to clear displayed questions but keep filters +function clearDisplayedQuestions() { + domCache.rightCol.innerHTML = ''; + document.querySelectorAll('#markscheme-box > div').forEach(el => el.remove()); + document.querySelectorAll('#report-box > div').forEach(el => el.remove()); + document.querySelectorAll('#markscheme-box2 > svg').forEach(el => el.remove()); + document.querySelectorAll('#report-box2 > svg').forEach(el => el.remove()); +} - const fragment = document.createDocumentFragment(); +// Function to render questions (extracted from processData) +function renderQuestions(data) { + const fragment = document.createDocumentFragment(); - data.forEach(item => { + data.forEach(item => { const { - Question: question, - question_id: questionid, - Markscheme: markscheme, - 'Examiners report': report, - topics, - subtopics + Question: question, + question_id: questionid, + Markscheme: markscheme, + 'Examiners report': report, + topics, + subtopics, + year } = item; const bigQuestionBox = document.createElement("div"); bigQuestionBox.id = questionid; - /*const allClasses = [...topics.map(t => t.trim()), - subtopics, - "hidden"]; - bigQuestionBox.classList.add(...allClasses);*/ - const allClasses = [ ...topics.map(t => t.trim()).filter(t => t), ...(typeof subtopics === "string" ? [subtopics] : []), @@ -350,133 +426,112 @@ function processData(data, filename) { bigQuestionBox.classList.add(...allClasses); - const btnContainer = document.createElement("div"); btnContainer.classList.add("btn-container"); function toggleMScont(questionid) { - const markschemeContainer = document.getElementById(`markscheme-${questionid} ${currentFileName}`); - toggleMSSvg.classList.toggle('hidden'); - markschemeContainer.classList.toggle('hidden'); - activeQuestionId = markschemeContainer.classList.contains('hidden') ? null : questionid; + const markschemeContainer = document.getElementById(`markscheme-${questionid} ${currentFileName}`); + const toggleMSSvg = document.querySelector(`#markscheme-box2 svg`); // Simplified selector + toggleMSSvg.classList.toggle('hidden'); + markschemeContainer.classList.toggle('hidden'); + activeQuestionId = markschemeContainer.classList.contains('hidden') ? null : questionid; } function toggleRepcont(questionid) { - const reportContainer = document.getElementById(`report-${questionid} ${currentFileName}`); - toggleRepSvg.classList.toggle('hidden'); - reportContainer.classList.toggle('hidden'); - activeQuestionId = reportContainer.classList.contains('hidden') ? null : questionid; + const reportContainer = document.getElementById(`report-${questionid} ${currentFileName}`); + const toggleRepSvg = document.querySelector(`#report-box2 svg`); // Simplified selector + toggleRepSvg.classList.toggle('hidden'); + reportContainer.classList.toggle('hidden'); + activeQuestionId = reportContainer.classList.contains('hidden') ? null : questionid; } const buttons = [ - { text: "Markscheme", handler: () => { toggleMS(); toggleMScont(questionid); } }, - { text: "Examiners report", handler: () => { toggleR(); toggleRepcont(questionid); } }, - { text: "Add to PDF", handler: createPDFButtonHandler(questionid) } + { text: "Markscheme", handler: () => { toggleMS(); toggleMScont(questionid); } }, + { text: "Examiners report", handler: () => { toggleR(); toggleRepcont(questionid); } }, + { text: "Add to PDF", handler: createPDFButtonHandler(questionid) } ].map(createButton); buttons.forEach(button => btnContainer.appendChild(button)); + // Add full year from extracted two-digit year + const extractedYear = extractYear(questionid); + const yearText = extractedYear ? `

Year: ${extractedYear}

` : ''; + const content = ` -

${questionid}

-

Topics: ${topics.join(', ')}

-

Subtopics: ${subtopics.join(', ')}

-
${question}
- `; +

${questionid}

+ ${yearText} +

Topics: ${topics.join(', ')}

+

Subtopics: ${subtopics.join(', ')}

+
${question}
+ `; bigQuestionBox.innerHTML = content; bigQuestionBox.querySelector('h3').after(btnContainer); if (markscheme) { - createContainer('markscheme', questionid, filename, markscheme, domCache.msbox); + createContainer('markscheme', questionid, currentFileName, markscheme, domCache.msbox); } if (report) { - createContainer('report', questionid, filename, report, domCache.reportbox); + createContainer('report', questionid, currentFileName, report, domCache.reportbox); } - /********** Removed ID appending method since it was slowing down interaction with the X svg *********/ - const toggleMSSvg = createSVGElement(questionid); - //toggleMSSvg.id = `toggleMSSvg-${questionid}`; - const toggleRepSvg = createSVGElement(questionid); - //toggleRepSvg.id = `toggleRepSvg-${questionid}`; domCache.msbox2.appendChild(toggleMSSvg); domCache.repbox2.appendChild(toggleRepSvg); - /*toggleMSSvg.addEventListener('click', () => { - toggleMScont(questionid); - toggleMS(); - });*/ - - /********** Identifying by active question instead **********/ let activeQuestionId = null; const handleToggle = () => { - if (activeQuestionId) { - const markschemeContainer = document.getElementById(`markscheme-${activeQuestionId} ${currentFileName}`); - const reportContainer = document.getElementById(`report-${activeQuestionId} ${currentFileName}`); + if (activeQuestionId) { + const markschemeContainer = document.getElementById(`markscheme-${activeQuestionId} ${currentFileName}`); + const reportContainer = document.getElementById(`report-${activeQuestionId} ${currentFileName}`); - if (markschemeContainer && !markschemeContainer.classList.contains('hidden')) { - toggleMSSvg.classList.toggle('hidden'); - toggleMS(); - markschemeContainer.classList.toggle('hidden'); - activeQuestionId = null; - - } else if (reportContainer && !reportContainer.classList.contains('hidden')) { - toggleRepSvg.classList.toggle('hidden'); - toggleR(); - reportContainer.classList.toggle('hidden'); - activeQuestionId = null; - } + if (markschemeContainer && !markschemeContainer.classList.contains('hidden')) { + toggleMSSvg.classList.toggle('hidden'); + toggleMS(); + markschemeContainer.classList.toggle('hidden'); + activeQuestionId = null; + } else if (reportContainer && !reportContainer.classList.contains('hidden')) { + toggleRepSvg.classList.toggle('hidden'); + toggleR(); + reportContainer.classList.toggle('hidden'); + activeQuestionId = null; + } } - } + }; toggleMSSvg.addEventListener('click', handleToggle); toggleRepSvg.addEventListener('click', handleToggle); - document.addEventListener('keydown', (event) => { - if (event.key === 'Escape') { - handleToggle(); - } - }); - - - /*toggleRepSvg.addEventListener('click', () => { - toggleRepcont(questionid); - toggleR(); - });*/ fragment.appendChild(bigQuestionBox); - }); - - domCache.rightCol.appendChild(fragment); - updateSquareContainers(); - toggleDownAllQs(); + }); + domCache.rightCol.appendChild(fragment); + updateSquareContainers(); + toggleDownAllQs(); } +function processData(data, filename) { + jsonData = data; // Store the data for sorting + jsonDataFetched = true; + currentFileName = filename; -/******** Old function for fetching JSON, deprecated in favor of IndexedDB ********/ -/*function loadJSON(filename) { - fetch(`https://pub-59370068cd854c158959e7ca4578e5bd.r2.dev/${filename}`) // ../assets/jsonqb/ - .then(response => response.json()) - .then(data => { - jsonDataFetched = true; - currentFileName = filename; + topics = [...new Set(data.flatMap(item => item.topics))].sort(); + subtopics = [...new Set(data.flatMap(item => item.subtopics))].sort(); + renderTopics(); - topics = [...new Set(data.flatMap(item => item.topics))].sort(); - renderTopics(); - - const fragment = document.createDocumentFragment(); - - data.forEach(item => {}); - - domCache.rightCol.appendChild(fragment); - updateSquareContainers(); - }) - .catch(error => console.error('Error fetching JSON:', error)); -}*/ + // Reset sort button state + const yearButton = document.getElementById('sort-by-year'); + if (yearButton) { + yearButton.textContent = 'Year ▼'; + } + + // Render questions + renderQuestions(data); +} function createButton({ text, handler, className = 'btn-secondary' }) { const button = document.createElement("button"); @@ -552,9 +607,6 @@ document.addEventListener('DOMContentLoaded', () => { if (jsonDataFetched && filename !== currentFileName) { resetState(); loadJSON(filename); - //} else if (jsonDataFetched && filename === currentFileName) { - //resetState(); - //jsonDataFetched = false; } else if (!jsonDataFetched) { loadJSON(filename); } @@ -564,5 +616,28 @@ document.addEventListener('DOMContentLoaded', () => { function resetState() { domCache.rightCol.innerHTML = ''; document.querySelectorAll('.topic-label').forEach(label => label.remove()); + const yearButton = document.getElementById('sort-by-year'); + if (yearButton) { + yearButton.textContent = 'Year ▼'; + } sessionStorage.setItem('selectedQuestionIds', '[]'); + sortOrder = null; +} + +function toggleDarkMode() { + var body = document.body; + var head = document.head; + var toggleButton = document.getElementById("darkmodebtn"); + + if (localStorage.getItem("darkMode") === "disabled") { + body.classList.add("dark-mode"); + head.classList.add("dark-mode"); + localStorage.setItem("darkMode", "enabled"); + toggleButton.innerText = "Light Mode"; + } else { + body.classList.remove("dark-mode"); + head.classList.remove("dark-mode"); + localStorage.setItem("darkMode", "disabled"); + toggleButton.innerText = "Dark Mode"; + } } \ No newline at end of file diff --git a/assets/style.css b/assets/style.css index feb26e3..33c7579 100644 --- a/assets/style.css +++ b/assets/style.css @@ -356,6 +356,23 @@ h4 { overflow-y: scroll; } +#sorting-container { + margin-bottom: 15px; + padding: 10px; + background-color: #f5f5f5; + border-radius: 5px; + border: 1px solid #ddd; +} + +#sorting-container label { + font-weight: bold; + margin-right: 10px; +} + +#sorting-container button { + margin-right: 5px; +} + .square-container { display: flex; flex-direction: column;