diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md
index 913598d..b107898 100644
--- a/IMPLEMENTATION_PLAN.md
+++ b/IMPLEMENTATION_PLAN.md
@@ -286,12 +286,12 @@ Drawer selection → update_drug_selection → app-state store → load_pathway_
- **Checkpoint**: Drug selection filters chart without "multiple implied roots" error.
### 7.3 Restructure sidebar: move chart views to sidebar, remove placeholder items
-- [ ] **Remove** from sidebar: "Cost Analysis" and "Export Data" items (no functionality behind them)
-- [ ] **Remove** from sidebar: "Drug Selection", "Trust Selection", "Directory Selection", "Indications" items (filters moving to top bar — see 7.5)
-- [ ] **Add** to sidebar: chart view buttons — "Icicle Chart" (active), "Sankey Diagram" (disabled), "Timeline" (disabled). These replace the tab row currently in chart_card.py.
-- [ ] **Keep**: "Pathway Overview" as the top active item
-- [ ] Update sidebar IDs and callback wiring. The chart type toggle pills (By Directory / By Indication) stay in the filter bar — they're data filters, not view selectors.
-- [ ] Remove the tab row from `chart_card.py` since chart view selection moves to sidebar
+- [x] **Remove** from sidebar: "Cost Analysis" and "Export Data" items (no functionality behind them)
+- [x] **Remove** from sidebar: "Drug Selection", "Trust Selection", "Directory Selection", "Indications" items (filters moving to top bar — see 7.5)
+- [x] **Add** to sidebar: chart view buttons — "Icicle Chart" (active), "Sankey Diagram" (disabled), "Timeline" (disabled). These replace the tab row currently in chart_card.py.
+- [x] **Keep**: "Pathway Overview" as the top active item
+- [x] Update sidebar IDs and callback wiring. The chart type toggle pills (By Directory / By Indication) stay in the filter bar — they're data filters, not view selectors.
+- [x] Remove the tab row from `chart_card.py` since chart view selection moves to sidebar
- **Checkpoint**: Sidebar shows chart view options, no placeholder items, app runs without errors.
### 7.4 Replace dmc.Drawer with dmc.Modal for filter selection
@@ -335,8 +335,8 @@ All tasks marked `[x]` AND:
- [x] "Clear Filters" resets all selections
- [x] KPIs update dynamically (patients, drugs, cost)
- [x] No Reflex imports in `dash_app/`
-- [ ] No duplicate component ID errors on first load
-- [ ] Sidebar shows chart views (icicle/sankey/timeline), not filter triggers
+- [x] No duplicate component ID errors on first load
+- [x] Sidebar shows chart views (icicle/sankey/timeline), not filter triggers
- [ ] Filter bar has drug/trust/directorate trigger buttons with selection count badges
---
diff --git a/dash_app/assets/nhs.css b/dash_app/assets/nhs.css
index 050c50b..d6c7e2c 100644
--- a/dash_app/assets/nhs.css
+++ b/dash_app/assets/nhs.css
@@ -100,6 +100,11 @@ body {
color: var(--nhs-blue);
font-weight: 600;
}
+.sidebar__item--disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+ pointer-events: none;
+}
.sidebar__item svg { width: 18px; height: 18px; flex-shrink: 0; }
.sidebar__icon { width: 18px; height: 18px; flex-shrink: 0; }
.sidebar__footer {
diff --git a/dash_app/callbacks/drawer.py b/dash_app/callbacks/drawer.py
index 65e2a51..49f7b50 100644
--- a/dash_app/callbacks/drawer.py
+++ b/dash_app/callbacks/drawer.py
@@ -1,20 +1,14 @@
-"""Callbacks for the drug browser drawer: open/close, drug selection, fragment matching, clear."""
+"""Callbacks for drug selection: fragment matching and clear filters.
+
+The open_drawer callback was removed in Task 7.3 (sidebar restructure) because
+the sidebar no longer has filter trigger items. Task 7.4 will replace the drawer
+with modals opened from the filter bar.
+"""
from dash import Input, Output, State, ctx, no_update, ALL
def register_drawer_callbacks(app):
- """Register drawer-related callbacks."""
-
- @app.callback(
- Output("drug-drawer", "opened"),
- Input("sidebar-drug-selection", "n_clicks"),
- Input("sidebar-indications", "n_clicks"),
- Input("sidebar-trust-selection", "n_clicks"),
- prevent_initial_call=True,
- )
- def open_drawer(_drug_clicks, _indication_clicks, _trust_clicks):
- """Open the drawer when sidebar Drug Selection, Indications, or Trust Selection is clicked."""
- return True
+ """Register drug/trust selection callbacks (fragment matching + clear)."""
@app.callback(
Output("all-drugs-chips", "value"),
diff --git a/dash_app/components/chart_card.py b/dash_app/components/chart_card.py
index 067bbd2..fb3043b 100644
--- a/dash_app/components/chart_card.py
+++ b/dash_app/components/chart_card.py
@@ -1,4 +1,4 @@
-"""Chart card component — header, tabs, and dcc.Graph for icicle chart."""
+"""Chart card component — header and dcc.Graph for icicle chart."""
from dash import html, dcc
@@ -7,8 +7,8 @@ def make_chart_card():
Contains:
- Header with title and dynamic subtitle (hierarchy label)
- - Tab row (Icicle active, Sankey and Timeline as disabled placeholders)
- dcc.Loading wrapper around dcc.Graph for loading spinner
+ Chart view selection (icicle/sankey/timeline) is in the sidebar.
"""
return html.Section(
className="chart-card",
@@ -33,33 +33,6 @@ def make_chart_card():
),
],
),
- # Tab row
- html.Div(
- className="chart-card__tabs",
- role="tablist",
- children=[
- html.Button(
- "Icicle",
- className="chart-tab chart-tab--active",
- role="tab",
- **{"aria-selected": "true"},
- ),
- html.Button(
- "Sankey",
- className="chart-tab",
- role="tab",
- disabled=True,
- **{"aria-selected": "false"},
- ),
- html.Button(
- "Timeline",
- className="chart-tab",
- role="tab",
- disabled=True,
- **{"aria-selected": "false"},
- ),
- ],
- ),
# Chart area with loading spinner
dcc.Loading(
type="circle",
diff --git a/dash_app/components/sidebar.py b/dash_app/components/sidebar.py
index 42326ca..47d49fd 100644
--- a/dash_app/components/sidebar.py
+++ b/dash_app/components/sidebar.py
@@ -5,11 +5,7 @@ from dash import html
def _svg_icon(svg_body):
- """Wrap an SVG body string into an html.Img using a data URI.
-
- This avoids needing dash-svg or dangerouslySetInnerHTML.
- The SVG icons are copied from 01_nhs_classic.html.
- """
+ """Wrap an SVG body string into an html.Img using a data URI."""
svg = (
f''
@@ -20,15 +16,12 @@ def _svg_icon(svg_body):
)
-# SVG icon bodies from 01_nhs_classic.html
+# SVG icon bodies (Feather-style)
_ICONS = {
"pathway": '',
- "drug": '',
- "trust": '',
- "directory": '',
- "indication": '',
- "cost": '',
- "export": '',
+ "icicle": '',
+ "sankey": '',
+ "timeline": '',
}
@@ -38,31 +31,22 @@ def make_sidebar():
className="sidebar",
**{"aria-label": "Main navigation"},
children=[
- # Analysis section
+ # Overview section
html.Div(
className="sidebar__section",
children=[
- html.Div("Analysis", className="sidebar__label"),
+ html.Div("Overview", className="sidebar__label"),
_sidebar_item("Pathway Overview", "pathway", active=True),
- _sidebar_item(
- "Drug Selection", "drug", item_id="sidebar-drug-selection"
- ),
- _sidebar_item(
- "Trust Selection", "trust", item_id="sidebar-trust-selection"
- ),
- _sidebar_item("Directory Selection", "directory"),
- _sidebar_item(
- "Indications", "indication", item_id="sidebar-indications"
- ),
],
),
- # Reports section
+ # Chart views section
html.Div(
className="sidebar__section",
children=[
- html.Div("Reports", className="sidebar__label"),
- _sidebar_item("Cost Analysis", "cost"),
- _sidebar_item("Export Data", "export"),
+ html.Div("Chart Views", className="sidebar__label"),
+ _sidebar_item("Icicle Chart", "icicle", active=True),
+ _sidebar_item("Sankey Diagram", "sankey", disabled=True),
+ _sidebar_item("Timeline", "timeline", disabled=True),
],
),
# Footer
@@ -78,11 +62,13 @@ def make_sidebar():
)
-def _sidebar_item(label, icon_key, active=False, item_id=None):
+def _sidebar_item(label, icon_key, active=False, disabled=False, item_id=None):
"""Create a single sidebar navigation item."""
class_name = "sidebar__item"
if active:
class_name += " sidebar__item--active"
+ if disabled:
+ class_name += " sidebar__item--disabled"
props = {"className": class_name}
if item_id: