diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md
index f00be30..c0654ee 100644
--- a/IMPLEMENTATION_PLAN.md
+++ b/IMPLEMENTATION_PLAN.md
@@ -147,10 +147,10 @@ cd pathways_app && timeout 60 python -m reflex run 2>&1 | head -30
- [x] Call prepare_chart_data() from apply_filters() for reactive updates
### 4.2 Reactive Plotly Integration
-- [ ] Create `generate_icicle_chart()` computed property that returns Plotly figure
-- [ ] Configure chart colors using design system palette
-- [ ] Configure chart interactivity (zoom, pan, click, hover)
-- [ ] Set responsive sizing
+- [x] Create `generate_icicle_chart()` computed property that returns Plotly figure
+- [x] Configure chart colors using design system palette
+- [x] Configure chart interactivity (zoom, pan, click, hover)
+- [x] Set responsive sizing
### 4.3 Chart Component
- [ ] Integrate `rx.plotly()` component in chart_section
diff --git a/pathways_app/app_v2.py b/pathways_app/app_v2.py
index b7118d7..8cd3b86 100644
--- a/pathways_app/app_v2.py
+++ b/pathways_app/app_v2.py
@@ -9,6 +9,7 @@ from datetime import datetime, timedelta
from pathlib import Path
from typing import Any
+import plotly.graph_objects as go
import reflex as rx
from pathways_app.styles import (
@@ -903,6 +904,114 @@ class AppState(rx.State):
return " | ".join(parts)
return "All Patients"
+ # =========================================================================
+ # Plotly Chart Generation
+ # =========================================================================
+
+ @rx.var
+ def icicle_figure(self) -> go.Figure:
+ """
+ Generate Plotly icicle chart from chart_data.
+
+ This computed property creates a go.Figure with the hierarchical icicle chart
+ using data from prepare_chart_data(). The chart displays patient pathways:
+ Root → Trust → Directory → Drug
+
+ Colors use a custom NHS-inspired blue gradient colorscale.
+ Hover displays patient count, cost, and percentage of parent.
+
+ Returns:
+ Plotly Figure object ready for rx.plotly() component
+ """
+ # Return empty figure if no data
+ if not self.chart_data:
+ return go.Figure()
+
+ # Extract lists from chart_data
+ parents = [d.get("parents", "") for d in self.chart_data]
+ ids = [d.get("ids", "") for d in self.chart_data]
+ labels = [d.get("labels", "") for d in self.chart_data]
+ values = [d.get("value", 0) for d in self.chart_data]
+ costs = [d.get("cost", 0.0) for d in self.chart_data]
+ colours = [d.get("colour", 0.0) for d in self.chart_data]
+
+ # NHS-inspired blue gradient colorscale (from design system)
+ # Heritage Blue → Primary Blue → Vibrant Blue → Sky Blue → Pale Blue
+ colorscale = [
+ [0.0, "#003087"], # Heritage Blue
+ [0.25, "#0066CC"], # Primary Blue
+ [0.5, "#1E88E5"], # Vibrant Blue
+ [0.75, "#4FC3F7"], # Sky Blue
+ [1.0, "#E3F2FD"], # Pale Blue
+ ]
+
+ # Create the icicle chart
+ fig = go.Figure(
+ go.Icicle(
+ labels=labels,
+ ids=ids,
+ parents=parents,
+ values=values,
+ branchvalues="total",
+ marker=dict(
+ colors=colours,
+ colorscale=colorscale,
+ line=dict(width=1, color="#FFFFFF"),
+ ),
+ maxdepth=3,
+ # Custom data for hover template
+ customdata=list(zip(values, colours, costs)),
+ # Text shown on chart segments
+ texttemplate="%{label}
%{value:,} patients",
+ # Hover text with full details
+ hovertemplate=(
+ "%{label}
"
+ "Patients: %{customdata[0]:,} (%{customdata[1]:.1%} of parent)
"
+ "Total Cost: £%{customdata[2]:,.0f}"
+ ""
+ ),
+ textfont=dict(
+ family="Inter, system-ui, sans-serif",
+ size=12,
+ ),
+ )
+ )
+
+ # Configure layout
+ fig.update_layout(
+ title=dict(
+ text=f"Patient Pathways — {self.chart_title}",
+ font=dict(
+ family="Inter, system-ui, sans-serif",
+ size=18,
+ color="#1E293B", # Slate 900
+ ),
+ x=0.5,
+ xanchor="center",
+ ),
+ margin=dict(t=60, l=10, r=10, b=30),
+ hoverlabel=dict(
+ bgcolor="#FFFFFF",
+ bordercolor="#CBD5E1", # Slate 300
+ font=dict(
+ family="Inter, system-ui, sans-serif",
+ size=13,
+ color="#1E293B", # Slate 900
+ ),
+ ),
+ paper_bgcolor="rgba(0,0,0,0)", # Transparent background
+ plot_bgcolor="rgba(0,0,0,0)",
+ # Responsive sizing - height set but width auto
+ height=600,
+ # Enable interactivity
+ clickmode="event+select",
+ )
+
+ # Disable sort to maintain hierarchy order
+ fig.update_traces(sort=False)
+
+ return fig
+
# =============================================================================
# Layout Components