File size: 3,401 Bytes
05b4ab1 539ec6f 05b4ab1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | // src/components/Header.jsx
import React, { useEffect, useState } from 'react'
import { healthCheck } from '../lib/api'
const NAV_LINKS = [
{ label: 'Dashboard', action: () => window.scrollTo({ top: 0, behavior: 'smooth' }) },
{ label: 'Pipeline', action: () => document.getElementById('pipeline-section')?.scrollIntoView({ behavior: 'smooth' }) },
{ label: 'Docs', action: () => window.open('http://localhost:8000/docs', '_blank') },
{ label: 'GitHub', action: () => window.open('https://github.com/', '_blank') },
]
export default function Header() {
const [apiStatus, setApiStatus] = useState('checking')
const [activeNav, setActiveNav] = useState('Dashboard')
useEffect(() => {
healthCheck()
.then(() => setApiStatus('online'))
.catch(() => setApiStatus('offline'))
}, [])
const statusDot = {
checking: 'bg-amber animate-pulse',
online: 'bg-green',
offline: 'bg-red',
}[apiStatus]
const statusLabel = {
checking: 'Connecting…',
online: 'API Online',
offline: 'Demo Mode',
}[apiStatus]
return (
<header className="flex items-center justify-between px-6 py-4 border-b border-[#1a3a6e40] bg-[#050d1b] sticky top-0 z-50">
{/* Logo */}
<div
className="flex items-center gap-3 cursor-pointer"
onClick={() => { setActiveNav('Dashboard'); window.scrollTo({ top: 0, behavior: 'smooth' }) }}
>
<div className="w-8 h-8 rounded-md bg-teal flex items-center justify-center">
<span className="font-mono font-bold text-bg text-[11px]">EM</span>
</div>
<div>
<div className="font-display font-semibold text-[15px] gradient-text">perceive_ai</div>
<div className="font-mono text-[9px] text-muted tracking-[0.1em]">MULTIMODAL EMOTIONAL INTELLIGENCE</div>
</div>
</div>
{/* Center nav */}
<nav className="hidden md:flex items-center gap-1">
{NAV_LINKS.map(({ label, action }) => {
const isActive = activeNav === label
return (
<button
key={label}
onClick={() => { setActiveNav(label); action() }}
className={`
font-mono text-[11px] px-4 py-2 rounded-md border transition-all duration-150 cursor-pointer
${isActive
? 'text-teal bg-[#0ec9a815] border-[#0ec9a830]'
: 'text-muted bg-transparent border-transparent hover:text-[#cfe2f7] hover:bg-[#0c1e38]'}
`}
>
{label}
</button>
)
})}
</nav>
{/* Status */}
<div className="flex items-center gap-3">
<div className="flex items-center gap-1.5 bg-[#081527] border border-[#1a3a6e40] rounded-full px-3 py-1.5">
<span className={`w-[6px] h-[6px] rounded-full flex-shrink-0 ${statusDot}`} />
<span className="font-mono text-[10px] text-muted">{statusLabel}</span>
</div>
<button
onClick={() => window.open('http://localhost:8000/docs', '_blank')}
className="hidden sm:flex items-center gap-1 bg-[#081527] border border-[#1a3a6e40] rounded-full px-3 py-1.5 cursor-pointer hover:border-teal transition-colors duration-150"
>
<span className="font-mono text-[10px] text-muted">FastAPI :8000</span>
</button>
</div>
</header>
)
}
|