diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md
index 507b1aa..b0d1684 100644
--- a/IMPLEMENTATION_PLAN.md
+++ b/IMPLEMENTATION_PLAN.md
@@ -469,16 +469,16 @@ Additionally: KPI row removed, fraction KPIs moved to header, global filter sub-
- **Checkpoint**: Design mockups/specifications ready for all 5 areas above
### 10.2 State management + sidebar restructure
-- [ ] Add `active_view` to `app-state`: `"patient-pathways"` (default) or `"trust-comparison"`
-- [ ] Add `selected_comparison_directorate` to `app-state`: `null` (landing page) or directorate name
-- [ ] Update `dash_app/components/sidebar.py`:
+- [x] Add `active_view` to `app-state`: `"patient-pathways"` (default) or `"trust-comparison"`
+- [x] Add `selected_comparison_directorate` to `app-state`: `null` (landing page) or directorate name
+- [x] Update `dash_app/components/sidebar.py`:
- Rename "Pathway Overview" → "Patient Pathways"
- Add "Trust Comparison" nav item below it
- Active state tracks `active_view`
-- [ ] Add callback: sidebar clicks → update `active_view` in app-state
-- [ ] Main content area switches between Patient Pathways view and Trust Comparison view based on `active_view`
-- [ ] Date filter + chart type toggle remain in global sub-header (visible in both views)
-- **Checkpoint**: Sidebar switches between two views, active state highlights correctly, app starts without errors
+- [x] Add callback: sidebar clicks → update `active_view` in app-state
+- [x] Main content area switches between Patient Pathways view and Trust Comparison view based on `active_view`
+- [x] Date filter + chart type toggle remain in global sub-header (visible in both views)
+- **Checkpoint**: Sidebar switches between two views, active state highlights correctly, app starts without errors ✓
### 10.3 Header redesign — remove KPI row, add fraction KPIs
- [ ] Remove `dash_app/components/kpi_row.py` (or gut it)
diff --git a/dash_app/app.py b/dash_app/app.py
index e2947a2..6864824 100644
--- a/dash_app/app.py
+++ b/dash_app/app.py
@@ -26,6 +26,8 @@ app.layout = dmc.MantineProvider(
"selected_drugs": [],
"selected_directorates": [],
"selected_trusts": [],
+ "active_view": "patient-pathways",
+ "selected_comparison_directorate": None,
}),
dcc.Store(id="chart-data", storage_type="memory"),
dcc.Store(id="reference-data", storage_type="session"),
@@ -39,9 +41,55 @@ app.layout = dmc.MantineProvider(
html.Main(
className="main",
children=[
- make_kpi_row(),
- make_filter_bar(),
- make_chart_card(),
+ # View container — switched by active_view in app-state
+ html.Div(
+ id="view-container",
+ children=[
+ # Patient Pathways view (default, visible)
+ html.Div(
+ id="patient-pathways-view",
+ children=[
+ make_kpi_row(),
+ make_filter_bar(),
+ make_chart_card(),
+ ],
+ ),
+ # Trust Comparison view (hidden initially)
+ html.Div(
+ id="trust-comparison-view",
+ style={"display": "none"},
+ children=[
+ html.Div(
+ className="tc-landing",
+ id="trust-comparison-landing",
+ children=[
+ html.Div(
+ className="tc-landing__header",
+ children=[
+ html.H2(
+ "Trust Comparison",
+ className="tc-landing__title",
+ ),
+ html.P(
+ "Select a directorate to compare drug usage across trusts.",
+ className="tc-landing__desc",
+ id="tc-landing-desc",
+ ),
+ ],
+ ),
+ html.Div(
+ className="tc-landing__grid",
+ id="tc-landing-grid",
+ children=[
+ # Populated by callback in later task
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
make_footer(),
],
),
diff --git a/dash_app/callbacks/__init__.py b/dash_app/callbacks/__init__.py
index 25e4d32..f53c441 100644
--- a/dash_app/callbacks/__init__.py
+++ b/dash_app/callbacks/__init__.py
@@ -7,8 +7,10 @@ def register_callbacks(app):
from dash_app.callbacks.chart import register_chart_callbacks
from dash_app.callbacks.kpi import register_kpi_callbacks
from dash_app.callbacks.modals import register_modal_callbacks
+ from dash_app.callbacks.navigation import register_navigation_callbacks
register_filter_callbacks(app)
register_chart_callbacks(app)
register_kpi_callbacks(app)
register_modal_callbacks(app)
+ register_navigation_callbacks(app)
diff --git a/dash_app/callbacks/filters.py b/dash_app/callbacks/filters.py
index 8352212..bb26649 100644
--- a/dash_app/callbacks/filters.py
+++ b/dash_app/callbacks/filters.py
@@ -73,13 +73,15 @@ def register_filter_callbacks(app):
Input("filter-last-seen", "value"),
Input("all-drugs-chips", "value"),
Input("trust-chips", "value"),
+ Input("nav-patient-pathways", "n_clicks"),
+ Input("nav-trust-comparison", "n_clicks"),
State("app-state", "data"),
)
def update_app_state(
_dir_clicks, _ind_clicks, initiated, last_seen, selected_drugs,
- selected_trusts, current_state
+ selected_trusts, _nav_pp_clicks, _nav_tc_clicks, current_state
):
- """Update app-state when chart type toggle, date filters, drug chips, or trust chips change."""
+ """Update app-state when chart type toggle, date filters, drug/trust chips, or sidebar nav change."""
if not current_state:
current_state = {
"chart_type": "directory",
@@ -89,6 +91,8 @@ def register_filter_callbacks(app):
"selected_drugs": [],
"selected_directorates": [],
"selected_trusts": [],
+ "active_view": "patient-pathways",
+ "selected_comparison_directorate": None,
}
triggered_id = ctx.triggered_id
@@ -100,6 +104,13 @@ def register_filter_callbacks(app):
elif triggered_id == "chart-type-indication":
chart_type = "indication"
+ # Determine active view from sidebar nav
+ active_view = current_state.get("active_view", "patient-pathways")
+ if triggered_id == "nav-patient-pathways":
+ active_view = "patient-pathways"
+ elif triggered_id == "nav-trust-comparison":
+ active_view = "trust-comparison"
+
# Compute date_filter_id from dropdown values
date_filter_id = f"{initiated}_{last_seen}"
@@ -112,12 +123,13 @@ def register_filter_callbacks(app):
"date_filter_id": date_filter_id,
"selected_drugs": selected_drugs or [],
"selected_trusts": selected_trusts or [],
+ "active_view": active_view,
}
# Toggle pill CSS classes
base = "toggle-pill"
- active = f"{base} toggle-pill--active"
- dir_class = active if chart_type == "directory" else base
- ind_class = active if chart_type == "indication" else base
+ active_cls = f"{base} toggle-pill--active"
+ dir_class = active_cls if chart_type == "directory" else base
+ ind_class = active_cls if chart_type == "indication" else base
return updated_state, dir_class, ind_class
diff --git a/dash_app/callbacks/navigation.py b/dash_app/callbacks/navigation.py
new file mode 100644
index 0000000..2bfe3ae
--- /dev/null
+++ b/dash_app/callbacks/navigation.py
@@ -0,0 +1,29 @@
+"""Callbacks for view switching between Patient Pathways and Trust Comparison."""
+from dash import Input, Output
+
+
+def register_navigation_callbacks(app):
+ """Register view switching callbacks."""
+
+ @app.callback(
+ Output("patient-pathways-view", "style"),
+ Output("trust-comparison-view", "style"),
+ Output("nav-patient-pathways", "className"),
+ Output("nav-trust-comparison", "className"),
+ Input("app-state", "data"),
+ )
+ def switch_view(app_state):
+ """Show/hide views and update sidebar active state based on active_view."""
+ if not app_state:
+ return {}, {"display": "none"}, "sidebar__item sidebar__item--active", "sidebar__item"
+
+ view = app_state.get("active_view", "patient-pathways")
+ show = {}
+ hide = {"display": "none"}
+ active_cls = "sidebar__item sidebar__item--active"
+ inactive_cls = "sidebar__item"
+
+ if view == "patient-pathways":
+ return show, hide, active_cls, inactive_cls
+ else:
+ return hide, show, inactive_cls, active_cls
diff --git a/dash_app/components/sidebar.py b/dash_app/components/sidebar.py
index 777a43a..6197848 100644
--- a/dash_app/components/sidebar.py
+++ b/dash_app/components/sidebar.py
@@ -19,6 +19,7 @@ def _svg_icon(svg_body):
# SVG icon bodies (Feather-style)
_ICONS = {
"pathway": '',
+ "compare": '',
}
@@ -28,15 +29,20 @@ def make_sidebar():
className="sidebar",
**{"aria-label": "Main navigation"},
children=[
- # Overview section
html.Div(
className="sidebar__section",
children=[
- html.Div("Overview", className="sidebar__label"),
- _sidebar_item("Pathway Overview", "pathway", active=True),
+ html.Div("Analysis", className="sidebar__label"),
+ _sidebar_item(
+ "Patient Pathways", "pathway",
+ active=True, item_id="nav-patient-pathways",
+ ),
+ _sidebar_item(
+ "Trust Comparison", "compare",
+ active=False, item_id="nav-trust-comparison",
+ ),
],
),
- # Footer
html.Div(
className="sidebar__footer",
children=[