feat: add Trends sidebar nav item + 3-view switching (Task E.2)
This commit is contained in:
@@ -265,16 +265,16 @@ Comprehensive review and improvement of all Plotly charts in the Dash dashboard.
|
||||
- **Checkpoint**: Patient Pathways has 9 tabs (Icicle through Doses, no Trends). `python run_dash.py` starts cleanly. PASSED.
|
||||
|
||||
### E.2 Add Trends sidebar nav item + view container
|
||||
- [ ] Add `"trends"` icon SVG to `_ICONS` dict in `dash_app/components/sidebar.py` — use a line chart icon: `<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>`
|
||||
- [ ] Add `_sidebar_item("Trends", "trends", active=False, item_id="nav-trends")` to sidebar children
|
||||
- [ ] Add `html.Div(id="trends-view", style={"display": "none"}, children=[...])` to `app.py` layout inside `view-container`, after `trust-comparison-view`
|
||||
- [ ] Update `switch_view()` in `dash_app/callbacks/navigation.py`:
|
||||
- [x] Add `"trends"` icon SVG to `_ICONS` dict in `dash_app/components/sidebar.py` — use a line chart icon: `<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>`
|
||||
- [x] Add `_sidebar_item("Trends", "trends", active=False, item_id="nav-trends")` to sidebar children
|
||||
- [x] Add `html.Div(id="trends-view", style={"display": "none"}, children=[...])` to `app.py` layout inside `view-container`, after `trust-comparison-view`
|
||||
- [x] Update `switch_view()` in `dash_app/callbacks/navigation.py`:
|
||||
- Add `Output("trends-view", "style")` and `Output("nav-trends", "className")` — now 3 views, 3 nav items (6 outputs total)
|
||||
- Handle 3-way switching: `"patient-pathways"`, `"trust-comparison"`, `"trends"`
|
||||
- [ ] Update `update_app_state()` in `dash_app/callbacks/filters.py`:
|
||||
- [x] Update `update_app_state()` in `dash_app/callbacks/filters.py`:
|
||||
- Add `Input("nav-trends", "n_clicks")`
|
||||
- Add `elif triggered_id == "nav-trends": active_view = "trends"` case
|
||||
- **Checkpoint**: 3 sidebar items visible. Clicking "Trends" switches to empty trends view. `python run_dash.py` starts cleanly.
|
||||
- **Checkpoint**: 3 sidebar items visible. Clicking "Trends" switches to empty trends view. `python run_dash.py` starts cleanly. PASSED.
|
||||
|
||||
### E.3 Create Trends landing page — directorate-level trends
|
||||
- [ ] Create `dash_app/components/trends.py`:
|
||||
@@ -354,8 +354,8 @@ Comprehensive review and improvement of all Plotly charts in the Dash dashboard.
|
||||
- [x] `python run_dash.py` starts cleanly
|
||||
|
||||
### Phase E
|
||||
- [ ] Trends tab removed from Patient Pathways (9 tabs remain)
|
||||
- [ ] 3rd sidebar item "Trends" visible and functional
|
||||
- [x] Trends tab removed from Patient Pathways (9 tabs remain)
|
||||
- [x] 3rd sidebar item "Trends" visible and functional
|
||||
- [ ] Trends landing page shows directorate-level line chart with metric toggle
|
||||
- [ ] Clicking a directorate drills into drug-level trends
|
||||
- [ ] Back button returns to directorate overview
|
||||
|
||||
@@ -29,6 +29,7 @@ app.layout = dmc.MantineProvider(
|
||||
"selected_trusts": [],
|
||||
"active_view": "patient-pathways",
|
||||
"selected_comparison_directorate": None,
|
||||
"selected_trends_directorate": None,
|
||||
}),
|
||||
dcc.Store(id="chart-data", storage_type="memory"),
|
||||
dcc.Store(id="reference-data", storage_type="session"),
|
||||
@@ -64,6 +65,14 @@ app.layout = dmc.MantineProvider(
|
||||
make_tc_dashboard(),
|
||||
],
|
||||
),
|
||||
# Trends view (hidden initially)
|
||||
html.Div(
|
||||
id="trends-view",
|
||||
style={"display": "none"},
|
||||
children=[
|
||||
html.H3("Trends", style={"padding": "24px"}),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
make_footer(),
|
||||
|
||||
@@ -75,13 +75,14 @@ def register_filter_callbacks(app):
|
||||
Input("trust-chips", "value"),
|
||||
Input("nav-patient-pathways", "n_clicks"),
|
||||
Input("nav-trust-comparison", "n_clicks"),
|
||||
Input("nav-trends", "n_clicks"),
|
||||
Input({"type": "tc-selector", "index": ALL}, "n_clicks"),
|
||||
Input("tc-back-btn", "n_clicks"),
|
||||
State("app-state", "data"),
|
||||
)
|
||||
def update_app_state(
|
||||
_dir_clicks, _ind_clicks, initiated, last_seen, selected_drugs,
|
||||
selected_trusts, _nav_pp_clicks, _nav_tc_clicks,
|
||||
selected_trusts, _nav_pp_clicks, _nav_tc_clicks, _nav_trends_clicks,
|
||||
_tc_selector_clicks, _tc_back_clicks, current_state
|
||||
):
|
||||
"""Update app-state when any filter, nav, or TC selector changes."""
|
||||
@@ -96,6 +97,7 @@ def register_filter_callbacks(app):
|
||||
"selected_trusts": [],
|
||||
"active_view": "patient-pathways",
|
||||
"selected_comparison_directorate": None,
|
||||
"selected_trends_directorate": None,
|
||||
}
|
||||
|
||||
triggered_id = ctx.triggered_id
|
||||
@@ -114,6 +116,8 @@ def register_filter_callbacks(app):
|
||||
active_view = "patient-pathways"
|
||||
elif triggered_id == "nav-trust-comparison":
|
||||
active_view = "trust-comparison"
|
||||
elif triggered_id == "nav-trends":
|
||||
active_view = "trends"
|
||||
|
||||
# Trust Comparison directorate selection
|
||||
selected_comparison_directorate = current_state.get("selected_comparison_directorate")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Callbacks for view switching between Patient Pathways and Trust Comparison."""
|
||||
"""Callbacks for view switching between Patient Pathways, Trust Comparison, and Trends."""
|
||||
from dash import Input, Output
|
||||
|
||||
|
||||
@@ -8,22 +8,27 @@ def register_navigation_callbacks(app):
|
||||
@app.callback(
|
||||
Output("patient-pathways-view", "style"),
|
||||
Output("trust-comparison-view", "style"),
|
||||
Output("trends-view", "style"),
|
||||
Output("nav-patient-pathways", "className"),
|
||||
Output("nav-trust-comparison", "className"),
|
||||
Output("nav-trends", "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 not app_state:
|
||||
return show, hide, hide, active_cls, inactive_cls, inactive_cls
|
||||
|
||||
view = app_state.get("active_view", "patient-pathways")
|
||||
|
||||
if view == "patient-pathways":
|
||||
return show, hide, active_cls, inactive_cls
|
||||
else:
|
||||
return hide, show, inactive_cls, active_cls
|
||||
return show, hide, hide, active_cls, inactive_cls, inactive_cls
|
||||
elif view == "trust-comparison":
|
||||
return hide, show, hide, inactive_cls, active_cls, inactive_cls
|
||||
else: # trends
|
||||
return hide, hide, show, inactive_cls, inactive_cls, active_cls
|
||||
|
||||
@@ -20,6 +20,7 @@ def _svg_icon(svg_body):
|
||||
_ICONS = {
|
||||
"pathway": '<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>',
|
||||
"compare": '<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>',
|
||||
"trends": '<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>',
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +42,10 @@ def make_sidebar():
|
||||
"Trust Comparison", "compare",
|
||||
active=False, item_id="nav-trust-comparison",
|
||||
),
|
||||
_sidebar_item(
|
||||
"Trends", "trends",
|
||||
active=False, item_id="nav-trends",
|
||||
),
|
||||
],
|
||||
),
|
||||
html.Div(
|
||||
|
||||
Reference in New Issue
Block a user