Generate a modern web app landing + dashboard UI for an app called "PySQL Labs" β a Python + SQL interactive learning platform with hands-on labs and interview-style drills. Create a polished, developer-focused, slightly playful but professional design.
509f3c2
verified
| // PySQL Labs - Main JavaScript | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize Feather Icons | |
| if (typeof feather !== 'undefined') { | |
| feather.replace(); | |
| } | |
| // Mobile Menu Toggle (for navbar component) | |
| document.addEventListener('click', function(e) { | |
| if (e.target.closest('[data-menu-toggle]')) { | |
| const menu = document.getElementById('mobile-menu'); | |
| if (menu) { | |
| menu.classList.toggle('hidden'); | |
| } | |
| }); | |
| // Code Execution Simulation | |
| function simulateCodeExecution(editorId, outputId) { | |
| const runBtn = document.getElementById('run-btn'); | |
| const editor = document.getElementById(editorId); | |
| const output = document.getElementById(outputId); | |
| if (runBtn && editor && output) { | |
| runBtn.addEventListener('click', function() { | |
| // Show loading state | |
| runBtn.innerHTML = '<i data-feather="loader" class="w-4 h-4 animate-spin mr-2"></i>Running...'; | |
| feather.replace(); | |
| runBtn.disabled = true; | |
| output.innerHTML = '<div class="text-gray-400 animate-pulse">Executing code...</div>'; | |
| // Simulate API call delay | |
| setTimeout(() => { | |
| // Simulated output | |
| output.innerHTML = ` | |
| <div class="text-green-400 font-mono text-sm"> | |
| > Code executed successfully!<br> | |
| > Tests passed: 4/4<br> | |
| > Execution time: 0.8s | |
| </div>`; | |
| // Reset button | |
| runBtn.innerHTML = '<i data-feather="play" class="w-4 h-4 mr-2"></i>Run Code'; | |
| feather.replace(); | |
| runBtn.disabled = false; | |
| }, 1500); | |
| } | |
| } | |
| } | |
| // Carousel functionality for problem cards | |
| function initProblemCarousel() { | |
| const carousel = document.querySelector('.problems-carousel'); | |
| if (!carousel) return; | |
| const cards = carousel.querySelectorAll('.problem-card'); | |
| let currentIndex = 0; | |
| function showCard(index) { | |
| cards.forEach((card, i) => { | |
| card.classList.toggle('active', i === index); | |
| card.classList.toggle('opacity-50', i !== index); | |
| } | |
| // Auto-rotate every 5 seconds | |
| setInterval(() => { | |
| currentIndex = (currentIndex + 1) % cards.length; | |
| showCard(currentIndex); | |
| }, 5000); | |
| showCard(0); | |
| } | |
| // SQL Query Execution Simulation | |
| function simulateSQLExecution() { | |
| const sqlRunBtn = document.getElementById('sql-run-btn'); | |
| const sqlResults = document.getElementById('sql-results'); | |
| if (sqlRunBtn && sqlResults) { | |
| sqlRunBtn.addEventListener('click', function() { | |
| sqlRunBtn.innerHTML = '<i data-feather="loader" class="w-4 h-4 animate-spin mr-2"></i>Executing...'; | |
| feather.replace(); | |
| sqlRunBtn.disabled = true; | |
| setTimeout(() => { | |
| sqlResults.innerHTML = ` | |
| <table class="w-full text-sm"> | |
| <thead> | |
| <tr class="border-b border-gray-700"> | |
| <th class="py-2 text-left">id</th> | |
| <th class="py-2 text-left">name</th> | |
| <th class="py-2 text-left">department</th> | |
| <th class="py-2 text-left">salary</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr class="border-b border-gray-800"> | |
| <td class="py-2">101</td> | |
| <td class="py-2">Alice Chen</td> | |
| <td class="py-2">Engineering</td> | |
| <td class="py-2">$95,000</td> | |
| </tr> | |
| <tr class="border-b border-gray-800"> | |
| <td class="py-2">102</td> | |
| <td class="py-2">Bob Johnson</td> | |
| <td class="py-2">Sales</td> | |
| <td class="py-2">$85,500</td> | |
| </tr> | |
| <tr> | |
| <td class="py-2">103</td> | |
| <td class="py-2">Charlie Brown</td> | |
| <td class="py-2">Marketing</td> | |
| <td class="py-2">$78,000</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <div class="mt-3 text-xs text-gray-400"> | |
| 3 rows returned. Execution time: 0.2s | |
| </div>`; | |
| sqlRunBtn.innerHTML = '<i data-feather="play" class="w-4 h-4 mr-2"></i>Run Query'; | |
| feather.replace(); | |
| sqlRunBtn.disabled = false; | |
| }, 1000); | |
| }); | |
| } | |
| } | |
| // Interview Timer Simulation | |
| function initInterviewTimer() { | |
| const timerElement = document.getElementById('interview-timer'); | |
| if (!timerElement) return; | |
| let timeLeft = 1800; // 30 minutes in seconds | |
| function updateTimer() { | |
| const minutes = Math.floor(timeLeft / 60); | |
| const seconds = timeLeft % 60; | |
| timerElement.textContent = | |
| `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}'; | |
| if (timeLeft > 0) { | |
| timeLeft--; | |
| } | |
| } | |
| // Update every second | |
| setInterval(updateTimer, 1000); | |
| updateTimer(); // Initial call | |
| } | |
| // Initialize all features | |
| setTimeout(() => { | |
| simulateCodeExecution('python-editor', 'python-output'); | |
| simulateSQLExecution(); | |
| initProblemCarousel(); | |
| initInterviewTimer(); | |
| }, 500); | |
| // Dark mode toggle (if needed) | |
| const darkModeToggle = document.getElementById('dark-mode-toggle'); | |
| if (darkModeToggle) { | |
| darkModeToggle.addEventListener('click', function() { | |
| document.documentElement.classList.toggle('dark'); | |
| const icon = this.querySelector('i'); | |
| if (document.documentElement.classList.contains('dark')) { | |
| icon.setAttribute('data-feather', 'moon'); | |
| feather.replace(); | |
| } | |
| } | |
| // Add keyboard shortcuts for better UX | |
| document.addEventListener('keydown', function(e) { | |
| // Ctrl+Enter to run code (when editor is focused) | |
| if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { | |
| const activeRunBtn = document.querySelector('#run-btn:not([disabled]), #sql-run-btn:not([disabled])'); | |
| if (activeRunBtn) { | |
| e.preventDefault(); | |
| activeRunBtn.click(); | |
| } | |
| } | |
| }); | |
| // Tooltip initialization | |
| const tooltipTriggers = document.querySelectorAll('[data-tooltip]'); | |
| tooltipTriggers.forEach(trigger => { | |
| trigger.addEventListener('mouseenter', function() { | |
| const tooltipText = this.getAttribute('data-tooltip'); | |
| if (tooltipText) { | |
| const tooltip = document.createElement('div'); | |
| tooltip.className = 'absolute z-50 px-3 py-2 text-sm font-medium bg-gray-800 rounded-lg shadow-sm opacity-0 invisible transition-opacity duration-300'; | |
| tooltip.textContent = tooltipText; | |
| this.appendChild(tooltip); | |
| // Show tooltip | |
| setTimeout(() => { | |
| tooltip.classList.remove('opacity-0', 'invisible'); | |
| tooltip.classList.add('opacity-100'); | |
| }, 100); | |
| this.addEventListener('mouseleave', function() { | |
| tooltip.remove(); | |
| }, { once: true }); | |
| } | |
| }); | |
| }); | |
| }); |