diff --git a/Ralph/prd.json b/Ralph/prd.json index 039bd2f..e0f6d30 100644 --- a/Ralph/prd.json +++ b/Ralph/prd.json @@ -153,7 +153,7 @@ "Verify in browser using dev-browser skill" ], "priority": 9, - "passes": false, + "passes": true, "notes": "Education is the bottom-most subsection in Patient Pathway. Include A-Levels: Mathematics (A*), Chemistry (B), Politics (C) — Highworth Grammar School, 2009–2011." }, { @@ -173,7 +173,7 @@ "Verify in browser using dev-browser skill" ], "priority": 10, - "passes": false, + "passes": true, "notes": "ProjectsTile may remain as a standalone tile or be absorbed — check if it still makes sense as standalone. If so, keep it. The key outcome is that the deleted tiles have no remaining references." }, { diff --git a/Ralph/progress.txt b/Ralph/progress.txt index 9dd1933..0f77ead 100644 --- a/Ralph/progress.txt +++ b/Ralph/progress.txt @@ -957,3 +957,29 @@ - CoreSkillsTile.tsx still exists but is no longer imported from DashboardLayout — US-010 will clean it up - The RepeatMedicationsSubsection is a near-copy of CoreSkillsTile internals minus the Card wrapper — both files exist until cleanup in US-010 --- + +## 2026-02-14 - US-009 +- Moved Education content from standalone EducationTile into Patient Pathway ParentSection as the bottom-most subsection +- Created `src/components/EducationSubsection.tsx` — standalone component with purple dot CardHeader "EDUCATION", renders 3 entries (Mary Seacole, MPharm, A-Levels) with hover, click-to-detail panel, inline details +- Deleted `src/components/tiles/EducationTile.tsx` — content fully moved to EducationSubsection +- Updated DashboardLayout: replaced EducationTile import/render with EducationSubsection inside Patient Pathway +- Files changed: src/components/DashboardLayout.tsx (modified), src/components/EducationSubsection.tsx (new), src/components/tiles/EducationTile.tsx (deleted) +- **Learnings for future iterations:** + - Git detected the move as a rename (91% similarity) — the EducationSubsection is nearly identical to EducationTile minus the Card wrapper + - Same pattern as WorkExperienceSubsection and RepeatMedicationsSubsection: standalone file, `marginTop: 24px` for spacing from preceding section, uses CardHeader for subsection header + - All Patient Pathway subsections now in place: CareerConstellation → LastConsultation → WorkExperience + RepeatMedications (two-column) → Education +--- + +## 2026-02-14 - US-010 +- Deleted 3 orphaned tile files: CareerActivityTile.tsx, CoreSkillsTile.tsx, LatestResultsTile.tsx (LastConsultationTile and EducationTile were already deleted in US-007/US-009) +- Removed unused `.activity-grid` CSS class from index.css (was only used by deleted CareerActivityTile) +- Updated 2 stale comments referencing deleted tiles (index.css, SkillsAllDetail.tsx) +- Verified no broken imports remain — grep found zero import references to any deleted tile +- Verified dashboard grid: PatientSummaryTile (full width) + ProjectsTile (half width) + Patient Pathway ParentSection (full width) +- Browser verification: no visual gaps, all content renders correctly +- Files changed: src/components/tiles/CareerActivityTile.tsx (deleted), src/components/tiles/CoreSkillsTile.tsx (deleted), src/components/tiles/LatestResultsTile.tsx (deleted), src/index.css, src/components/detail/SkillsAllDetail.tsx +- **Learnings for future iterations:** + - ProjectsTile remains as a standalone tile — it makes sense outside Patient Pathway since projects are cross-cutting, not career-timeline-specific + - The `.activity-grid` CSS was orphaned when CareerActivityTile was removed — always check for CSS classes that were only used by deleted components + - Only 2 tile files remain in src/components/tiles/: PatientSummaryTile.tsx and ProjectsTile.tsx +--- diff --git a/src/components/detail/SkillsAllDetail.tsx b/src/components/detail/SkillsAllDetail.tsx index 2d7c339..cdfd1ed 100644 --- a/src/components/detail/SkillsAllDetail.tsx +++ b/src/components/detail/SkillsAllDetail.tsx @@ -61,7 +61,7 @@ export function SkillsAllDetail({ category }: SkillsAllDetailProps) { key={group.id} ref={(el) => { categoryRefs.current[group.id] = el }} > - {/* Category header — matches CoreSkillsTile divider style */} + {/* Category header — divider style */}
d.id === 'doc-mpharm') - if (mpharm) { - entries.push({ - id: mpharm.id, - type: 'edu', - title: 'MPharm (Hons) — 2:1', - meta: 'University of East Anglia', - date: '2011 – 2015', - sortYear: 2011, - }) - } - - entries.push({ - id: 'edu-alevels', - type: 'edu', - title: 'A-Levels: Mathematics (A*), Chemistry (B), Politics (C)', - meta: 'Highworth Grammar School', - date: '2009 – 2011', - sortYear: 2009, - }) - - return entries.sort((a, b) => { - if (b.sortYear !== a.sortYear) return b.sortYear - a.sortYear - return 0 - }) -} - -const dotColorMap: Record = { - role: '#0D6E6E', - project: '#D97706', - cert: '#059669', - edu: '#7C3AED', -} - -interface ActivityItemProps { - entry: ActivityEntry - onItemClick: () => void -} - -const ActivityItem: React.FC = ({ entry, onItemClick }) => { - const [isHovered, setIsHovered] = useState(false) - const dotColor = dotColorMap[entry.type] - const isClickable = entry.type === 'role' && entry.consultationId - - const handleKeyDown = useCallback( - (e: React.KeyboardEvent) => { - if (!isClickable) return - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault() - onItemClick() - } - }, - [isClickable, onItemClick], - ) - - // Get consultation data for preview text - const consultation = isClickable - ? consultations.find((c) => c.id === entry.consultationId) - : null - - // Get preview text (first 1-2 lines from examination) - const previewText = - consultation && consultation.examination.length > 0 - ? consultation.examination[0] - : null - - return ( -
setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - style={{ - display: 'flex', - flexDirection: 'column', - background: 'var(--bg-dashboard)', - borderRadius: 'var(--radius-sm)', - border: '1px solid var(--border-light)', - fontSize: '12px', - transition: 'all 0.15s ease-out', - cursor: isClickable ? 'pointer' : 'default', - transform: isHovered && isClickable ? 'translateY(-1px)' : 'none', - boxShadow: isHovered && isClickable - ? '0 2px 8px rgba(26,43,42,0.08)' - : '0 1px 2px rgba(26,43,42,0.05)', - borderColor: isHovered && isClickable ? 'var(--accent-border)' : 'var(--border-light)', - }} - > - {/* Item header row */} -
- -
- ) -} - -export const CareerActivityTile: React.FC = () => { - const timeline = buildTimeline() - const { openPanel } = useDetailPanel() - - const handleRoleClick = useCallback( - (roleId: string) => { - const consultation = consultations.find((c) => c.id === roleId) - if (consultation) { - openPanel({ type: 'career-role', consultation }) - } - }, - [openPanel], - ) - - const handleSkillClick = useCallback( - (skillId: string) => { - const skill = skills.find((s) => s.id === skillId) - if (skill) { - openPanel({ type: 'skill', skill }) - } - }, - [openPanel], - ) - - const handleItemClick = useCallback( - (entry: ActivityEntry) => { - if (entry.type === 'role' && entry.consultationId) { - handleRoleClick(entry.consultationId) - } - }, - [handleRoleClick], - ) - - return ( - - - -
- -
- -
- {timeline.map((entry) => ( - handleItemClick(entry)} - /> - ))} -
-
- ) -} diff --git a/src/components/tiles/CoreSkillsTile.tsx b/src/components/tiles/CoreSkillsTile.tsx deleted file mode 100644 index 8c9b6c7..0000000 --- a/src/components/tiles/CoreSkillsTile.tsx +++ /dev/null @@ -1,304 +0,0 @@ -import React from 'react' -import type { LucideIcon } from 'lucide-react' -import { - BarChart3, Code2, Database, PieChart, FileCode2, - Sheet, GitBranch, Workflow, Pill, Users, FileCheck, - TrendingUp, Route, ShieldAlert, Banknote, Handshake, - MessageSquare, UserPlus, RefreshCw, Calculator, Presentation, - ChevronRight, -} from 'lucide-react' -import { Card, CardHeader } from '../Card' -import { skills } from '@/data/skills' -import { useDetailPanel } from '@/contexts/DetailPanelContext' -import type { SkillMedication, SkillCategory } from '@/types/pmr' - -const iconMap: Record = { - BarChart3, - Code2, - Database, - PieChart, - FileCode2, - Sheet, - GitBranch, - Workflow, - Pill, - Users, - FileCheck, - TrendingUp, - Route, - ShieldAlert, - Banknote, - Handshake, - MessageSquare, - UserPlus, - RefreshCw, - Calculator, - Presentation, -} - -const SKILLS_PER_CATEGORY = 4 - -const categoryConfig: { id: SkillCategory; label: string }[] = [ - { id: 'Technical', label: 'Technical' }, - { id: 'Domain', label: 'Healthcare Domain' }, - { id: 'Leadership', label: 'Strategic & Leadership' }, -] - -interface SkillRowProps { - skill: SkillMedication - onClick: () => void -} - -function SkillRow({ skill, onClick }: SkillRowProps) { - const IconComponent = iconMap[skill.icon] - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault() - onClick() - } - } - - return ( -
{ - e.currentTarget.style.borderColor = 'var(--accent-border)' - e.currentTarget.style.boxShadow = 'var(--shadow-md)' - }} - onMouseLeave={(e) => { - e.currentTarget.style.borderColor = 'var(--border-light)' - e.currentTarget.style.boxShadow = 'none' - }} - > - {/* Icon */} -
- {IconComponent && } -
- - {/* Text */} -
-
- {skill.name} -
-
- {skill.frequency} · {skill.yearsOfExperience} yrs -
-
- - {/* Status badge */} -
- {skill.status} -
- - {/* Chevron */} - -
- ) -} - -interface CategorySectionProps { - label: string - categoryId: SkillCategory - skills: SkillMedication[] - onSkillClick: (skill: SkillMedication) => void - onViewAll: (category: SkillCategory) => void - isFirst: boolean -} - -function CategorySection({ - label, - categoryId, - skills: categorySkills, - onSkillClick, - onViewAll, - isFirst, -}: CategorySectionProps) { - const visibleSkills = categorySkills.slice(0, SKILLS_PER_CATEGORY) - const remainingCount = categorySkills.length - SKILLS_PER_CATEGORY - - return ( -
- {/* Category header — sidebar section divider style */} -
- - {label} - -
- - {categorySkills.length} items - -
- - {/* Skill rows */} -
- {visibleSkills.map((skill) => ( - onSkillClick(skill)} - /> - ))} -
- - {/* View all button */} - {remainingCount > 0 && ( - - )} -
- ) -} - -export function CoreSkillsTile() { - const { openPanel } = useDetailPanel() - - // Group skills by category, sorted by proficiency descending - const groupedSkills = categoryConfig.map(({ id, label }) => ({ - id, - label, - skills: skills - .filter((s) => s.category === id) - .sort((a, b) => b.proficiency - a.proficiency), - })) - - const handleSkillClick = (skill: SkillMedication) => { - openPanel({ type: 'skill', skill }) - } - - const handleViewAll = (category: SkillCategory) => { - openPanel({ type: 'skills-all', category }) - } - - return ( - - - {groupedSkills.map((group, index) => ( - - ))} - - ) -} diff --git a/src/components/tiles/LatestResultsTile.tsx b/src/components/tiles/LatestResultsTile.tsx deleted file mode 100644 index b0ddeb2..0000000 --- a/src/components/tiles/LatestResultsTile.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React from 'react' -import { Card, CardHeader } from '../Card' -import { kpis } from '@/data/kpis' -import type { KPI } from '@/types/pmr' -import { useDetailPanel } from '@/contexts/DetailPanelContext' - -const colorMap: Record = { - green: '#059669', - amber: '#D97706', - teal: '#0D6E6E', -} - -interface MetricCardProps { - kpi: KPI -} - -function MetricCard({ kpi }: MetricCardProps) { - const { openPanel } = useDetailPanel() - - const handleClick = () => { - openPanel({ type: 'kpi', kpi }) - } - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault() - openPanel({ type: 'kpi', kpi }) - } - } - - const buttonStyles: React.CSSProperties = { - width: '100%', - textAlign: 'left', - padding: '16px', - background: 'var(--surface)', - border: '1px solid var(--border-light)', - borderRadius: 'var(--radius-sm)', - cursor: 'pointer', - transition: 'border-color 150ms ease-out, box-shadow 150ms ease-out', - } - - const valueStyles: React.CSSProperties = { - fontSize: '28px', - fontWeight: 700, - letterSpacing: '-0.02em', - lineHeight: 1.2, - color: colorMap[kpi.colorVariant], - } - - const labelStyles: React.CSSProperties = { - fontSize: '12px', - fontWeight: 500, - color: 'var(--text-primary)', - marginTop: '4px', - } - - const subStyles: React.CSSProperties = { - fontSize: '10px', - color: 'var(--text-tertiary)', - fontFamily: 'var(--font-geist-mono)', - marginTop: '2px', - } - - return ( - - ) -} - -export function LatestResultsTile() { - const gridStyles: React.CSSProperties = { - display: 'grid', - gridTemplateColumns: '1fr 1fr', - gap: '12px', - } - - return ( - - -
- {kpis.map((kpi) => ( - - ))} -
-
- ) -} diff --git a/src/index.css b/src/index.css index adb7806..89f1b2e 100644 --- a/src/index.css +++ b/src/index.css @@ -286,20 +286,6 @@ html { } -/* Activity grid responsive — mobile-first (used in CareerActivityTile) */ -.activity-grid { - display: grid; - grid-template-columns: 1fr; - gap: 10px; -} - -/* Tablet and up: 2 columns */ -@media (min-width: 768px) { - .activity-grid { - grid-template-columns: repeat(2, 1fr); - } -} - /* Pathway two-column layout — mobile-first (used in Patient Pathway) */ .pathway-columns { display: grid;