feat: add global filter sub-header bar (Task 10.4)
Extract chart type toggle and date filter dropdowns from filter_bar.py into a new sub-header component. Sub-header is fixed-position below the main header, visible across both views. Filter bar now contains only drug/trust/directorate buttons for Patient Pathways view.
This commit is contained in:
@@ -494,13 +494,13 @@ Additionally: KPI row removed, fraction KPIs moved to header, global filter sub-
|
|||||||
- **Checkpoint**: Header shows fraction KPIs, KPI row is gone, header looks clean with design from 10.1 ✓
|
- **Checkpoint**: Header shows fraction KPIs, KPI row is gone, header looks clean with design from 10.1 ✓
|
||||||
|
|
||||||
### 10.4 Global filter sub-header bar
|
### 10.4 Global filter sub-header bar
|
||||||
- [ ] Extract date filter dropdowns + chart type toggle from `filter_bar.py` into a new sub-header component (or restyle existing filter_bar)
|
- [x] Extract date filter dropdowns + chart type toggle from `filter_bar.py` into a new sub-header component (or restyle existing filter_bar)
|
||||||
- [ ] Style as a prominent bar directly below the main header — visually distinct per design from 10.1
|
- [x] Style as a prominent bar directly below the main header — visually distinct per design from 10.1
|
||||||
- [ ] Remove drug/trust/directorate filter buttons from this bar (they move to Patient Pathways view only — see 10.7)
|
- [x] Remove drug/trust/directorate filter buttons from this bar (they move to Patient Pathways view only — see 10.7)
|
||||||
- [ ] Ensure sub-header is constant across both views (Patient Pathways and Trust Comparison)
|
- [x] Ensure sub-header is constant across both views (Patient Pathways and Trust Comparison)
|
||||||
- [ ] Date filter and chart type toggle changes update `app-state` globally (triggering updates in whichever view is active)
|
- [x] Date filter and chart type toggle changes update `app-state` globally (triggering updates in whichever view is active)
|
||||||
- [ ] Update CSS per design from 10.1
|
- [x] Update CSS per design from 10.1
|
||||||
- **Checkpoint**: Global sub-header renders below main header, date/chart-type controls work, visible in both views
|
- **Checkpoint**: Global sub-header renders below main header, date/chart-type controls work, visible in both views ✓
|
||||||
|
|
||||||
### 10.5 Patient Pathways view — reduce to Icicle + Sankey
|
### 10.5 Patient Pathways view — reduce to Icicle + Sankey
|
||||||
- [ ] Create a Patient Pathways view component (or update chart_card.py) with only 2 tabs: Icicle, Sankey
|
- [ ] Create a Patient Pathways view component (or update chart_card.py) with only 2 tabs: Icicle, Sankey
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from dash import Dash, html, dcc
|
|||||||
import dash_mantine_components as dmc
|
import dash_mantine_components as dmc
|
||||||
|
|
||||||
from dash_app.components.header import make_header
|
from dash_app.components.header import make_header
|
||||||
|
from dash_app.components.sub_header import make_sub_header
|
||||||
from dash_app.components.sidebar import make_sidebar
|
from dash_app.components.sidebar import make_sidebar
|
||||||
from dash_app.components.filter_bar import make_filter_bar
|
from dash_app.components.filter_bar import make_filter_bar
|
||||||
from dash_app.components.chart_card import make_chart_card
|
from dash_app.components.chart_card import make_chart_card
|
||||||
@@ -35,6 +36,7 @@ app.layout = dmc.MantineProvider(
|
|||||||
|
|
||||||
# Page structure
|
# Page structure
|
||||||
make_header(),
|
make_header(),
|
||||||
|
make_sub_header(),
|
||||||
make_sidebar(),
|
make_sidebar(),
|
||||||
make_modals(),
|
make_modals(),
|
||||||
html.Main(
|
html.Main(
|
||||||
|
|||||||
+37
-2
@@ -155,12 +155,47 @@ body {
|
|||||||
font-size: 12px; color: var(--nhs-mid-grey);
|
font-size: 12px; color: var(--nhs-mid-grey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Global Filter Sub-Header ── */
|
||||||
|
.sub-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 56px;
|
||||||
|
left: var(--sidebar-w);
|
||||||
|
right: 0;
|
||||||
|
z-index: 150;
|
||||||
|
height: 44px;
|
||||||
|
background: #E8F0FE;
|
||||||
|
border-bottom: 1px solid #C5D4E8;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 24px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.sub-header__group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.sub-header__label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
color: var(--nhs-dark-blue);
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
.sub-header__divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 24px;
|
||||||
|
background: rgba(0, 48, 135, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Main Content ── */
|
/* ── Main Content ── */
|
||||||
.main {
|
.main {
|
||||||
margin-left: var(--sidebar-w);
|
margin-left: var(--sidebar-w);
|
||||||
margin-top: 56px;
|
margin-top: 100px;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
min-height: calc(100vh - 56px);
|
min-height: calc(100vh - 100px);
|
||||||
display: flex; flex-direction: column; gap: 20px;
|
display: flex; flex-direction: column; gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,88 +1,17 @@
|
|||||||
"""Filter bar component — chart type toggle, date filters, and modal trigger buttons."""
|
"""Filter bar component — drug, trust, and directorate filter buttons.
|
||||||
from dash import html, dcc
|
|
||||||
|
View-specific controls for Patient Pathways. Global controls (chart type
|
||||||
|
toggle, date filters) live in sub_header.py.
|
||||||
|
"""
|
||||||
|
from dash import html
|
||||||
|
|
||||||
|
|
||||||
def make_filter_bar():
|
def make_filter_bar():
|
||||||
"""Return a filter bar with chart type toggle, date dropdowns, and filter buttons.
|
"""Return a filter bar with drug, trust, and directorate filter buttons."""
|
||||||
|
|
||||||
Filter buttons open modals for drug, trust, and directorate selection.
|
|
||||||
Each button shows a selection count badge (updated via callbacks).
|
|
||||||
"""
|
|
||||||
return html.Section(
|
return html.Section(
|
||||||
className="filter-bar",
|
className="filter-bar",
|
||||||
**{"aria-label": "Filters"},
|
**{"aria-label": "Filters"},
|
||||||
children=[
|
children=[
|
||||||
# Chart type toggle
|
|
||||||
html.Div(
|
|
||||||
className="filter-bar__group",
|
|
||||||
children=[
|
|
||||||
html.Span("View", className="filter-bar__label"),
|
|
||||||
html.Div(
|
|
||||||
className="toggle-pills",
|
|
||||||
role="radiogroup",
|
|
||||||
**{"aria-label": "Chart view type"},
|
|
||||||
children=[
|
|
||||||
html.Button(
|
|
||||||
"By Directory",
|
|
||||||
id="chart-type-directory",
|
|
||||||
className="toggle-pill toggle-pill--active",
|
|
||||||
role="radio",
|
|
||||||
n_clicks=0,
|
|
||||||
**{"aria-checked": "true"},
|
|
||||||
),
|
|
||||||
html.Button(
|
|
||||||
"By Indication",
|
|
||||||
id="chart-type-indication",
|
|
||||||
className="toggle-pill",
|
|
||||||
role="radio",
|
|
||||||
n_clicks=0,
|
|
||||||
**{"aria-checked": "false"},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
# Divider
|
|
||||||
html.Div(className="filter-bar__divider"),
|
|
||||||
# Initiated filter
|
|
||||||
html.Div(
|
|
||||||
className="filter-bar__group",
|
|
||||||
children=[
|
|
||||||
html.Span("Initiated", className="filter-bar__label"),
|
|
||||||
dcc.Dropdown(
|
|
||||||
id="filter-initiated",
|
|
||||||
options=[
|
|
||||||
{"label": "All years", "value": "all"},
|
|
||||||
{"label": "Last 2 years", "value": "2yr"},
|
|
||||||
{"label": "Last 1 year", "value": "1yr"},
|
|
||||||
],
|
|
||||||
value="all",
|
|
||||||
clearable=False,
|
|
||||||
searchable=False,
|
|
||||||
className="filter-dropdown",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
# Last seen filter
|
|
||||||
html.Div(
|
|
||||||
className="filter-bar__group",
|
|
||||||
children=[
|
|
||||||
html.Span("Last seen", className="filter-bar__label"),
|
|
||||||
dcc.Dropdown(
|
|
||||||
id="filter-last-seen",
|
|
||||||
options=[
|
|
||||||
{"label": "Last 6 months", "value": "6mo"},
|
|
||||||
{"label": "Last 12 months", "value": "12mo"},
|
|
||||||
],
|
|
||||||
value="6mo",
|
|
||||||
clearable=False,
|
|
||||||
searchable=False,
|
|
||||||
className="filter-dropdown",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
# Divider before filter buttons
|
|
||||||
html.Div(className="filter-bar__divider"),
|
|
||||||
# Filter trigger buttons
|
# Filter trigger buttons
|
||||||
html.Div(
|
html.Div(
|
||||||
className="filter-bar__group",
|
className="filter-bar__group",
|
||||||
@@ -90,7 +19,10 @@ def make_filter_bar():
|
|||||||
html.Button(
|
html.Button(
|
||||||
children=[
|
children=[
|
||||||
"Drugs",
|
"Drugs",
|
||||||
html.Span(id="drug-count-badge", className="filter-btn__badge filter-btn__badge--hidden"),
|
html.Span(
|
||||||
|
id="drug-count-badge",
|
||||||
|
className="filter-btn__badge filter-btn__badge--hidden",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
id="open-drug-modal",
|
id="open-drug-modal",
|
||||||
className="filter-btn",
|
className="filter-btn",
|
||||||
@@ -99,7 +31,10 @@ def make_filter_bar():
|
|||||||
html.Button(
|
html.Button(
|
||||||
children=[
|
children=[
|
||||||
"Trusts",
|
"Trusts",
|
||||||
html.Span(id="trust-count-badge", className="filter-btn__badge filter-btn__badge--hidden"),
|
html.Span(
|
||||||
|
id="trust-count-badge",
|
||||||
|
className="filter-btn__badge filter-btn__badge--hidden",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
id="open-trust-modal",
|
id="open-trust-modal",
|
||||||
className="filter-btn",
|
className="filter-btn",
|
||||||
@@ -108,7 +43,10 @@ def make_filter_bar():
|
|||||||
html.Button(
|
html.Button(
|
||||||
children=[
|
children=[
|
||||||
"Directorates",
|
"Directorates",
|
||||||
html.Span(id="directorate-count-badge", className="filter-btn__badge filter-btn__badge--hidden"),
|
html.Span(
|
||||||
|
id="directorate-count-badge",
|
||||||
|
className="filter-btn__badge filter-btn__badge--hidden",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
id="open-directorate-modal",
|
id="open-directorate-modal",
|
||||||
className="filter-btn",
|
className="filter-btn",
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
"""Global filter sub-header — chart type toggle + date filter dropdowns.
|
||||||
|
|
||||||
|
Fixed bar below the main header, constant across both views.
|
||||||
|
"""
|
||||||
|
from dash import html, dcc
|
||||||
|
|
||||||
|
|
||||||
|
def make_sub_header():
|
||||||
|
"""Return the global filter sub-header bar."""
|
||||||
|
return html.Div(
|
||||||
|
className="sub-header",
|
||||||
|
children=[
|
||||||
|
# Chart type toggle
|
||||||
|
html.Div(
|
||||||
|
className="sub-header__group",
|
||||||
|
children=[
|
||||||
|
html.Span("View", className="sub-header__label"),
|
||||||
|
html.Div(
|
||||||
|
className="toggle-pills",
|
||||||
|
role="radiogroup",
|
||||||
|
**{"aria-label": "Chart view type"},
|
||||||
|
children=[
|
||||||
|
html.Button(
|
||||||
|
"By Directory",
|
||||||
|
id="chart-type-directory",
|
||||||
|
className="toggle-pill toggle-pill--active",
|
||||||
|
role="radio",
|
||||||
|
n_clicks=0,
|
||||||
|
**{"aria-checked": "true"},
|
||||||
|
),
|
||||||
|
html.Button(
|
||||||
|
"By Indication",
|
||||||
|
id="chart-type-indication",
|
||||||
|
className="toggle-pill",
|
||||||
|
role="radio",
|
||||||
|
n_clicks=0,
|
||||||
|
**{"aria-checked": "false"},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
# Divider
|
||||||
|
html.Div(className="sub-header__divider"),
|
||||||
|
# Initiated filter
|
||||||
|
html.Div(
|
||||||
|
className="sub-header__group",
|
||||||
|
children=[
|
||||||
|
html.Span("Initiated", className="sub-header__label"),
|
||||||
|
dcc.Dropdown(
|
||||||
|
id="filter-initiated",
|
||||||
|
options=[
|
||||||
|
{"label": "All years", "value": "all"},
|
||||||
|
{"label": "Last 2 years", "value": "2yr"},
|
||||||
|
{"label": "Last 1 year", "value": "1yr"},
|
||||||
|
],
|
||||||
|
value="all",
|
||||||
|
clearable=False,
|
||||||
|
searchable=False,
|
||||||
|
className="filter-dropdown",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
# Last seen filter
|
||||||
|
html.Div(
|
||||||
|
className="sub-header__group",
|
||||||
|
children=[
|
||||||
|
html.Span("Last seen", className="sub-header__label"),
|
||||||
|
dcc.Dropdown(
|
||||||
|
id="filter-last-seen",
|
||||||
|
options=[
|
||||||
|
{"label": "Last 6 months", "value": "6mo"},
|
||||||
|
{"label": "Last 12 months", "value": "12mo"},
|
||||||
|
],
|
||||||
|
value="6mo",
|
||||||
|
clearable=False,
|
||||||
|
searchable=False,
|
||||||
|
className="filter-dropdown",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user