| <style> |
| @keyframes sweepIn { |
| 0% { |
| clip-path: polygon(0 0, 0% 0, 0% 100%, 0 100%); |
| } |
| 100% { |
| clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); |
| } |
| } |
| |
| @keyframes sweepOut { |
| 0% { |
| clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); |
| } |
| 100% { |
| clip-path: polygon(0 0, 0% 0, 0% 100%, 0 100%); |
| } |
| } |
| |
| @keyframes linkFadeIn { |
| from { |
| opacity: 0; |
| transform: translateX(-20px); |
| } |
| to { |
| opacity: 1; |
| transform: translateX(0); |
| } |
| } |
| |
| body { |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| background-color: #121212; |
| margin: 0; |
| color: #e0e0e0; |
| } |
| |
| header { |
| background: #1a1a1a; |
| color: #e0e0e0; |
| padding: 0.8rem 1.25rem; |
| display: flex; |
| align-items: center; |
| z-index: 1001; |
| position: relative; |
| border-bottom: 1px solid #282828; |
| } |
| |
| .menu-btn-wrapper { |
| background: transparent; |
| border: none; |
| color: #00bcd4; |
| cursor: pointer; |
| padding: 0.5rem; |
| margin-right: 1.2rem; |
| display: flex; |
| flex-direction: column; |
| justify-content: center; |
| align-items: center; |
| width: 40px; |
| height: 40px; |
| z-index: 1003; |
| position: relative; |
| transition: transform 0.3s ease; |
| } |
| .menu-btn-wrapper:hover { |
| transform: rotate(15deg); |
| } |
| |
| .menu-btn-bar { |
| display: block; |
| width: 24px; |
| height: 2.5px; |
| background-color: #00bcd4; |
| border-radius: 1px; |
| transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1); |
| margin: 2.5px 0; |
| } |
| |
| .menu-btn-wrapper.open .menu-btn-bar:nth-child(1) { |
| transform: rotate(45deg) translate(4.5px, 4.5px) scaleX(1.2); |
| } |
| .menu-btn-wrapper.open .menu-btn-bar:nth-child(2) { |
| opacity: 0; |
| transform: scaleX(0); |
| } |
| .menu-btn-wrapper.open .menu-btn-bar:nth-child(3) { |
| transform: rotate(-45deg) translate(4.5px, -4.5px) scaleX(1.2); |
| } |
| |
| nav { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 270px; |
| height: 100vh; |
| background-color: #1e1e1e; |
| z-index: 1002; |
| box-shadow: 4px 0 15px rgba(0, 0, 0, 0.3); |
| display: flex; |
| flex-direction: column; |
| clip-path: polygon(0 0, 0% 0, 0% 100%, 0 100%); |
| visibility: hidden; |
| } |
| |
| nav.open { |
| animation: sweepIn 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; |
| visibility: visible; |
| transition: visibility 0s; |
| } |
| nav.closing { |
| animation: sweepOut 0.4s cubic-bezier(0.55, 0.055, 0.675, 0.19) forwards; |
| visibility: visible; |
| } |
| |
| .sidebar-backdrop { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100vw; |
| height: 100vh; |
| background: rgba(0, 0, 0, 0.7); |
| z-index: 1000; |
| opacity: 0; |
| pointer-events: none; |
| transition: opacity 0.4s ease; |
| } |
| |
| .sidebar-backdrop.open { |
| opacity: 1; |
| pointer-events: auto; |
| } |
| |
| .nav-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| padding: 0.8rem 1rem 0.8rem 1.5rem; |
| background-color: #161616; |
| border-bottom: 1px solid #2a2a2a; |
| } |
| |
| .nav-header-title { |
| font-size: 1.1rem; |
| font-weight: 600; |
| color: #00bcd4; |
| letter-spacing: 0.5px; |
| } |
| |
| .close-btn-nav { |
| background: transparent; |
| border: none; |
| color: #888; |
| font-size: 1.6rem; |
| font-weight: bold; |
| cursor: pointer; |
| padding: 0.5rem; |
| transition: |
| color 0.2s ease-in-out, |
| transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); |
| } |
| |
| .close-btn-nav:hover { |
| color: #00bcd4; |
| transform: rotate(180deg) scale(1.1); |
| } |
| |
| .nav-links { |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| padding: 0.75rem 0; |
| overflow-y: auto; |
| } |
| |
| .nav-links a { |
| color: #b0b0b0; |
| text-decoration: none; |
| padding: 0.95rem 1.75rem; |
| display: flex; |
| align-items: center; |
| position: relative; |
| font-size: 0.95rem; |
| letter-spacing: 0.3px; |
| transition: |
| color 0.25s ease, |
| background-color 0.25s ease; |
| opacity: 0; |
| } |
| |
| nav.open .nav-links a { |
| animation: linkFadeIn 0.3s ease-out forwards; |
| } |
| nav.open .nav-links a:nth-child(1) { |
| animation-delay: 0.15s; |
| } |
| nav.open .nav-links a:nth-child(2) { |
| animation-delay: 0.2s; |
| } |
| nav.open .nav-links a:nth-child(3) { |
| animation-delay: 0.25s; |
| } |
| nav.open .nav-links a:nth-child(4) { |
| animation-delay: 0.3s; |
| } |
| nav.open .nav-links a:nth-child(5) { |
| animation-delay: 0.35s; |
| } |
| nav.open .nav-links a:nth-child(6) { |
| animation-delay: 0.4s; |
| } |
| |
| |
| .nav-links a::before { |
| content: ''; |
| position: absolute; |
| left: 0; |
| top: 0; |
| bottom: 0; |
| width: 0px; |
| background-color: #00bcd4; |
| transition: width 0.3s cubic-bezier(0.23, 1, 0.32, 1); |
| } |
| |
| .nav-links a:hover { |
| color: #ffffff; |
| background-color: rgba(0, 188, 212, 0.1); |
| } |
| .nav-links a:hover::before, |
| .nav-links a.active::before { |
| width: 4px; |
| } |
| |
| .nav-links a.active { |
| color: #ffffff; |
| font-weight: 500; |
| background-color: rgba(0, 188, 212, 0.15); |
| } |
| .nav-links a.active::before { |
| width: 4px; |
| } |
| |
| h1.header-title { |
| margin: 0; |
| flex-grow: 1; |
| font-weight: 700; |
| font-size: 1.5rem; |
| color: #f0f0f0; |
| letter-spacing: 0.5px; |
| text-shadow: 0 0 5px rgba(0, 188, 212, 0.3); |
| } |
| </style> |
|
|
| <header> |
| <button class="menu-btn-wrapper" id="menuBtnWrapper" aria-label="Open menu" aria-expanded="false"> |
| <span class="menu-btn-bar"></span> |
| <span class="menu-btn-bar"></span> |
| <span class="menu-btn-bar"></span> |
| </button> |
| <h1 class="header-title">Exocore Dashboard</h1> |
| </header> |
|
|
| <div class="sidebar-backdrop" id="sidebarBackdrop"></div> |
|
|
| <nav id="sideNav"> |
| <div class="nav-header"> |
| <span class="nav-header-title">EXOCORE</span> |
| <button class="close-btn-nav" id="closeBtnNav" aria-label="Close menu">×</button> |
| </div> |
| <div class="nav-links"> |
| <a href="/private/server/exocore/web/public/dashboard">Dashboard</a> |
| <a href="/private/server/exocore/web/public/profile">Profile</a> |
| <a href="/private/server/exocore/web/public/manager">File Manager</a> |
| <a href="/private/server/exocore/web/public/console">Console</a> |
| <a href="/private/server/exocore/web/public/shell">Shell</a> |
| <a href="#" id="logoutLink">Logout</a> |
| </div> |
| </nav> |
|
|
| <script> |
| const menuBtnWrapper = document.getElementById('menuBtnWrapper'); |
| const sideNav = document.getElementById('sideNav'); |
| const closeBtnNav = document.getElementById('closeBtnNav'); |
| const backdrop = document.getElementById('sidebarBackdrop'); |
| const navLinks = document.querySelectorAll('.nav-links a'); |
| const logoutLink = document.getElementById('logoutLink'); |
| let isNavAnimating = false; |
| |
| function openSidebar() { |
| if (isNavAnimating || sideNav.classList.contains('open')) return; |
| isNavAnimating = true; |
| |
| sideNav.classList.remove('closing'); |
| sideNav.classList.add('open'); |
| backdrop.classList.add('open'); |
| menuBtnWrapper.classList.add('open'); |
| menuBtnWrapper.setAttribute('aria-expanded', 'true'); |
| |
| sideNav.addEventListener( |
| 'animationend', |
| () => { |
| isNavAnimating = false; |
| }, |
| { once: true } |
| ); |
| } |
| |
| function closeSidebar() { |
| if (isNavAnimating || !sideNav.classList.contains('open')) return; |
| isNavAnimating = true; |
| |
| sideNav.classList.remove('open'); |
| sideNav.classList.add('closing'); |
| backdrop.classList.remove('open'); |
| menuBtnWrapper.classList.remove('open'); |
| menuBtnWrapper.setAttribute('aria-expanded', 'false'); |
| |
| sideNav.addEventListener( |
| 'animationend', |
| () => { |
| sideNav.classList.remove('closing'); |
| isNavAnimating = false; |
| }, |
| { once: true } |
| ); |
| } |
| |
| function toggleSidebar() { |
| if (sideNav.classList.contains('open')) { |
| closeSidebar(); |
| } else { |
| openSidebar(); |
| } |
| } |
| |
| menuBtnWrapper.addEventListener('click', toggleSidebar); |
| closeBtnNav.addEventListener('click', closeSidebar); |
| backdrop.addEventListener('click', closeSidebar); |
| |
| const currentPath = window.location.pathname; |
| const currentPathSpan = document.getElementById('currentPathSpan'); |
| if (currentPathSpan) { |
| currentPathSpan.textContent = currentPath; |
| } |
| |
| navLinks.forEach((link) => { |
| if (link.id !== 'logoutLink' && link.getAttribute('href') === currentPath) { |
| link.classList.add('active'); |
| } |
| }); |
| |
| if (logoutLink) { |
| logoutLink.addEventListener('click', async function(event) { |
| event.preventDefault(); |
| |
| try { |
| await fetch('/private/server/exocore/web/logout', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| }); |
| } catch (error) { |
| console.error('Logout fetch error:', error); |
| } finally { |
| localStorage.removeItem('exocore-token'); |
| localStorage.removeItem('exocore-cookies'); |
| window.location.href = '/private/server/exocore/web/public/login'; |
| |
| } |
| }); |
| } |
| </script> |
|
|