Skip to content

2026 04 08 Hematology Comparison Summary Cards

Hematology Comparison Summary Cards Implementation Plan

Section titled “Hematology Comparison Summary Cards Implementation Plan”

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Fix comparison page summary cards to properly handle hematology diseases — hide stage-specific fields, fix the therapy txEligible data bug, and adapt DOCX export.

Architecture: Thread diseaseType from geoDiseaseConfigAtom through ComparisonScenarioData into ComparisonSummaryCard and buildComparisonReport. Fix therapy-group eligible count capture in collectComparisonData.

Tech Stack: React 19, TypeScript, Jotai, docx library

Spec: docs/superpowers/specs/2026-04-08-hematology-comparison-summary-cards-design.md


Task 1: Add diseaseType to ComparisonScenarioData type

Section titled “Task 1: Add diseaseType to ComparisonScenarioData type”

Files:

  • Modify: src/features/reports/forecasting/sections/Comparison/types.ts:5-21

  • Step 1: Add diseaseType field to the interface

In src/features/reports/forecasting/sections/Comparison/types.ts, add diseaseType after the selectedYear field (line 9):

export interface ComparisonScenarioData {
meta: TabMeta;
geo: string;
indication: string | null;
selectedYear: number;
diseaseType: "Solid Tumor" | "Hematology";
// Summary card
incidence: number;
// ... rest unchanged
  • Step 2: Verify no type errors

Run: pnpm lint Expected: Type errors in collectComparisonData.ts (missing diseaseType in return object). This is expected — Task 2 fixes it.

  • Step 3: Commit
Terminal window
git add src/features/reports/forecasting/sections/Comparison/types.ts
git commit -m "feat(comparison): add diseaseType field to ComparisonScenarioData"

Task 2: Populate diseaseType and fix therapy txEligible bug in collectComparisonData

Section titled “Task 2: Populate diseaseType and fix therapy txEligible bug in collectComparisonData”

Files:

  • Modify: src/features/reports/forecasting/sections/Comparison/collectComparisonData.ts:46,97-103,215-237

  • Step 1: Fix therapy group eligible count capture

In src/features/reports/forecasting/sections/Comparison/collectComparisonData.ts, find the eligible count capture block (lines 97-103):

// First line's eligibleCount = L1 tx-eligible for that stage
if (group.stageCategory === "early") {
txEligibleEarly = items[0]?.eligibleCount ?? 0;
} else if (group.stageCategory === "metastatic") {
txEligibleMetL1 = items[0]?.eligibleCount ?? 0;
}

Replace with:

// First line's eligibleCount = L1 tx-eligible for that stage
if (group.stageCategory === "early") {
txEligibleEarly = items[0]?.eligibleCount ?? 0;
} else if (group.stageCategory === "metastatic") {
txEligibleMetL1 = items[0]?.eligibleCount ?? 0;
} else if (group.stageCategory === "therapy") {
txEligibleMetL1 = items[0]?.eligibleCount ?? 0;
}

This fixes the data bug where hematology therapy lines were never captured (metL1 was always 0). Since hematology has no metastatic lines, the metL1 slot is reused for the first therapy line’s eligible count.

