From adc32b9005134af369358b0ebe7881dd4cf1ff20 Mon Sep 17 00:00:00 2001 From: A Charlwood Date: Fri, 13 Feb 2026 17:14:18 +0000 Subject: [PATCH] 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 --- src/App.tsx | 4 +- src/components/DashboardLayout.tsx | 111 +++++++++++++++++++++++++++++ src/index.css | 11 +++ 3 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 src/components/DashboardLayout.tsx diff --git a/src/App.tsx b/src/App.tsx index fad4473..10da190 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,7 +3,7 @@ import type { Phase } from './types' import { BootSequence } from './components/BootSequence' import { ECGAnimation } from './components/ECGAnimation' import { LoginScreen } from './components/LoginScreen' -import { PMRInterface } from './components/PMRInterface' +import { DashboardLayout } from './components/DashboardLayout' import { AccessibilityProvider } from './contexts/AccessibilityContext' function SkipButton({ onSkip }: { onSkip: () => void }) { @@ -76,7 +76,7 @@ function App() { setPhase('pmr')} /> )} - {phase === 'pmr' && } + {phase === 'pmr' && } {(phase === 'boot' || phase === 'ecg') && ( diff --git a/src/components/DashboardLayout.tsx b/src/components/DashboardLayout.tsx new file mode 100644 index 0000000..0e2cf3d --- /dev/null +++ b/src/components/DashboardLayout.tsx @@ -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 ( +
+ {/* TopBar — fixed at top */} + + + + + {/* Layout below TopBar: Sidebar + Main */} +
+ {/* Sidebar — fixed left */} + + + + + {/* Main content — scrollable card 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 */} +
+
+
+ + {/* Command palette will be rendered here (Task 18) */} +
+ ) +} diff --git a/src/index.css b/src/index.css index f1f8b24..1a8fba5 100644 --- a/src/index.css +++ b/src/index.css @@ -265,3 +265,14 @@ html { .pmr-scrollbar::-webkit-scrollbar-thumb:hover { 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; + } +}