feat: US-013 - Update command palette data for restructured dashboard
This commit is contained in:
+1
-1
@@ -231,7 +231,7 @@
|
|||||||
"Typecheck passes"
|
"Typecheck passes"
|
||||||
],
|
],
|
||||||
"priority": 13,
|
"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."
|
"notes": "The palette data model uses tileId for scroll targeting — these need to match the new data-tile-id attributes on ParentSection components."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1015,3 +1015,17 @@
|
|||||||
- External highlight useEffect must reset styles when highlightedNodeId becomes null — use the same baseline values as the hover mouseleave handler
|
- 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)
|
- 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
|
||||||
|
---
|
||||||
|
|||||||
+29
-139
@@ -1,11 +1,7 @@
|
|||||||
import Fuse, { type FuseResult } from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
import type { ViewId } from '@/types/pmr'
|
|
||||||
|
|
||||||
import { consultations } from '@/data/consultations'
|
import { consultations } from '@/data/consultations'
|
||||||
import { medications } from '@/data/medications'
|
|
||||||
import { problems } from '@/data/problems'
|
|
||||||
import { investigations } from '@/data/investigations'
|
import { investigations } from '@/data/investigations'
|
||||||
import { documents } from '@/data/documents'
|
|
||||||
import { skills } from '@/data/skills'
|
import { skills } from '@/data/skills'
|
||||||
import { kpis } from '@/data/kpis'
|
import { kpis } from '@/data/kpis'
|
||||||
import type { DetailPanelContent } from '@/types/pmr'
|
import type { DetailPanelContent } from '@/types/pmr'
|
||||||
@@ -36,44 +32,17 @@ export interface PaletteItem {
|
|||||||
export function buildPaletteData(): PaletteItem[] {
|
export function buildPaletteData(): PaletteItem[] {
|
||||||
const items: PaletteItem[] = []
|
const items: PaletteItem[] = []
|
||||||
|
|
||||||
// Experience — matching concept HTML entries
|
// Experience — all 4 roles from consultations.ts, open detail panel on select
|
||||||
const experienceEntries: Array<{ title: string; sub: string; keywords: string; activityId: string }> = [
|
consultations.forEach((c) => {
|
||||||
{
|
|
||||||
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) => {
|
|
||||||
items.push({
|
items.push({
|
||||||
id: `exp-${i}`,
|
id: `exp-${c.id}`,
|
||||||
title: entry.title,
|
title: c.role,
|
||||||
subtitle: entry.sub,
|
subtitle: `${c.organization} \u00b7 ${c.duration}`,
|
||||||
section: 'Experience',
|
section: 'Experience',
|
||||||
iconVariant: 'teal',
|
iconVariant: 'teal',
|
||||||
iconType: 'role',
|
iconType: 'role',
|
||||||
keywords: entry.keywords,
|
keywords: `${c.role.toLowerCase()} ${c.organization.toLowerCase()} ${c.duration.toLowerCase()}`,
|
||||||
action: { type: 'expand', tileId: 'career-activity', itemId: entry.activityId },
|
action: { type: 'panel', panelContent: { type: 'career-role', consultation: c } },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -91,41 +60,17 @@ export function buildPaletteData(): PaletteItem[] {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Active Projects — matching concept HTML entries
|
// Active Projects — all 5 investigations from investigations.ts
|
||||||
const projectEntries: Array<{ name: string; sub: string; keywords: string; investigationId: string }> = [
|
investigations.forEach((inv) => {
|
||||||
{
|
|
||||||
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)
|
|
||||||
items.push({
|
items.push({
|
||||||
id: `proj-${entry.investigationId}`,
|
id: `proj-${inv.id}`,
|
||||||
title: entry.name,
|
title: inv.name,
|
||||||
subtitle: entry.sub,
|
subtitle: `${inv.methodology.split('.')[0]} \u00b7 ${inv.requestedYear}`,
|
||||||
section: 'Active Projects',
|
section: 'Active Projects',
|
||||||
iconVariant: 'amber',
|
iconVariant: 'amber',
|
||||||
iconType: 'project',
|
iconType: 'project',
|
||||||
keywords: entry.keywords,
|
keywords: `${inv.name.toLowerCase()} ${inv.methodology.toLowerCase()} ${inv.techStack.join(' ').toLowerCase()} ${inv.requestedYear}`,
|
||||||
action: investigation
|
action: { type: 'panel', panelContent: { type: 'project', investigation: inv } },
|
||||||
? { type: 'panel', panelContent: { type: 'project', investigation } }
|
|
||||||
: { type: 'scroll', tileId: 'projects' },
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -169,31 +114,31 @@ export function buildPaletteData(): PaletteItem[] {
|
|||||||
keywords: entry.keywords,
|
keywords: entry.keywords,
|
||||||
action: kpi
|
action: kpi
|
||||||
? { type: 'panel', panelContent: { type: 'kpi', 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 }> = [
|
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',
|
title: 'MPharm (Hons) \u2014 2:1',
|
||||||
sub: 'University of East Anglia \u00b7 2011\u20132015',
|
sub: 'University of East Anglia \u00b7 2011\u20132015',
|
||||||
keywords: 'mpharm hons 2:1 university east anglia uea 2011 2015 pharmacy degree',
|
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',
|
title: 'GPhC Registration',
|
||||||
sub: 'General Pharmaceutical Council \u00b7 August 2016',
|
sub: 'General Pharmaceutical Council \u00b7 August 2016',
|
||||||
keywords: 'gphc registration general pharmaceutical council 2016 registered',
|
keywords: 'gphc registration general pharmaceutical council 2016 registered pharmacist',
|
||||||
},
|
|
||||||
{
|
|
||||||
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',
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -206,7 +151,7 @@ export function buildPaletteData(): PaletteItem[] {
|
|||||||
iconVariant: 'purple',
|
iconVariant: 'purple',
|
||||||
iconType: 'edu',
|
iconType: 'edu',
|
||||||
keywords: entry.keywords,
|
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)! }))
|
.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<SearchResult> {
|
|
||||||
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<SearchResult>[]): Map<string, FuseResult<SearchResult>[]> {
|
|
||||||
const grouped = new Map<string, FuseResult<SearchResult>[]>()
|
|
||||||
results.forEach(result => {
|
|
||||||
const sectionLabel = result.item.sectionLabel
|
|
||||||
if (!grouped.has(sectionLabel)) {
|
|
||||||
grouped.set(sectionLabel, [])
|
|
||||||
}
|
|
||||||
grouped.get(sectionLabel)!.push(result)
|
|
||||||
})
|
|
||||||
return grouped
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user