11"""
22Premium Sidebar Component - AI Healthcare System
33=================================================
4- Clean, modern navigation with polished styling .
4+ Clean, modern navigation using standard Streamlit components .
55"""
66import streamlit as st
77from streamlit_option_menu import option_menu
88from frontend .utils import api , i18n
99
10-
1110def 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