UNPKG

mcp-quiz-server

Version:

🧠 AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.

487 lines (441 loc) 34.6 kB
<!DOCTYPE html> <!-- /** * @moduleName: Quiz Platform - Multi-Quiz Interface * @version: 2.0.0 * @since: 2025-07-21 * @lastUpdated: 2025-07-22 * @projectSummary: Enhanced multi-quiz platform with sidebar navigation, card-based layout, and database integration * @techStack: HTML5, Tailwind CSS, TypeScript, Responsive Design, Grid Layout * @dependency: output.css (Tailwind), dist/main.js (compiled modular TypeScript) * @interModuleDependency: js/main.ts (modular frontend architecture), enhanced backend REST API * @requirementsTraceability: REQ-031 (Web Interface), REQ-032 (Responsive Design), REQ-034 (Multi-Quiz Management) * @briefDescription: Modern quiz platform with sidebar navigation, card-based quiz selection, search functionality, and enhanced user experience * @contributors: Jorge Sequeira, Database Integration Team * @examples: Accessible at http://localhost:3000 when enhanced server running * @vulnerabilitiesAssessment: Static HTML, XSS prevention via controlled script execution, HTTPS recommended for production */ --> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AI Quiz Platform</title> <link href="output.css?v=3" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/lucide@latest/dist/umd/lucide.js"></script> </head> <body class="bg-surface-50 dark:bg-surface-900 min-h-screen transition-colors"> <!-- Header --> <header class="bg-white dark:bg-surface-800 shadow-sm border-b border-surface-200 dark:border-surface-700"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="flex justify-between items-center h-16"> <div class="flex items-center"> <h1 class="text-xl font-bold text-surface-900 dark:text-surface-100">AI Quiz Platform</h1> </div> <div class="flex items-center space-x-4"> <!-- Settings Button --> <div class="relative"> <button id="settings-menu-btn" class="flex items-center space-x-2 px-3 py-2 rounded-lg text-sm font-medium text-surface-700 dark:text-surface-300 hover:bg-surface-100 dark:hover:bg-surface-700 transition-colors"> <i data-lucide="settings" class="w-4 h-4"></i> <span class="hidden sm:inline">Settings</span> </button> <!-- Settings Dropdown --> <div id="settings-dropdown" class="absolute right-0 mt-2 w-80 bg-white dark:bg-surface-800 rounded-lg shadow-lg border border-surface-200 dark:border-surface-600 z-50 hidden"> <div class="p-4"> <div class="flex items-center justify-between mb-4"> <h3 class="text-lg font-medium text-surface-900 dark:text-surface-100">Settings</h3> <button id="close-settings-menu" class="text-surface-400 hover:text-surface-600 dark:hover:text-surface-300"> <i data-lucide="x" class="w-4 h-4"></i> </button> </div> <!-- UI Preferences --> <div class="space-y-4"> <div> <h4 class="text-sm font-medium text-surface-900 dark:text-surface-100 mb-2">Settings</h4> <!-- 🎮 Quiz Experience (Primary Settings) --> <div class="space-y-4"> <h5 class="text-xs font-medium text-surface-600 dark:text-surface-400 uppercase tracking-wide">Quiz Experience</h5> <!-- Quiz Mode (Unified Control) --> <div class="flex items-center justify-between"> <div> <label class="text-sm text-surface-700 dark:text-surface-300">Quiz Mode</label> <div class="text-xs text-surface-500 dark:text-surface-400">How answers are displayed and selected</div> </div> <select id="quiz-mode" class="text-sm border-surface-300 dark:border-surface-600 rounded px-2 py-1 bg-white dark:bg-surface-800"> <option value="traditional">Traditional (radio buttons)</option> <option value="cards" selected>Click-to-select cards</option> <option value="instant">Instant feedback mode</option> </select> </div> <!-- Auto-advance (Unified Control) --> <div class="flex items-center justify-between"> <div> <label class="text-sm text-surface-700 dark:text-surface-300">Auto-advance</label> <div class="text-xs text-surface-500 dark:text-surface-400">Automatically move to next question</div> </div> <select id="auto-advance-mode" class="text-sm border-surface-300 dark:border-surface-600 rounded px-2 py-1 bg-white dark:bg-surface-800"> <option value="off">Off - manual navigation</option> <option value="basic">Basic (2 seconds)</option> <option value="enhanced" selected>Enhanced with feedback (3 seconds)</option> <option value="custom">Custom delay...</option> </select> </div> <!-- Custom delay (hidden by default) --> <div class="flex items-center justify-between" id="custom-delay-row" style="display: none;"> <div> <label class="text-sm text-surface-700 dark:text-surface-300">Custom delay</label> <div class="text-xs text-surface-500 dark:text-surface-400">Seconds to wait before advancing</div> </div> <div class="flex items-center space-x-2"> <input type="range" id="auto-advance-delay-slider" min="1" max="10" value="3" class="w-20"> <span id="delay-value" class="text-sm text-surface-600 dark:text-surface-400 min-w-[4rem]">3 seconds</span> </div> </div> </div> <!-- ⚡ Quick Settings (Secondary) --> <div class="border-t border-surface-200 dark:border-surface-700 pt-4 space-y-4"> <h5 class="text-xs font-medium text-surface-600 dark:text-surface-400 uppercase tracking-wide">Quick Settings</h5> <div class="space-y-3"> <label class="flex items-center justify-between cursor-pointer"> <span class="text-sm text-surface-700 dark:text-surface-300">Show question numbers</span> <input type="checkbox" id="show-question-numbers" checked class="rounded border-surface-300 dark:border-surface-600 text-primary-600 focus:ring-primary-500"> </label> <!-- Timer settings moved to QuizStartModal for better UX --> <div class="text-xs text-surface-500 dark:text-surface-400 italic"> 💡 Timer settings available when starting a quiz </div> </div> </div> <!-- 🔧 Advanced Settings (Collapsed) --> <div class="border-t border-surface-200 dark:border-surface-700 pt-4"> <details class="advanced-settings"> <summary class="text-xs font-medium text-surface-600 dark:text-surface-400 uppercase tracking-wide cursor-pointer hover:text-surface-800 dark:hover:text-surface-200"> Advanced Settings </summary> <div class="mt-3 space-y-3"> <label class="flex items-center justify-between cursor-pointer"> <span class="text-sm text-surface-700 dark:text-surface-300">Smart submit behavior</span> <input type="checkbox" id="smart-submit-logic" checked class="rounded border-surface-300 dark:border-surface-600 text-primary-600 focus:ring-primary-500"> </label> <label class="flex items-center justify-between cursor-pointer"> <span class="text-sm text-surface-700 dark:text-surface-300">Enable animations</span> <input type="checkbox" id="enable-animations" checked class="rounded border-surface-300 dark:border-surface-600 text-primary-600 focus:ring-primary-500"> </label> <label class="flex items-center justify-between cursor-pointer"> <span class="text-sm text-surface-700 dark:text-surface-300">Hide disabled navigation</span> <input type="checkbox" id="hide-nav-buttons" class="rounded border-surface-300 dark:border-surface-600 text-primary-600 focus:ring-primary-500"> </label> </div> </details> </div> </div> <!-- Quiz Defaults --> <div class="border-t border-surface-200 dark:border-surface-700 pt-4"> <h4 class="text-sm font-medium text-surface-900 dark:text-surface-100 mb-2">Quiz Defaults</h4> <div class="space-y-3"> <!-- Default view mode --> <div class="flex items-center justify-between"> <label class="text-sm text-surface-700 dark:text-surface-300">Default view mode</label> <select id="default-view-mode" class="text-sm border border-surface-300 dark:border-surface-600 rounded px-2 py-1 bg-white dark:bg-surface-700 text-surface-900 dark:text-surface-100"> <option value="list">All Questions</option> <option value="single">Single Question</option> </select> </div> <!-- Animation preferences --> <div class="flex items-center justify-between"> <label class="text-sm text-surface-700 dark:text-surface-300">Enable animations</label> <input type="checkbox" id="enable-animations" checked class="rounded border-surface-300 dark:border-surface-600 text-primary-600 focus:ring-primary-500"> </div> </div> </div> <!-- Reset Settings --> <div class="border-t border-surface-200 dark:border-surface-700 pt-4"> <button id="reset-settings" class="w-full text-sm text-surface-600 dark:text-surface-400 hover:text-surface-800 dark:hover:text-surface-200 border border-surface-300 dark:border-surface-600 rounded px-3 py-2 hover:bg-surface-50 dark:hover:bg-surface-700 transition-colors"> Reset to Defaults </button> </div> </div> </div> </div> </div> <!-- Theme Selector --> <div class="relative"> <button id="theme-menu-btn" class="flex items-center space-x-2 px-3 py-2 rounded-lg text-sm font-medium text-surface-700 dark:text-surface-300 hover:bg-surface-100 dark:hover:bg-surface-700 transition-colors"> <i data-lucide="palette" class="w-4 h-4"></i> <span class="hidden sm:inline">Theme</span> </button> <!-- Theme Dropdown --> <div id="theme-dropdown" class="absolute right-0 mt-2 w-64 bg-white dark:bg-surface-800 rounded-lg shadow-lg border border-surface-200 dark:border-surface-600 z-50 hidden"> <div class="p-4"> <div class="flex items-center justify-between mb-4"> <h3 class="text-lg font-medium text-surface-900 dark:text-surface-100">Theme</h3> <button id="close-theme-menu" class="text-surface-400 hover:text-surface-600 dark:hover:text-surface-300"> <i data-lucide="x" class="w-4 h-4"></i> </button> </div> <!-- Modern Theme Options --> <div class="space-y-2"> <button class="theme-option active" data-theme="light"> <div class="theme-icon bg-yellow-100 text-yellow-600"> <i data-lucide="sun" class="w-5 h-5"></i> </div> <div class="flex-1 text-left"> <div class="font-medium text-surface-900 dark:text-surface-100">Light</div> <div class="text-sm text-surface-500 dark:text-surface-400">Clean and bright interface</div> </div> </button> <button class="theme-option" data-theme="dark"> <div class="theme-icon bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-400"> <i data-lucide="moon" class="w-5 h-5"></i> </div> <div class="flex-1 text-left"> <div class="font-medium text-surface-900 dark:text-surface-100">Dark</div> <div class="text-sm text-surface-500 dark:text-surface-400">Easy on the eyes</div> </div> </button> <button class="theme-option" data-theme="system"> <div class="theme-icon bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400"> <i data-lucide="monitor" class="w-5 h-5"></i> </div> <div class="flex-1 text-left"> <div class="font-medium text-surface-900 dark:text-surface-100">System</div> <div class="text-sm text-surface-500 dark:text-surface-400">Follow system preference</div> </div> </button> </div> </div> </div> </div> <button id="create-quiz-btn" class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-md text-sm font-medium flex items-center transition-colors"> <i data-lucide="plus" class="w-4 h-4 mr-2"></i> Create Quiz </button> </div> </div> </div> </header> <!-- Main Layout --> <div class="flex h-screen pt-16"> <!-- Sidebar --> <aside id="sidebar" class="w-80 bg-white dark:bg-surface-800 shadow-sm border-r border-surface-200 dark:border-surface-700 flex flex-col transition-all duration-300 ease-in-out"> <!-- Sidebar Header with Toggle --> <div class="p-4 border-b border-surface-200 dark:border-surface-700 flex items-center justify-between"> <h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100">Quizzes</h2> <button id="sidebar-toggle" class="p-1 rounded-lg hover:bg-surface-100 dark:hover:bg-surface-700 transition-colors" title="Toggle sidebar"> <i data-lucide="panel-left-close" class="w-5 h-5 text-surface-600 dark:text-surface-400"></i> </button> </div> <!-- Search Bar --> <div class="p-4 border-b border-surface-200 dark:border-surface-700"> <div class="relative"> <input type="text" id="search-input" placeholder="Search quizzes..." class="w-full pl-10 pr-4 py-2 border border-surface-300 dark:border-surface-600 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent bg-white dark:bg-surface-700 text-surface-900 dark:text-surface-100 placeholder-surface-500 dark:placeholder-surface-400" > <i data-lucide="search" class="absolute left-3 top-2.5 w-4 h-4 text-surface-400 dark:text-surface-500"></i> </div> </div> <!-- Filter Tabs --> <div class="px-4 py-2 border-b border-surface-200 dark:border-surface-700"> <div class="flex items-center justify-between"> <div class="flex space-x-1"> <button class="filter-tab active px-3 py-1 text-sm rounded-md bg-primary-100 dark:bg-primary-900 text-primary-700 dark:text-primary-300" data-filter="all"> All </button> <button class="filter-tab px-3 py-1 text-sm rounded-md text-surface-600 dark:text-surface-400 hover:bg-surface-100 dark:hover:bg-surface-700" data-filter="recent"> Recent </button> <button class="filter-tab px-3 py-1 text-sm rounded-md text-surface-600 dark:text-surface-400 hover:bg-surface-100 dark:hover:bg-surface-700" data-filter="favorites"> Favorites </button> </div> <!-- Refresh Button --> <button id="refresh-quizzes-btn" class="p-1 text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300 hover:bg-surface-100 dark:hover:bg-surface-700 rounded-md transition-colors" title="Refresh quiz list"> <i data-lucide="refresh-cw" class="w-4 h-4"></i> </button> </div> </div> <!-- Quiz List --> <div class="flex-1 overflow-y-auto"> <div id="quiz-list" class="p-4 space-y-3"> <!-- Quiz cards will be populated here --> </div> </div> </aside> <!-- Main Content --> <main id="main-content" class="flex-1 overflow-hidden transition-all duration-300 ease-in-out"> <!-- Floating Sidebar Toggle (when collapsed) --> <button id="floating-sidebar-toggle" class="fixed top-20 left-4 z-40 p-3 bg-white dark:bg-surface-800 rounded-lg shadow-lg border border-surface-200 dark:border-surface-600 hover:bg-surface-50 dark:hover:bg-surface-700 transition-colors hidden" title="Show sidebar"> <i data-lucide="panel-left-open" class="w-5 h-5 text-surface-600 dark:text-surface-400"></i> </button> <!-- Welcome Screen --> <div id="welcome-screen" class="h-full flex items-center justify-center"> <div class="text-center"> <i data-lucide="brain" class="w-16 h-16 text-gray-400 mx-auto mb-4"></i> <h2 class="text-2xl font-bold text-gray-900 mb-2">Welcome to AI Quiz Platform</h2> <p class="text-gray-600 mb-6">Select a quiz from the sidebar to get started</p> <button id="welcome-create-btn" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg font-medium"> Create Your First Quiz </button> </div> </div> <!-- Quiz Content --> <div id="quiz-content" class="hidden h-full flex flex-col"> <!-- Quiz Header with Enhanced Status Bar - STICKY TOP --> <div class="sticky top-0 z-30 bg-white dark:bg-surface-800 border-b border-surface-200 dark:border-surface-700 px-3 lg:px-6 py-4 shadow-sm"> <!-- Top Status Bar with Progress and Timer --> <div class="flex items-center justify-between mb-3 pb-3 border-b border-gray-100 dark:border-surface-600"> <div class="flex items-center space-x-3"> <!-- Breadcrumb Navigation --> <nav class="flex items-center space-x-2 text-sm text-gray-500 dark:text-surface-400"> <span>Quiz Platform</span> <i data-lucide="chevron-right" class="w-3 h-3"></i> <span id="quiz-breadcrumb" class="text-gray-900 dark:text-surface-100 font-medium">Loading...</span> </nav> </div> <!-- Progress Indicator with Timer --> <div class="flex items-center space-x-4"> <!-- Timer (when enabled) --> <div id="quiz-timer" class="hidden items-center space-x-2 text-sm text-orange-600 dark:text-orange-400"> <i data-lucide="clock" class="w-4 h-4"></i> <span id="timer-display">05:00</span> </div> <!-- Progress --> <div class="flex items-center space-x-3"> <span id="quiz-progress-text" class="text-sm text-gray-600 dark:text-surface-300">Question 1 of 10</span> <div class="w-32 h-2 bg-gray-200 dark:bg-surface-600 rounded-full overflow-hidden"> <div id="quiz-progress-bar" class="h-full bg-primary-500 rounded-full transition-all duration-300" style="width: 10%"></div> </div> <span id="quiz-completion" class="text-sm font-medium text-primary-600 dark:text-primary-400">10%</span> </div> </div> </div> <!-- Quiz Title Section --> <div class="flex items-center justify-between"> <div> <h2 id="quiz-title" class="text-xl font-bold text-gray-900 dark:text-surface-100"></h2> <p id="quiz-description" class="text-gray-600 dark:text-surface-300 text-sm mt-1"></p> </div> <div class="flex items-center space-x-3"> <!-- Timer Display (Display-only, configured in quiz start modal) --> <div id="timer-display" class="flex items-center space-x-2 text-sm"> <span class="text-gray-500 dark:text-surface-400">⏱️</span> <span id="timer-status" class="text-gray-600 dark:text-surface-300">Not configured</span> <span id="timer-countdown" class="font-mono text-gray-800 dark:text-surface-200 hidden">00:00</span> </div> <!-- View Mode Toggle --> <div id="view-mode-toggle" class="flex items-center space-x-2"> <span class="text-sm text-gray-500 dark:text-surface-400">View:</span> <div class="flex bg-gray-100 dark:bg-surface-700 rounded-lg p-1"> <button id="view-mode-single" class="view-mode-btn px-3 py-1 text-xs rounded-md transition-all duration-200" data-mode="single"> 📝 Single </button> <button id="view-mode-list" class="view-mode-btn px-3 py-1 text-xs rounded-md transition-all duration-200 bg-white dark:bg-surface-600 shadow-sm" data-mode="list"> 📋 All </button> </div> </div> <button id="reset-quiz-btn" class="text-gray-400 hover:text-gray-600 dark:text-surface-500 dark:hover:text-surface-300" title="Reset Quiz"> <i data-lucide="refresh-cw" class="w-4 h-4"></i> </button> </div> </div> </div> <!-- Quiz Questions - NO INTERNAL SCROLLING --> <div class="flex-1 min-h-0 p-3 lg:p-6"> <div id="quiz-container" class="max-w-5xl mx-auto h-full w-full px-4"> <!-- Quiz questions will be populated here --> </div> </div> <!-- Quiz Actions - FIXED BOTTOM --> <div id="quiz-navigation-bar" class="quiz-navigation-bar fixed bottom-0 left-0 right-0 z-30 bg-white dark:bg-surface-800 border-t border-surface-200 dark:border-surface-700 px-3 lg:px-6 py-4 shadow-sm"> <div class="flex justify-between items-center"> <button id="prev-btn" class="nav-button prev-button flex items-center space-x-2 px-4 py-2 rounded-lg font-medium transition-all duration-300 disabled:opacity-30 disabled:cursor-not-allowed disabled:transform-none hover:scale-105 active:scale-95" disabled> <div class="nav-icon-container"> <i data-lucide="chevron-left" class="w-4 h-4"></i> </div> <span>Previous Question</span> </button> <button id="submit-button" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg font-medium transition duration-200"> Submit Quiz </button> <button id="next-btn" class="nav-button next-button flex items-center space-x-2 px-4 py-2 rounded-lg font-medium transition-all duration-300 disabled:opacity-30 disabled:cursor-not-allowed disabled:transform-none hover:scale-105 active:scale-95"> <span>Next Question</span> <div class="nav-icon-container"> <i data-lucide="chevron-right" class="w-4 h-4"></i> </div> </button> </div> </div> </div> <!-- Results Modal --> <div id="result-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50 p-4"> <!-- Mobile: slide up animation, Desktop: center modal --> <div class="bg-white dark:bg-surface-800 rounded-lg shadow-xl w-full max-w-lg md:max-w-2xl transform transition-all duration-300 ease-out max-h-[85vh] md:max-h-[80vh] flex flex-col translate-y-full md:translate-y-0 modal-content"> <!-- Fixed Header --> <div class="flex items-center justify-between p-4 md:p-6 border-b border-surface-200 dark:border-surface-600 flex-shrink-0"> <h3 class="text-lg md:text-xl font-bold text-surface-900 dark:text-surface-100">Quiz Results</h3> <button id="close-results-btn" class="text-surface-400 hover:text-surface-600 dark:hover:text-surface-300 p-1 rounded-full hover:bg-surface-100 dark:hover:bg-surface-700 transition-colors"> <i data-lucide="x" class="w-5 h-5 md:w-6 md:h-6"></i> </button> </div> <!-- Scrollable Content --> <div class="flex-1 overflow-y-auto overscroll-contain"> <div id="result-container" class="p-4 md:p-6"> <!-- Results will be displayed here --> </div> </div> <!-- Fixed Footer with Actions --> <div id="result-actions" class="p-4 md:p-6 border-t border-surface-200 dark:border-surface-600 bg-surface-50 dark:bg-surface-700/50 flex-shrink-0"> <!-- Action buttons will be moved here --> </div> </div> </div> <!-- Tour Modal - SIMPLIFIED FOR BETTER VISIBILITY --> <div id="tour-modal" class="fixed inset-0 bg-black bg-opacity-80 hidden items-center justify-center z-[9999] p-4"> <div class="bg-white rounded-xl border-4 border-blue-500 shadow-2xl w-full max-w-lg p-8 text-center relative min-h-[300px] modal-content"> <!-- Simple Header --> <div class="mb-6"> <h2 id="tour-title" class="text-2xl font-bold text-black mb-2">Welcome to Quiz Server!</h2> <button id="tour-close-btn" class="absolute top-4 right-4 text-black hover:text-red-500 text-2xl font-bold"> × </button> </div> <!-- Simple Content --> <div class="mb-8"> <div id="tour-content" class="text-black text-lg leading-relaxed"> This is your first time here! Would you like a quick tour of the features? </div> </div> <!-- Simple Buttons --> <div class="flex gap-4 justify-center"> <button id="tour-skip-btn" class="bg-gray-300 hover:bg-gray-400 text-black px-6 py-3 rounded-lg text-lg font-semibold"> Skip Tour </button> <button id="tour-next-btn" class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-3 rounded-lg text-lg font-semibold"> Start Tour </button> <button id="tour-prev-btn" class="hidden bg-gray-300 hover:bg-gray-400 text-black px-6 py-3 rounded-lg text-lg font-semibold"> Previous </button> </div> </div> </div> </main> </div> <script src="dist/main.js?v=4" type="module"></script> <script src="debug-settings-timer.js"></script> <script> // Initialize Lucide icons lucide.createIcons(); </script> </body> </html>