From 1426b5f5127095224f22d4dd873d3ecf7eee6bf4 Mon Sep 17 00:00:00 2001 From: PARVATHI PR Date: Thu, 25 Jun 2026 16:49:51 +0530 Subject: [PATCH] Added personalized report feature --- Styles/main.css | 212 ++++++++++++++- Styles/quiz.css | 269 +++++++++++++++--- index.html | 66 ++++- quiz.html | 17 +- scripts/main.js | 203 +++++++++++++- scripts/quiz-data.js | 36 ++- scripts/quiz.js | 630 +++++++++++++++++++++++++++++-------------- 7 files changed, 1183 insertions(+), 250 deletions(-) diff --git a/Styles/main.css b/Styles/main.css index 83dd353..c95d834 100644 --- a/Styles/main.css +++ b/Styles/main.css @@ -27,6 +27,42 @@ --box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); } +[data-theme="dark"] .section h2, +[data-theme="dark"] .card-content h3, +[data-theme="dark"] .course-card h2, +[data-theme="dark"] .profile-header h1, +[data-theme="dark"] .profile-card h2 { + color: #7dd3fc; +} + +[data-theme="dark"] .section:nth-of-type(even) h2 { + color: #fde047; +} + +[data-theme="dark"] .card-content h3 i, +[data-theme="dark"] .footer-section h3 { + color: #f9a8d4; +} + +[data-theme="dark"] .card-content p, +[data-theme="dark"] .course-card p, +[data-theme="dark"] .footer-section, +[data-theme="dark"] .footer-section a, +[data-theme="dark"] .footer-bottom p { + color: #ffffff; +} + +[data-theme="dark"] .theme-option { + color: #ffffff; +} + +footer, +footer a, +.footer-section h3, +.footer-bottom p { + color: #ffffff; +} + * { margin: 0; padding: 0; @@ -175,10 +211,20 @@ body { } .card-content p { - margin-bottom: 1.5rem; + margin-bottom: 1rem; color: var(--text-color); } +.card-actions { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; +} + +.card-actions .btn { + flex: 1; +} + /* Hero Section */ .hero { background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); @@ -717,6 +763,168 @@ footer { font-size: 0.9rem; } +/* Learning Hub Tabs */ +.tab-menu { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.75rem; + margin-bottom: 1.5rem; +} + +.tab-button { + border: 2px solid var(--secondary-color); + background: transparent; + color: var(--secondary-color); + padding: 0.8rem 1.25rem; + border-radius: var(--border-radius); + cursor: pointer; + transition: var(--transition); +} + +.tab-button.active, +.tab-button:hover { + background: var(--secondary-color); + color: var(--light-text); +} + +.tab-content { + display: none; + background: var(--card-background); + border-radius: 1rem; + padding: 1.75rem; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08); +} + +.tab-content.active { + display: block; +} + +.tab-note { + color: var(--text-secondary); + font-size: 1rem; + margin-bottom: 1rem; + text-align: center; +} + +.current-session { + background: rgba(46, 134, 222, 0.08); + border: 1px solid rgba(46, 134, 222, 0.2); + border-radius: 1rem; + padding: 1rem 1.2rem; + margin-bottom: 1.5rem; + color: var(--text-color); + font-weight: 600; +} + +.form-row.full-width { + grid-column: 1 / -1; +} + +.calendar-events { + display: grid; + gap: 1rem; + margin-top: 1.5rem; +} + +.calendar-event-item { + background: var(--background-color); + border-radius: 1rem; + padding: 1.2rem; + display: flex; + flex-direction: column; + gap: 0.75rem; + border: 1px solid rgba(0, 0, 0, 0.08); +} + +.calendar-event-item div { + display: flex; + flex-direction: column; + gap: 0.35rem; +} + +.calendar-event-item strong { + color: var(--text-color); + font-size: 1rem; +} + +.calendar-event-item span { + color: var(--text-secondary); + font-size: 0.95rem; +} + +.calendar-event-item .remove-event { + align-self: flex-end; +} + +.scheduler-header { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 1rem; + align-items: center; + margin-bottom: 1.5rem; +} + +.scheduler-header h3 { + margin: 0; +} + +.scheduled-count { + background: var(--secondary-color); + color: var(--light-text); + padding: 0.75rem 1rem; + border-radius: 999px; + font-weight: 600; +} + +.calendar-panel .calendar-form { + display: grid; + gap: 1rem; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + align-items: end; +} + +.calendar-panel .form-actions { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + align-items: center; +} + +.calendar-panel .calendar-form label { + display: block; + margin-bottom: 0.5rem; + color: var(--text-color); + font-weight: 600; +} + +.calendar-panel .calendar-form input { + width: 100%; + padding: 0.9rem 1rem; + border-radius: var(--border-radius); + border: 1px solid rgba(0, 0, 0, 0.12); + background: var(--background-color); + color: var(--text-color); +} + + .calendar-event-item strong { + display: block; + font-size: 1rem; + color: var(--text-color); + } + + .calendar-event-item span { + color: var(--text-secondary); + font-size: 0.95rem; +} + +.empty-state { + color: var(--text-secondary); + font-size: 0.95rem; + padding: 1rem 0; +} + /* Responsive Design */ @media (max-width: 1024px) { /* Tablet Layout */ @@ -1144,4 +1352,4 @@ ul li i.fa-check { width: 95%; margin: 10% auto; } -} \ No newline at end of file +} diff --git a/Styles/quiz.css b/Styles/quiz.css index a9cf083..c4340f1 100644 --- a/Styles/quiz.css +++ b/Styles/quiz.css @@ -64,14 +64,14 @@ } /* Quiz Styles */ -.quiz-container { +.quiz-container { max-width: 800px; margin: 0 auto; padding: 2rem; background-color: var(--card-background); border-radius: 8px; - box-shadow: var(--shadow); -} + box-shadow: var(--box-shadow); +} .quiz-header { display: flex; @@ -119,14 +119,15 @@ gap: 1rem; } -.option { - padding: 1rem; - background-color: var(--background-color); - border: 2px solid var(--border-color); - border-radius: 4px; - cursor: pointer; - transition: var(--transition); -} +.option { + padding: 1rem; + background-color: var(--background-color); + border: 2px solid var(--border-color); + border-radius: 4px; + cursor: pointer; + transition: var(--transition); + color: var(--text-color); +} .option:hover { background-color: var(--primary-color); @@ -148,15 +149,15 @@ } /* Results Section */ -.results-container { - max-width: 600px; - margin: 0 auto; - padding: 2rem; - background-color: var(--card-background); - border-radius: 8px; - box-shadow: var(--shadow); - text-align: center; -} +.results-container { + max-width: 900px; + margin: 0 auto; + padding: 2rem; + background-color: var(--card-background); + border-radius: 8px; + box-shadow: var(--box-shadow); + text-align: center; +} .results-content { margin: 2rem 0; @@ -177,10 +178,192 @@ color: var(--secondary-color); } -.result-value { - color: var(--primary-color); - font-size: 1.2rem; -} +.result-value { + color: var(--text-color); + font-size: 1.2rem; + font-weight: 700; +} + +.report-progress, +.personalized-report, +.course-save-panel, +.schedule-panel { + margin-top: 1.5rem; + padding: 1.5rem; + background: var(--background-color); + border: 1px solid var(--border-color); + border-radius: 8px; + text-align: left; +} + +.report-progress-info { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; + margin-bottom: 0.75rem; + color: var(--text-color); +} + +.personalized-report h3, +.course-save-panel h3, +.schedule-panel h3 { + color: var(--secondary-color); + margin-bottom: 0.75rem; +} + +.report-summary, +.course-save-panel p { + color: var(--text-color); + margin-bottom: 1rem; +} + +.improvement-topics { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin-bottom: 1.25rem; +} + +.topic-chip { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.45rem 0.75rem; + border-radius: 999px; + background: rgba(52, 152, 219, 0.16); + color: var(--secondary-color); + border: 1px solid rgba(52, 152, 219, 0.35); + font-weight: 700; + font-size: 0.9rem; +} + +.question-report { + display: grid; + gap: 1rem; +} + +.question-review { + padding: 1rem; + border: 1px solid var(--border-color); + border-left: 5px solid var(--secondary-color); + border-radius: 8px; + background: var(--card-background); +} + +.question-review.correct { + border-left-color: #22c55e; +} + +.question-review.incorrect { + border-left-color: #ef4444; +} + +.question-review h4 { + color: var(--text-color); + margin-bottom: 0.75rem; +} + +.question-review p { + color: var(--text-color); + margin-bottom: 0.5rem; +} + +.question-review strong { + color: var(--secondary-color); +} + +.saved-courses { + margin-top: 1.25rem; +} + +.saved-courses h4 { + color: var(--text-color); + margin-bottom: 0.75rem; +} + +.saved-courses-list, +.scheduled-classes { + display: grid; + gap: 0.75rem; +} + +.saved-course-item, +.scheduled-class-item { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; + padding: 0.85rem; + border: 1px solid var(--border-color); + border-radius: 8px; + background: var(--card-background); + color: var(--text-color); +} + +.saved-course-item span, +.scheduled-class-item span { + display: block; + font-size: 0.9rem; + opacity: 0.8; +} + +.empty-state { + color: var(--text-color); + opacity: 0.75; + font-style: italic; +} + +.schedule-form { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.85rem; + margin-bottom: 1rem; +} + +.schedule-form label { + color: var(--text-color); + font-weight: 700; +} + +.schedule-form input { + width: 100%; + padding: 0.75rem; + border: 1px solid var(--border-color); + border-radius: 6px; + background: var(--card-background); + color: var(--text-color); +} + +.schedule-form button, +.schedule-form label[for="classTopic"], +.schedule-form #classTopic { + grid-column: 1 / -1; +} + +[data-theme="dark"] .quiz-header h2, +[data-theme="dark"] .course-selection h1, +[data-theme="dark"] .question-container h3 { + color: #7dd3fc; +} + +[data-theme="dark"] .results-container h2, +[data-theme="dark"] .personalized-report h3, +[data-theme="dark"] .course-save-panel h3, +[data-theme="dark"] .schedule-panel h3 { + color: #fde047; +} + +[data-theme="dark"] .result-label, +[data-theme="dark"] .question-review strong { + color: #f9a8d4; +} + +[data-theme="dark"] footer, +[data-theme="dark"] footer a, +[data-theme="dark"] .footer-section h3 { + color: #ffffff; +} .results-actions { display: flex; @@ -214,10 +397,20 @@ gap: 0.5rem; } - .results-actions { - flex-direction: column; - } -} + .results-actions { + flex-direction: column; + } + + .schedule-form { + grid-template-columns: 1fr; + } + + .saved-course-item, + .scheduled-class-item { + align-items: flex-start; + flex-direction: column; + } +} /* Animations */ @keyframes fadeIn { @@ -237,9 +430,9 @@ } /* Footer Styles */ -footer { - background-color: var(--primary-color); - color: var(--text-color-light); +footer { + background-color: var(--primary-color); + color: var(--light-text); padding: 2rem 0; margin-top: 3rem; } @@ -253,8 +446,8 @@ footer { gap: 2rem; } -.footer-section h3 { - color: var(--text-color-light); +.footer-section h3 { + color: var(--light-text); margin-bottom: 1rem; font-size: 1.2rem; } @@ -268,8 +461,8 @@ footer { margin-bottom: 0.5rem; } -.footer-section ul li a { - color: var(--text-color-light); +.footer-section ul li a { + color: var(--light-text); text-decoration: none; transition: color 0.3s ease; } @@ -283,14 +476,14 @@ footer { gap: 1rem; } -.social-button { +.social-button { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; background-color: var(--secondary-color); - color: var(--text-color-light); + color: var(--light-text); border-radius: 50%; transition: all 0.3s ease; } @@ -309,4 +502,4 @@ footer { .social-links { justify-content: center; } -} \ No newline at end of file +} diff --git a/index.html b/index.html index edf7b35..19fbfb2 100644 --- a/index.html +++ b/index.html @@ -71,7 +71,10 @@

Featured Courses

Web Development

Master frontend and backend technologies to build modern web applications.

- Learn More +
+ Learn More + +
@@ -83,7 +86,10 @@

Web Development

AI & Machine Learning

Dive into artificial intelligence, machine learning, and data science.

- Learn More +
+ Learn More + +
@@ -95,12 +101,66 @@

AI & Machine Learning

Career Guidance

Get expert advice on building a successful career in technology.

- Learn More +
+ Learn More + +
+ +
+

My Learning Hub

+
+ + +
+
+

Browse courses above, then use the Scheduler tab to add study sessions directly.

+
+
+
+
+
+

Class Scheduler

+

Schedule your own sessions and manage upcoming study events in one screen.

+
+ 0 sessions scheduled +
+
No current session selected.
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+

Why Choose EduBridge

diff --git a/quiz.html b/quiz.html index beb7c9d..a85b88a 100644 --- a/quiz.html +++ b/quiz.html @@ -137,6 +137,21 @@

Quiz Results

0 +
+
+ Quiz Progress + 0% +
+
+
+
+
+
+

Personalized Report

+

+
+
+
@@ -229,4 +244,4 @@

Follow Us

}); - \ No newline at end of file + diff --git a/scripts/main.js b/scripts/main.js index 2b604aa..e4b2f20 100644 --- a/scripts/main.js +++ b/scripts/main.js @@ -270,4 +270,205 @@ document.addEventListener('DOMContentLoaded', function() { } }); } -}); \ No newline at end of file +}); + +// Learning Hub tab logic for index.html +function initLearningHub() { + const tabButtons = document.querySelectorAll('.tab-button'); + const tabPanels = document.querySelectorAll('.tab-content'); + const calendarForm = document.getElementById('calendarForm'); + const calendarEventsContainer = document.getElementById('calendarEvents'); + const clearAllEventsButton = document.getElementById('clearAllEventsBtn'); + const scheduledCount = document.getElementById('scheduledCount'); + const currentSessionPanel = document.getElementById('currentSessionPanel'); + + if (tabButtons.length > 0) { + tabButtons.forEach(button => { + button.addEventListener('click', () => { + const targetId = button.getAttribute('data-tab'); + setActiveLearningTab(targetId, tabButtons, tabPanels); + }); + }); + } + + const scheduleCourseButtons = document.querySelectorAll('.schedule-course-btn'); + if (scheduleCourseButtons.length > 0) { + scheduleCourseButtons.forEach(button => { + button.addEventListener('click', () => { + const topic = button.dataset.topic || 'Study session'; + const topicInput = document.getElementById('calendarTopic'); + if (topicInput) { + topicInput.value = `Study ${topic}`; + } + updateCurrentSessionPanel(`Current session: Study ${topic}`); + setActiveLearningTab('calendarTab', tabButtons, tabPanels); + const startDateInput = document.getElementById('calendarStartDate'); + if (startDateInput) { + startDateInput.focus(); + } + }); + }); + } + + if (calendarForm) { + calendarForm.addEventListener('submit', handleCalendarFormSubmit); + } + + if (clearAllEventsButton) { + clearAllEventsButton.addEventListener('click', () => clearAllCalendarEvents(calendarEventsContainer, scheduledCount, currentSessionPanel)); + } + + if (calendarEventsContainer) { + calendarEventsContainer.addEventListener('click', handleCalendarEventClick); + renderCalendarEventsOnHome(calendarEventsContainer, scheduledCount, currentSessionPanel); + } + + function setActiveLearningTab(activeId, buttons, panels) { + buttons.forEach(btn => { + const tabId = btn.getAttribute('data-tab'); + const isActive = tabId === activeId; + btn.classList.toggle('active', isActive); + btn.setAttribute('aria-selected', String(isActive)); + }); + + panels.forEach(panel => { + panel.classList.toggle('active', panel.id === activeId); + }); + } + + function handleCalendarFormSubmit(event) { + event.preventDefault(); + const startDateInput = document.getElementById('calendarStartDate'); + const endDateInput = document.getElementById('calendarEndDate'); + const startTimeInput = document.getElementById('calendarStartTime'); + const endTimeInput = document.getElementById('calendarEndTime'); + const topicInput = document.getElementById('calendarTopic'); + const eventsContainer = document.getElementById('calendarEvents'); + + if (!startDateInput || !endDateInput || !startTimeInput || !endTimeInput || !topicInput || !eventsContainer) { + return; + } + + const startDate = startDateInput.value; + const endDate = endDateInput.value; + const startTime = startTimeInput.value; + const endTime = endTimeInput.value; + const eventTopic = topicInput.value.trim(); + + if (!startDate || !endDate || !startTime || !endTime || !eventTopic) { + return; + } + + if (endDate < startDate || (endDate === startDate && endTime <= startTime)) { + alert('End date/time must be later than start date/time.'); + return; + } + + const events = getStoredList('scheduledClasses'); + const id = typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; + events.push({ id, startDate, endDate, startTime, endTime, topic: eventTopic }); + localStorage.setItem('scheduledClasses', JSON.stringify(events)); + + startDateInput.value = ''; + endDateInput.value = ''; + startTimeInput.value = ''; + endTimeInput.value = ''; + topicInput.value = ''; + updateCurrentSessionPanel(`Current session: ${eventTopic} from ${startDate} ${startTime} to ${endDate} ${endTime}`); + renderCalendarEventsOnHome(eventsContainer, scheduledCount, currentSessionPanel); + } + + function renderCalendarEventsOnHome(container, countLabel, sessionLabel) { + const events = getStoredList('scheduledClasses'); + + if (countLabel) { + countLabel.textContent = `${events.length} session${events.length === 1 ? '' : 's'} scheduled`; + } + + if (sessionLabel) { + if (events.length === 0) { + sessionLabel.textContent = 'No current session selected.'; + } else if (!sessionLabel.textContent || sessionLabel.textContent.startsWith('No current')) { + const latest = events[events.length - 1]; + sessionLabel.textContent = `Current session: ${latest.topic} from ${latest.startDate} ${latest.startTime} to ${latest.endDate} ${latest.endTime}`; + } + } + + if (events.length === 0) { + container.innerHTML = '

