Files
HighCostDrugsDemo/dash_app/components/drawer.py
T

173 lines
5.3 KiB
Python

"""
Drug browser drawer component using Dash Mantine Components.
Provides a right-side drawer with:
- "All Drugs" section: flat alphabetical list of all drugs from pathway_nodes
- Directorate cards: grouped by PrimaryDirectorate from DimSearchTerm.csv,
with Accordion items per Search_Term containing drug fragment chips
- "Clear Filters" button at the bottom
"""
from dash import html
import dash_mantine_components as dmc
from dash_app.data.card_browser import build_directorate_tree, get_all_drugs
def _make_drug_chips(drugs: list[str]) -> dmc.ChipGroup:
"""Create a ChipGroup with multiple selection for the 'All Drugs' section."""
return dmc.ChipGroup(
id="all-drugs-chips",
multiple=True,
value=[],
children=[
dmc.Chip(drug, value=drug, size="xs")
for drug in drugs
],
)
def _make_directorate_card(directorate: str, indications: dict[str, list[str]]) -> dmc.AccordionItem:
"""
Create an AccordionItem for a single directorate.
Each indication becomes a panel with drug fragment badges inside.
"""
panels = []
for search_term, fragments in indications.items():
panels.append(
dmc.AccordionItem(
value=f"{directorate}|{search_term}",
children=[
dmc.AccordionControl(
search_term.title(),
className="drawer-indication",
),
dmc.AccordionPanel(
dmc.Group(
gap="xs",
children=[
dmc.Badge(
frag,
id={"type": "drug-fragment", "index": f"{directorate}|{frag}"},
variant="light",
size="sm",
className="drawer-drug-badge",
style={"cursor": "pointer"},
)
for frag in fragments
],
),
),
],
)
)
return dmc.AccordionItem(
value=directorate,
children=[
dmc.AccordionControl(
dmc.Group(
gap="xs",
children=[
dmc.Text(directorate.title(), fw=600, size="sm"),
dmc.Badge(
str(len(indications)),
size="xs",
variant="light",
color="gray",
),
],
),
),
dmc.AccordionPanel(
dmc.Accordion(
variant="separated",
children=panels,
),
),
],
)
def make_drawer():
"""
Build the drug browser drawer component.
Returns a dmc.Drawer that will be opened/closed via callbacks in Phase 4.2.
"""
drugs = get_all_drugs()
directorate_tree = build_directorate_tree()
# All Drugs section
all_drugs_section = html.Div(
className="drawer-section",
children=[
dmc.Text("All Drugs", fw=700, size="sm", className="drawer-section-title"),
dmc.Text(
f"{len(drugs)} drugs from pathway data",
size="xs",
c="dimmed",
),
html.Div(
className="drawer-chips-wrap",
children=_make_drug_chips(drugs),
),
],
)
# Directorate cards section
directorate_items = [
_make_directorate_card(directorate, indications)
for directorate, indications in directorate_tree.items()
]
directorate_section = html.Div(
className="drawer-section",
children=[
dmc.Text("By Directorate", fw=700, size="sm", className="drawer-section-title"),
dmc.Text(
f"{len(directorate_tree)} directorates \u00b7 {sum(len(v) for v in directorate_tree.values())} indications",
size="xs",
c="dimmed",
),
dmc.Accordion(
variant="separated",
children=directorate_items,
className="drawer-directorate-accordion",
),
],
)
# Clear filters button
clear_button = dmc.Button(
"Clear All Filters",
id="clear-drug-filters",
variant="outline",
color="red",
fullWidth=True,
className="drawer-clear-btn",
)
return dmc.Drawer(
id="drug-drawer",
opened=False,
position="right",
size="480px",
title=dmc.Text("Drug & Indication Browser", fw=700, size="lg"),
children=[
dmc.ScrollArea(
h="calc(100vh - 140px)",
children=dmc.Stack(
gap="md",
children=[
all_drugs_section,
dmc.Divider(),
directorate_section,
clear_button,
],
),
),
],
)