From 06f0d658b0467fdeb755f0fbc9b241fc6d19f5ef Mon Sep 17 00:00:00 2001 From: A Charlwood Date: Fri, 13 Feb 2026 00:56:35 +0000 Subject: [PATCH] Task 9: Rebuild MedicationsView (Skills view) Rebuild medications/skills view from ref-medications.md spec with Clinical Luxury design direction. Three category tabs with count badges, semantic table with sortable columns, expandable prescribing history with vertical timeline, and Framer Motion height animation. Co-Authored-By: Claude Opus 4.6 --- src/components/views/MedicationsView.tsx | 547 ++++++++++++----------- 1 file changed, 280 insertions(+), 267 deletions(-) diff --git a/src/components/views/MedicationsView.tsx b/src/components/views/MedicationsView.tsx index 67f04a0..74dd7b1 100644 --- a/src/components/views/MedicationsView.tsx +++ b/src/components/views/MedicationsView.tsx @@ -1,8 +1,10 @@ import { useState, useMemo } from 'react' -import { ChevronDown, ChevronUp, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react' +import { motion, AnimatePresence } from 'framer-motion' +import { ChevronDown, ChevronUp, ChevronsUpDown } from 'lucide-react' import { medications } from '@/data/medications' import type { Medication } from '@/types/pmr' import { useBreakpoint } from '@/hooks/useBreakpoint' +import { useAccessibility } from '@/contexts/AccessibilityContext' type SortField = 'name' | 'dose' | 'frequency' | 'startYear' | 'status' type SortDirection = 'asc' | 'desc' | null @@ -12,21 +14,30 @@ interface SortState { direction: SortDirection } -const categoryTabs = [ - { id: 'Active', label: 'Active Medications', shortLabel: 'Active', description: 'Technical skills (daily use)' }, - { id: 'Clinical', label: 'Clinical Medications', shortLabel: 'Clinical', description: 'Healthcare domain skills' }, - { id: 'PRN', label: 'PRN (As Required)', shortLabel: 'PRN', description: 'Strategic & leadership skills' }, -] as const +type CategoryId = 'Active' | 'Clinical' | 'PRN' + +const categoryTabs: { id: CategoryId; label: string; shortLabel: string }[] = [ + { id: 'Active', label: 'Active Medications', shortLabel: 'Active' }, + { id: 'Clinical', label: 'Clinical Medications', shortLabel: 'Clinical' }, + { id: 'PRN', label: 'PRN (As Required)', shortLabel: 'PRN' }, +] + +const categoryCounts: Record = { + Active: medications.filter(m => m.category === 'Active').length, + Clinical: medications.filter(m => m.category === 'Clinical').length, + PRN: medications.filter(m => m.category === 'PRN').length, +} + +const prefersReducedMotion = typeof window !== 'undefined' + ? window.matchMedia('(prefers-reduced-motion: reduce)').matches + : false export function MedicationsView() { - const [activeTab, setActiveTab] = useState<'Active' | 'Clinical' | 'PRN'>('Active') + const [activeTab, setActiveTab] = useState('Active') const [expandedRow, setExpandedRow] = useState(null) const [sort, setSort] = useState({ field: 'name', direction: null }) const { isMobile } = useBreakpoint() - - const prefersReducedMotion = typeof window !== 'undefined' - ? window.matchMedia('(prefers-reduced-motion: reduce)').matches - : false + const { setExpandedItem } = useAccessibility() const filteredMedications = useMemo(() => { return medications.filter(med => med.category === activeTab) @@ -45,8 +56,8 @@ export function MedicationsView() { comparison = a.dose - b.dose break case 'frequency': { - const freqOrder = { 'Daily': 0, 'Weekly': 1, 'Monthly': 2, 'As needed': 3 } - comparison = freqOrder[a.frequency] - freqOrder[b.frequency] + const freqOrder: Record = { 'Daily': 0, 'Weekly': 1, 'Monthly': 2, 'As needed': 3 } + comparison = (freqOrder[a.frequency] ?? 4) - (freqOrder[b.frequency] ?? 4) break } case 'startYear': @@ -74,33 +85,37 @@ export function MedicationsView() { } } - const toggleRow = (id: string) => { - setExpandedRow(expandedRow === id ? null : id) + const toggleRow = (id: string, name: string) => { + const nextExpanded = expandedRow === id ? null : id + setExpandedRow(nextExpanded) + setExpandedItem(nextExpanded ? name : null) } - const getSortIcon = (field: SortField) => { + const SortIndicator = ({ field }: { field: SortField }) => { if (sort.field !== field || !sort.direction) { - return + return } - return sort.direction === 'asc' - ? - : + return sort.direction === 'asc' + ? + : } return (
-
-
-

+
+ {/* Header */} +
+

Current Medications

-

+

Skills mapped as active medications — proficiency shown as dosage

-
-
) } +/* ─── Desktop Table Row ────────────────────────────────────────────── */ + interface MedicationRowProps { medication: Medication isExpanded: boolean - isAlternating: boolean + isEven: boolean onToggle: () => void - prefersReducedMotion: boolean } -function MedicationRow({ medication, isExpanded, isAlternating, onToggle, prefersReducedMotion }: MedicationRowProps) { - const statusColors = { - 'Active': 'bg-green-500', - 'Historical': 'bg-gray-400', - } - +function MedicationRow({ medication, isExpanded, isEven, onToggle }: MedicationRowProps) { return ( <> { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + onToggle() + } + }} > - - + +
+ + + + + {medication.name} + +
- - - {medication.name} - - - - + + {medication.dose}% - - + + {medication.frequency} - - + + {medication.startYear} - - - - {medication.status} - + + - {isExpanded && ( - - )} + + {isExpanded && ( + + + +
+ +
+
+ +
+ )} +
) } -interface PrescribingHistoryProps { - history: { year: number; description: string }[] - prefersReducedMotion: boolean -} +/* ─── Status Dot ───────────────────────────────────────────────────── */ -function PrescribingHistory({ history, prefersReducedMotion }: PrescribingHistoryProps) { +function StatusDot({ status }: { status: 'Active' | 'Historical' }) { + const color = status === 'Active' ? 'bg-[#22C55E]' : 'bg-gray-400' return ( - - -
-

- Prescribing History -

-
- {history.map((entry, index) => ( -
- - {entry.year} - - - {entry.description} - -
- ))} -
-
- - +
+
+ ) +} + +/* ─── Prescribing History (shared) ─────────────────────────────────── */ + +interface PrescribingHistoryProps { + history: { year: number; description: string }[] +} + +function PrescribingHistory({ history }: PrescribingHistoryProps) { + return ( +
+

+ Prescribing History +

+
+ {/* Vertical timeline line */} + +
) }