No sessions scheduled yet. Use the form above to create one.

'; + return; + } + + container.innerHTML = events + .sort((a, b) => `${a.startDate} ${a.startTime}`.localeCompare(`${b.startDate} ${b.startTime}`)) + .map(item => ` +
+
+ ${escapeHtml(item.topic)} + ${escapeHtml(item.startDate)} ${escapeHtml(item.startTime)} → ${escapeHtml(item.endDate)} ${escapeHtml(item.endTime)} +
+ +
+ `).join(''); + } + + function clearAllCalendarEvents(container, countLabel, sessionLabel) { + localStorage.setItem('scheduledClasses', JSON.stringify([])); + if (sessionLabel) { + sessionLabel.textContent = 'No current session selected.'; + } + renderCalendarEventsOnHome(container, countLabel, sessionLabel); + } + + function handleCalendarEventClick(event) { + const button = event.target.closest('.remove-event'); + if (!button) { + return; + } + + const eventId = button.getAttribute('data-event-id'); + const eventsContainer = document.getElementById('calendarEvents'); + if (!eventId || !eventsContainer) { + return; + } + + deleteCalendarEvent(eventId, eventsContainer, scheduledCount, currentSessionPanel); + } + + function deleteCalendarEvent(eventId, container, countLabel, sessionLabel) { + const events = getStoredList('scheduledClasses').filter(item => item.id !== eventId); + localStorage.setItem('scheduledClasses', JSON.stringify(events)); + renderCalendarEventsOnHome(container, countLabel, sessionLabel); + } + + function updateCurrentSessionPanel(text) { + if (!currentSessionPanel) { + return; + } + currentSessionPanel.textContent = text; + } + + function getStoredList(key) { + try { + const stored = JSON.parse(localStorage.getItem(key) || '[]'); + return Array.isArray(stored) ? stored : []; + } catch (error) { + return []; + } + } + + function escapeHtml(value) { + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initLearningHub); +} else { + initLearningHub(); +} \ No newline at end of file diff --git a/scripts/quiz-data.js b/scripts/quiz-data.js index 1496b49..8ed8dd2 100644 --- a/scripts/quiz-data.js +++ b/scripts/quiz-data.js @@ -10,7 +10,9 @@ const defaultQuizData = { 'Hyper Transfer Markup Language', 'Home Tool Markup Language' ], - correct: 0 + correct: 0, + topic: 'HTML basics', + explanation: 'HTML is the markup language used to structure web pages. Review tags, elements, and document structure.' }, { question: 'Which of these is not a JavaScript framework?', @@ -20,7 +22,9 @@ const defaultQuizData = { 'Vue', 'Django' ], - correct: 3 + correct: 3, + topic: 'JavaScript frameworks', + explanation: 'Django is a Python web framework, while React, Angular, and Vue are commonly used JavaScript frameworks.' }, { question: 'What is the purpose of CSS?', @@ -30,7 +34,9 @@ const defaultQuizData = { 'To handle server-side operations', 'To manage databases' ], - correct: 1 + correct: 1, + topic: 'CSS styling', + explanation: 'CSS controls the visual presentation of web pages, including layout, colors, spacing, and typography.' } ] }, @@ -45,7 +51,9 @@ const defaultQuizData = { 'A subset of AI that enables systems to learn from data', 'A database management system' ], - correct: 2 + correct: 2, + topic: 'Machine learning basics', + explanation: 'Machine learning is a part of AI where systems learn patterns from data and improve predictions or decisions.' }, { question: 'Which of these is not a type of machine learning?', @@ -55,7 +63,9 @@ const defaultQuizData = { 'Reinforcement Learning', 'Static Learning' ], - correct: 3 + correct: 3, + topic: 'Types of learning', + explanation: 'The common machine learning types are supervised, unsupervised, and reinforcement learning.' }, { question: 'What is the purpose of neural networks?', @@ -65,7 +75,9 @@ const defaultQuizData = { 'To create web pages', 'To manage databases' ], - correct: 1 + correct: 1, + topic: 'Neural networks', + explanation: 'Neural networks are used to model complex patterns in data, especially for tasks like vision, text, and prediction.' } ] }, @@ -80,7 +92,9 @@ const defaultQuizData = { 'Writing a resume', 'Networking' ], - correct: 1 + correct: 1, + topic: 'Career planning', + explanation: 'Career planning starts with self-assessment so goals, strengths, and learning needs are clear.' }, { question: 'Which of these is not a soft skill?', @@ -90,7 +104,9 @@ const defaultQuizData = { 'Programming', 'Problem-solving' ], - correct: 2 + correct: 2, + topic: 'Soft skills', + explanation: 'Programming is a technical skill. Communication, teamwork, and problem-solving are soft skills.' }, { question: 'What is the purpose of a cover letter?', @@ -100,7 +116,9 @@ const defaultQuizData = { 'To provide references', 'To request a salary' ], - correct: 1 + correct: 1, + topic: 'Job applications', + explanation: 'A cover letter introduces you and connects your experience to the role you want.' } ] } diff --git a/scripts/quiz.js b/scripts/quiz.js index a0d8730..c0cd93f 100644 --- a/scripts/quiz.js +++ b/scripts/quiz.js @@ -1,196 +1,434 @@ -// Quiz data for different courses (load safely from localStorage) -let quizData = {}; -const storedQuizData = localStorage.getItem('adminQuizzes'); -if (storedQuizData) { - try { - const parsed = JSON.parse(storedQuizData); - if (parsed && typeof parsed === 'object') { - quizData = parsed; - } else { - throw new Error('invalid quiz data'); - } - } catch (error) { - if (typeof defaultQuizData !== 'undefined') { - quizData = JSON.parse(JSON.stringify(defaultQuizData)); - try { localStorage.setItem('adminQuizzes', JSON.stringify(quizData)); } catch (e) {} - } else { - quizData = {}; - } - } -} else { - if (typeof defaultQuizData !== 'undefined') { - quizData = JSON.parse(JSON.stringify(defaultQuizData)); - try { localStorage.setItem('adminQuizzes', JSON.stringify(quizData)); } catch (e) {} - } else { - quizData = {}; - } -} - -// Quiz state variables -let currentQuiz = null; -let currentQuestionIndex = 0; -let score = 0; -let timer = null; -let timeLeft = 60; // 60 seconds per quiz -let selectedOption = null; - -// DOM elements -const courseSelection = document.getElementById('courseSelection'); -const quizContainer = document.getElementById('quizContainer'); -const resultsContainer = document.getElementById('resultsContainer'); -const timerDisplay = document.getElementById('timer'); -const scoreDisplay = document.getElementById('score'); -const questionText = document.getElementById('questionText'); -const optionsContainer = document.getElementById('options'); -const nextButton = document.getElementById('nextBtn'); -const prevButton = document.getElementById('prevBtn'); -const submitButton = document.getElementById('submitBtn'); -const retryButton = document.getElementById('retryBtn'); -const backButton = document.getElementById('backToCoursesBtn'); -const finalScore = document.getElementById('finalScore'); -const timeTaken = document.getElementById('timeTaken'); -const correctAnswers = document.getElementById('correctAnswers'); -const incorrectAnswers = document.getElementById('incorrectAnswers'); - -// Event listeners for quiz buttons -document.querySelectorAll('.start-quiz').forEach(button => { - button.addEventListener('click', () => { - const course = button.dataset.course; - startQuiz(course); - }); -}); - -if (nextButton) nextButton.addEventListener('click', nextQuestion); -if (prevButton) prevButton.addEventListener('click', previousQuestion); -if (submitButton) submitButton.addEventListener('click', submitQuiz); -if (retryButton) retryButton.addEventListener('click', () => { - if (resultsContainer) resultsContainer.style.display = 'none'; - if (courseSelection) courseSelection.style.display = 'block'; -}); -if (backButton) backButton.addEventListener('click', () => { - if (resultsContainer) resultsContainer.style.display = 'none'; - if (courseSelection) courseSelection.style.display = 'block'; -}); - -// Start quiz function -function startQuiz(course) { - currentQuiz = quizData[course]; - currentQuestionIndex = 0; - score = 0; - timeLeft = 60; - selectedOption = null; - - if (courseSelection) courseSelection.style.display = 'none'; - if (quizContainer) quizContainer.style.display = 'block'; - if (resultsContainer) resultsContainer.style.display = 'none'; - - const quizTitleEl = document.getElementById('quizTitle'); - if (quizTitleEl && currentQuiz && currentQuiz.title) quizTitleEl.textContent = currentQuiz.title; - updateTimer(); - updateScore(); - showQuestion(); - startTimer(); -} - -// Show current question -function showQuestion() { - if (!currentQuiz || !Array.isArray(currentQuiz.questions)) { - return; - } - const question = currentQuiz.questions[currentQuestionIndex]; - if (questionText) questionText.textContent = question.question || ''; - - if (optionsContainer) { - optionsContainer.innerHTML = ''; - (question.options || []).forEach((option, index) => { - const optionElement = document.createElement('div'); - optionElement.className = 'option'; - optionElement.textContent = option; - optionElement.dataset.index = index; - optionElement.addEventListener('click', () => selectOption(index)); - optionsContainer.appendChild(optionElement); - }); - } - - // Update navigation buttons - if (prevButton) prevButton.disabled = currentQuestionIndex === 0; - if (nextButton) nextButton.disabled = currentQuestionIndex === currentQuiz.questions.length - 1; - if (submitButton) submitButton.style.display = currentQuestionIndex === currentQuiz.questions.length - 1 ? 'block' : 'none'; -} - -// Select option -function selectOption(index) { - selectedOption = index; - document.querySelectorAll('.option').forEach(option => { - option.classList.remove('selected'); - }); - const el = document.querySelector(`.option[data-index="${index}"]`); - if (el) el.classList.add('selected'); - - // Update score if correct - if (currentQuiz && currentQuiz.questions && currentQuiz.questions[currentQuestionIndex]) { - if (index === currentQuiz.questions[currentQuestionIndex].correct) { - score++; - updateScore(); - } - } -} - -// Next question -function nextQuestion() { - if (currentQuestionIndex < currentQuiz.questions.length - 1) { - currentQuestionIndex++; - selectedOption = null; - showQuestion(); - } -} - -// Previous question -function previousQuestion() { - if (currentQuestionIndex > 0) { - currentQuestionIndex--; - selectedOption = null; - showQuestion(); - } -} - -// Submit quiz -function submitQuiz() { - clearInterval(timer); - quizContainer.style.display = 'none'; - resultsContainer.style.display = 'block'; - - const totalQuestions = currentQuiz.questions.length; - const correctCount = score; - const incorrectCount = totalQuestions - correctCount; - - finalScore.textContent = `${score}/${totalQuestions}`; - timeTaken.textContent = `${60 - timeLeft} seconds`; - correctAnswers.textContent = correctCount; - incorrectAnswers.textContent = incorrectCount; -} - -// Update timer -function updateTimer() { - const minutes = Math.floor(timeLeft / 60); - const seconds = timeLeft % 60; - timerDisplay.textContent = `Time: ${minutes}:${seconds.toString().padStart(2, '0')}`; -} - -// Start timer -function startTimer() { - clearInterval(timer); - timer = setInterval(() => { - timeLeft--; - updateTimer(); - if (timeLeft <= 0) { - clearInterval(timer); - submitQuiz(); - } - }, 1000); -} - -// Update score -function updateScore() { - scoreDisplay.textContent = `Score: ${score}`; -} \ No newline at end of file +// Quiz data for different courses (load safely from localStorage) +let quizData = {}; +const storedQuizData = localStorage.getItem('adminQuizzes'); +if (storedQuizData) { + try { + const parsed = JSON.parse(storedQuizData); + if (parsed && typeof parsed === 'object') { + quizData = parsed; + } else { + throw new Error('invalid quiz data'); + } + } catch (error) { + if (typeof defaultQuizData !== 'undefined') { + quizData = JSON.parse(JSON.stringify(defaultQuizData)); + try { localStorage.setItem('adminQuizzes', JSON.stringify(quizData)); } catch (e) {} + } else { + quizData = {}; + } + } +} else if (typeof defaultQuizData !== 'undefined') { + quizData = JSON.parse(JSON.stringify(defaultQuizData)); + try { localStorage.setItem('adminQuizzes', JSON.stringify(quizData)); } catch (e) {} +} + +const courseDetails = { + webdev: { + name: 'Web Development', + url: 'webdev.html', + fallbackTopic: 'Web development foundations' + }, + ai: { + name: 'AI & Machine Learning', + url: 'ai.html', + fallbackTopic: 'AI and machine learning foundations' + }, + career: { + name: 'Career Guidance', + url: 'career.html', + fallbackTopic: 'Career readiness' + } +}; + +let currentQuiz = null; +let currentCourse = ''; +let currentQuestionIndex = 0; +let score = 0; +let timer = null; +let timeLeft = 60; +let answers = []; +let latestReport = null; + +const courseSelection = document.getElementById('courseSelection'); +const quizContainer = document.getElementById('quizContainer'); +const resultsContainer = document.getElementById('resultsContainer'); +const timerDisplay = document.getElementById('timer'); +const scoreDisplay = document.getElementById('score'); +const questionText = document.getElementById('questionText'); +const optionsContainer = document.getElementById('options'); +const nextButton = document.getElementById('nextBtn'); +const prevButton = document.getElementById('prevBtn'); +const submitButton = document.getElementById('submitBtn'); +const retryButton = document.getElementById('retryBtn'); +const backButton = document.getElementById('backToCoursesBtn'); +const finalScore = document.getElementById('finalScore'); +const timeTaken = document.getElementById('timeTaken'); +const correctAnswers = document.getElementById('correctAnswers'); +const incorrectAnswers = document.getElementById('incorrectAnswers'); +const progressPercent = document.getElementById('progressPercent'); +const resultProgress = document.getElementById('resultProgress'); +const reportSummary = document.getElementById('reportSummary'); +const improvementTopics = document.getElementById('improvementTopics'); +const questionReport = document.getElementById('questionReport'); +const saveCourseButton = document.getElementById('saveCourseBtn'); +const recommendedCourseText = document.getElementById('recommendedCourseText'); +const savedCoursesList = document.getElementById('savedCoursesList'); +const scheduleForm = document.getElementById('scheduleForm'); +const scheduledClasses = document.getElementById('scheduledClasses'); + +document.querySelectorAll('.start-quiz').forEach(button => { + button.addEventListener('click', () => startQuiz(button.dataset.course)); +}); + +if (nextButton) nextButton.addEventListener('click', nextQuestion); +if (prevButton) prevButton.addEventListener('click', previousQuestion); +if (submitButton) submitButton.addEventListener('click', submitQuiz); +if (retryButton) retryButton.addEventListener('click', resetToCourseSelection); +if (backButton) backButton.addEventListener('click', resetToCourseSelection); +if (saveCourseButton) saveCourseButton.addEventListener('click', saveRecommendedCourse); +if (scheduleForm) scheduleForm.addEventListener('submit', scheduleClass); + +renderSavedCourses(); +renderScheduledClasses(); + +function startQuiz(course) { + currentQuiz = quizData[course]; + currentCourse = course; + + if (!currentQuiz || !Array.isArray(currentQuiz.questions) || currentQuiz.questions.length === 0) { + alert('No quiz questions are available for this course yet.'); + return; + } + + currentQuestionIndex = 0; + score = 0; + timeLeft = 60; + answers = new Array(currentQuiz.questions.length).fill(null); + latestReport = null; + + if (courseSelection) courseSelection.style.display = 'none'; + if (quizContainer) quizContainer.style.display = 'block'; + if (resultsContainer) resultsContainer.style.display = 'none'; + + const quizTitleEl = document.getElementById('quizTitle'); + if (quizTitleEl) quizTitleEl.textContent = currentQuiz.title || courseDetails[course]?.name || 'Course Quiz'; + + updateTimer(); + updateScore(); + showQuestion(); + startTimer(); +} + +function showQuestion() { + const question = currentQuiz.questions[currentQuestionIndex]; + if (questionText) questionText.textContent = question.question || ''; + + if (optionsContainer) { + optionsContainer.innerHTML = ''; + (question.options || []).forEach((option, index) => { + const optionElement = document.createElement('div'); + optionElement.className = 'option'; + optionElement.textContent = option; + optionElement.dataset.index = index; + optionElement.addEventListener('click', () => selectOption(index)); + + if (answers[currentQuestionIndex] === index) { + optionElement.classList.add('selected'); + } + + optionsContainer.appendChild(optionElement); + }); + } + + const progress = ((currentQuestionIndex + 1) / currentQuiz.questions.length) * 100; + const progressBar = quizContainer?.querySelector('.progress'); + if (progressBar) progressBar.style.width = `${progress}%`; + + if (prevButton) prevButton.disabled = currentQuestionIndex === 0; + if (nextButton) nextButton.disabled = currentQuestionIndex === currentQuiz.questions.length - 1; + if (submitButton) submitButton.style.display = currentQuestionIndex === currentQuiz.questions.length - 1 ? 'block' : 'none'; +} + +function selectOption(index) { + answers[currentQuestionIndex] = index; + document.querySelectorAll('.option').forEach(option => option.classList.remove('selected')); + const selected = document.querySelector(`.option[data-index="${index}"]`); + if (selected) selected.classList.add('selected'); + updateScore(); +} + +function nextQuestion() { + if (currentQuestionIndex < currentQuiz.questions.length - 1) { + currentQuestionIndex++; + showQuestion(); + } +} + +function previousQuestion() { + if (currentQuestionIndex > 0) { + currentQuestionIndex--; + showQuestion(); + } +} + +function submitQuiz() { + clearInterval(timer); + if (!currentQuiz || !Array.isArray(currentQuiz.questions)) return; + + const totalQuestions = currentQuiz.questions.length; + score = calculateScore(); + const correctCount = score; + const incorrectCount = totalQuestions - correctCount; + const percent = Math.round((correctCount / totalQuestions) * 100); + + if (quizContainer) quizContainer.style.display = 'none'; + if (resultsContainer) resultsContainer.style.display = 'block'; + + finalScore.textContent = `${score}/${totalQuestions}`; + timeTaken.textContent = `${60 - timeLeft} seconds`; + correctAnswers.textContent = correctCount; + incorrectAnswers.textContent = incorrectCount; + if (progressPercent) progressPercent.textContent = `${percent}%`; + if (resultProgress) resultProgress.style.width = `${percent}%`; + + latestReport = buildReport(percent); + renderReport(latestReport); + renderSavedCourses(); + renderScheduledClasses(); +} + +function buildReport(percent) { + const questions = currentQuiz.questions.map((question, index) => { + const selectedIndex = answers[index]; + const correctIndex = Number(question.correct); + const isCorrect = selectedIndex === correctIndex; + const selectedAnswer = selectedIndex === null || selectedIndex === undefined + ? 'Not answered' + : question.options?.[selectedIndex] || 'Not answered'; + const correctAnswer = question.options?.[correctIndex] || 'Correct answer unavailable'; + const topic = question.topic || inferTopic(question); + + return { + number: index + 1, + question: question.question || '', + selectedAnswer, + correctAnswer, + isCorrect, + topic, + feedback: getQuestionFeedback(question, isCorrect, selectedAnswer, correctAnswer, topic) + }; + }); + + const missedTopics = questions + .filter(item => !item.isCorrect) + .map(item => item.topic); + const topicCounts = missedTopics.reduce((counts, topic) => { + counts[topic] = (counts[topic] || 0) + 1; + return counts; + }, {}); + const improvement = Object.entries(topicCounts) + .sort((a, b) => b[1] - a[1]) + .map(([topic, count]) => ({ topic, count })); + + return { + percent, + questions, + improvement, + courseName: courseDetails[currentCourse]?.name || currentQuiz.title || 'Course', + courseUrl: courseDetails[currentCourse]?.url || 'index.html' + }; +} + +function renderReport(report) { + if (reportSummary) { + const missedCount = report.questions.length - score; + reportSummary.textContent = missedCount === 0 + ? `Excellent work. You scored ${report.percent}% and are ready to keep moving in ${report.courseName}.` + : `You scored ${report.percent}%. Focus on the topics below, then retake the quiz to check your improvement.`; + } + + if (recommendedCourseText) { + recommendedCourseText.textContent = `Recommended course: ${report.courseName}. Save it so it appears in your saved courses list.`; + } + + if (improvementTopics) { + if (report.improvement.length === 0) { + improvementTopics.innerHTML = ' No weak topics found'; + } else { + improvementTopics.innerHTML = report.improvement.map(item => ` + ${escapeHtml(item.topic)} (${item.count}) + `).join(''); + } + } + + if (questionReport) { + questionReport.innerHTML = report.questions.map(item => ` +
+

