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>
This commit is contained in:
2026-02-13 17:14:18 +00:00
parent a60b0701c2
commit adc32b9005
3 changed files with 124 additions and 2 deletions
+2 -2
View File
@@ -3,7 +3,7 @@ import type { Phase } from './types'
import { BootSequence } from './components/BootSequence' import { BootSequence } from './components/BootSequence'
import { ECGAnimation } from './components/ECGAnimation' import { ECGAnimation } from './components/ECGAnimation'
import { LoginScreen } from './components/LoginScreen' import { LoginScreen } from './components/LoginScreen'
import { PMRInterface } from './components/PMRInterface' import { DashboardLayout } from './components/DashboardLayout'
import { AccessibilityProvider } from './contexts/AccessibilityContext' import { AccessibilityProvider } from './contexts/AccessibilityContext'
function SkipButton({ onSkip }: { onSkip: () => void }) { function SkipButton({ onSkip }: { onSkip: () => void }) {
@@ -76,7 +76,7 @@ function App() {
<LoginScreen onComplete={() => setPhase('pmr')} /> <LoginScreen onComplete={() => setPhase('pmr')} />
)} )}
{phase === 'pmr' && <PMRInterface />} {phase === 'pmr' && <DashboardLayout />}
{(phase === 'boot' || phase === 'ecg') && ( {(phase === 'boot' || phase === 'ecg') && (
<SkipButton onSkip={skipToLogin} /> <SkipButton onSkip={skipToLogin} />
+111
View File
@@ -0,0 +1,111 @@
import { useState } from 'react'
import { motion } from 'framer-motion'
import { TopBar } from './TopBar'
import Sidebar from './Sidebar'
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
const topbarVariants = {
hidden: prefersReducedMotion ? { y: 0, opacity: 1 } : { y: -48, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: prefersReducedMotion
? { duration: 0 }
: { duration: 0.2, ease: 'easeOut' },
},
}
const sidebarVariants = {
hidden: prefersReducedMotion ? { x: 0, opacity: 1 } : { x: -272, opacity: 0 },
visible: {
x: 0,
opacity: 1,
transition: prefersReducedMotion
? { duration: 0 }
: { duration: 0.25, ease: 'easeOut', delay: 0.05 },
},
}
const contentVariants = {
hidden: prefersReducedMotion ? { opacity: 1 } : { opacity: 0 },
visible: {
opacity: 1,
transition: prefersReducedMotion
? { duration: 0 }
: { duration: 0.3, delay: 0.15 },
},
}
export function DashboardLayout() {
const [, setCommandPaletteOpen] = useState(false)
const handleSearchClick = () => {
setCommandPaletteOpen(true)
}
return (
<div
className="font-ui"
style={{ background: 'var(--bg-dashboard)', minHeight: '100vh' }}
>
{/* TopBar — fixed at top */}
<motion.div initial="hidden" animate="visible" variants={topbarVariants}>
<TopBar onSearchClick={handleSearchClick} />
</motion.div>
{/* Layout below TopBar: Sidebar + Main */}
<div
style={{
display: 'flex',
marginTop: 'var(--topbar-height)',
height: 'calc(100vh - var(--topbar-height))',
}}
>
{/* Sidebar — fixed left */}
<motion.div
initial="hidden"
animate="visible"
variants={sidebarVariants}
style={{ flexShrink: 0 }}
>
<Sidebar />
</motion.div>
{/* Main content — scrollable card grid */}
<motion.main
initial="hidden"
animate="visible"
variants={contentVariants}
aria-label="Dashboard content"
className="pmr-scrollbar"
style={{
flex: 1,
overflowY: 'auto',
padding: '24px 28px 40px',
}}
>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(2, 1fr)',
gap: '16px',
}}
className="dashboard-grid"
>
{/* Tiles will be added in Tasks 8-15 */}
{/* PatientSummaryTile — full width */}
{/* LatestResultsTile — half width (left) */}
{/* CoreSkillsTile — half width (right) */}
{/* LastConsultationTile — full width */}
{/* CareerActivityTile — full width */}
{/* EducationTile — full width */}
{/* ProjectsTile — full width */}
</div>
</motion.main>
</div>
{/* Command palette will be rendered here (Task 18) */}
</div>
)
}
+11
View File
@@ -265,3 +265,14 @@ html {
.pmr-scrollbar::-webkit-scrollbar-thumb:hover { .pmr-scrollbar::-webkit-scrollbar-thumb:hover {
background: var(--text-tertiary); background: var(--text-tertiary);
} }
/* Dashboard card grid responsive */
.dashboard-grid {
grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 900px) {
.dashboard-grid {
grid-template-columns: 1fr;
}
}