feat(dashboard): replace home with approved minimal design
CI / ruff · pytest (pull_request) Successful in 2m1s
CI / ruff · pytest (pull_request) Successful in 2m1s
Refs #73
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from html import escape
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from dashboard.components import helpers as h
|
||||
@@ -10,20 +12,31 @@ from dashboard.components import patient_card, state, ui
|
||||
NAV_ICON = ":material/groups:"
|
||||
|
||||
COLOR_FILTERS = {
|
||||
"Все": None,
|
||||
"🔴 Красные": "red",
|
||||
"🟡 Жёлтые": "yellow",
|
||||
"⚪ Нет ответа": "grey",
|
||||
"🟢 Стабильные": "green",
|
||||
"Все пациенты": None,
|
||||
"Срочно": "red",
|
||||
"Внимание": "yellow",
|
||||
"Нет ответа": "grey",
|
||||
"Без уведомлений": "green",
|
||||
}
|
||||
|
||||
|
||||
def _open(token: str) -> None:
|
||||
st.session_state["patient_list_state"] = {
|
||||
key: st.session_state[key]
|
||||
for key in ("status_filter", "patient_query", "patient_details")
|
||||
if key in st.session_state
|
||||
}
|
||||
st.query_params["patient"] = token
|
||||
|
||||
|
||||
def _close() -> None:
|
||||
st.query_params.pop("patient", None)
|
||||
st.session_state.update(st.session_state.pop("patient_list_state", {}))
|
||||
|
||||
|
||||
def _reset_filters() -> None:
|
||||
st.session_state["status_filter"] = "Все пациенты"
|
||||
st.session_state["patient_query"] = ""
|
||||
|
||||
|
||||
def render_card(token: str) -> None:
|
||||
@@ -38,56 +51,70 @@ def render_list() -> None:
|
||||
_, doctor_gen = state.doctor_names()
|
||||
today = state.today()
|
||||
patients = state.call(lambda: state.client().patients())
|
||||
subtitle = "Пациенты на сопровождении между визитами — сначала те, кому нужно внимание сегодня"
|
||||
subtitle = "Всё важное о состоянии пациентов — в одном месте."
|
||||
if today:
|
||||
subtitle += f" · данные на {h.short_date(today)}"
|
||||
|
||||
head, action = st.columns([4, 1.4], vertical_alignment="bottom")
|
||||
with head:
|
||||
st.html(ui.page_header(f"Кабинет {doctor_gen}", subtitle, kicker="Красные зоны"))
|
||||
st.html(ui.page_header("Пациенты под наблюдением", subtitle, kicker="Рабочее пространство"))
|
||||
with action:
|
||||
if st.button("Подключить пациента", icon=":material/person_add:", type="primary", width="stretch"):
|
||||
st.switch_page(state.CONNECT_PAGE)
|
||||
if patients is None:
|
||||
st.button("Повторить загрузку", icon=":material/refresh:")
|
||||
return
|
||||
|
||||
items = h.sort_patients(patients)
|
||||
st.html(ui.kpi_tiles(h.kpis(items)))
|
||||
|
||||
f1, f2 = st.columns([3, 1.4], vertical_alignment="bottom")
|
||||
with f1:
|
||||
choice = st.segmented_control(
|
||||
"Статус", list(COLOR_FILTERS), default="Все", key="status_filter", label_visibility="collapsed"
|
||||
)
|
||||
with f2:
|
||||
query = st.text_input(
|
||||
"Поиск", placeholder="Поиск по имени или препарату", key="patient_query", label_visibility="collapsed"
|
||||
)
|
||||
shown = h.filter_patients(items, COLOR_FILTERS.get(choice or "Все"), query or "")
|
||||
|
||||
head_cols = st.columns([11, 1.5])
|
||||
with head_cols[0]:
|
||||
st.html(ui.list_header())
|
||||
if not shown:
|
||||
st.caption("Нет пациентов с выбранным статусом")
|
||||
for p in shown:
|
||||
with st.container(key=f"prow-{p.status_color}-{p.token}"):
|
||||
row, btn = st.columns([11, 1.5], vertical_alignment="center")
|
||||
with row:
|
||||
st.html(ui.patient_row(p))
|
||||
with btn:
|
||||
st.button(
|
||||
"Открыть",
|
||||
key=f"open-{p.token}",
|
||||
on_click=_open,
|
||||
args=(p.token,),
|
||||
type="primary" if p.status_color == "red" else "secondary",
|
||||
width="stretch",
|
||||
)
|
||||
st.caption(
|
||||
"Цвет — уровень открытых алертов (ADR-006): 🔴 кризис · 🟡 высокий/умеренный · ⚪ только инфо · 🟢 нет. "
|
||||
"Риск прерывания — индекс правил (пропуски, побочки, намерение бросить, низкое настроение), не ML."
|
||||
st.html(
|
||||
f'<div class="mt-section-head"><h2>Ваши пациенты <span>{len(items)}</span></h2>'
|
||||
"<span>Сначала — пациенты с более высоким риском</span></div>"
|
||||
)
|
||||
with st.container(key="patient-list"):
|
||||
f1, f2 = st.columns([3, 1.4], vertical_alignment="bottom")
|
||||
with f1:
|
||||
st.session_state.setdefault("status_filter", "Все пациенты")
|
||||
choice = st.segmented_control(
|
||||
"Статус", list(COLOR_FILTERS), key="status_filter", label_visibility="collapsed"
|
||||
)
|
||||
with f2:
|
||||
query = st.text_input(
|
||||
"Поиск пациента", placeholder="Имя или препарат…", key="patient_query", label_visibility="collapsed"
|
||||
)
|
||||
detailed = st.toggle(
|
||||
"Подробный список", key="patient_details", help="Назначение, последнее событие и факторы риска"
|
||||
)
|
||||
shown = h.filter_patients(items, COLOR_FILTERS.get(choice or "Все пациенты"), query or "")
|
||||
if not items:
|
||||
st.html(ui.empty_state("Пациентов пока нет", "Подключите первого пациента кнопкой вверху страницы."))
|
||||
elif not shown:
|
||||
st.html(ui.empty_state("Пациенты не найдены", "Измените имя, препарат или выбранный статус."))
|
||||
st.button("Сбросить фильтры", on_click=_reset_filters)
|
||||
else:
|
||||
head_cols = st.columns([11, 1.4])
|
||||
with head_cols[0]:
|
||||
st.html(ui.list_header())
|
||||
for p in shown:
|
||||
with st.container(key=f"prow-{p.status_color}-{p.token}"):
|
||||
row, btn = st.columns([11, 1.4], vertical_alignment="center")
|
||||
with row:
|
||||
st.html(ui.patient_row(p, detailed=detailed))
|
||||
with btn:
|
||||
st.button(
|
||||
"Открыть",
|
||||
key=f"open-{p.token}",
|
||||
on_click=_open,
|
||||
args=(p.token,),
|
||||
help=f"Открыть карточку: {p.display_name}",
|
||||
width="stretch",
|
||||
)
|
||||
st.html(
|
||||
'<div class="mt-list-footer"><span role="status" aria-live="polite">'
|
||||
f"Показано {len(shown)} из {len(items)} пациентов</span>"
|
||||
f"<span>Кабинет {escape(doctor_gen)}</span></div>"
|
||||
)
|
||||
st.caption("Статус указан цветом и текстом. Риск прерывания терапии рассчитан по правилам; детали — в карточке.")
|
||||
|
||||
|
||||
token = st.query_params.get("patient")
|
||||
|
||||
Reference in New Issue
Block a user