  • Step 2: Add diseaseType to return object

In the same file, find the return object (line 215 onwards). Add diseaseType after selectedYear:

return {
meta,
geo,
indication,
selectedYear,
diseaseType: diseaseConfig?.config.type ?? "Solid Tumor",
incidence: baseIncidence,
// ... rest unchanged

diseaseConfig is already read at line 46 (store.get(geoDiseaseConfigAtom)), so no new atom reads needed.

  • Step 3: Verify lint passes

Run: pnpm lint Expected: PASS (type error from Task 1 is now resolved)

  • Step 4: Commit
Terminal window
git add src/features/reports/forecasting/sections/Comparison/collectComparisonData.ts
git commit -m "fix(comparison): populate diseaseType and fix therapy txEligible bug"

Task 3: Conditional rendering in ComparisonSummaryCard

Section titled “Task 3: Conditional rendering in ComparisonSummaryCard”

Files:

  • Modify: src/features/reports/forecasting/sections/Comparison/ComparisonSummaryCard.tsx:11-92

  • Step 1: Add isSolidTumor derivation

In src/features/reports/forecasting/sections/Comparison/ComparisonSummaryCard.tsx, inside the ComparisonSummaryCard function body (after line 13, the color const), add:

const isSolidTumor = data.diseaseType === "Solid Tumor";
  • Step 2: Conditionally render the Incidence section heading

Find line 51-52:

<div className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-1.5">
Incidence &amp; Stage
</div>

Replace with:

<div className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-1.5">
{isSolidTumor ? "Incidence & Stage" : "Incidence"}
</div>

Note: Since we’re now using a JSX expression, &amp; becomes & inside the string literal.

  • Step 3: Conditionally render stage distribution rows

Find lines 59-70 (the Early Stage and Metastatic rows inside the “Incidence & Stage” section):

<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Early Stage</span>
<span className="font-medium tabular-nums">
{data.stageDistribution.earlyPercent.toFixed(1)}%
</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Metastatic</span>
<span className="font-medium tabular-nums">
{data.stageDistribution.metPercent.toFixed(1)}%
</span>
</div>

Replace with:

{isSolidTumor && (
<>
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Early Stage</span>
<span className="font-medium tabular-nums">
{data.stageDistribution.earlyPercent.toFixed(1)}%
</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Metastatic</span>
<span className="font-medium tabular-nums">
{data.stageDistribution.metPercent.toFixed(1)}%
</span>
</div>
</>
)}
  • Step 4: Conditionally render Treatment Eligible section

Find lines 79-92 (the Treatment Eligible section):

<div className="space-y-0.5">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Early Stage</span>
<span className="font-semibold tabular-nums" style={{ color }}>
{Math.round(data.txEligible.early).toLocaleString()}
</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Metastatic Line 1</span>
<span className="font-semibold tabular-nums" style={{ color }}>
{Math.round(data.txEligible.metL1).toLocaleString()}
</span>
</div>
</div>

Replace with:

<div className="space-y-0.5">
{isSolidTumor && (
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Early Stage</span>
<span className="font-semibold tabular-nums" style={{ color }}>
{Math.round(data.txEligible.early).toLocaleString()}
</span>
</div>
)}
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">
{isSolidTumor ? "Metastatic Line 1" : "Therapy Line 1"}
</span>
<span className="font-semibold tabular-nums" style={{ color }}>
{Math.round(data.txEligible.metL1).toLocaleString()}
</span>
</div>
</div>
  • Step 5: Verify lint passes

Run: pnpm lint Expected: PASS

  • Step 6: Commit
Terminal window
git add src/features/reports/forecasting/sections/Comparison/ComparisonSummaryCard.tsx
git commit -m "feat(comparison): conditional summary card rendering for hematology diseases"

Task 4: Adapt DOCX summary table for hematology

Section titled “Task 4: Adapt DOCX summary table for hematology”

Files:

  • Modify: src/features/reports/forecasting/sections/Comparison/reporting/buildComparisonReport.ts:33-51

  • Step 1: Update buildSummaryTable to check disease type

In src/features/reports/forecasting/sections/Comparison/reporting/buildComparisonReport.ts, find the buildSummaryTable function (lines 33-51):

function buildSummaryTable(input: ComparisonReportInput): Table {
const headers = [
"Scenario",
"Geography",
"Indication",
"Incidence",
"Early Tx Eligible",
"Met L1 Tx Eligible",
];
const rows = input.data.map((s) => [
s.meta.label,
s.geo,
s.indication ?? "",
String(Math.round(s.incidence)),
String(Math.round(s.txEligible.early)),
String(Math.round(s.txEligible.metL1)),
]);
return buildTable(headers, rows);
}

Replace with:

function buildSummaryTable(input: ComparisonReportInput): Table {
const allHematology = input.data.every((s) => s.diseaseType === "Hematology");
const headers = allHematology
? ["Scenario", "Geography", "Indication", "Incidence", "Therapy L1 Tx Eligible"]
: ["Scenario", "Geography", "Indication", "Incidence", "Early Tx Eligible", "Met L1 Tx Eligible"];
const rows = input.data.map((s) =>
allHematology
? [
s.meta.label,
s.geo,
s.indication ?? "",
String(Math.round(s.incidence)),
String(Math.round(s.txEligible.metL1)),
]
: [
s.meta.label,
s.geo,
s.indication ?? "",
String(Math.round(s.incidence)),
String(Math.round(s.txEligible.early)),
String(Math.round(s.txEligible.metL1)),
],
);
return buildTable(headers, rows);
}
  • Step 2: Verify lint passes

Run: pnpm lint Expected: PASS

  • Step 3: Commit
Terminal window
git add src/features/reports/forecasting/sections/Comparison/reporting/buildComparisonReport.ts
git commit -m "feat(comparison): adapt DOCX summary table headers for hematology"

  • Step 1: Full lint check

Run: pnpm lint Expected: PASS with no errors

  • Step 2: Build check

Run: pnpm build Expected: PASS — no type errors, no build failures

  • Step 3: Manual verification

Open the app and verify:

  1. Select a hematology disease (e.g., CLL), create 2+ scenarios, open the Comparison page
    • Summary cards show: Context (Geo + Indication), Incidence (no stage rows), Treatment Eligible with “Therapy Line 1” label and correct non-zero value
  2. Select a solid tumour disease, create 2+ scenarios, open the Comparison page
    • Summary cards show the full layout: Context, Incidence & Stage (with Early % and Met %), Treatment Eligible with both “Early Stage” and “Metastatic Line 1”
  3. Export a comparison DOCX report for a hematology disease
    • Summary table has 5 columns (no “Early Tx Eligible”), “Therapy L1 Tx Eligible” header