Major Changes: - 🪟 Popup Menu System: Converted slide-down navigation to draggable, minimizable popup window - ❓ Help Bubble: Added contextual help system for admin dashboard (accessible via ? bubble) - 🎨 Universal Theme Engine: All elements now properly match selected theme across all panels - 📱 Enhanced Tablet Support: Comprehensive touch gestures, handwriting recognition, and Apple Pencil optimization - 🖊️ Unified Write Mode: Combined handwriting and keyboard input with large canvas interface Technical Implementation: - Draggable popup with minimize/restore functionality and boundary constraints - Dynamic theme switching using CSS variables throughout all components - Help system with modal overlay and keyboard shortcuts (ESC to close) - Responsive design optimizations for mobile, tablet, and desktop - Comprehensive CSS overrides to eliminate hardcoded colors - JavaScript fixes for canvas stroke colors and input styling UI/UX Improvements: - Clean admin dashboard without instruction clutter - Professional popup interface with smooth animations - Better contrast and readability across all themes (default, playground, chalkboard, galaxy, nature) - Improved accessibility with proper focus management - Enhanced mobile responsiveness with touch-optimized controls Files Modified: - Menu system: tablet-menu.js, tablet-menu.css - Theme engine: styles.css, themes integration - Admin interface: index.html, ui.js - Input systems: game.js, unified-write.js, handwriting.js - Authentication: auth.js with tablet menu cleanup
905 lines
30 KiB
JavaScript
905 lines
30 KiB
JavaScript
/**
|
||
* Tablet Menu Management System
|
||
* Handles collapsible navigation, touch gestures, and optimized menu interactions for tablets
|
||
*/
|
||
|
||
class TabletMenuManager {
|
||
constructor() {
|
||
this.isMenuExpanded = false;
|
||
this.isMinimized = false;
|
||
this.isWritingActive = false;
|
||
this.touchStartX = 0;
|
||
this.touchStartY = 0;
|
||
this.swipeThreshold = 100;
|
||
this.isDragging = false;
|
||
this.dragOffset = { x: 0, y: 0 };
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
this.createMenuToggle();
|
||
this.createMenuPopup();
|
||
this.setupEventListeners();
|
||
this.setupPopupDragging();
|
||
this.setupHelpBubble();
|
||
this.optimizeAdminTabs();
|
||
this.createQuickActions();
|
||
this.setupWritingAreaDetection();
|
||
|
||
console.log('📱 Universal Popup Menu Manager initialized');
|
||
}
|
||
|
||
isTabletDevice() {
|
||
// Check for tablet characteristics
|
||
const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||
const screenSize = window.innerWidth >= 600 && window.innerWidth <= 1200;
|
||
const isLandscape = window.innerWidth > window.innerHeight;
|
||
const aspectRatio = window.innerWidth / window.innerHeight;
|
||
|
||
return hasTouch && (screenSize || (isLandscape && aspectRatio < 2));
|
||
}
|
||
|
||
createMenuToggle() {
|
||
// Remove existing toggle if present
|
||
const existingToggle = document.querySelector('.menu-toggle');
|
||
if (existingToggle) existingToggle.remove();
|
||
|
||
const toggle = document.createElement('button');
|
||
toggle.className = 'menu-toggle';
|
||
toggle.innerHTML = '☰';
|
||
toggle.setAttribute('aria-label', 'Toggle Navigation Menu');
|
||
toggle.addEventListener('click', () => this.toggleMenu());
|
||
|
||
document.body.appendChild(toggle);
|
||
this.menuToggle = toggle;
|
||
}
|
||
|
||
createMenuPopup() {
|
||
// Remove existing popup if present
|
||
const existingPopup = document.querySelector('.menu-popup');
|
||
if (existingPopup) existingPopup.remove();
|
||
|
||
// Create popup container
|
||
const popup = document.createElement('div');
|
||
popup.className = 'menu-popup hidden';
|
||
popup.id = 'menuPopup';
|
||
|
||
// Create popup header
|
||
const header = document.createElement('div');
|
||
header.className = 'popup-header';
|
||
header.innerHTML = `
|
||
<div class="popup-title">
|
||
<span class="popup-icon">📋</span>
|
||
<span>Admin Functions</span>
|
||
</div>
|
||
<div class="popup-controls">
|
||
<button class="minimize-btn" title="Minimize">−</button>
|
||
<button class="close-btn" title="Close">×</button>
|
||
</div>
|
||
`;
|
||
|
||
// Create popup content (will be populated by existing nav content)
|
||
const content = document.createElement('div');
|
||
content.className = 'popup-content';
|
||
content.id = 'popupContent';
|
||
|
||
popup.appendChild(header);
|
||
popup.appendChild(content);
|
||
document.body.appendChild(popup);
|
||
|
||
this.menuPopup = popup;
|
||
this.setupPopupControls();
|
||
}
|
||
|
||
setupPopupControls() {
|
||
const minimizeBtn = this.menuPopup.querySelector('.minimize-btn');
|
||
const closeBtn = this.menuPopup.querySelector('.close-btn');
|
||
|
||
minimizeBtn.addEventListener('click', () => this.minimizePopup());
|
||
closeBtn.addEventListener('click', () => this.closePopup());
|
||
}
|
||
|
||
setupPopupDragging() {
|
||
const header = this.menuPopup.querySelector('.popup-header');
|
||
|
||
header.addEventListener('mousedown', (e) => this.startDrag(e));
|
||
header.addEventListener('touchstart', (e) => this.startDrag(e), { passive: false });
|
||
|
||
document.addEventListener('mousemove', (e) => this.onDrag(e));
|
||
document.addEventListener('touchmove', (e) => this.onDrag(e), { passive: false });
|
||
|
||
document.addEventListener('mouseup', () => this.endDrag());
|
||
document.addEventListener('touchend', () => this.endDrag());
|
||
}
|
||
|
||
startDrag(e) {
|
||
this.isDragging = true;
|
||
const rect = this.menuPopup.getBoundingClientRect();
|
||
const clientX = e.clientX || (e.touches && e.touches[0].clientX);
|
||
const clientY = e.clientY || (e.touches && e.touches[0].clientY);
|
||
|
||
this.dragOffset.x = clientX - rect.left;
|
||
this.dragOffset.y = clientY - rect.top;
|
||
|
||
this.menuPopup.style.cursor = 'grabbing';
|
||
e.preventDefault();
|
||
}
|
||
|
||
onDrag(e) {
|
||
if (!this.isDragging) return;
|
||
|
||
const clientX = e.clientX || (e.touches && e.touches[0].clientX);
|
||
const clientY = e.clientY || (e.touches && e.touches[0].clientY);
|
||
|
||
const newX = clientX - this.dragOffset.x;
|
||
const newY = clientY - this.dragOffset.y;
|
||
|
||
// Keep popup within viewport bounds
|
||
const maxX = window.innerWidth - this.menuPopup.offsetWidth;
|
||
const maxY = window.innerHeight - this.menuPopup.offsetHeight;
|
||
|
||
const constrainedX = Math.max(0, Math.min(newX, maxX));
|
||
const constrainedY = Math.max(0, Math.min(newY, maxY));
|
||
|
||
this.menuPopup.style.left = constrainedX + 'px';
|
||
this.menuPopup.style.top = constrainedY + 'px';
|
||
this.menuPopup.style.transform = 'none';
|
||
}
|
||
|
||
endDrag() {
|
||
this.isDragging = false;
|
||
this.menuPopup.style.cursor = 'default';
|
||
}
|
||
|
||
minimizePopup() {
|
||
this.isMinimized = !this.isMinimized;
|
||
const content = this.menuPopup.querySelector('.popup-content');
|
||
const minimizeBtn = this.menuPopup.querySelector('.minimize-btn');
|
||
|
||
if (this.isMinimized) {
|
||
content.style.display = 'none';
|
||
this.menuPopup.classList.add('minimized');
|
||
minimizeBtn.innerHTML = '+';
|
||
minimizeBtn.title = 'Restore';
|
||
} else {
|
||
content.style.display = 'block';
|
||
this.menuPopup.classList.remove('minimized');
|
||
minimizeBtn.innerHTML = '−';
|
||
minimizeBtn.title = 'Minimize';
|
||
}
|
||
}
|
||
|
||
closePopup() {
|
||
this.menuPopup.classList.add('hidden');
|
||
this.menuToggle.classList.remove('active');
|
||
this.menuToggle.innerHTML = '☰';
|
||
this.isMenuExpanded = false;
|
||
this.isMinimized = false;
|
||
this.showToast('Menu closed');
|
||
}
|
||
|
||
setupHelpBubble() {
|
||
const helpBubble = document.getElementById('adminHelpBubble');
|
||
if (!helpBubble) return;
|
||
|
||
helpBubble.addEventListener('click', () => this.showHelpPopup());
|
||
|
||
// Only show help bubble for admin users
|
||
this.updateHelpBubbleVisibility();
|
||
}
|
||
|
||
updateHelpBubbleVisibility() {
|
||
const helpBubble = document.getElementById('adminHelpBubble');
|
||
const adminPanel = document.getElementById('adminPanel');
|
||
|
||
if (helpBubble && adminPanel) {
|
||
if (!adminPanel.classList.contains('hidden')) {
|
||
helpBubble.style.display = 'flex';
|
||
} else {
|
||
helpBubble.style.display = 'none';
|
||
}
|
||
}
|
||
}
|
||
|
||
showHelpPopup() {
|
||
// Create overlay if it doesn't exist
|
||
let overlay = document.getElementById('helpOverlay');
|
||
if (!overlay) {
|
||
overlay = document.createElement('div');
|
||
overlay.id = 'helpOverlay';
|
||
overlay.className = 'help-overlay';
|
||
|
||
const popup = document.createElement('div');
|
||
popup.className = 'help-popup';
|
||
|
||
const header = document.createElement('div');
|
||
header.className = 'help-popup-header';
|
||
header.innerHTML = `
|
||
<div class="help-popup-title">
|
||
<span>📱</span>
|
||
<span>Admin Functions Guide</span>
|
||
</div>
|
||
<button class="help-close-btn" onclick="window.tabletMenuManager.closeHelpPopup()">×</button>
|
||
`;
|
||
|
||
const content = document.createElement('div');
|
||
content.className = 'help-popup-content';
|
||
|
||
// Copy content from hidden instructions
|
||
const helpContent = document.getElementById('adminHelpContent');
|
||
if (helpContent) {
|
||
content.innerHTML = helpContent.innerHTML;
|
||
}
|
||
|
||
popup.appendChild(header);
|
||
popup.appendChild(content);
|
||
overlay.appendChild(popup);
|
||
document.body.appendChild(overlay);
|
||
|
||
// Close on overlay click
|
||
overlay.addEventListener('click', (e) => {
|
||
if (e.target === overlay) {
|
||
this.closeHelpPopup();
|
||
}
|
||
});
|
||
|
||
// Close on escape key
|
||
const handleEscape = (e) => {
|
||
if (e.key === 'Escape') {
|
||
this.closeHelpPopup();
|
||
document.removeEventListener('keydown', handleEscape);
|
||
}
|
||
};
|
||
document.addEventListener('keydown', handleEscape);
|
||
}
|
||
|
||
// Show overlay
|
||
setTimeout(() => {
|
||
overlay.classList.add('visible');
|
||
}, 10);
|
||
|
||
this.showToast('Help opened');
|
||
}
|
||
|
||
closeHelpPopup() {
|
||
const overlay = document.getElementById('helpOverlay');
|
||
if (overlay) {
|
||
overlay.classList.remove('visible');
|
||
setTimeout(() => {
|
||
overlay.remove();
|
||
}, 300);
|
||
}
|
||
this.showToast('Help closed');
|
||
}
|
||
|
||
setupEventListeners() {
|
||
// Keyboard shortcuts
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Escape' && this.isMenuExpanded) {
|
||
this.closeMenu();
|
||
}
|
||
if (e.ctrlKey && e.key === 'm') {
|
||
e.preventDefault();
|
||
this.toggleMenu();
|
||
}
|
||
});
|
||
|
||
// Click outside to close menu
|
||
document.addEventListener('click', (e) => {
|
||
const nav = document.getElementById('nav');
|
||
if (this.isMenuExpanded &&
|
||
!nav.contains(e.target) &&
|
||
!this.menuToggle.contains(e.target)) {
|
||
this.closeMenu();
|
||
}
|
||
});
|
||
|
||
// Orientation change handling
|
||
window.addEventListener('orientationchange', () => {
|
||
setTimeout(() => this.handleOrientationChange(), 100);
|
||
});
|
||
|
||
// Resize handling
|
||
window.addEventListener('resize', () => {
|
||
this.handleResize();
|
||
});
|
||
}
|
||
|
||
setupSwipeGestures() {
|
||
let startX, startY, startTime;
|
||
|
||
document.addEventListener('touchstart', (e) => {
|
||
if (e.touches.length === 1) {
|
||
startX = e.touches[0].clientX;
|
||
startY = e.touches[0].clientY;
|
||
startTime = Date.now();
|
||
}
|
||
}, { passive: true });
|
||
|
||
document.addEventListener('touchend', (e) => {
|
||
if (e.changedTouches.length === 1) {
|
||
const endX = e.changedTouches[0].clientX;
|
||
const endY = e.changedTouches[0].clientY;
|
||
const endTime = Date.now();
|
||
|
||
const deltaX = endX - startX;
|
||
const deltaY = endY - startY;
|
||
const deltaTime = endTime - startTime;
|
||
|
||
// Swipe detection
|
||
if (Math.abs(deltaX) > Math.abs(deltaY) &&
|
||
Math.abs(deltaX) > this.swipeThreshold &&
|
||
deltaTime < 500) {
|
||
|
||
if (deltaX > 0 && startX < 50) {
|
||
// Swipe right from left edge - open menu
|
||
this.openMenu();
|
||
} else if (deltaX < 0 && this.isMenuExpanded) {
|
||
// Swipe left when menu open - close menu
|
||
this.closeMenu();
|
||
}
|
||
}
|
||
}
|
||
}, { passive: true });
|
||
}
|
||
|
||
optimizeAdminTabs() {
|
||
const adminTabs = document.getElementById('adminTabs');
|
||
if (!adminTabs) return;
|
||
|
||
// Add scroll indicators
|
||
this.addScrollIndicators(adminTabs);
|
||
|
||
// Smooth scrolling for tab navigation
|
||
adminTabs.addEventListener('wheel', (e) => {
|
||
if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) return;
|
||
e.preventDefault();
|
||
adminTabs.scrollLeft += e.deltaY;
|
||
}, { passive: false });
|
||
|
||
// Center active tab
|
||
const activeTab = adminTabs.querySelector('button.active');
|
||
if (activeTab) {
|
||
this.scrollToTab(activeTab);
|
||
}
|
||
}
|
||
|
||
addScrollIndicators(container) {
|
||
const leftIndicator = document.createElement('div');
|
||
leftIndicator.className = 'scroll-indicator left';
|
||
leftIndicator.innerHTML = '‹';
|
||
|
||
const rightIndicator = document.createElement('div');
|
||
rightIndicator.className = 'scroll-indicator right';
|
||
rightIndicator.innerHTML = '›';
|
||
|
||
container.parentNode.insertBefore(leftIndicator, container);
|
||
container.parentNode.appendChild(rightIndicator);
|
||
|
||
// Update indicators on scroll
|
||
const updateIndicators = () => {
|
||
leftIndicator.style.opacity = container.scrollLeft > 0 ? '1' : '0.3';
|
||
rightIndicator.style.opacity =
|
||
container.scrollLeft < container.scrollWidth - container.clientWidth ? '1' : '0.3';
|
||
};
|
||
|
||
container.addEventListener('scroll', updateIndicators);
|
||
updateIndicators();
|
||
|
||
// Click handlers
|
||
leftIndicator.addEventListener('click', () => {
|
||
container.scrollBy({ left: -120, behavior: 'smooth' });
|
||
});
|
||
|
||
rightIndicator.addEventListener('click', () => {
|
||
container.scrollBy({ left: 120, behavior: 'smooth' });
|
||
});
|
||
}
|
||
|
||
scrollToTab(tab) {
|
||
const container = tab.parentNode;
|
||
const tabRect = tab.getBoundingClientRect();
|
||
const containerRect = container.getBoundingClientRect();
|
||
|
||
if (tabRect.left < containerRect.left || tabRect.right > containerRect.right) {
|
||
tab.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
||
}
|
||
}
|
||
|
||
createQuickActions() {
|
||
// Remove existing quick actions
|
||
const existing = document.querySelector('.quick-actions');
|
||
if (existing) existing.remove();
|
||
|
||
const quickActions = document.createElement('div');
|
||
quickActions.className = 'quick-actions';
|
||
|
||
// Back to top button
|
||
const backToTop = document.createElement('button');
|
||
backToTop.className = 'quick-action-btn secondary';
|
||
backToTop.innerHTML = '↑';
|
||
backToTop.title = 'Back to Top';
|
||
backToTop.addEventListener('click', () => {
|
||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||
});
|
||
|
||
quickActions.appendChild(backToTop);
|
||
|
||
// Show/hide based on scroll position
|
||
window.addEventListener('scroll', () => {
|
||
const scrolled = window.pageYOffset > 200;
|
||
backToTop.style.opacity = scrolled ? '1' : '0';
|
||
backToTop.style.pointerEvents = scrolled ? 'auto' : 'none';
|
||
});
|
||
|
||
document.body.appendChild(quickActions);
|
||
}
|
||
|
||
setupWritingAreaDetection() {
|
||
// Detect when user is actively writing
|
||
const writingAreas = [
|
||
'#unifiedWriteContainer canvas',
|
||
'#handwritingCanvas',
|
||
'#userInput'
|
||
];
|
||
|
||
writingAreas.forEach(selector => {
|
||
const element = document.querySelector(selector);
|
||
if (element) {
|
||
element.addEventListener('focus', () => this.setWritingMode(true));
|
||
element.addEventListener('blur', () => this.setWritingMode(false));
|
||
|
||
if (element.tagName === 'CANVAS') {
|
||
element.addEventListener('pointerdown', () => this.setWritingMode(true));
|
||
element.addEventListener('touchstart', () => this.setWritingMode(true));
|
||
}
|
||
}
|
||
});
|
||
|
||
// Auto-close menu when writing starts
|
||
document.addEventListener('touchstart', (e) => {
|
||
const isWritingArea = writingAreas.some(selector => {
|
||
const element = document.querySelector(selector);
|
||
return element && element.contains(e.target);
|
||
});
|
||
|
||
if (isWritingArea && this.isMenuExpanded) {
|
||
setTimeout(() => this.closeMenu(), 100);
|
||
}
|
||
});
|
||
}
|
||
|
||
setWritingMode(active) {
|
||
this.isWritingActive = active;
|
||
document.body.classList.toggle('writing-focused', active);
|
||
|
||
if (active && this.isMenuExpanded) {
|
||
this.closeMenu();
|
||
}
|
||
}
|
||
|
||
toggleMenu() {
|
||
if (this.isMenuExpanded) {
|
||
this.closePopup();
|
||
} else {
|
||
this.openPopup();
|
||
}
|
||
}
|
||
|
||
openPopup() {
|
||
// Move navigation content to popup
|
||
this.populatePopupContent();
|
||
|
||
// Show popup
|
||
this.menuPopup.classList.remove('hidden');
|
||
this.menuToggle.classList.add('active');
|
||
this.menuToggle.innerHTML = '✕';
|
||
|
||
// Position popup in center initially
|
||
if (!this.menuPopup.style.left || !this.menuPopup.style.top) {
|
||
this.centerPopup();
|
||
}
|
||
|
||
this.isMenuExpanded = true;
|
||
}
|
||
|
||
populatePopupContent() {
|
||
const content = this.menuPopup.querySelector('.popup-content');
|
||
const nav = document.getElementById('nav');
|
||
|
||
if (nav && content) {
|
||
// Clone the navigation content
|
||
content.innerHTML = nav.innerHTML;
|
||
}
|
||
}
|
||
|
||
centerPopup() {
|
||
const rect = this.menuPopup.getBoundingClientRect();
|
||
const x = (window.innerWidth - rect.width) / 2;
|
||
const y = (window.innerHeight - rect.height) / 2;
|
||
|
||
this.menuPopup.style.left = Math.max(20, x) + 'px';
|
||
this.menuPopup.style.top = Math.max(20, y) + 'px';
|
||
this.menuPopup.style.transform = 'none';
|
||
|
||
this.showToast('Menu opened');
|
||
|
||
// Focus first interactive element
|
||
setTimeout(() => {
|
||
const firstButton = this.menuPopup.querySelector('button');
|
||
if (firstButton) firstButton.focus();
|
||
}, 300);
|
||
}
|
||
|
||
closeMenu() {
|
||
// Keep this method for backward compatibility
|
||
this.closePopup();
|
||
}
|
||
|
||
showToast(message, duration = 2000) {
|
||
// Remove existing toasts
|
||
document.querySelectorAll('.toast').forEach(toast => toast.remove());
|
||
|
||
// Create toast container if needed
|
||
let container = document.querySelector('.toast-container');
|
||
if (!container) {
|
||
container = document.createElement('div');
|
||
container.className = 'toast-container';
|
||
document.body.appendChild(container);
|
||
}
|
||
|
||
// Create toast
|
||
const toast = document.createElement('div');
|
||
toast.className = 'toast';
|
||
toast.textContent = message;
|
||
|
||
container.appendChild(toast);
|
||
|
||
// Animate in
|
||
setTimeout(() => toast.classList.add('show'), 100);
|
||
|
||
// Auto remove
|
||
setTimeout(() => {
|
||
toast.classList.remove('show');
|
||
setTimeout(() => toast.remove(), 300);
|
||
}, duration);
|
||
}
|
||
|
||
handleOrientationChange() {
|
||
const nav = document.getElementById('nav');
|
||
if (!nav) return;
|
||
|
||
const isLandscape = window.innerWidth > window.innerHeight;
|
||
|
||
if (isLandscape && window.innerWidth >= 900) {
|
||
nav.classList.add('landscape');
|
||
document.body.classList.toggle('landscape-nav-expanded', this.isMenuExpanded);
|
||
} else {
|
||
nav.classList.remove('landscape');
|
||
document.body.classList.remove('landscape-nav-expanded');
|
||
}
|
||
|
||
// Reoptimize admin tabs
|
||
setTimeout(() => this.optimizeAdminTabs(), 100);
|
||
}
|
||
|
||
handleResize() {
|
||
if (!this.isTabletDevice() && this.isMenuExpanded) {
|
||
this.closeMenu();
|
||
}
|
||
|
||
// Re-evaluate tablet status
|
||
if (this.isTabletDevice() && !this.menuToggle) {
|
||
this.createMenuToggle();
|
||
} else if (!this.isTabletDevice() && this.menuToggle) {
|
||
this.menuToggle.remove();
|
||
this.menuToggle = null;
|
||
}
|
||
}
|
||
|
||
// Enhanced navigation functions
|
||
updateUserInfo() {
|
||
const userInfo = document.getElementById('userInfo');
|
||
const userBadge = document.getElementById('userBadge');
|
||
|
||
if (window.currentUser) {
|
||
const { username, role } = window.currentUser;
|
||
|
||
// Don't override if userInfo already has content from main UI
|
||
if (userInfo && !userInfo.textContent.includes('(')) {
|
||
userInfo.textContent = `Welcome, ${username}`;
|
||
}
|
||
|
||
if (userBadge) {
|
||
userBadge.textContent = role === 'admin' ? '👨💼 Admin' : '🎓 Student';
|
||
userBadge.style.background = role === 'admin' ? 'var(--error)' : 'var(--primary)';
|
||
}
|
||
|
||
this.showRoleSpecificMenu(role);
|
||
}
|
||
}
|
||
|
||
showRoleSpecificMenu(role) {
|
||
const studentNav = document.getElementById('studentNavItems');
|
||
const adminNav = document.getElementById('adminNavItems');
|
||
|
||
if (role === 'admin') {
|
||
if (studentNav) studentNav.classList.add('hidden');
|
||
if (adminNav) adminNav.classList.remove('hidden');
|
||
} else {
|
||
if (adminNav) adminNav.classList.add('hidden');
|
||
if (studentNav) studentNav.classList.remove('hidden');
|
||
}
|
||
}
|
||
|
||
// Public API methods
|
||
isMenuOpen() {
|
||
return this.isMenuExpanded;
|
||
}
|
||
|
||
forceCloseMenu() {
|
||
this.closeMenu();
|
||
}
|
||
|
||
updateNavigation() {
|
||
this.updateUserInfo();
|
||
}
|
||
|
||
setTabActive(tabId) {
|
||
const adminTabs = document.getElementById('adminTabs');
|
||
if (!adminTabs) return;
|
||
|
||
const tab = adminTabs.querySelector(`[data-tab="${tabId}"]`);
|
||
if (tab) {
|
||
// Remove active class from all tabs
|
||
adminTabs.querySelectorAll('button').forEach(btn => btn.classList.remove('active'));
|
||
|
||
// Add active class to selected tab
|
||
tab.classList.add('active');
|
||
|
||
// Scroll to active tab
|
||
this.scrollToTab(tab);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Initialize tablet menu manager when DOM is ready
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Wait for other systems to initialize
|
||
setTimeout(() => {
|
||
try {
|
||
window.tabletMenuManager = new TabletMenuManager();
|
||
} catch (error) {
|
||
console.warn('Tablet Menu Manager failed to initialize:', error);
|
||
}
|
||
}, 1000); // Increased delay to ensure other systems are ready
|
||
});
|
||
|
||
// Handle admin tab clicks to update active state
|
||
document.addEventListener('click', function(e) {
|
||
if (e.target.matches('#adminTabs button[data-tab]')) {
|
||
if (window.tabletMenuManager) {
|
||
window.tabletMenuManager.setTabActive(e.target.dataset.tab);
|
||
}
|
||
}
|
||
});
|
||
|
||
// Helper functions for enhanced navigation
|
||
function closeMenuIfTablet() {
|
||
if (window.tabletMenuManager && window.tabletMenuManager.isMenuOpen()) {
|
||
window.tabletMenuManager.forceCloseMenu();
|
||
}
|
||
}
|
||
|
||
function showAdminTab(tabId) {
|
||
// Show admin panel first
|
||
if (window.showAdmin) {
|
||
window.showAdmin();
|
||
}
|
||
|
||
// Then activate the specific tab
|
||
setTimeout(() => {
|
||
// Hide the dashboard overview
|
||
const adminOverview = document.querySelector('.admin-overview');
|
||
const menuInstructions = document.querySelector('.menu-instructions');
|
||
if (adminOverview) adminOverview.style.display = 'none';
|
||
if (menuInstructions) menuInstructions.style.display = 'none';
|
||
|
||
// Don't show the old admin tabs interface - keep it hidden
|
||
const adminTabsContainer = document.querySelector('#adminTabs').parentElement;
|
||
if (adminTabsContainer) {
|
||
adminTabsContainer.style.display = 'none';
|
||
}
|
||
|
||
// Show the specific admin tab content directly
|
||
document.querySelectorAll('.adminTab').forEach(tab => {
|
||
tab.classList.add('hidden');
|
||
});
|
||
|
||
const targetTab = document.getElementById(tabId);
|
||
if (targetTab) {
|
||
targetTab.classList.remove('hidden');
|
||
targetTab.style.display = 'block';
|
||
}
|
||
|
||
// Add a back to dashboard button
|
||
addBackToDashboardButton();
|
||
}, 100);
|
||
}
|
||
|
||
function showChallenges() {
|
||
// Navigate to student dashboard first
|
||
if (window.showStudent) {
|
||
window.showStudent();
|
||
}
|
||
|
||
// Scroll to challenges section
|
||
setTimeout(() => {
|
||
const challengesContainer = document.getElementById('challengesContainer');
|
||
if (challengesContainer) {
|
||
challengesContainer.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
}
|
||
}, 300);
|
||
}
|
||
|
||
function showLeaderboards() {
|
||
// Navigate to student dashboard first
|
||
if (window.showStudent) {
|
||
window.showStudent();
|
||
}
|
||
|
||
// Scroll to leaderboards section
|
||
setTimeout(() => {
|
||
const leaderboardsContainer = document.getElementById('leaderboardsContainer');
|
||
if (leaderboardsContainer) {
|
||
leaderboardsContainer.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
}
|
||
}, 300);
|
||
}
|
||
|
||
function showBadges() {
|
||
// Navigate to student dashboard first
|
||
if (window.showStudent) {
|
||
window.showStudent();
|
||
}
|
||
|
||
// Scroll to user stats which includes badges
|
||
setTimeout(() => {
|
||
const userStatsContainer = document.getElementById('userStatsContainer');
|
||
if (userStatsContainer) {
|
||
userStatsContainer.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
}
|
||
}, 300);
|
||
}
|
||
|
||
function showThemeSelector() {
|
||
// Navigate to student dashboard first
|
||
if (window.showStudent) {
|
||
window.showStudent();
|
||
}
|
||
|
||
// Focus on theme selector
|
||
setTimeout(() => {
|
||
const themeSelect = document.getElementById('studentThemeSelect');
|
||
if (themeSelect) {
|
||
themeSelect.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
themeSelect.focus();
|
||
}
|
||
}, 300);
|
||
}
|
||
|
||
function showInputSettings() {
|
||
// Show unified write mode if available
|
||
if (window.unifiedWriteMode) {
|
||
// Navigate to student dashboard first
|
||
if (window.showStudent) {
|
||
window.showStudent();
|
||
}
|
||
|
||
setTimeout(() => {
|
||
// Start a game to show the input system
|
||
if (window.startGame) {
|
||
window.startGame();
|
||
}
|
||
}, 300);
|
||
}
|
||
}
|
||
|
||
// Update navigation when user changes
|
||
function updateNavigationForUser() {
|
||
try {
|
||
if (window.tabletMenuManager) {
|
||
window.tabletMenuManager.updateNavigation();
|
||
} else {
|
||
// Fallback: update navigation elements directly
|
||
updateNavigationFallback();
|
||
}
|
||
} catch (error) {
|
||
console.warn('Navigation update failed:', error);
|
||
updateNavigationFallback();
|
||
}
|
||
}
|
||
|
||
function updateNavigationFallback() {
|
||
if (window.currentUser) {
|
||
const { role } = window.currentUser;
|
||
const studentNav = document.getElementById('studentNavItems');
|
||
const adminNav = document.getElementById('adminNavItems');
|
||
|
||
if (role === 'admin') {
|
||
if (studentNav) studentNav.classList.add('hidden');
|
||
if (adminNav) adminNav.classList.remove('hidden');
|
||
} else {
|
||
if (adminNav) adminNav.classList.add('hidden');
|
||
if (studentNav) studentNav.classList.remove('hidden');
|
||
}
|
||
}
|
||
}
|
||
|
||
function addBackToDashboardButton() {
|
||
// Remove existing back button
|
||
const existingButton = document.querySelector('.back-to-dashboard');
|
||
if (existingButton) existingButton.remove();
|
||
|
||
// Add back button to admin panel
|
||
const adminPanel = document.getElementById('adminPanel');
|
||
if (adminPanel) {
|
||
const backButton = document.createElement('button');
|
||
backButton.className = 'back-to-dashboard btn-secondary';
|
||
backButton.innerHTML = '← Back to Dashboard';
|
||
backButton.style.cssText = 'margin-bottom: 1rem; display: inline-block;';
|
||
backButton.onclick = showAdminDashboard;
|
||
|
||
// Insert at the beginning of admin panel
|
||
const firstChild = adminPanel.firstElementChild;
|
||
if (firstChild) {
|
||
adminPanel.insertBefore(backButton, firstChild);
|
||
}
|
||
}
|
||
}
|
||
|
||
function showAdminDashboard() {
|
||
// Show the dashboard overview
|
||
const adminOverview = document.querySelector('.admin-overview');
|
||
const menuInstructions = document.querySelector('.menu-instructions');
|
||
if (adminOverview) adminOverview.style.display = 'block';
|
||
if (menuInstructions) menuInstructions.style.display = 'block';
|
||
|
||
// Hide the admin tabs container
|
||
const adminTabs = document.getElementById('adminTabs');
|
||
if (adminTabs && adminTabs.parentElement) {
|
||
adminTabs.parentElement.style.display = 'none';
|
||
}
|
||
|
||
// Hide all admin tab content
|
||
document.querySelectorAll('.adminTab').forEach(tab => {
|
||
tab.classList.add('hidden');
|
||
});
|
||
|
||
// Remove back button
|
||
const backButton = document.querySelector('.back-to-dashboard');
|
||
if (backButton) backButton.remove();
|
||
|
||
// Load dashboard stats
|
||
loadAdminDashboardStats();
|
||
}
|
||
|
||
function loadAdminDashboardStats() {
|
||
// Update stats with real data
|
||
try {
|
||
// Total users
|
||
if (window.users && Array.isArray(window.users)) {
|
||
document.getElementById('totalUsers').textContent = window.users.length;
|
||
}
|
||
|
||
// You can add more stats here as needed
|
||
document.getElementById('totalWordLists').textContent = '5+';
|
||
document.getElementById('totalSessions').textContent = '0';
|
||
document.getElementById('totalBadges').textContent = '8';
|
||
} catch (error) {
|
||
console.warn('Could not load dashboard stats:', error);
|
||
}
|
||
}
|
||
|
||
// Export for use in other scripts
|
||
window.TabletMenuManager = TabletMenuManager; |