Skip to content

Commit 11700a0

Browse files
committed
fix: Rewrite sidebar using robust standard components
1 parent bc0a7cf commit 11700a0

File tree

1 file changed

+43
-219
lines changed

1 file changed

+43
-219
lines changed

frontend/components/sidebar.py

Lines changed: 43 additions & 219 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
"""
22
Premium Sidebar Component - AI Healthcare System
33
=================================================
4-
Clean, modern navigation with polished styling.
4+
Clean, modern navigation using standard Streamlit components.
55
"""
66
import streamlit as st
77
from streamlit_option_menu import option_menu
88
from frontend.utils import api, i18n
99

10-
1110
def render_sidebar():
1211
"""
1312
Renders a clean, modern sidebar with:
@@ -17,195 +16,39 @@ def render_sidebar():
1716
4. Sign out button
1817
"""
1918

20-
# --- SIDEBAR CONTROLLER (Python Fallback) ---
21-
# If native toggle fails, this button in the MAIN area allows forcing sidebar open
22-
23-
# Initialize state
24-
if 'sidebar_force_open' not in st.session_state:
25-
st.session_state.sidebar_force_open = False
26-
27-
# Logic to handle toggle
28-
def toggle_sidebar():
29-
st.session_state.sidebar_force_open = not st.session_state.sidebar_force_open
30-
31-
# Render a subtle floating toggle button in main area
32-
placeholder = st.container()
33-
col1, col2 = placeholder.columns([1, 20])
34-
with col1:
35-
if not st.session_state.sidebar_force_open:
36-
# Styled ghost button for "Show" - Ultra Minimal
37-
st.markdown("""
38-
<style>
39-
div[data-testid="stButton"] button[kind="secondary"] {
40-
background: transparent !important;
41-
border: none !important;
42-
color: #94A3B8 !important;
43-
padding: 0 !important;
44-
font-size: 1.5rem !important;
45-
line-height: 1 !important;
46-
min-height: 0px !important;
47-
height: 40px !important;
48-
width: 40px !important;
49-
margin-top: -15px !important; /* Pull up to align with where native toggle would be */
50-
margin-left: -5px !important;
51-
}
52-
div[data-testid="stButton"] button[kind="secondary"]:hover {
53-
background: rgba(59, 130, 246, 0.1) !important;
54-
color: #3B82F6 !important;
55-
border-radius: 50% !important;
56-
}
57-
/* Hide the container padding to make it flush */
58-
div[data-testid="stVerticalBlock"] > div:has(div[data-testid="stButton"]) {
59-
gap: 0 !important;
60-
}
61-
</style>
62-
""", unsafe_allow_html=True)
63-
st.button("☰", key="custom_sidebar_show", on_click=toggle_sidebar, help="Open Menu")
64-
65-
# If forced open, inject CSS to override collapse
66-
if st.session_state.sidebar_force_open:
67-
st.markdown("""
68-
<style>
69-
section[data-testid="stSidebar"] {
70-
transform: translateX(0) !important;
71-
visibility: visible !important;
72-
width: 350px !important;
73-
min-width: 350px !important;
74-
background-color: #0F172A !important;
75-
z-index: 999990 !important;
76-
}
77-
78-
/* Hide Native Toggle - All possible selectors */
79-
button[data-testid="stSidebarCollapseButton"],
80-
section[data-testid="stSidebar"] button[kind="header"],
81-
button[aria-label="Collapse sidebar"],
82-
div[data-testid="stSidebarHeader"] button,
83-
button[data-testid="baseButton-header"] {
84-
display: none !important;
85-
visibility: hidden !important;
86-
opacity: 0 !important;
87-
height: 0 !important;
88-
width: 0 !important;
89-
position: absolute !important;
90-
top: -10000px !important;
91-
pointer-events: none !important;
92-
}
93-
94-
/* Hide the container of the close button if possible */
95-
section[data-testid="stSidebar"] > div:first-child:has(button[kind="header"]) {
96-
display: none !important;
97-
}
98-
99-
/* Fix Iframe Transparency */
100-
iframe {
101-
background: transparent !important;
102-
background-color: transparent !important;
103-
}
104-
</style>
105-
""", unsafe_allow_html=True)
106-
107-
# Show a standard "Close Menu" button at the very top of sidebar
108-
with st.sidebar:
109-
st.markdown("<div style='margin-top: 0.5rem;'></div>", unsafe_allow_html=True)
110-
# Use on_click pattern for better state consistency
111-
st.button("✖ Close Menu", key="custom_sidebar_close", type="primary", width="stretch", on_click=toggle_sidebar)
112-
st.markdown("<div style='margin-bottom: 1rem;'></div>", unsafe_allow_html=True)
113-
11419
# --- SIDEBAR RENDER ---
11520
with st.sidebar:
116-
# --- 1. BRAND HEADER ---
117-
st.markdown("""
118-
<div style="
119-
display: flex;
120-
align-items: center;
121-
gap: 12px;
122-
padding: 1rem;
123-
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(139, 92, 246, 0.05) 100%);
124-
border-radius: 12px;
125-
border: 1px solid rgba(59, 130, 246, 0.2);
126-
margin-bottom: 1.5rem;
127-
">
128-
<div style="font-size: 2rem;">🏥</div>
129-
<div>
130-
<div style="
131-
font-weight: 700;
132-
font-size: 1.1rem;
133-
color: white;
134-
">AI Healthcare</div>
135-
<div style="
136-
font-size: 0.75rem;
137-
color: #64748B;
138-
">Powered by AI</div>
139-
</div>
140-
</div>
141-
""", unsafe_allow_html=True)
142-
143-
# --- 2. USER QUICK INFO (if logged in) ---
21+
# 1. Logo & Brand
22+
# Using columns to center/style the logo nicely without raw HTML
23+
c1, c2 = st.columns([1, 4])
24+
with c1:
25+
try:
26+
st.image("frontend/static/logo.png", width=50)
27+
except:
28+
st.markdown("🏥") # Fallback
29+
with c2:
30+
st.markdown("### AI Healthcare\n<small style='color: #64748B'>Patient Portal</small>", unsafe_allow_html=True)
31+
32+
st.markdown("---")
33+
34+
# 2. User Info (Standard Streamlit Container)
14435
username = st.session_state.get('username', 'Guest')
145-
avatar_letter = username[0].upper() if username else 'G'
146-
147-
st.markdown(f"""
148-
<div style="
149-
display: flex;
150-
align-items: center;
151-
gap: 12px;
152-
padding: 0.75rem 1rem;
153-
background: rgba(30, 41, 59, 0.5);
154-
border-radius: 10px;
155-
margin-bottom: 1.5rem;
156-
">
157-
<div style="
158-
width: 40px;
159-
height: 40px;
160-
border-radius: 10px;
161-
background: linear-gradient(135deg, #3B82F6, #8B5CF6);
162-
display: flex;
163-
align-items: center;
164-
justify-content: center;
165-
color: white;
166-
font-weight: 700;
167-
font-size: 1rem;
168-
">{avatar_letter}</div>
169-
<div style="flex: 1; min-width: 0;">
170-
<div style="
171-
color: #F1F5F9;
172-
font-size: 0.9rem;
173-
font-weight: 600;
174-
white-space: nowrap;
175-
overflow: hidden;
176-
text-overflow: ellipsis;
177-
">{username}</div>
178-
<div style="
179-
display: flex;
180-
align-items: center;
181-
gap: 4px;
182-
">
183-
<span style="
184-
width: 6px;
185-
height: 6px;
186-
background: #22C55E;
187-
border-radius: 50%;
188-
"></span>
189-
<span style="color: #22C55E; font-size: 0.7rem;">Online</span>
190-
</div>
191-
</div>
192-
</div>
193-
""", unsafe_allow_html=True)
194-
195-
# --- 3. NAVIGATION LABEL ---
196-
st.markdown("""
197-
<div style="
198-
font-size: 0.7rem;
199-
color: #64748B;
200-
text-transform: uppercase;
201-
letter-spacing: 0.1em;
202-
font-weight: 600;
203-
margin-bottom: 0.5rem;
204-
padding-left: 0.5rem;
205-
">Menu</div>
206-
""", unsafe_allow_html=True)
36+
if username != 'Guest':
37+
with st.container():
38+
# Simple clean profile display
39+
st.markdown(f"""
40+
<div style="background-color: rgba(255, 255, 255, 0.05); padding: 10px; border-radius: 8px; display: flex; align-items: center; gap: 10px; margin-bottom: 20px;">
41+
<div style="background: #3B82F6; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; color: white;">
42+
{username[0].upper()}
43+
</div>
44+
<div>
45+
<div style="font-weight: 600; font-size: 14px; color: #F1F5F9;">{username}</div>
46+
<div style="font-size: 12px; color: #4ADE80;">● Online</div>
47+
</div>
48+
</div>
49+
""", unsafe_allow_html=True)
20750

