151 lines
5.0 KiB
TypeScript
151 lines
5.0 KiB
TypeScript
import { NavLink, Outlet, useNavigate } from 'react-router-dom';
|
|
import { useAuth } from '../../contexts/AuthContext';
|
|
import { useCallback } from 'react';
|
|
|
|
const INTRANET_LINKS = [
|
|
{ to: '/intranet', label: 'Dashboard', icon: '[>]', end: true },
|
|
{ to: '/intranet/bugs', label: 'Bug Reports', icon: '[!]', end: false },
|
|
{ to: '/intranet/feed', label: 'Team Feed', icon: '[~]', end: false },
|
|
{ to: '/intranet/events', label: 'Events', icon: '[E]', end: false },
|
|
{ to: '/intranet/users', label: 'Users', icon: '[U]', end: false },
|
|
{ to: '/intranet/moderation', label: 'Moderation', icon: '[M]', end: false },
|
|
];
|
|
|
|
export function IntranetLayout() {
|
|
const { user, logout } = useAuth();
|
|
const navigate = useNavigate();
|
|
|
|
const handleLogout = useCallback(() => {
|
|
logout();
|
|
navigate('/');
|
|
}, [logout, navigate]);
|
|
|
|
return (
|
|
<div style={{ display: 'flex', minHeight: '100vh', background: 'var(--color-bg)' }}>
|
|
{/* Sidebar */}
|
|
<aside
|
|
style={{
|
|
width: '220px',
|
|
flexShrink: 0,
|
|
background: 'var(--color-bg-alt)',
|
|
borderRight: '2px solid var(--color-border)',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
padding: '1.5rem 0',
|
|
}}
|
|
>
|
|
{/* Logo */}
|
|
<div style={{ padding: '0 1.25rem 1.25rem', borderBottom: '1px solid var(--color-border)' }}>
|
|
<div
|
|
style={{
|
|
fontFamily: 'var(--font-heading)',
|
|
color: 'var(--color-yellow)',
|
|
fontSize: '1.3rem',
|
|
letterSpacing: '0.08em',
|
|
}}
|
|
>
|
|
INTRANET
|
|
</div>
|
|
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-muted)', fontSize: '0.62rem', marginTop: '2px' }}>
|
|
CROWMATE STUDIO
|
|
</div>
|
|
</div>
|
|
|
|
{/* Nav links */}
|
|
<nav style={{ flex: 1, padding: '0.75rem 0' }}>
|
|
{INTRANET_LINKS.map(({ to, label, icon, end }) => (
|
|
<NavLink
|
|
key={to}
|
|
to={to}
|
|
end={end}
|
|
style={({ isActive }) => ({
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '0.6rem',
|
|
padding: '0.55rem 1.25rem',
|
|
fontFamily: 'var(--font-mono)',
|
|
fontSize: '0.78rem',
|
|
color: isActive ? 'var(--color-yellow)' : 'var(--color-text-muted)',
|
|
background: isActive ? 'rgba(37,99,235,0.08)' : 'transparent',
|
|
borderLeft: isActive ? '3px solid var(--color-yellow)' : '3px solid transparent',
|
|
textDecoration: 'none',
|
|
transition: 'color 0.1s, background 0.1s',
|
|
letterSpacing: '0.05em',
|
|
})}
|
|
>
|
|
<span style={{ opacity: 0.6, fontSize: '0.68rem' }}>{icon}</span>
|
|
{label}
|
|
</NavLink>
|
|
))}
|
|
</nav>
|
|
|
|
{/* User info */}
|
|
<div
|
|
style={{
|
|
padding: '1rem 1.25rem',
|
|
borderTop: '1px solid var(--color-border)',
|
|
fontFamily: 'var(--font-mono)',
|
|
}}
|
|
>
|
|
<div style={{ color: 'var(--color-text-dim)', fontSize: '0.75rem', marginBottom: '0.4rem' }}>
|
|
{user?.username}
|
|
</div>
|
|
<div
|
|
style={{
|
|
display: 'inline-block',
|
|
background: 'rgba(255,255,0,0.08)',
|
|
border: '1px solid var(--color-yellow)',
|
|
color: 'var(--color-yellow)',
|
|
fontSize: '0.6rem',
|
|
padding: '0.1rem 0.4rem',
|
|
letterSpacing: '0.1em',
|
|
textTransform: 'uppercase',
|
|
marginBottom: '0.75rem',
|
|
}}
|
|
>
|
|
{user?.role}
|
|
</div>
|
|
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
|
<button
|
|
onClick={handleLogout}
|
|
style={{
|
|
background: 'transparent',
|
|
border: '1px solid var(--color-red)',
|
|
color: 'var(--color-red)',
|
|
fontFamily: 'var(--font-mono)',
|
|
fontSize: '0.63rem',
|
|
padding: '0.2rem 0.5rem',
|
|
cursor: 'pointer',
|
|
letterSpacing: '0.05em',
|
|
}}
|
|
>
|
|
Logout
|
|
</button>
|
|
<NavLink
|
|
to="/"
|
|
style={{
|
|
background: 'transparent',
|
|
border: '1px solid var(--color-border)',
|
|
color: 'var(--color-text-muted)',
|
|
fontFamily: 'var(--font-mono)',
|
|
fontSize: '0.63rem',
|
|
padding: '0.2rem 0.5rem',
|
|
letterSpacing: '0.05em',
|
|
textDecoration: 'none',
|
|
display: 'inline-block',
|
|
}}
|
|
>
|
|
Public
|
|
</NavLink>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
{/* Main content */}
|
|
<main style={{ flex: 1, overflowY: 'auto', padding: '2rem', background: 'var(--color-bg)' }}>
|
|
<Outlet />
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|