|
<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> |
|
|