208-
# --- 4. MAIN NAVIGATION ---
51+
# 3. Navigation Menu
20952
nav_options = [
21053
i18n.get_text("dashboard"),
21154
i18n.get_text("chat"),
@@ -221,17 +64,9 @@ def toggle_sidebar():
22164
]
22265

22366
nav_icons = [
224-
"speedometer2", # Dashboard
225-
"chat-dots", # Chat
226-
"droplet", # Diabetes
227-
"heart", # Heart
228-
"clipboard2-pulse", # Liver
229-
"capsule", # Kidney
230-
"lungs", # Lungs
231-
"person", # Profile
232-
"credit-card", # Pricing
233-
"camera-video", # Telemedicine
234-
"info-circle" # About
67+
"speedometer2", "chat-dots", "droplet", "heart",
68+
"clipboard2-pulse", "capsule", "lungs", "person",
69+
"credit-card", "camera-video", "info-circle"
23570
]
23671

23772
# Admin option
@@ -246,41 +81,30 @@ def toggle_sidebar():
24681
default_index=0,
24782
key="main_sidebar_nav",
24883
styles={
249-
"container": {"background-color": "#0F172A", "padding": "5px"},
250-
"icon": {"color": "#94A3B8", "font-size": "16px"},
84+
"container": {"background-color": "transparent", "padding": "0"},
85+
"icon": {"color": "#94A3B8", "font-size": "14px"},
25186
"nav-link": {
25287
"font-size": "14px",
25388
"text-align": "left",
254-
"margin": "0px",
89+
"margin": "0px",
90+
"padding": "10px",
25591
"color": "#CBD5E1",
256-
"--hover-color": "rgba(59, 130, 246, 0.1)"
25792
},
25893
"nav-link-selected": {
259-
"background-color": "rgba(59, 130, 246, 0.15)",
94+
"background-color": "rgba(59, 130, 246, 0.2)",
26095
"color": "#60A5FA",
26196
"font-weight": "600",
26297
"border-left": "3px solid #3B82F6"
26398
},
26499
}
265100
)
266101

267-
# --- 5. SPACER + SIGN OUT ---
268-
st.markdown("<div style='height: 2rem'></div>", unsafe_allow_html=True)
269-
270-
if st.button("🚪 Sign Out", type="secondary", width="stretch", key="logout_btn"):
102+
# 4. Footer / Sign Out
103+
st.markdown("---")
104+
if st.button("🚪 Sign Out", use_container_width=True):
271105
api.clear_session()
272106
st.rerun()
273-
274-
# --- 6. VERSION ---
275-
st.markdown("""
276-
<div style="
277-
text-align: center;
278-
padding-top: 1.5rem;
279-
color: #475569;
280-
font-size: 0.7rem;
281-
">
282-
v2.1 — © 2026 AI Healthcare
283-
</div>
284-
""", unsafe_allow_html=True)
107+
108+
st.markdown("<div style='text-align: center; color: #475569; font-size: 12px; margin-top: 20px;'>v2.1 • © 2026 AI Healthcare</div>", unsafe_allow_html=True)
285109

286110
return selected

0 commit comments

Comments
 (0)