Files
portfolio/src/App.tsx
T
admin adc32b9005 Task 7: Build DashboardLayout and wire up App.tsx
Three-zone layout: TopBar (fixed) + Sidebar (fixed left) + Main
(scrollable card grid). Framer Motion staggered entrance animations
with prefers-reduced-motion support. Card grid responsive at 900px.
Replaces PMRInterface in the pmr phase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:14:18 +00:00

90 lines
2.8 KiB
TypeScript

import { useState, useRef, useEffect } from 'react'
import type { Phase } from './types'
import { BootSequence } from './components/BootSequence'
import { ECGAnimation } from './components/ECGAnimation'
import { LoginScreen } from './components/LoginScreen'
import { DashboardLayout } from './components/DashboardLayout'
import { AccessibilityProvider } from './contexts/AccessibilityContext'
function SkipButton({ onSkip }: { onSkip: () => void }) {
const [visible, setVisible] = useState(false)
useEffect(() => {
const timer = setTimeout(() => setVisible(true), 1500)
return () => clearTimeout(timer)
}, [])
return (
<button
onClick={onSkip}
aria-label="Skip intro animation"
className="fixed bottom-6 left-1/2 -translate-x-1/2 z-[100] px-4 py-1.5 text-xs tracking-widest uppercase font-mono border rounded transition-all duration-700 cursor-pointer select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-white/40"
style={{
color: '#555',
borderColor: '#333',
backgroundColor: 'rgba(255,255,255,0.03)',
opacity: visible ? 1 : 0,
pointerEvents: visible ? 'auto' : 'none',
}}
onMouseEnter={(e) => {
e.currentTarget.style.color = '#888'
e.currentTarget.style.borderColor = '#555'
e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.06)'
}}
onMouseLeave={(e) => {
e.currentTarget.style.color = '#555'
e.currentTarget.style.borderColor = '#333'
e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.03)'
}}
>
Skip
</button>
)
}
function App() {
const [phase, setPhase] = useState<Phase>('boot')
const cursorPositionRef = useRef<{ x: number; y: number } | null>(null)
const skipToLogin = () => setPhase('login')
return (
<AccessibilityProvider>
<div className="min-h-screen bg-black">
{/* Screen reader announcement for PMR phase */}
{phase === 'pmr' && (
<div className="sr-only" role="status" aria-live="polite" aria-atomic="true">
Patient Record for Charlwood, Andrew. Summary view.
</div>
)}
{phase === 'boot' && (
<BootSequence
onComplete={() => setPhase('ecg')}
onCursorPositionReady={(pos) => { cursorPositionRef.current = pos }}
/>
)}
{phase === 'ecg' && (
<ECGAnimation
onComplete={() => setPhase('login')}
startPosition={cursorPositionRef.current}
/>
)}
{phase === 'login' && (
<LoginScreen onComplete={() => setPhase('pmr')} />
)}
{phase === 'pmr' && <DashboardLayout />}
{(phase === 'boot' || phase === 'ecg') && (
<SkipButton onSkip={skipToLogin} />
)}
</div>
</AccessibilityProvider>
)
}
export default App