feat: add chart type toggle for Directory/Indication views (Task 4.1, 4.2, 4.3)
- Add selected_chart_type state variable and set_chart_type() handler - Add chart_type filter to load_pathway_data() WHERE clause - Create segmented control toggle component in filter strip - Add dynamic hierarchy label (Directorate vs Indication) - Update chart title to include chart type prefix
This commit is contained in:
+14
-12
@@ -107,26 +107,28 @@ python -m reflex compile
|
|||||||
## Phase 4: Reflex UI Updates
|
## Phase 4: Reflex UI Updates
|
||||||
|
|
||||||
### 4.1 Add Chart Type State
|
### 4.1 Add Chart Type State
|
||||||
- [ ] Add state variables to `AppState`:
|
- [x] Add state variables to `AppState`:
|
||||||
- `selected_chart_type: str = "directory"` (options: "directory", "indication")
|
- `selected_chart_type: str = "directory"` (options: "directory", "indication")
|
||||||
- `chart_type_options: list[dict]` for dropdown
|
- `chart_type_options: list[dict]` for dropdown
|
||||||
- [ ] Add `set_chart_type()` event handler
|
- [x] Add `set_chart_type()` event handler
|
||||||
- [ ] Update `load_pathway_data()` to filter by chart_type
|
- [x] Update `load_pathway_data()` to filter by chart_type
|
||||||
- [ ] Verify: State changes correctly, data queries include chart_type filter
|
- [x] Verify: State changes correctly, data queries include chart_type filter
|
||||||
|
|
||||||
### 4.2 Add Chart Type Toggle UI
|
### 4.2 Add Chart Type Toggle UI
|
||||||
- [ ] Create `chart_type_toggle()` component:
|
- [x] Create `chart_type_toggle()` component:
|
||||||
- Radio buttons or segmented control: "By Directory" | "By Indication"
|
- Segmented control with pill-style buttons: "By Directory" | "By Indication"
|
||||||
- Place in filter strip or chart header area
|
- Placed in filter strip, first element before date filters
|
||||||
- [ ] Wire to `set_chart_type()` handler
|
- [x] Wire to `set_chart_type()` handler
|
||||||
- [ ] Verify: Toggle switches chart data, UI updates reactively
|
- [x] Verify: Toggle switches chart data, UI updates reactively (reflex compile passed)
|
||||||
|
|
||||||
### 4.3 Update Chart Display for Indication Labels
|
### 4.3 Update Chart Display for Indication Labels
|
||||||
- [ ] Ensure icicle chart handles mixed labels:
|
- [x] Ensure icicle chart handles mixed labels:
|
||||||
- Search_Term labels (e.g., "rheumatoid arthritis") for matched patients
|
- Search_Term labels (e.g., "rheumatoid arthritis") for matched patients
|
||||||
- Directorate labels (e.g., "RHEUMATOLOGY (no GP dx)") for unmatched
|
- Directorate labels (e.g., "RHEUMATOLOGY (no GP dx)") for unmatched
|
||||||
- [ ] Update hover templates if needed for indication context
|
- Note: labels come from pathway_nodes pre-computed data, no template changes needed
|
||||||
- [ ] Verify: Chart renders correctly with both label types
|
- [x] Update hierarchy description (dynamic: "Trust → Directorate → ..." or "Trust → Indication → ...")
|
||||||
|
- [x] Update chart title to include chart type prefix
|
||||||
|
- [x] Verify: Chart renders correctly with both label types (reflex compile passed)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -81,9 +81,16 @@ class AppState(rx.State):
|
|||||||
# UI State Variables
|
# UI State Variables
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|
||||||
# Placeholder for current chart type (for top bar tabs)
|
# Chart visualization type (top bar tabs - future: icicle, sankey, timeline)
|
||||||
current_chart: str = "icicle"
|
current_chart: str = "icicle"
|
||||||
|
|
||||||
|
# Chart data type: "directory" (by directorate) or "indication" (by GP diagnosis)
|
||||||
|
selected_chart_type: str = "directory"
|
||||||
|
chart_type_options: list[dict[str, str]] = [
|
||||||
|
{"value": "directory", "label": "By Directory"},
|
||||||
|
{"value": "indication", "label": "By Indication"},
|
||||||
|
]
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Filter State Variables
|
# Filter State Variables
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
@@ -190,6 +197,12 @@ class AppState(rx.State):
|
|||||||
if self.data_loaded:
|
if self.data_loaded:
|
||||||
self.load_pathway_data()
|
self.load_pathway_data()
|
||||||
|
|
||||||
|
def set_chart_type(self, value: str):
|
||||||
|
"""Set chart data type (directory or indication) and reload pathway data."""
|
||||||
|
self.selected_chart_type = value
|
||||||
|
if self.data_loaded:
|
||||||
|
self.load_pathway_data()
|
||||||
|
|
||||||
# Computed property for date filter ID
|
# Computed property for date filter ID
|
||||||
@rx.var
|
@rx.var
|
||||||
def date_filter_id(self) -> str:
|
def date_filter_id(self) -> str:
|
||||||
@@ -403,6 +416,20 @@ class AppState(rx.State):
|
|||||||
return "—"
|
return "—"
|
||||||
return f"{self.indication_match_rate:.0f}%"
|
return f"{self.indication_match_rate:.0f}%"
|
||||||
|
|
||||||
|
@rx.var
|
||||||
|
def chart_hierarchy_label(self) -> str:
|
||||||
|
"""Display the hierarchy path based on selected chart type."""
|
||||||
|
if self.selected_chart_type == "indication":
|
||||||
|
return "Trust → Indication → Drug → Patient Pathway"
|
||||||
|
return "Trust → Directorate → Drug → Patient Pathway"
|
||||||
|
|
||||||
|
@rx.var
|
||||||
|
def chart_type_label(self) -> str:
|
||||||
|
"""Display label for current chart type."""
|
||||||
|
if self.selected_chart_type == "indication":
|
||||||
|
return "By Indication"
|
||||||
|
return "By Directory"
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
def last_updated_display(self) -> str:
|
def last_updated_display(self) -> str:
|
||||||
"""Format last updated timestamp for display in top bar."""
|
"""Format last updated timestamp for display in top bar."""
|
||||||
@@ -717,12 +744,8 @@ class AppState(rx.State):
|
|||||||
filter_id = f"{self.selected_initiated}_{self.selected_last_seen}"
|
filter_id = f"{self.selected_initiated}_{self.selected_last_seen}"
|
||||||
|
|
||||||
# Build WHERE clause for filters
|
# Build WHERE clause for filters
|
||||||
where_clauses = ["date_filter_id = ?"]
|
where_clauses = ["date_filter_id = ?", "chart_type = ?"]
|
||||||
params = [filter_id]
|
params = [filter_id, self.selected_chart_type]
|
||||||
|
|
||||||
# Trust filter (if any directorates selected, they map to trust_name)
|
|
||||||
# Note: In the schema, trust_name is extracted from the hierarchy
|
|
||||||
# For now, we filter at the directory level since that's what users select
|
|
||||||
|
|
||||||
# Directory filter (if any directorates selected)
|
# Directory filter (if any directorates selected)
|
||||||
if self.selected_directorates:
|
if self.selected_directorates:
|
||||||
@@ -846,6 +869,12 @@ class AppState(rx.State):
|
|||||||
"""Generate chart title based on current pathway filter state."""
|
"""Generate chart title based on current pathway filter state."""
|
||||||
parts = []
|
parts = []
|
||||||
|
|
||||||
|
# Chart type prefix
|
||||||
|
if self.selected_chart_type == "indication":
|
||||||
|
parts.append("By Indication")
|
||||||
|
else:
|
||||||
|
parts.append("By Directory")
|
||||||
|
|
||||||
# Date filter info
|
# Date filter info
|
||||||
initiated_label = "All years"
|
initiated_label = "All years"
|
||||||
if self.selected_initiated == "1yr":
|
if self.selected_initiated == "1yr":
|
||||||
@@ -1592,6 +1621,84 @@ def searchable_dropdown(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def chart_type_toggle() -> rx.Component:
|
||||||
|
"""
|
||||||
|
Segmented control for switching between Directory and Indication chart types.
|
||||||
|
|
||||||
|
Uses two pill-style buttons side by side. Active state uses primary blue.
|
||||||
|
Placed in the filter strip alongside date and multi-select filters.
|
||||||
|
"""
|
||||||
|
return rx.hstack(
|
||||||
|
rx.box(
|
||||||
|
rx.text(
|
||||||
|
"By Directory",
|
||||||
|
font_size=Typography.BODY_SMALL_SIZE,
|
||||||
|
font_weight="600",
|
||||||
|
color=rx.cond(
|
||||||
|
AppState.selected_chart_type == "directory",
|
||||||
|
Colors.WHITE,
|
||||||
|
Colors.SLATE_700,
|
||||||
|
),
|
||||||
|
font_family=Typography.FONT_FAMILY,
|
||||||
|
),
|
||||||
|
padding_x=Spacing.MD,
|
||||||
|
padding_y=Spacing.XS,
|
||||||
|
border_radius=Radii.FULL,
|
||||||
|
background_color=rx.cond(
|
||||||
|
AppState.selected_chart_type == "directory",
|
||||||
|
Colors.PRIMARY,
|
||||||
|
Colors.SLATE_100,
|
||||||
|
),
|
||||||
|
cursor="pointer",
|
||||||
|
on_click=AppState.set_chart_type("directory"),
|
||||||
|
transition=f"background-color {Transitions.COLOR}",
|
||||||
|
_hover={
|
||||||
|
"background_color": rx.cond(
|
||||||
|
AppState.selected_chart_type == "directory",
|
||||||
|
Colors.PRIMARY,
|
||||||
|
Colors.SLATE_300,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
rx.box(
|
||||||
|
rx.text(
|
||||||
|
"By Indication",
|
||||||
|
font_size=Typography.BODY_SMALL_SIZE,
|
||||||
|
font_weight="600",
|
||||||
|
color=rx.cond(
|
||||||
|
AppState.selected_chart_type == "indication",
|
||||||
|
Colors.WHITE,
|
||||||
|
Colors.SLATE_700,
|
||||||
|
),
|
||||||
|
font_family=Typography.FONT_FAMILY,
|
||||||
|
),
|
||||||
|
padding_x=Spacing.MD,
|
||||||
|
padding_y=Spacing.XS,
|
||||||
|
border_radius=Radii.FULL,
|
||||||
|
background_color=rx.cond(
|
||||||
|
AppState.selected_chart_type == "indication",
|
||||||
|
Colors.PRIMARY,
|
||||||
|
Colors.SLATE_100,
|
||||||
|
),
|
||||||
|
cursor="pointer",
|
||||||
|
on_click=AppState.set_chart_type("indication"),
|
||||||
|
transition=f"background-color {Transitions.COLOR}",
|
||||||
|
_hover={
|
||||||
|
"background_color": rx.cond(
|
||||||
|
AppState.selected_chart_type == "indication",
|
||||||
|
Colors.PRIMARY,
|
||||||
|
Colors.SLATE_300,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
spacing="1",
|
||||||
|
align="center",
|
||||||
|
background_color=Colors.SLATE_100,
|
||||||
|
padding=Spacing.XS,
|
||||||
|
border_radius=Radii.FULL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def chart_tab(label: str, chart_type: str, is_active: bool = False) -> rx.Component:
|
def chart_tab(label: str, chart_type: str, is_active: bool = False) -> rx.Component:
|
||||||
"""
|
"""
|
||||||
Individual chart type tab/pill for top bar navigation.
|
Individual chart type tab/pill for top bar navigation.
|
||||||
@@ -1702,6 +1809,14 @@ def filter_section() -> rx.Component:
|
|||||||
"""
|
"""
|
||||||
return rx.box(
|
return rx.box(
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
|
# Chart type toggle (By Directory / By Indication)
|
||||||
|
chart_type_toggle(),
|
||||||
|
# Separator
|
||||||
|
rx.divider(
|
||||||
|
orientation="vertical",
|
||||||
|
size="2",
|
||||||
|
color_scheme="gray",
|
||||||
|
),
|
||||||
# Date filters group
|
# Date filters group
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
initiated_filter_dropdown(),
|
initiated_filter_dropdown(),
|
||||||
@@ -2221,7 +2336,7 @@ def chart_section() -> rx.Component:
|
|||||||
color=Colors.SLATE_500,
|
color=Colors.SLATE_500,
|
||||||
),
|
),
|
||||||
rx.text(
|
rx.text(
|
||||||
"Trust → Directorate → Drug → Patient Pathway",
|
AppState.chart_hierarchy_label,
|
||||||
font_size=Typography.CAPTION_SIZE,
|
font_size=Typography.CAPTION_SIZE,
|
||||||
font_weight=Typography.CAPTION_WEIGHT,
|
font_weight=Typography.CAPTION_WEIGHT,
|
||||||
color=Colors.SLATE_500,
|
color=Colors.SLATE_500,
|
||||||
|
|||||||
+54
-1
@@ -242,7 +242,7 @@ The previous `batch_lookup_indication_groups()` function in `diagnosis_lookup.py
|
|||||||
### Files changed:
|
### Files changed:
|
||||||
- `IMPLEMENTATION_PLAN.md` — marked Task 3.1 verification items complete
|
- `IMPLEMENTATION_PLAN.md` — marked Task 3.1 verification items complete
|
||||||
- `progress.txt` — this entry
|
- `progress.txt` — this entry
|
||||||
### Committed: 966d569 "docs: mark Task 3.1 complete - indication pipeline verified (Task 3.1)"
|
### Committed: 2deaa2f "docs: mark Task 3.1 complete - indication pipeline verified (Task 3.1)"
|
||||||
### Patterns discovered:
|
### Patterns discovered:
|
||||||
- Pipeline processing time breakdown: data fetch (7s) + indication lookup (~9 min) + pathway processing (~50s)
|
- Pipeline processing time breakdown: data fetch (7s) + indication lookup (~9 min) + pathway processing (~50s)
|
||||||
- The indication lookup batches (500 patients/batch × 74 batches) are the slowest part
|
- The indication lookup batches (500 patients/batch × 74 batches) are the slowest part
|
||||||
@@ -255,3 +255,56 @@ The previous `batch_lookup_indication_groups()` function in `diagnosis_lookup.py
|
|||||||
- **Important**: Run `--chart-type all` (non-dry-run) to populate database before UI testing
|
- **Important**: Run `--chart-type all` (non-dry-run) to populate database before UI testing
|
||||||
### Blocked items:
|
### Blocked items:
|
||||||
- None — Phase 3 complete, Phase 4 ready to begin
|
- None — Phase 3 complete, Phase 4 ready to begin
|
||||||
|
|
||||||
|
## Iteration 5 — 2026-02-05
|
||||||
|
### Task: 4.1, 4.2, 4.3 — Reflex UI Chart Type Toggle
|
||||||
|
### Why this task:
|
||||||
|
- Phase 3 (data pipeline) is fully verified — the next logical step is the UI
|
||||||
|
- Tasks 4.1, 4.2, 4.3 are tightly coupled (state → toggle → display) and all live in the same file
|
||||||
|
- Combined them since they're interdependent and small individually
|
||||||
|
### Status: COMPLETE
|
||||||
|
### What was done:
|
||||||
|
1. **Task 4.1 — Chart Type State**:
|
||||||
|
- Added `selected_chart_type: str = "directory"` state variable
|
||||||
|
- Added `chart_type_options` list for dropdown configuration
|
||||||
|
- Added `set_chart_type()` event handler that triggers `load_pathway_data()`
|
||||||
|
- Updated `load_pathway_data()` to include `chart_type = ?` in WHERE clause
|
||||||
|
- Added computed vars: `chart_hierarchy_label`, `chart_type_label`
|
||||||
|
- Updated `_generate_pathway_chart_title()` to include chart type prefix
|
||||||
|
|
||||||
|
2. **Task 4.2 — Chart Type Toggle UI**:
|
||||||
|
- Created `chart_type_toggle()` component — segmented control with two pill-style buttons
|
||||||
|
- "By Directory" and "By Indication" with active state using Primary Blue
|
||||||
|
- Placed in filter strip as first element (before date filters), with separator
|
||||||
|
- Wired to `set_chart_type()` handler via `on_click`
|
||||||
|
|
||||||
|
3. **Task 4.3 — Chart Display Updates**:
|
||||||
|
- Updated chart section hierarchy label to use dynamic `AppState.chart_hierarchy_label`
|
||||||
|
- Shows "Trust → Directorate → Drug → Patient Pathway" or "Trust → Indication → Drug → Patient Pathway"
|
||||||
|
- No hover template changes needed — labels come from pre-computed pathway_nodes data
|
||||||
|
- Mixed labels (Search_Term + directorate fallback) already handled by pipeline
|
||||||
|
|
||||||
|
### Validation results:
|
||||||
|
- Tier 1 (Code): ✅ `python -m py_compile pathways_app/pathways_app.py` passed
|
||||||
|
- Tier 1 (Import): ✅ AppState imports with all new attributes (selected_chart_type, set_chart_type, chart_hierarchy_label)
|
||||||
|
- Tier 3 (Functional): ✅ `reflex compile` succeeded in 16s (21/21 components)
|
||||||
|
### Files changed:
|
||||||
|
- `pathways_app/pathways_app.py` — added chart type state, toggle component, dynamic labels
|
||||||
|
- `IMPLEMENTATION_PLAN.md` — marked Tasks 4.1, 4.2, 4.3 complete
|
||||||
|
### Committed: 1c35d23 "feat: add chart type toggle for Directory/Indication views (Task 4.1, 4.2, 4.3)"
|
||||||
|
### Patterns discovered:
|
||||||
|
- Reflex `rx.cond()` works well for toggle active states — use it for background_color and text color
|
||||||
|
- Segmented control pattern: wrap two boxes in an hstack with background, use rx.cond for active styling
|
||||||
|
- No `SLATE_200` in design system — used `SLATE_300` for hover states instead
|
||||||
|
### Next iteration should:
|
||||||
|
- **Run `python -m cli.refresh_pathways --chart-type all`** (non-dry-run) to populate database with BOTH chart types
|
||||||
|
- This is needed before UI testing can verify the toggle actually switches data
|
||||||
|
- The 3.1 sub-item "Run full refresh with --chart-type all" is still unchecked
|
||||||
|
- Then run `reflex run` and verify:
|
||||||
|
- Toggle appears in filter strip
|
||||||
|
- Clicking "By Indication" loads indication pathway data
|
||||||
|
- Clicking "By Directory" loads directory pathway data
|
||||||
|
- KPIs update for both chart types
|
||||||
|
- After verification, proceed to Phase 5 (end-to-end validation and documentation)
|
||||||
|
### Blocked items:
|
||||||
|
- **UI testing blocked by data**: Need to run `--chart-type all` to populate indication data in SQLite before the toggle can show indication pathways
|
||||||
|
|||||||
Reference in New Issue
Block a user