Question ${item.number}: ${escapeHtml(item.question)}

+

Your answer: ${escapeHtml(item.selectedAnswer)}

+

Correct answer: ${escapeHtml(item.correctAnswer)}

+

What ${item.isCorrect ? 'went right' : 'went wrong'}: ${escapeHtml(item.feedback)}

+

Topic: ${escapeHtml(item.topic)}

+
+ `).join(''); + } +} + +function getQuestionFeedback(question, isCorrect, selectedAnswer, correctAnswer, topic) { + if (isCorrect) { + return `You understood ${topic} and selected the correct answer.`; + } + + if (selectedAnswer === 'Not answered') { + return `This was skipped. Review ${topic}, then practice identifying why "${correctAnswer}" is correct.`; + } + + return question.explanation || `You selected "${selectedAnswer}", but the correct answer is "${correctAnswer}". Review ${topic} and compare the key terms in the options.`; +} + +function inferTopic(question) { + const text = `${question.question || ''} ${(question.options || []).join(' ')}`.toLowerCase(); + if (text.includes('html')) return 'HTML basics'; + if (text.includes('css') || text.includes('style')) return 'CSS styling'; + if (text.includes('javascript') || text.includes('framework')) return 'JavaScript frameworks'; + if (text.includes('machine learning')) return 'Machine learning basics'; + if (text.includes('neural')) return 'Neural networks'; + if (text.includes('career') || text.includes('job')) return 'Career planning'; + if (text.includes('soft skill')) return 'Soft skills'; + return courseDetails[currentCourse]?.fallbackTopic || 'Course fundamentals'; +} + +function calculateScore() { + return currentQuiz.questions.reduce((total, question, index) => { + return total + (answers[index] === Number(question.correct) ? 1 : 0); + }, 0); +} + +function updateTimer() { + const minutes = Math.floor(timeLeft / 60); + const seconds = timeLeft % 60; + if (timerDisplay) timerDisplay.textContent = `Time: ${minutes}:${seconds.toString().padStart(2, '0')}`; +} + +function startTimer() { + clearInterval(timer); + timer = setInterval(() => { + timeLeft--; + updateTimer(); + if (timeLeft <= 0) { + clearInterval(timer); + submitQuiz(); + } + }, 1000); +} + +function updateScore() { + score = currentQuiz ? calculateScore() : 0; + if (scoreDisplay) scoreDisplay.textContent = `Score: ${score}`; +} + +function resetToCourseSelection() { + clearInterval(timer); + if (resultsContainer) resultsContainer.style.display = 'none'; + if (quizContainer) quizContainer.style.display = 'none'; + if (courseSelection) courseSelection.style.display = 'block'; +} + +function saveRecommendedCourse() { + if (!latestReport) return; + + const savedCourses = getStoredList('savedCourses'); + const exists = savedCourses.some(course => course.key === currentCourse); + if (!exists) { + savedCourses.push({ + key: currentCourse, + name: latestReport.courseName, + url: latestReport.courseUrl, + score: latestReport.percent, + savedAt: new Date().toISOString() + }); + localStorage.setItem('savedCourses', JSON.stringify(savedCourses)); + } + + renderSavedCourses(); + if (saveCourseButton) saveCourseButton.textContent = exists ? 'Already Saved' : 'Saved'; +} + +function renderSavedCourses() { + if (!savedCoursesList) return; + const savedCourses = getStoredList('savedCourses'); + + if (savedCourses.length === 0) { + savedCoursesList.innerHTML = '

No saved courses yet.

'; + return; + } + + savedCoursesList.innerHTML = savedCourses.map(course => ` +
+
+ ${escapeHtml(course.name)} + Saved score: ${course.score || 0}% +
+ Open +
+ `).join(''); +} + +function scheduleClass(event) { + event.preventDefault(); + const dateInput = document.getElementById('classDate'); + const timeInput = document.getElementById('classTime'); + const topicInput = document.getElementById('classTopic'); + + const scheduled = getStoredList('scheduledClasses'); + scheduled.push({ + date: dateInput.value, + time: timeInput.value, + topic: topicInput.value.trim(), + course: latestReport?.courseName || courseDetails[currentCourse]?.name || 'EduBridge Class' + }); + localStorage.setItem('scheduledClasses', JSON.stringify(scheduled)); + + scheduleForm.reset(); + renderScheduledClasses(); +} + +function renderScheduledClasses() { + if (!scheduledClasses) return; + const scheduled = getStoredList('scheduledClasses'); + + if (scheduled.length === 0) { + scheduledClasses.innerHTML = '

No classes scheduled yet.

'; + return; + } + + scheduledClasses.innerHTML = scheduled + .sort((a, b) => `${a.date} ${a.time}`.localeCompare(`${b.date} ${b.time}`)) + .map(item => ` +
+
+ ${escapeHtml(item.topic)} + ${escapeHtml(item.course)} - ${escapeHtml(item.date)} at ${escapeHtml(item.time)} +
+ +
+ `).join(''); +} + +function getStoredList(key) { + try { + const value = JSON.parse(localStorage.getItem(key) || '[]'); + return Array.isArray(value) ? value : []; + } catch (error) { + return []; + } +} + +function escapeHtml(value) { + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +}