|
|
<!DOCTYPE html> |
|
|
<html lang="es"> |
|
|
|
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>{{ title or "MAKER SPACE" }}</title> |
|
|
|
|
|
|
|
|
|
|
|
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}"> |
|
|
<meta name="theme-color" content="#38bdf8"> |
|
|
<link rel="apple-touch-icon" href="{{ url_for('static', filename='assets/apple-touch-icon.png') }}"> |
|
|
<meta name="apple-mobile-web-app-capable" content="yes"> |
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> |
|
|
<meta name="apple-mobile-web-app-title" content="Maker Space"> |
|
|
|
|
|
|
|
|
<link rel="icon" href="{{ url_for('static', filename='assets/favicon.png') }}"> |
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> |
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Outfit:wght@500;700&display=swap" |
|
|
rel="stylesheet"> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> |
|
|
|
|
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script> |
|
|
</head> |
|
|
|
|
|
<body class="dark-theme"> |
|
|
|
|
|
<nav class="navbar glass"> |
|
|
<div class="nav-container"> |
|
|
<a href="/" class="nav-logo"> |
|
|
<i class="fas fa-cube"></i> |
|
|
<span>MAKER SPACE</span> |
|
|
</a> |
|
|
|
|
|
<button class="hamburger" id="hamburger-btn" aria-label="Menu"> |
|
|
<i class="fas fa-bars"></i> |
|
|
</button> |
|
|
|
|
|
|
|
|
<div class="nav-overlay" id="nav-overlay"> |
|
|
<div class="nav-sidebar glass"> |
|
|
<button class="close-btn" id="close-btn">×</button> |
|
|
<div class="sidebar-links"> |
|
|
<a href="/" class="nav-item"><i class="fas fa-home"></i> INICIO</a> |
|
|
<a href="/tutoria" class="nav-item"><i class="fas fa-book"></i> TUTORÍA</a> |
|
|
|
|
|
{% if current_user.is_authenticated %} |
|
|
<a href="/classroom" class="nav-item"><i class="fas fa-chalkboard-teacher"></i> AULAS</a> |
|
|
<div style="margin-top: auto; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 1rem;"> |
|
|
<a href="/logout" class="nav-item" style="color: #ef4444;"><i |
|
|
class="fas fa-sign-out-alt"></i> Salir</a> |
|
|
</div> |
|
|
{% else %} |
|
|
<a href="/login" class="nav-item" style="color: #38bdf8;"><i class="fas fa-sign-in-alt"></i> |
|
|
Acceder</a> |
|
|
{% endif %} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div style="display: flex; gap: 1rem; align-items: center;"> |
|
|
|
|
|
<button id="install-btn" class="btn-icon" style="display: none;"> |
|
|
<i class="fas fa-download"></i> |
|
|
</button> |
|
|
|
|
|
<button id="notif-btn" class="btn-icon"> |
|
|
<i class="fas fa-bell"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</nav> |
|
|
|
|
|
|
|
|
<main class="content-wrapper"> |
|
|
|
|
|
{% with messages = get_flashed_messages(with_categories=true) %} |
|
|
{% if messages %} |
|
|
{% for category, message in messages %} |
|
|
<div class="notification glass {{ category }}" style="margin-bottom: 2rem;"> |
|
|
<i class="fas fa-exclamation-circle"></i> <span>{{ message }}</span> |
|
|
</div> |
|
|
{% endfor %} |
|
|
{% endif %} |
|
|
{% endwith %} |
|
|
|
|
|
|
|
|
{% block content %}{% endblock %} |
|
|
</main> |
|
|
|
|
|
|
|
|
<div id="notification-container"></div> |
|
|
|
|
|
<script src="{{ url_for('static', filename='js/script.js') }}"></script> |
|
|
<script> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const socket = io(); |
|
|
socket.on('notification', (data) => { |
|
|
|
|
|
showNotification(data.text, data.color || 'blue'); |
|
|
}); |
|
|
|
|
|
|
|
|
function showNotification(text, color) { |
|
|
const container = document.getElementById('notification-container'); |
|
|
const notif = document.createElement('div'); |
|
|
notif.className = `notification glass ${color}`; |
|
|
notif.innerHTML = `<i class="fas fa-info-circle"></i> <span>${text}</span>`; |
|
|
container.appendChild(notif); |
|
|
|
|
|
|
|
|
if ("Notification" in window && Notification.permission === "granted") { |
|
|
navigator.serviceWorker.ready.then(registration => { |
|
|
registration.showNotification("MAKER SPACE", { |
|
|
body: text, |
|
|
icon: "/static/assets/icon192x192.png", |
|
|
vibrate: [200, 100, 200], |
|
|
badge: "/static/assets/favicon.png" |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
notif.style.opacity = '0'; |
|
|
setTimeout(() => notif.remove(), 500); |
|
|
}, 5000); |
|
|
} |
|
|
|
|
|
|
|
|
const notifBtn = document.getElementById('notif-btn'); |
|
|
notifBtn.addEventListener('click', () => { |
|
|
if ("Notification" in window) { |
|
|
Notification.requestPermission().then(permission => { |
|
|
if (permission === 'granted') { |
|
|
notifBtn.style.color = '#10b981'; |
|
|
showNotification("Notificaciones activadas", "green"); |
|
|
} else if (permission === 'denied') { |
|
|
notifBtn.style.color = '#ef4444'; |
|
|
alert("Has bloqueado las notificaciones. Actívalas en la configuración de tu navegador."); |
|
|
} |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
if ('serviceWorker' in navigator) { |
|
|
window.addEventListener('load', () => { |
|
|
navigator.serviceWorker.register('/static/sw.js').then((reg) => { |
|
|
console.log('SW Registered', reg); |
|
|
}).catch((err) => { |
|
|
console.log('SW Registration failed', err); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const hamburgerBtn = document.getElementById('hamburger-btn'); |
|
|
const closeBtn = document.getElementById('close-btn'); |
|
|
const navOverlay = document.getElementById('nav-overlay'); |
|
|
|
|
|
function toggleMenu() { |
|
|
navOverlay.classList.toggle('active'); |
|
|
} |
|
|
|
|
|
if (hamburgerBtn) hamburgerBtn.addEventListener('click', toggleMenu); |
|
|
if (closeBtn) closeBtn.addEventListener('click', toggleMenu); |
|
|
|
|
|
|
|
|
if (navOverlay) navOverlay.addEventListener('click', (e) => { |
|
|
if (e.target === navOverlay) toggleMenu(); |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
|
|
|
</html> |