From 6bd12dd776ffed4dc2145f5333bb36cc99137951 Mon Sep 17 00:00:00 2001 From: Andy Charlwood Date: Sat, 14 Feb 2026 18:32:36 +0000 Subject: [PATCH] feat: US-013 - Update command palette data for restructured dashboard --- Ralph/prd.json | 2 +- Ralph/progress.txt | 14 ++++ src/lib/search.ts | 168 ++++++++------------------------------------- 3 files changed, 44 insertions(+), 140 deletions(-) diff --git a/Ralph/prd.json b/Ralph/prd.json index 4fab94a..f8e4991 100644 --- a/Ralph/prd.json +++ b/Ralph/prd.json @@ -231,7 +231,7 @@ "Typecheck passes" ], "priority": 13, - "passes": false, + "passes": true, "notes": "The palette data model uses tileId for scroll targeting — these need to match the new data-tile-id attributes on ParentSection components." }, { diff --git a/Ralph/progress.txt b/Ralph/progress.txt index 75d04b8..646abbd 100644 --- a/Ralph/progress.txt +++ b/Ralph/progress.txt @@ -1015,3 +1015,17 @@ - External highlight useEffect must reset styles when highlightedNodeId becomes null — use the same baseline values as the hover mouseleave handler - The onNodeHighlight callbacks integrate cleanly with existing mouseenter/mouseleave handlers in SkillRow (just append the highlight call) --- + +## 2026-02-14 - US-013 +- Updated command palette data to reflect restructured dashboard with accurate entries +- **Experience**: Replaced 4 hardcoded inaccurate role entries with dynamic iteration over all consultations from consultations.ts. Changed action from `expand` (targeting deleted career-activity tile) to `panel` (opening career-role detail panel directly) +- **Projects**: Replaced 3 hardcoded fake entries (£220M Budget, SQL Analytics Transformation, Team Data Literacy) with dynamic iteration over all 5 investigations from investigations.ts. Subtitle uses first sentence of methodology + year +- **Education**: Removed Power BI Data Analyst Associate cert and Clinical Pharmacy Diploma (not in CV). Added Mary Seacole Programme and A-Levels. Updated tileId from `education` to `patient-pathway` +- **Achievements**: Updated fallback tileId from `latest-results` to `patient-summary` (LatestResults is now a subsection within PatientSummary) +- **Legacy exports**: Removed `SearchResult` interface, `buildLegacySearchIndex()`, and `groupResultsBySection()` — confirmed no imports exist anywhere in the codebase. Also removed unused imports (medications, problems, documents, ViewId, FuseResult) +- Files changed: src/lib/search.ts, Ralph/prd.json, Ralph/progress.txt +- **Learnings for future iterations:** + - Investigation type uses `requestedYear` not `year` — always check the type interface before referencing properties + - Dynamically iterating over data arrays (consultations, investigations, skills) is more maintainable than hardcoded entries — data changes propagate automatically + - The `expand` palette action type is still in the union but no longer used by any items — keeping the handler in DashboardLayout is harmless +--- diff --git a/src/lib/search.ts b/src/lib/search.ts index 9e92573..b8251ee 100644 --- a/src/lib/search.ts +++ b/src/lib/search.ts @@ -1,11 +1,7 @@ -import Fuse, { type FuseResult } from 'fuse.js' -import type { ViewId } from '@/types/pmr' +import Fuse from 'fuse.js' import { consultations } from '@/data/consultations' -import { medications } from '@/data/medications' -import { problems } from '@/data/problems' import { investigations } from '@/data/investigations' -import { documents } from '@/data/documents' import { skills } from '@/data/skills' import { kpis } from '@/data/kpis' import type { DetailPanelContent } from '@/types/pmr' @@ -36,44 +32,17 @@ export interface PaletteItem { export function buildPaletteData(): PaletteItem[] { const items: PaletteItem[] = [] - // Experience — matching concept HTML entries - const experienceEntries: Array<{ title: string; sub: string; keywords: string; activityId: string }> = [ - { - title: 'Interim Head, Population Health & Data Analysis', - sub: 'NHS Norfolk & Waveney ICB \u00b7 2024\u20132025', - keywords: 'head interim population health data analysis nhs norfolk waveney icb 2024 2025 latest current', - activityId: 'interim-head', - }, - { - title: 'Senior Data Analyst \u2014 Medicines Optimisation', - sub: 'NHS Norfolk & Waveney ICB \u00b7 2021\u20132024', - keywords: 'senior data analyst medicines optimisation nhs norfolk waveney icb 2021 2024', - activityId: 'senior-analyst', - }, - { - title: 'Prescribing Data Pharmacist', - sub: 'NHS Norwich CCG \u00b7 2018\u20132021', - keywords: 'prescribing data pharmacist nhs norwich ccg 2018 2021', - activityId: 'prescribing-pharmacist', - }, - { - title: 'Community Pharmacist', - sub: 'Boots UK \u00b7 2016\u20132018', - keywords: 'community pharmacist boots uk 2016 2018', - activityId: 'community-pharmacist', - }, - ] - - experienceEntries.forEach((entry, i) => { + // Experience — all 4 roles from consultations.ts, open detail panel on select + consultations.forEach((c) => { items.push({ - id: `exp-${i}`, - title: entry.title, - subtitle: entry.sub, + id: `exp-${c.id}`, + title: c.role, + subtitle: `${c.organization} \u00b7 ${c.duration}`, section: 'Experience', iconVariant: 'teal', iconType: 'role', - keywords: entry.keywords, - action: { type: 'expand', tileId: 'career-activity', itemId: entry.activityId }, + keywords: `${c.role.toLowerCase()} ${c.organization.toLowerCase()} ${c.duration.toLowerCase()}`, + action: { type: 'panel', panelContent: { type: 'career-role', consultation: c } }, }) }) @@ -91,41 +60,17 @@ export function buildPaletteData(): PaletteItem[] { }) }) - // Active Projects — matching concept HTML entries - const projectEntries: Array<{ name: string; sub: string; keywords: string; investigationId: string }> = [ - { - name: '\u00a3220M Prescribing Budget', - sub: 'Budget oversight & analytical accountability \u00b7 2024', - keywords: '220m prescribing budget oversight analytical accountability 2024', - investigationId: 'inv-pharmetrics', - }, - { - name: 'SQL Analytics Transformation', - sub: 'Legacy migration to modern data stack \u00b7 2025', - keywords: 'sql analytics transformation legacy migration modern data stack 2025', - investigationId: 'inv-switching-algorithm', - }, - { - name: 'Team Data Literacy Programme', - sub: 'Upskilling 30+ non-technical staff \u00b7 2024', - keywords: 'team data literacy programme upskilling non-technical staff 2024 training', - investigationId: 'inv-blueteq-gen', - }, - ] - - projectEntries.forEach((entry) => { - const investigation = investigations.find(inv => inv.id === entry.investigationId) + // Active Projects — all 5 investigations from investigations.ts + investigations.forEach((inv) => { items.push({ - id: `proj-${entry.investigationId}`, - title: entry.name, - subtitle: entry.sub, + id: `proj-${inv.id}`, + title: inv.name, + subtitle: `${inv.methodology.split('.')[0]} \u00b7 ${inv.requestedYear}`, section: 'Active Projects', iconVariant: 'amber', iconType: 'project', - keywords: entry.keywords, - action: investigation - ? { type: 'panel', panelContent: { type: 'project', investigation } } - : { type: 'scroll', tileId: 'projects' }, + keywords: `${inv.name.toLowerCase()} ${inv.methodology.toLowerCase()} ${inv.techStack.join(' ').toLowerCase()} ${inv.requestedYear}`, + action: { type: 'panel', panelContent: { type: 'project', investigation: inv } }, }) }) @@ -169,31 +114,31 @@ export function buildPaletteData(): PaletteItem[] { keywords: entry.keywords, action: kpi ? { type: 'panel', panelContent: { type: 'kpi', kpi } } - : { type: 'scroll', tileId: 'latest-results' }, + : { type: 'scroll', tileId: 'patient-summary' }, }) }) - // Education — matching concept HTML entries + // Education — matching actual entries in EducationSubsection const educationEntries: Array<{ title: string; sub: string; keywords: string }> = [ + { + title: 'NHS Leadership Academy \u2014 Mary Seacole Programme', + sub: 'NHS Leadership Academy \u00b7 2018', + keywords: 'nhs leadership academy mary seacole programme 2018 qualification management', + }, { title: 'MPharm (Hons) \u2014 2:1', sub: 'University of East Anglia \u00b7 2011\u20132015', keywords: 'mpharm hons 2:1 university east anglia uea 2011 2015 pharmacy degree', }, + { + title: 'A-Levels', + sub: 'Highworth Grammar School \u00b7 2009\u20132011', + keywords: 'a-levels mathematics chemistry politics highworth grammar school 2009 2011', + }, { title: 'GPhC Registration', sub: 'General Pharmaceutical Council \u00b7 August 2016', - keywords: 'gphc registration general pharmaceutical council 2016 registered', - }, - { - title: 'Power BI Data Analyst Associate', - sub: 'Microsoft Certified \u00b7 2023', - keywords: 'power bi data analyst associate microsoft certified 2023 certification', - }, - { - title: 'Clinical Pharmacy Diploma', - sub: 'Professional development \u00b7 2019', - keywords: 'clinical pharmacy diploma professional development 2019', + keywords: 'gphc registration general pharmaceutical council 2016 registered pharmacist', }, ] @@ -206,7 +151,7 @@ export function buildPaletteData(): PaletteItem[] { iconVariant: 'purple', iconType: 'edu', keywords: entry.keywords, - action: { type: 'scroll', tileId: 'education' }, + action: { type: 'scroll', tileId: 'patient-pathway' }, }) }) @@ -296,58 +241,3 @@ export function groupBySection(items: PaletteItem[]): Array<{ section: PaletteSe .map(section => ({ section, items: groups.get(section)! })) } -// ===== LEGACY EXPORTS ===== -// Used by ClinicalSidebar.tsx (old component, will be removed in Task 21) - -export interface SearchResult { - id: string - title: string - section: ViewId - sectionLabel: string - highlight: string - score?: number -} - -/** @deprecated Use buildPaletteData() + buildSearchIndex() instead */ -export function buildLegacySearchIndex(): Fuse { - const searchableItems: SearchResult[] = [] - - consultations.forEach(c => { - searchableItems.push({ id: c.id, title: c.role, section: 'consultations', sectionLabel: 'Experience', highlight: `${c.role} at ${c.organization} — ${c.history}` }) - }) - medications.forEach(m => { - searchableItems.push({ id: m.id, title: m.name, section: 'medications', sectionLabel: 'Skills', highlight: `${m.name} — ${m.frequency} use since ${m.startYear}` }) - }) - problems.forEach(p => { - searchableItems.push({ id: p.id, title: p.description, section: 'problems', sectionLabel: 'Achievements', highlight: `[${p.code}] ${p.description} — ${p.narrative}` }) - }) - investigations.forEach(inv => { - searchableItems.push({ id: inv.id, title: inv.name, section: 'investigations', sectionLabel: 'Projects', highlight: `${inv.name} — ${inv.methodology}` }) - }) - documents.forEach(doc => { - searchableItems.push({ id: doc.id, title: doc.title, section: 'documents', sectionLabel: 'Education', highlight: `${doc.title} from ${doc.source} (${doc.date})` }) - }) - - return new Fuse(searchableItems, { - keys: [ - { name: 'title', weight: 2 }, - { name: 'highlight', weight: 1 }, - ], - threshold: 0.3, - includeScore: true, - minMatchCharLength: 2, - }) -} - -/** @deprecated Use groupBySection() instead */ -export function groupResultsBySection(results: FuseResult[]): Map[]> { - const grouped = new Map[]>() - results.forEach(result => { - const sectionLabel = result.item.sectionLabel - if (!grouped.has(sectionLabel)) { - grouped.set(sectionLabel, []) - } - grouped.get(sectionLabel)!.push(result) - }) - return grouped -}