Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Admin Gallery</title> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: #0d0d1a; | |
| color: #eee; | |
| padding: 20px; | |
| min-height: 100vh; | |
| } | |
| .login-container { | |
| max-width: 400px; | |
| margin: 100px auto; | |
| background: #1a1a40; | |
| padding: 40px; | |
| border-radius: 16px; | |
| border: 1px solid #533483; | |
| text-align: center; | |
| } | |
| .login-container h1 { margin-bottom: 20px; color: #f5a623; } | |
| .login-container input { | |
| width: 100%; | |
| padding: 12px; | |
| margin: 10px 0; | |
| border: 1px solid #533483; | |
| border-radius: 8px; | |
| background: #0d0d1a; | |
| color: #fff; | |
| font-size: 1rem; | |
| } | |
| .login-container button { | |
| width: 100%; | |
| padding: 12px; | |
| background: #e94560; | |
| border: none; | |
| border-radius: 8px; | |
| color: #fff; | |
| font-size: 1.1rem; | |
| cursor: pointer; | |
| } | |
| .login-container button:hover { background: #c73652; } | |
| .error { color: #ff4444; margin-top: 10px; } | |
| .gallery { | |
| display: none; | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| } | |
| .gallery-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 10px 0 20px; | |
| border-bottom: 1px solid #533483; | |
| margin-bottom: 20px; | |
| flex-wrap: wrap; | |
| gap: 10px; | |
| } | |
| .gallery-header h1 { color: #f5a623; } | |
| .gallery-header .badge { | |
| background: #533483; | |
| padding: 6px 16px; | |
| border-radius: 20px; | |
| font-size: 0.9rem; | |
| } | |
| .gallery-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); | |
| gap: 20px; | |
| } | |
| .gallery-item { | |
| background: #1a1a40; | |
| border-radius: 12px; | |
| overflow: hidden; | |
| border: 1px solid #2a2a5a; | |
| } | |
| .gallery-item img { | |
| width: 100%; | |
| height: 200px; | |
| object-fit: cover; | |
| display: block; | |
| background: #111; | |
| } | |
| .gallery-item .info { | |
| padding: 10px 14px; | |
| font-size: 0.8rem; | |
| color: #aaa; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .gallery-item .info a { | |
| color: #f5a623; | |
| text-decoration: none; | |
| font-weight: bold; | |
| } | |
| .empty-msg { | |
| grid-column: 1 / -1; | |
| text-align: center; | |
| padding: 60px; | |
| color: #666; | |
| font-size: 1.2rem; | |
| } | |
| .refresh-btn, .back-btn, .logout-btn { | |
| background: #533483; | |
| border: none; | |
| color: #fff; | |
| padding: 8px 20px; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| } | |
| .refresh-btn:hover, .back-btn:hover { background: #6a4a9a; } | |
| .logout-btn { background: #333; } | |
| .logout-btn:hover { background: #555; } | |
| .back-btn { background: #2a2a5a; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="loginScreen" class="login-container"> | |
| <h1>π Admin Gallery</h1> | |
| <p style="color:#aaa; margin-bottom:20px;">Enter password to view snapshots</p> | |
| <input type="password" id="passwordInput" placeholder="Enter password" /> | |
| <button id="loginBtn">Unlock Gallery</button> | |
| <div id="loginError" class="error"></div> | |
| </div> | |
| <div id="galleryScreen" class="gallery"> | |
| <div class="gallery-header"> | |
| <h1>πΈ Saved Snapshots</h1> | |
| <div> | |
| <button class="back-btn" id="backBtn">β¬ Back to Draw</button> | |
| <span class="badge" id="countBadge">0 images</span> | |
| <button class="refresh-btn" id="refreshBtn">β³ Refresh</button> | |
| <button class="logout-btn" id="logoutBtn">πͺ Logout</button> | |
| </div> | |
| </div> | |
| <div class="gallery-grid" id="galleryGrid"> | |
| <div class="empty-msg">Loading snapshots...</div> | |
| </div> | |
| </div> | |
| <script> | |
| const PASSWORD = '26jan24march'; | |
| const loginScreen = document.getElementById('loginScreen'); | |
| const galleryScreen = document.getElementById('galleryScreen'); | |
| const passwordInput = document.getElementById('passwordInput'); | |
| const loginBtn = document.getElementById('loginBtn'); | |
| const loginError = document.getElementById('loginError'); | |
| const galleryGrid = document.getElementById('galleryGrid'); | |
| const countBadge = document.getElementById('countBadge'); | |
| const refreshBtn = document.getElementById('refreshBtn'); | |
| const logoutBtn = document.getElementById('logoutBtn'); | |
| const backBtn = document.getElementById('backBtn'); | |
| backBtn.addEventListener('click', () => { window.location.href = '/'; }); | |
| if (sessionStorage.getItem('adminLoggedIn') === 'true') { | |
| loginScreen.style.display = 'none'; | |
| galleryScreen.style.display = 'block'; | |
| loadSnapshots(); | |
| } | |
| loginBtn.addEventListener('click', () => { | |
| const pass = passwordInput.value.trim(); | |
| if (pass === PASSWORD) { | |
| sessionStorage.setItem('adminLoggedIn', 'true'); | |
| loginScreen.style.display = 'none'; | |
| galleryScreen.style.display = 'block'; | |
| loginError.textContent = ''; | |
| loadSnapshots(); | |
| } else { | |
| loginError.textContent = 'β Incorrect password.'; | |
| passwordInput.value = ''; | |
| passwordInput.focus(); | |
| } | |
| }); | |
| passwordInput.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter') loginBtn.click(); | |
| }); | |
| logoutBtn.addEventListener('click', () => { | |
| sessionStorage.removeItem('adminLoggedIn'); | |
| galleryScreen.style.display = 'none'; | |
| loginScreen.style.display = 'block'; | |
| passwordInput.value = ''; | |
| loginError.textContent = ''; | |
| }); | |
| refreshBtn.addEventListener('click', loadSnapshots); | |
| async function loadSnapshots() { | |
| galleryGrid.innerHTML = '<div class="empty-msg">Loading snapshots...</div>'; | |
| try { | |
| const response = await fetch('/api/snapshots/list'); | |
| const data = await response.json(); | |
| if (!data.success || !data.files || data.files.length === 0) { | |
| galleryGrid.innerHTML = '<div class="empty-msg">π No snapshots saved yet.</div>'; | |
| countBadge.textContent = '0 images'; | |
| return; | |
| } | |
| const files = data.files.sort().reverse(); | |
| countBadge.textContent = `${files.length} images`; | |
| let html = ''; | |
| files.forEach(filename => { | |
| html += ` | |
| <div class="gallery-item"> | |
| <img src="/snapshots/${filename}" alt="${filename}" loading="lazy" /> | |
| <div class="info"> | |
| <span>${filename}</span> | |
| <a href="/snapshots/${filename}" target="_blank">π View</a> | |
| </div> | |
| </div> | |
| `; | |
| }); | |
| galleryGrid.innerHTML = html; | |
| } catch (err) { | |
| galleryGrid.innerHTML = '<div class="empty-msg">β Error loading snapshots.</div>'; | |
| } | |
| } | |
| setInterval(() => { | |
| if (sessionStorage.getItem('adminLoggedIn') === 'true') { | |
| loadSnapshots(); | |
| } | |
| }, 30000); | |
| </script> | |
| </body> | |
| </html> |