This repository has been archived on 2026-05-01. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Nest/nest-intra/src/components/layout/IntranetLayout.tsx

162 lines
5.6 KiB
TypeScript

import { NavLink, Outlet, useNavigate } from 'react-router-dom';
import { useAuth } from '../../contexts/AuthContext';
import { useCallback, Suspense } 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: 'Forum Mod', icon: '[M]', end: false },
{ to: '/intranet/services', label: 'Services', icon: '[S]', end: false },
];
export function IntranetLayout() {
const { user, logout } = useAuth();
const navigate = useNavigate();
const handleLogout = useCallback(() => {
logout();
navigate('/');
}, [logout, navigate]);
return (
<div style={{ display: 'flex', height: '100vh', overflow: 'hidden', 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 — only this area scrolls */}
<main style={{ flex: 1, overflowY: 'auto', padding: '2rem', background: 'var(--color-bg)' }}>
<Suspense
fallback={
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
<div style={{ fontFamily: 'var(--font-mono)', color: 'var(--color-text-muted)', fontSize: '0.8rem', letterSpacing: '0.1em' }}>
LOADING...
</div>
</div>
}
>
<Outlet />
</Suspense>
</main>
</div>
);
}