diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 4d39d2e..a5e9fff 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -35,7 +35,7 @@ export function Card({ children, full, className, tileId }: CardProps) { ) } -interface CardHeaderProps { +export interface CardHeaderProps { dotColor: 'teal' | 'amber' | 'green' | 'alert' | 'purple' title: string rightText?: string diff --git a/src/components/DetailPanel.tsx b/src/components/DetailPanel.tsx new file mode 100644 index 0000000..12b854a --- /dev/null +++ b/src/components/DetailPanel.tsx @@ -0,0 +1,229 @@ +import { useEffect, useRef } from 'react' +import { X } from 'lucide-react' +import { useDetailPanel } from '@/contexts/DetailPanelContext' +import { useFocusTrap } from '@/hooks/useFocusTrap' +import { DetailPanelContent } from '@/types/pmr' +import type { CardHeaderProps } from './Card' + +// Width mapping from content type +const widthMap: Record = { + kpi: 'narrow', + skill: 'narrow', + 'skills-all': 'narrow', + consultation: 'wide', + project: 'wide', + education: 'narrow', + 'career-role': 'wide', +} + +// Title mapping from content data +function getPanelTitle(content: DetailPanelContent): string { + switch (content.type) { + case 'kpi': + return content.kpi.label + case 'skill': + return content.skill.name + case 'skills-all': + return 'All Medications' + case 'consultation': + return content.consultation.role + case 'project': + return content.investigation.name + case 'education': + return content.document.title + case 'career-role': + return content.consultation.role + } +} + +// Dot color mapping from content type +function getDotColor(content: DetailPanelContent): CardHeaderProps['dotColor'] { + switch (content.type) { + case 'kpi': + return 'teal' + case 'skill': + case 'skills-all': + return 'amber' + case 'consultation': + case 'career-role': + return 'teal' + case 'project': + return 'amber' + case 'education': + return 'purple' + } +} + +// Dot color value map (from Card.tsx) +const dotColorValueMap: Record = { + teal: '#0D6E6E', + amber: '#D97706', + green: '#059669', + alert: '#DC2626', + purple: '#7C3AED', +} + +export function DetailPanel() { + const { content, closePanel, isOpen } = useDetailPanel() + const panelRef = useRef(null) + const titleId = 'detail-panel-title' + + // Focus trap when open + useFocusTrap(panelRef, isOpen) + + // Close on Escape key + useEffect(() => { + if (!isOpen) return + + const handleEscape = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + closePanel() + } + } + + document.addEventListener('keydown', handleEscape) + return () => document.removeEventListener('keydown', handleEscape) + }, [isOpen, closePanel]) + + if (!isOpen || !content) return null + + const width = widthMap[content.type] + const title = getPanelTitle(content) + const dotColor = getDotColor(content) + const dotColorValue = dotColorValueMap[dotColor] + + return ( + <> + {/* Backdrop */} +