Refactor to pull all text enteries into single location
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
import { CardHeader } from './Card'
|
||||
import { skills } from '@/data/skills'
|
||||
import { useDetailPanel } from '@/contexts/DetailPanelContext'
|
||||
import { getSkillsUICopy } from '@/lib/profile-content'
|
||||
import type { SkillMedication, SkillCategory } from '@/types/pmr'
|
||||
|
||||
const iconMap: Record<string, LucideIcon> = {
|
||||
@@ -21,19 +22,14 @@ const iconMap: Record<string, LucideIcon> = {
|
||||
|
||||
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
|
||||
yearsSuffix: string
|
||||
onClick: () => void
|
||||
onHighlight?: (id: string | null) => void
|
||||
}
|
||||
|
||||
function SkillRow({ skill, onClick, onHighlight }: SkillRowProps) {
|
||||
function SkillRow({ skill, yearsSuffix, onClick, onHighlight }: SkillRowProps) {
|
||||
const IconComponent = iconMap[skill.icon]
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
@@ -106,7 +102,7 @@ function SkillRow({ skill, onClick, onHighlight }: SkillRowProps) {
|
||||
fontFamily: '"Geist Mono", monospace',
|
||||
}}
|
||||
>
|
||||
{skill.frequency} · {skill.yearsOfExperience} yrs
|
||||
{skill.frequency} · {skill.yearsOfExperience} {yearsSuffix}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -135,6 +131,9 @@ interface CategorySectionProps {
|
||||
label: string
|
||||
categoryId: SkillCategory
|
||||
skills: SkillMedication[]
|
||||
itemCountSuffix: string
|
||||
yearsSuffix: string
|
||||
viewAllLabel: string
|
||||
onSkillClick: (skill: SkillMedication) => void
|
||||
onViewAll: (category: SkillCategory) => void
|
||||
isFirst: boolean
|
||||
@@ -145,6 +144,9 @@ function CategorySection({
|
||||
label,
|
||||
categoryId,
|
||||
skills: categorySkills,
|
||||
itemCountSuffix,
|
||||
yearsSuffix,
|
||||
viewAllLabel,
|
||||
onSkillClick,
|
||||
onViewAll,
|
||||
isFirst,
|
||||
@@ -190,7 +192,7 @@ function CategorySection({
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{categorySkills.length} items
|
||||
{categorySkills.length} {itemCountSuffix}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
@@ -198,6 +200,7 @@ function CategorySection({
|
||||
<SkillRow
|
||||
key={skill.id}
|
||||
skill={skill}
|
||||
yearsSuffix={yearsSuffix}
|
||||
onClick={() => onSkillClick(skill)}
|
||||
onHighlight={onNodeHighlight}
|
||||
/>
|
||||
@@ -228,9 +231,9 @@ function CategorySection({
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = 'var(--accent)'
|
||||
}}
|
||||
aria-label={`View all ${categorySkills.length} ${label} skills`}
|
||||
aria-label={`${viewAllLabel} ${categorySkills.length} ${label} skills`}
|
||||
>
|
||||
View all ({categorySkills.length})
|
||||
{viewAllLabel} ({categorySkills.length})
|
||||
<ChevronRight size={12} />
|
||||
</button>
|
||||
)}
|
||||
@@ -244,8 +247,9 @@ interface RepeatMedicationsSubsectionProps {
|
||||
|
||||
export function RepeatMedicationsSubsection({ onNodeHighlight }: RepeatMedicationsSubsectionProps) {
|
||||
const { openPanel } = useDetailPanel()
|
||||
const skillsCopy = getSkillsUICopy()
|
||||
|
||||
const groupedSkills = categoryConfig.map(({ id, label }) => ({
|
||||
const groupedSkills = skillsCopy.categories.map(({ id, label }) => ({
|
||||
id,
|
||||
label,
|
||||
skills: skills
|
||||
@@ -265,8 +269,8 @@ export function RepeatMedicationsSubsection({ onNodeHighlight }: RepeatMedicatio
|
||||
<div>
|
||||
<CardHeader
|
||||
dotColor="amber"
|
||||
title="REPEAT MEDICATIONS"
|
||||
rightText="Active prescriptions"
|
||||
title={skillsCopy.sectionTitle}
|
||||
rightText={skillsCopy.rightText}
|
||||
/>
|
||||
<div className="medications-grid">
|
||||
{groupedSkills.map((group) => (
|
||||
@@ -275,6 +279,9 @@ export function RepeatMedicationsSubsection({ onNodeHighlight }: RepeatMedicatio
|
||||
label={group.label}
|
||||
categoryId={group.id}
|
||||
skills={group.skills}
|
||||
itemCountSuffix={skillsCopy.itemCountSuffix}
|
||||
yearsSuffix={skillsCopy.yearsSuffix}
|
||||
viewAllLabel={skillsCopy.viewAllLabel}
|
||||
onSkillClick={handleSkillClick}
|
||||
onViewAll={handleViewAll}
|
||||
isFirst
|
||||
|
||||
+17
-15
@@ -17,6 +17,7 @@ import cvmisLogo from '../../cvmis-logo.svg'
|
||||
import { patient } from '@/data/patient'
|
||||
import { tags } from '@/data/tags'
|
||||
import { alerts } from '@/data/alerts'
|
||||
import { getSidebarCopy } from '@/lib/profile-content'
|
||||
import type { Tag, Alert } from '@/types/pmr'
|
||||
|
||||
interface SidebarProps {
|
||||
@@ -163,6 +164,7 @@ function AlertFlag({ alert }: AlertFlagProps) {
|
||||
}
|
||||
|
||||
export default function Sidebar({ activeSection, onNavigate, onSearchClick }: SidebarProps) {
|
||||
const sidebarCopy = getSidebarCopy()
|
||||
const [isDesktop, setIsDesktop] = useState(() => window.matchMedia('(min-width: 1024px)').matches)
|
||||
const [isMobileExpanded, setIsMobileExpanded] = useState(false)
|
||||
|
||||
@@ -257,7 +259,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
{isExpanded && <span style={{ fontSize: '12px', fontWeight: 600 }}>Menu</span>}
|
||||
{isExpanded && <span style={{ fontSize: '12px', fontWeight: 600 }}>{sidebarCopy.menuLabel}</span>}
|
||||
{isExpanded ? <X size={17} strokeWidth={2.4} /> : <Menu size={18} strokeWidth={2.4} />}
|
||||
</button>
|
||||
)}
|
||||
@@ -287,7 +289,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
type="button"
|
||||
onClick={onSearchClick}
|
||||
className="sidebar-control"
|
||||
aria-label="Search. Press Control plus K"
|
||||
aria-label={sidebarCopy.searchAriaLabel}
|
||||
style={{
|
||||
width: '100%',
|
||||
minHeight: '44px',
|
||||
@@ -305,7 +307,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
>
|
||||
<Search size={16} style={{ color: 'var(--text-tertiary)', flexShrink: 0 }} aria-hidden="true" />
|
||||
<span style={{ flex: 1, textAlign: 'left', fontSize: '13px' }}>
|
||||
Search
|
||||
{sidebarCopy.searchLabel}
|
||||
</span>
|
||||
<kbd
|
||||
style={{
|
||||
@@ -318,7 +320,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
Ctrl+K
|
||||
{sidebarCopy.searchShortcut}
|
||||
</kbd>
|
||||
</button>
|
||||
|
||||
@@ -326,7 +328,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
|
||||
|
||||
|
||||
<SectionTitle>Patient Data</SectionTitle>
|
||||
<SectionTitle>{sidebarCopy.sectionTitle}</SectionTitle>
|
||||
|
||||
<div
|
||||
style={{
|
||||
@@ -367,7 +369,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
marginTop: '2px',
|
||||
}}
|
||||
>
|
||||
Pharmacy Data Technologist
|
||||
{sidebarCopy.roleTitle}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -387,7 +389,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
padding: '4px 0',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>GPhC No.</span>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>{sidebarCopy.gphcLabel}</span>
|
||||
<span
|
||||
style={{
|
||||
color: 'var(--text-primary)',
|
||||
@@ -410,7 +412,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
padding: '4px 0',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>Education</span>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>{sidebarCopy.educationLabel}</span>
|
||||
<span style={{ color: 'var(--text-primary)', fontWeight: 500, textAlign: 'right' }}>
|
||||
{patient.qualification}
|
||||
</span>
|
||||
@@ -425,7 +427,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
padding: '4px 0',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>Location</span>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>{sidebarCopy.locationLabel}</span>
|
||||
<span style={{ color: 'var(--text-primary)', fontWeight: 500, textAlign: 'right' }}>
|
||||
{patient.address}
|
||||
</span>
|
||||
@@ -440,7 +442,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
padding: '4px 0',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>Phone</span>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>{sidebarCopy.phoneLabel}</span>
|
||||
<a
|
||||
href={`tel:${patient.phone}`}
|
||||
style={{
|
||||
@@ -465,7 +467,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
padding: '4px 0',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>Email</span>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>{sidebarCopy.emailLabel}</span>
|
||||
<a
|
||||
href={`mailto:${patient.email}`}
|
||||
style={{
|
||||
@@ -490,7 +492,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
padding: '4px 0',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>Registered</span>
|
||||
<span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>{sidebarCopy.registeredLabel}</span>
|
||||
<span style={{ color: 'var(--text-primary)', fontWeight: 500, textAlign: 'right' }}>
|
||||
{patient.registrationYear}
|
||||
</span>
|
||||
@@ -500,7 +502,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
)}
|
||||
|
||||
<section>
|
||||
{isExpanded && <SectionTitle>Navigation</SectionTitle>}
|
||||
{isExpanded && <SectionTitle>{sidebarCopy.navigationTitle}</SectionTitle>}
|
||||
<nav aria-label="Sidebar navigation" style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
|
||||
{navSections.map((section) => {
|
||||
const isActive = activeSection === section.id
|
||||
@@ -546,7 +548,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
{isExpanded && (
|
||||
<>
|
||||
<section style={{ paddingTop: '8px' }}>
|
||||
<SectionTitle>Tags</SectionTitle>
|
||||
<SectionTitle>{sidebarCopy.tagsTitle}</SectionTitle>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '5px' }}>
|
||||
{tags.map((tag) => (
|
||||
<TagPill key={tag.label} tag={tag} />
|
||||
@@ -555,7 +557,7 @@ export default function Sidebar({ activeSection, onNavigate, onSearchClick }: Si
|
||||
</section>
|
||||
|
||||
<section style={{ padding: '8px 0 4px' }}>
|
||||
<SectionTitle>Alerts / Highlights</SectionTitle>
|
||||
<SectionTitle>{sidebarCopy.alertsTitle}</SectionTitle>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
|
||||
{alerts.map((alert, index) => (
|
||||
<AlertFlag key={index} alert={alert} />
|
||||
|
||||
@@ -3,6 +3,7 @@ import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import { useDetailPanel } from '@/contexts/DetailPanelContext'
|
||||
import { timelineEntities, timelineConsultations } from '@/data/timeline'
|
||||
import { getExperienceEducationUICopy } from '@/lib/profile-content'
|
||||
import type { TimelineEntity } from '@/types/pmr'
|
||||
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
@@ -33,8 +34,9 @@ function TimelineInterventionItem({
|
||||
onViewFull,
|
||||
onHighlight,
|
||||
}: TimelineInterventionItemProps) {
|
||||
const experienceEducationCopy = getExperienceEducationUICopy()
|
||||
const isEducation = entity.kind === 'education'
|
||||
const interventionLabel = isEducation ? 'Education' : 'Employment'
|
||||
const interventionLabel = isEducation ? experienceEducationCopy.educationLabel : experienceEducationCopy.employmentLabel
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
@@ -284,7 +286,7 @@ function TimelineInterventionItem({
|
||||
e.currentTarget.style.opacity = '1'
|
||||
}}
|
||||
>
|
||||
View full record
|
||||
{experienceEducationCopy.viewFullRecordLabel}
|
||||
<ChevronRight size={12} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useCallback } from 'react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import { CardHeader } from './Card'
|
||||
import { consultations } from '@/data/consultations'
|
||||
import { timelineConsultations } from '@/data/timeline'
|
||||
import { useDetailPanel } from '@/contexts/DetailPanelContext'
|
||||
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
@@ -15,7 +15,7 @@ function hexToRgba(hex: string, opacity: number): string {
|
||||
}
|
||||
|
||||
interface RoleItemProps {
|
||||
consultation: typeof consultations[0]
|
||||
consultation: typeof timelineConsultations[0]
|
||||
isExpanded: boolean
|
||||
isHighlightedFromGraph: boolean
|
||||
onToggle: () => void
|
||||
@@ -279,7 +279,7 @@ export function WorkExperienceSubsection({ onNodeHighlight, highlightedRoleId }:
|
||||
}, [])
|
||||
|
||||
const handleViewFull = useCallback(
|
||||
(consultation: typeof consultations[0]) => {
|
||||
(consultation: typeof timelineConsultations[0]) => {
|
||||
openPanel({ type: 'career-role', consultation })
|
||||
},
|
||||
[openPanel],
|
||||
@@ -287,9 +287,9 @@ export function WorkExperienceSubsection({ onNodeHighlight, highlightedRoleId }:
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CardHeader dotColor="teal" title="WORK EXPERIENCE" rightText={`${consultations.length} roles`} />
|
||||
<CardHeader dotColor="teal" title="WORK EXPERIENCE" rightText={`${timelineConsultations.length} roles`} />
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
|
||||
{consultations.map((c) => (
|
||||
{timelineConsultations.map((c) => (
|
||||
<RoleItem
|
||||
key={c.id}
|
||||
consultation={c}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from 'lucide-react'
|
||||
import { skills } from '@/data/skills'
|
||||
import { useDetailPanel } from '@/contexts/DetailPanelContext'
|
||||
import { getSkillsUICopy } from '@/lib/profile-content'
|
||||
import type { SkillMedication, SkillCategory } from '@/types/pmr'
|
||||
|
||||
const iconMap: Record<string, LucideIcon> = {
|
||||
@@ -18,12 +19,6 @@ const iconMap: Record<string, LucideIcon> = {
|
||||
MessageSquare, UserPlus, RefreshCw, Calculator, Presentation,
|
||||
}
|
||||
|
||||
const categoryConfig: { id: SkillCategory; label: string }[] = [
|
||||
{ id: 'Technical', label: 'Technical' },
|
||||
{ id: 'Domain', label: 'Healthcare Domain' },
|
||||
{ id: 'Leadership', label: 'Strategic & Leadership' },
|
||||
]
|
||||
|
||||
interface SkillsAllDetailProps {
|
||||
category?: SkillCategory
|
||||
}
|
||||
@@ -31,6 +26,7 @@ interface SkillsAllDetailProps {
|
||||
export function SkillsAllDetail({ category }: SkillsAllDetailProps) {
|
||||
const { openPanel } = useDetailPanel()
|
||||
const categoryRefs = useRef<Record<string, HTMLDivElement | null>>({})
|
||||
const skillsCopy = getSkillsUICopy()
|
||||
|
||||
// Scroll to highlighted category on mount
|
||||
useEffect(() => {
|
||||
@@ -39,7 +35,7 @@ export function SkillsAllDetail({ category }: SkillsAllDetailProps) {
|
||||
}
|
||||
}, [category])
|
||||
|
||||
const groupedSkills = categoryConfig.map(({ id, label }) => ({
|
||||
const groupedSkills = skillsCopy.categories.map(({ id, label }) => ({
|
||||
id,
|
||||
label,
|
||||
skills: skills
|
||||
@@ -91,17 +87,17 @@ export function SkillsAllDetail({ category }: SkillsAllDetailProps) {
|
||||
background: 'var(--border-light)',
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '10px',
|
||||
color: 'var(--text-tertiary)',
|
||||
fontFamily: '"Geist Mono", monospace',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{group.skills.length} items
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '10px',
|
||||
color: 'var(--text-tertiary)',
|
||||
fontFamily: '"Geist Mono", monospace',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{group.skills.length} {skillsCopy.itemCountSuffix}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Skill rows */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
|
||||
@@ -109,6 +105,7 @@ export function SkillsAllDetail({ category }: SkillsAllDetailProps) {
|
||||
<SkillRow
|
||||
key={skill.id}
|
||||
skill={skill}
|
||||
yearsSuffix={skillsCopy.yearsSuffix}
|
||||
onClick={() => handleSkillClick(skill)}
|
||||
/>
|
||||
))}
|
||||
@@ -122,10 +119,11 @@ export function SkillsAllDetail({ category }: SkillsAllDetailProps) {
|
||||
|
||||
interface SkillRowProps {
|
||||
skill: SkillMedication
|
||||
yearsSuffix: string
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
function SkillRow({ skill, onClick }: SkillRowProps) {
|
||||
function SkillRow({ skill, yearsSuffix, onClick }: SkillRowProps) {
|
||||
const IconComponent = iconMap[skill.icon]
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
@@ -198,7 +196,7 @@ function SkillRow({ skill, onClick }: SkillRowProps) {
|
||||
fontFamily: '"Geist Mono", monospace',
|
||||
}}
|
||||
>
|
||||
{skill.frequency} · {skill.yearsOfExperience} yrs
|
||||
{skill.frequency} · {skill.yearsOfExperience} {yearsSuffix}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ParentSection } from '../ParentSection'
|
||||
import { kpis } from '@/data/kpis'
|
||||
import type { KPI } from '@/types/pmr'
|
||||
import { useDetailPanel } from '@/contexts/DetailPanelContext'
|
||||
import { getLatestResultsCopy, getProfileSectionTitle, getProfileSummaryText } from '@/lib/profile-content'
|
||||
|
||||
const colorMap: Record<KPI['colorVariant'], string> = {
|
||||
green: '#059669',
|
||||
@@ -18,6 +19,7 @@ interface MetricCardProps {
|
||||
|
||||
function MetricCard({ kpi }: MetricCardProps) {
|
||||
const { openPanel } = useDetailPanel()
|
||||
const latestResultsCopy = getLatestResultsCopy()
|
||||
|
||||
const handleClick = () => {
|
||||
openPanel({ type: 'kpi', kpi })
|
||||
@@ -102,7 +104,7 @@ function MetricCard({ kpi }: MetricCardProps) {
|
||||
fontFamily: 'var(--font-geist-mono)',
|
||||
}}
|
||||
>
|
||||
Click to view evidence
|
||||
{latestResultsCopy.evidenceCta}
|
||||
<ChevronRight size={12} />
|
||||
</div>
|
||||
</button>
|
||||
@@ -110,6 +112,10 @@ function MetricCard({ kpi }: MetricCardProps) {
|
||||
}
|
||||
|
||||
export function PatientSummaryTile() {
|
||||
const summaryText = getProfileSummaryText()
|
||||
const latestResultsCopy = getLatestResultsCopy()
|
||||
const sectionTitle = getProfileSectionTitle()
|
||||
|
||||
const profileTextStyles: React.CSSProperties = {
|
||||
fontSize: '15px',
|
||||
lineHeight: '1.65',
|
||||
@@ -123,22 +129,14 @@ export function PatientSummaryTile() {
|
||||
}
|
||||
|
||||
return (
|
||||
<ParentSection title="Patient Summary" tileId="patient-summary">
|
||||
<ParentSection title={sectionTitle} tileId="patient-summary">
|
||||
{/* Profile text */}
|
||||
<div style={profileTextStyles}>
|
||||
<strong>Healthcare leader</strong> combining clinical pharmacy expertise with proficiency in{' '}
|
||||
<strong>Python, SQL, and data analytics</strong>, self-taught over the past decade through a drive to find root causes in data and build the most efficient solutions to complex problems. Currently{' '}
|
||||
<strong>leading population health analytics for NHS Norfolk & Waveney ICB</strong>, serving a population of 1.2 million. Experienced in working with messy, real-world prescribing data at scale to deliver actionable insights—from{' '}
|
||||
<strong>financial scenario modelling</strong> and <strong>pharmaceutical rebate negotiation</strong> to{' '}
|
||||
<strong>algorithm design</strong> and <strong>population-level pathway development</strong>. Proven track record of identifying and prioritising efficiency programmes worth{' '}
|
||||
<strong>£14.6M+</strong> through automated, data-driven analysis. Skilled at translating complex clinical, financial, and analytical requirements into clear recommendations for{' '}
|
||||
<strong>executive stakeholders</strong>.
|
||||
</div>
|
||||
<div style={profileTextStyles}>{summaryText}</div>
|
||||
|
||||
{/* Latest Results subsection */}
|
||||
<div style={{ marginTop: '28px' }}>
|
||||
<div className="latest-results-header">
|
||||
<CardHeader dotColor="teal" title="LATEST RESULTS (CLICK TO VIEW FULL REFERENCE RANGE)" rightText="Updated May 2025" />
|
||||
<CardHeader dotColor="teal" title={latestResultsCopy.title} rightText={latestResultsCopy.rightText} />
|
||||
<p
|
||||
style={{
|
||||
margin: 0,
|
||||
@@ -147,7 +145,7 @@ export function PatientSummaryTile() {
|
||||
fontFamily: 'var(--font-geist-mono)',
|
||||
}}
|
||||
>
|
||||
Select a metric to inspect methodology, impact, and outcomes.
|
||||
{latestResultsCopy.helperText}
|
||||
</p>
|
||||
</div>
|
||||
<div className="latest-results-grid" style={kpiGridStyles}>
|
||||
|
||||
Reference in New Issue
Block a user