| <!DOCTYPE html> |
| <html lang="id"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Galeri Angkatan</title> |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css"> |
| <style> |
| body { background: linear-gradient(135deg, #667eea, #764ba2); min-height: 100vh; } |
| .navbar { background: rgba(255,255,255,0.95); backdrop-filter: blur(10px); } |
| .loading-screen { |
| position: fixed; top: 0; left: 0; width: 100%; height: 100%; |
| background: linear-gradient(135deg, #667eea, #764ba2); |
| z-index: 9999; display: flex; justify-content: center; align-items: center; |
| transition: opacity 0.5s; |
| } |
| .loading-screen.hidden { opacity: 0; pointer-events: none; } |
| .loading-slideshow { width: 100%; max-width: 600px; height: 300px; |
| border-radius: 15px; overflow: hidden; margin: 20px auto; |
| background-size: cover; background-position: center; } |
| .photo-card { transition: transform 0.3s; border: none; border-radius: 15px; overflow: hidden; } |
| .photo-card:hover { transform: translateY(-10px); box-shadow: 0 10px 30px rgba(0,0,0,0.2); } |
| .photo-card img { height: 250px; object-fit: cover; } |
| @media (max-width: 768px) { |
| .photo-card img { height: 200px; } |
| .loading-slideshow { height: 200px; } |
| } |
| </style> |
| </head> |
| <body> |
| <div id="loadingScreen" class="loading-screen"> |
| <div class="text-center text-white"> |
| <h3>Loading Galeri Angkatan...</h3> |
| <div id="slideshow" class="loading-slideshow"></div> |
| <div class="spinner-border mt-3" role="status"></div> |
| </div> |
| </div> |
|
|
| <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> |
| <div class="container"> |
| <a class="navbar-brand" href="/"><i class="bi bi-images"></i> Galeri Angkatan</a> |
| <div class="navbar-nav ms-auto"> |
| {% if user %} |
| <span class="nav-link text-white"><i class="bi bi-person-circle"></i> {{ user.username }}</span> |
| {% if user.is_admin %} |
| <a class="nav-link" href="/admin"><i class="bi bi-shield-lock"></i> Admin</a> |
| {% endif %} |
| <a class="nav-link" href="/logout"><i class="bi bi-box-arrow-right"></i> Logout</a> |
| {% else %} |
| <a class="nav-link" href="/login"><i class="bi bi-box-arrow-in-right"></i> Login Admin</a> |
| {% endif %} |
| </div> |
| </div> |
| </nav> |
|
|
| <div class="container mt-3"> |
| {% with messages = get_flashed_messages(with_categories=true) %} |
| {% if messages %} |
| {% for category, message in messages %} |
| <div class="alert alert-{{ category }} alert-dismissible fade show">{{ message }}</div> |
| {% endfor %} |
| {% endif %} |
| {% endwith %} |
| </div> |
|
|
| <main class="container my-4"> |
| {% block content %}{% endblock %} |
| </main> |
|
|
| <script> |
| fetch('/slideshow').then(r=>r.json()).then(photos=>{ |
| if(photos.length){ |
| let i=0; |
| const s=document.getElementById('slideshow'); |
| s.style.backgroundImage=`url(/static/uploads/${photos[0]})`; |
| setInterval(()=>{ |
| i=(i+1)%photos.length; |
| s.style.backgroundImage=`url(/static/uploads/${photos[i]})`; |
| },2000); |
| } |
| }); |
| setTimeout(()=>document.getElementById('loadingScreen').classList.add('hidden'),3000); |
| </script> |
| </body> |
| </html> |