diff --git a/frontend/components/risk_dashboard.py b/frontend/components/risk_dashboard.py index 98ba514..08d9d37 100644 --- a/frontend/components/risk_dashboard.py +++ b/frontend/components/risk_dashboard.py @@ -442,12 +442,14 @@ def render_response_execution( if "resp_active_event_sel" not in st.session_state: st.session_state["resp_active_event_sel"] = 0 - if "active_risk_event_id" in st.session_state: - target_id = st.session_state["active_risk_event_id"] - if target_id in event_ids: - new_idx = event_ids.index(target_id) - # 🧪 關鍵修正:若有外部跳轉指令,手動強制覆寫 selectbox 的內部 state - st.session_state["resp_active_event_sel"] = new_idx + # 外部跳轉(卡片「查看分析」)是一次性指令:取出後就清掉。 + # 原本用 active_risk_event_id(=目前選取的鏡像)來強制 selectbox,導致 + # 使用者在下拉選另一個事件時,rerun 又被拉回上一個,永遠切不過去。 + target_id = st.session_state.pop("jump_to_risk_event_id", None) + if target_id is not None and target_id in event_ids: # event_ids[0] 是佔位的 None + st.session_state["resp_active_event_sel"] = event_ids.index(target_id) + elif st.session_state["resp_active_event_sel"] >= len(event_options): + st.session_state["resp_active_event_sel"] = 0 # 事件被刪除後索引失效 selected_idx = st.selectbox( "選擇要分析與執行的風險事件", diff --git a/frontend/components/supply_map.py b/frontend/components/supply_map.py index c66a45d..4bc8a22 100644 --- a/frontend/components/supply_map.py +++ b/frontend/components/supply_map.py @@ -205,7 +205,8 @@ def render_risk_shortcuts(key: str, heatmap_rows=None, *, actor: str): if btn_state == "view": if st.button("📊 查看分析", key=f"{key}_quick_anal_{region_key}_{i}", use_container_width=True, type="secondary"): - st.session_state["active_risk_event_id"] = found_ev["id"] + # 一次性的跳轉指令;active_risk_event_id 只反映目前選取,不用來強制 selectbox + st.session_state["jump_to_risk_event_id"] = found_ev["id"] st.rerun() elif btn_state == "update": if st.button("🔄 更新應變建議", key=f"{key}_upd_{region_key}", use_container_width=True, type="primary"): diff --git a/tests/test_step3_event_switch.py b/tests/test_step3_event_switch.py new file mode 100644 index 0000000..4e3b0c9 --- /dev/null +++ b/tests/test_step3_event_switch.py @@ -0,0 +1,38 @@ +""" +tests/test_step3_event_switch.py +步驟 3 事件下拉:有多個正式事件時,使用者必須能從 A 切到 B。 + +原本卡片「查看分析」和 selectbox 都寫 active_risk_event_id,而 rerun 時又用它 +強制覆寫 selectbox,於是選了 B 也會被拉回 A。跳轉改成一次性的 jump_to_risk_event_id。 +""" + +from __future__ import annotations + +import ast +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def _assigned_keys(tree): + keys = set() + for node in ast.walk(tree): + if isinstance(node, ast.Assign): + for target in node.targets: + if (isinstance(target, ast.Subscript) + and isinstance(target.value, ast.Attribute) + and target.value.attr == "session_state" + and isinstance(target.slice, ast.Constant)): + keys.add(target.slice.value) + return keys + + +def test_card_jump_uses_one_shot_key_and_step3_consumes_it(): + supply_map = ast.parse((ROOT / "frontend/components/supply_map.py").read_text(encoding="utf-8")) + dashboard_src = (ROOT / "frontend/components/risk_dashboard.py").read_text(encoding="utf-8") + + assert "jump_to_risk_event_id" in _assigned_keys(supply_map) + assert "active_risk_event_id" not in _assigned_keys(supply_map) + # 步驟 3 取出即清除,且不再拿 active_risk_event_id 去覆寫 selectbox + assert 'st.session_state.pop("jump_to_risk_event_id"' in dashboard_src + assert 'target_id = st.session_state["active_risk_event_id"]' not in dashboard_src