Create src/lib/theme-colors.ts with DOT_COLORS, KPI_COLORS, PROJECT_STATUS_COLORS, and DEFAULT_ORG_COLOR constants. Add motionSafeTransition() utility to src/lib/utils.ts. Removes 6 duplicate color map definitions across Card, DetailPanel, PatientSummaryTile, KPIDetail, ProjectsTile, and ProjectDetail. Replaces 9 hardcoded '#0D6E6E' fallbacks and 7 inline motion ternaries. Fixes project status color inconsistency between ProjectsTile and ProjectDetail (Ongoing was teal in tile, amber in detail).
8.0 KiB
Task: Comprehensive Codebase Refactor & Simplification
Refactor the portfolio codebase to eliminate duplication, consolidate data sources, extract shared utilities, and simplify components — while preserving identical runtime behaviour and visual output.
Guiding Principle
Single Source of Truth: Every piece of information should live in exactly one place. Derived data is fine (for code-splitting/performance), but the canonical definition must not be duplicated.
Refactoring Checklist
Work through these IN ORDER. Each item is a self-contained refactoring that leaves the codebase in a passing state (lint + typecheck + build).
Phase 0: Dev Shortcut
- 0.1 — Disable boot/ECG/login sequence for faster visual review
- In
src/App.tsxline 48, changeuseState<Phase>('boot')touseState<Phase>('pmr') - This skips straight to the dashboard, saving ~10s per visual inspection
- Do NOT remove the BootSequence/ECGAnimation/LoginScreen components or imports — just bypass them
- Verify:
npm run buildpasses, app loads directly to dashboard at localhost:5173
- In
Phase 1: Data Consolidation
-
1.1 — Migrate medications.ts history into skills.ts, then delete medications.ts
src/data/medications.tshas ZERO imports anywhere (dead code) but containsprescribingHistory[]arrays with rich skill progression data- Merge the
prescribingHistorydata into corresponding entries insrc/data/skills.ts(add aprescribingHistoryfield to SkillMedication type) - Update
src/types/pmr.tsif needed for the new field - Delete
src/data/medications.ts - Verify: no broken imports, build passes
-
1.2 — Consolidate timeline narrative into timeline.ts
src/data/profile-content.tscontains atimelineNarrativesection (~320 lines) that is pulled intotimeline.tsviagetTimelineNarrativeEntry()- Inline the narrative content directly into the
TimelineEntityobjects intimeline.ts - Remove the
timelineNarrativesection fromprofile-content.ts - Remove
getTimelineNarrativeEntry()fromsrc/lib/profile-content.tsand all call sites - Verify: timeline entities still have all their description/details/outcomes/codedEntries data
-
1.3 — Split profile-content.ts into focused concerns
- After 1.2,
profile-content.tsshould be smaller. Split remaining content:- LLM system prompt → inline into
src/lib/llm.tsor a dedicatedsrc/data/llm-prompt.ts - Education narrative → merge into
src/data/documents.tsoreducationExtras.ts - Profile summary/achievements → keep in
profile-content.tsonly if genuinely unique
- LLM system prompt → inline into
- Goal:
profile-content.tseither deleted or contains only truly unique content with zero duplication - Update
src/lib/profile-content.tsaccessor functions and all consumers - Update
src/types/profile-content.tstypes to match
- After 1.2,
-
1.4 — Evaluate thin re-export layers
src/data/constellation.ts(9 lines) re-exports fromtimeline.tssrc/data/tags.ts(10 lines) derives fromtimeline.ts- For each: inline at call sites if few consumers, or keep if many consumers benefit
- If kept, add a brief comment explaining why the indirection exists
- If removed, update all import paths
Phase 2: Utility Extraction
-
2.1 — Extract duplicated utility functions into lib/utils.ts
hexToRgba()is defined locally in at least:DashboardLayout.tsx,TimelineInterventionsSubsection.tsx,WorkExperienceSubsection.tsxprefersReducedMotionmedia query is repeated across 8+ files- Extract both to
src/lib/utils.ts(currently only 8 lines withcn()) - Replace all local definitions with imports from
@/lib/utils - Verify: no duplicate function definitions remain, search codebase to confirm
-
2.2 — Audit and consolidate other repeated patterns
- Search for other duplicated helper functions, constants, or inline logic across components
- Extract anything used in 3+ places into shared modules
- Common candidates: date formatting, color manipulation, responsive breakpoint checks, animation config objects
Phase 3: Component Simplification
-
3.1 — Extract shared ExpandableCard component
WorkExperienceSubsection.tsx(306 lines),TimelineInterventionsSubsection.tsx(346 lines), andRepeatMedicationsSubsection.tsx(294 lines) all implement expand/collapse card patterns with similar styling and interaction logic- Extract the shared pattern into
src/components/ExpandableCard.tsx - The shared component handles: expand/collapse toggle, animation, consistent styling
- Each subsection keeps its unique content rendering via children/render props
- Goal: measurable line reduction across the three files
-
3.2 — Simplify detail panel components
- 6 detail panel components share structural patterns:
SkillDetail,SkillsAllDetail,ConsultationDetail,EducationDetail,ProjectDetail,KPIDetail - Extract shared layout into a base component: container, header, close button, scroll behaviour, enter/exit animation
- Each detail component keeps its unique content but reuses the shared shell
- Look at
src/components/detail/directory
- 6 detail panel components share structural patterns:
-
3.3 — Review large components for extraction opportunities
- Components over 400 lines: ECGAnimation (686), ChatWidget (648), Sidebar (572), DashboardLayout (503), BootSequence (497), CommandPalette (456), LoginScreen (449)
- For each: identify self-contained sections that can become sub-components
- Only extract where it genuinely reduces complexity — not arbitrary line-count reduction
- Prioritise sections with their own state/effects that don't need parent state
Phase 4: Final Cleanup
-
4.1 — Remove dead code and unused exports
- After all refactoring, scan for: unused imports, unused exports, unused types, orphaned files
- ESLint should catch most — run
npm run lintand fix everything - Manually check for files that are no longer imported anywhere
-
4.2 — Final validation and baseline comparison
npm run lintpasses with zero warningsnpm run typecheckpasses with zero errorsnpm run buildsucceeds- Compare total line count against baseline (recorded at start)
- Record the reduction in this file
-
4.3 — Re-enable boot/ECG/login sequence
- In
src/App.tsx, changeuseState<Phase>('pmr')back touseState<Phase>('boot') - Verify:
npm run buildpasses - Do a final Playwright visual check to confirm the full boot → ECG → login → dashboard flow works
- Commit:
fix: re-enable boot sequence after refactor
- In
Success Criteria
ALL of the following must be true:
- Every checklist item above is complete (or explicitly escalated with reason)
npm run lint && npm run typecheck && npm run buildpasses cleanly- No data is defined in more than one place (single source of truth)
src/data/medications.tsis deleted (history migrated to skills.ts)hexToRgba()exists in exactly one locationprefersReducedMotionquery is centralised- Shared component patterns are extracted (ExpandableCard, detail panel base)
- Total codebase line count is measurably reduced
- Zero runtime behaviour changes — identical visual output
Constraints
- TypeScript strict mode must be maintained
- Preserve all existing path aliases (
@/*) - Follow existing naming conventions (PascalCase components, kebab-case utils)
- Conventional commit messages for each logical change (
refactor: ...) - Do not modify the app's phases or lifecycle (boot → ECG → login → dashboard) — except the temporary Phase 0 bypass which is reverted in 4.3
- Do not change any Tailwind classes or visual styling
- Do not add new dependencies
- Do not remove the CLAUDE.md file
Baseline
Record line count before starting. Run at first iteration:
find src -name '*.ts' -o -name '*.tsx' | xargs wc -l
Store result in .ralph/plan.md for comparison at end.
Status
Track progress here. Mark items complete as you go. When ALL success criteria are met, print LOOP_COMPLETE.