bargav25 commited on
Commit
2071663
Β·
verified Β·
1 Parent(s): 73d7a81

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +600 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,602 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
1
+ import os
 
 
2
  import streamlit as st
3
+ import requests
4
+ import pandas as pd
5
+ from datetime import datetime
6
+ from typing import Dict, List, Optional, Tuple
7
+ from dataclasses import dataclass
8
+ import plotly.express as px
9
+ import plotly.graph_objects as go
10
+
11
+ # ─────────────────────────────────────────────────────────────────────────────
12
+ # Configuration & Data Classes
13
+ # ─────────────────────────────────────────────────────────────────────────────
14
+
15
+ @dataclass
16
+ class UserConfig:
17
+ id: int
18
+ name: str
19
+ email: str
20
+ age: int
21
+ gender: str
22
+ user_type: str
23
+ created_at: str
24
+ updated_at: str
25
+
26
+ @dataclass
27
+ class ChatMessage:
28
+ role: str
29
+ content: str
30
+ timestamp: Optional[datetime] = None
31
+
32
+ class Config:
33
+ API_URL = os.getenv("API_URL", "https://clinical-agents-333016757590.us-central1.run.app/api/v1")
34
+ PAGE_TITLE = "AI Triage System"
35
+ PAGE_ICON = "πŸ₯"
36
+
37
+ # UI Constants
38
+ SIDEBAR_WIDTH = 300
39
+ CHAT_HEIGHT = 500
40
+
41
+ # Colors
42
+ PRIMARY_COLOR = "#1f77b4"
43
+ SUCCESS_COLOR = "#2ca02c"
44
+ WARNING_COLOR = "#ff7f0e"
45
+ ERROR_COLOR = "#d62728"
46
+
47
+ # ─────────────────────────────────────────────────────────────────────────────
48
+ # Session State Manager
49
+ # ─────────────────────────────────────────────────────────────────────────────
50
+
51
+ class SessionStateManager:
52
+ @staticmethod
53
+ def init_state():
54
+ """Initialize session state with default values"""
55
+ defaults = {
56
+ "user_type": "patient",
57
+ "auth_done": False,
58
+ "user_id": None,
59
+ "user_data": None,
60
+ "messages": [],
61
+ "notes": [],
62
+ "chat_active": False,
63
+ "finished": False,
64
+ "current_assessment_id": None,
65
+ "show_help": False
66
+ }
67
+
68
+ for key, val in defaults.items():
69
+ if key not in st.session_state:
70
+ st.session_state[key] = val
71
+
72
+ @staticmethod
73
+ def get_user_config() -> Optional[UserConfig]:
74
+ """Get current user configuration"""
75
+ if st.session_state.auth_done and st.session_state.user_data:
76
+ data = st.session_state.user_data
77
+ return UserConfig(
78
+ id=data["id"],
79
+ name=data["name"],
80
+ email=data["email"],
81
+ age=data["age"],
82
+ gender=data["gender"],
83
+ user_type=data["user_type"],
84
+ created_at=data["created_at"],
85
+ updated_at=data["updated_at"]
86
+ )
87
+ return None
88
+
89
+ @staticmethod
90
+ def reset_chat():
91
+ """Reset chat state"""
92
+ st.session_state.messages = []
93
+ st.session_state.chat_active = False
94
+ st.session_state.finished = False
95
+ st.session_state.current_assessment_id = None
96
+
97
+ # ─────────────────────────────────────────────────────────────────────────────
98
+ # API Service Layer
99
+ # ─────────────────────────────────────────────────────────────────────────────
100
+
101
+ class APIService:
102
+ @staticmethod
103
+ def login_user(name: str, email: str, age: int, gender: str, user_type: str) -> Tuple[bool, Dict]:
104
+ """Login user with the new API structure"""
105
+ try:
106
+ payload = {
107
+ "name": name,
108
+ "email": email,
109
+ "age": age,
110
+ "gender": gender,
111
+ "user_type": user_type
112
+ }
113
+
114
+ resp = requests.post(
115
+ f"{Config.API_URL}/users/login",
116
+ json=payload,
117
+ timeout=10
118
+ )
119
+ resp.raise_for_status()
120
+ return True, resp.json()
121
+ except Exception as e:
122
+ return False, {"error": str(e)}
123
+
124
+ @staticmethod
125
+ def get_user_by_id(user_id: int) -> Tuple[bool, Dict]:
126
+ """Get user information by ID"""
127
+ try:
128
+ resp = requests.get(f"{Config.API_URL}/users/{user_id}", timeout=10)
129
+ resp.raise_for_status()
130
+ return True, resp.json()
131
+ except Exception as e:
132
+ return False, {"error": str(e)}
133
+
134
+ @staticmethod
135
+ def fetch_assessments() -> List[Dict]:
136
+ """Fetch all assessments from API"""
137
+ try:
138
+ resp = requests.get(f"{Config.API_URL}/assessments", timeout=10)
139
+ resp.raise_for_status()
140
+ return resp.json()
141
+ except Exception as e:
142
+ st.error(f"Failed to fetch assessments: {str(e)}")
143
+ return []
144
+
145
+ @staticmethod
146
+ def send_chat_message(message: str, history: List[Dict], patient_id: int) -> Tuple[bool, Dict]:
147
+ """Send chat message to triage API"""
148
+ try:
149
+ payload = {
150
+ "message": message,
151
+ "history": history,
152
+ "patient_id": patient_id
153
+ }
154
+
155
+ resp = requests.post(
156
+ f"{Config.API_URL}/triage/chat",
157
+ json=payload,
158
+ timeout=30
159
+ )
160
+ resp.raise_for_status()
161
+ return True, resp.json()
162
+ except Exception as e:
163
+ return False, {"error": str(e)}
164
+
165
+ # ─────────────────────────────────────────────────────────────────────────────
166
+ # UI Components
167
+ # ─────────────────────────────────────────────────────────────────────────────
168
+
169
+ class UIComponents:
170
+ @staticmethod
171
+ def render_header():
172
+ """Render main header with branding"""
173
+ st.markdown("""
174
+ <div style="text-align: center; padding: 1rem 0; background: linear-gradient(90deg, #1f77b4, #2ca02c);
175
+ border-radius: 10px; margin-bottom: 2rem;">
176
+ <h1 style="color: white; margin: 0; font-size: 2.5rem;">πŸ₯ AI Triage System</h1>
177
+ <p style="color: #f0f0f0; margin: 0.5rem 0 0 0; font-size: 1.1rem;">
178
+ Intelligent healthcare assessment at your fingertips
179
+ </p>
180
+ </div>
181
+ """, unsafe_allow_html=True)
182
+
183
+ @staticmethod
184
+ def render_sidebar_auth() -> bool:
185
+ """Render sidebar authentication section"""
186
+ with st.sidebar:
187
+ st.markdown("### πŸ‘€ Authentication")
188
+
189
+ # User type selection
190
+ user_type = st.selectbox(
191
+ "Select your role:",
192
+ ["patient", "staff"],
193
+ key="user_type_select",
194
+ help="Choose whether you're a patient seeking assessment or medical staff"
195
+ )
196
+ st.session_state.user_type = user_type
197
+
198
+ # Sign in form
199
+ with st.form("signin_form"):
200
+ st.markdown("**Sign In / Register**")
201
+ name = st.text_input("Full Name", placeholder="Enter your full name")
202
+ email = st.text_input("Email", placeholder="Enter your email address")
203
+
204
+ # Additional fields for patients
205
+ if user_type == "patient":
206
+ col1, col2 = st.columns(2)
207
+ with col1:
208
+ age = st.number_input("Age", min_value=1, max_value=120, value=25)
209
+ with col2:
210
+ gender = st.selectbox("Gender", ["male", "female", "other"])
211
+ else:
212
+ age = 30 # Default for staff
213
+ gender = "not_specified"
214
+
215
+ submitted = st.form_submit_button("πŸ” Sign In", use_container_width=True)
216
+
217
+ if submitted:
218
+ if name.strip() and email.strip():
219
+ with st.spinner("πŸ”„ Signing in..."):
220
+ success, response = APIService.login_user(
221
+ name.strip(),
222
+ email.strip(),
223
+ age,
224
+ gender,
225
+ user_type
226
+ )
227
+
228
+ if success:
229
+ st.session_state.user_data = response
230
+ st.session_state.user_id = response["id"]
231
+ st.session_state.auth_done = True
232
+ st.success("βœ… Successfully signed in!")
233
+ st.rerun()
234
+ else:
235
+ st.error(f"❌ Login failed: {response.get('error', 'Unknown error')}")
236
+ else:
237
+ st.error("❌ Please enter both name and email")
238
+
239
+ # Show current user info
240
+ if st.session_state.auth_done and st.session_state.user_data:
241
+ st.markdown("---")
242
+ st.markdown("**Current User:**")
243
+ user_data = st.session_state.user_data
244
+ st.info(f"""
245
+ πŸ‘€ **{user_data['name']}**
246
+ πŸ“§ {user_data['email']}
247
+ 🏷️ {user_data['user_type'].title()}
248
+ πŸ†” ID: {user_data['id']}
249
+ """)
250
+
251
+ if st.button("πŸšͺ Sign Out", use_container_width=True):
252
+ for key in list(st.session_state.keys()):
253
+ del st.session_state[key]
254
+ st.rerun()
255
+
256
+ return st.session_state.auth_done
257
+
258
+ @staticmethod
259
+ def render_chat_interface(user_config: UserConfig):
260
+ """Render patient chat interface"""
261
+ st.markdown("### πŸ’¬ Chat Assessment")
262
+
263
+ # Chat container
264
+ chat_container = st.container(height=Config.CHAT_HEIGHT)
265
+
266
+ with chat_container:
267
+ # Display chat history
268
+ for i, msg in enumerate(st.session_state.messages):
269
+ with st.chat_message(msg["role"]):
270
+ st.markdown(msg["content"])
271
+
272
+ # Chat controls
273
+ col1, col2, col3 = st.columns([2, 1, 1])
274
+
275
+ with col1:
276
+ if not st.session_state.chat_active:
277
+ if st.button("πŸš€ Start New Assessment", use_container_width=True, type="primary"):
278
+ UIComponents._start_new_assessment(user_config)
279
+
280
+ with col2:
281
+ if st.session_state.chat_active and st.button("πŸ”„ Reset Chat", use_container_width=True):
282
+ SessionStateManager.reset_chat()
283
+ st.rerun()
284
+
285
+ with col3:
286
+ if st.button("❓ Help", use_container_width=True):
287
+ st.session_state.show_help = not st.session_state.get("show_help", False)
288
+
289
+ # Help section
290
+ if st.session_state.get("show_help", False):
291
+ st.markdown("""
292
+ ---
293
+ **ℹ️ How to use the AI Triage System:**
294
+
295
+ 1. **Start Assessment**: Click 'Start New Assessment' to begin
296
+ 2. **Describe Symptoms**: Be detailed about your symptoms, when they started, and their severity
297
+ 3. **Answer Questions**: The AI will ask follow-up questions to better understand your condition
298
+ 4. **Get Results**: Receive your triage level and recommended next steps
299
+
300
+ **Tips for better results:**
301
+ - Be honest and specific about your symptoms
302
+ - Include timeline information (when symptoms started)
303
+ - Mention any relevant medical history
304
+ - Don't hesitate to ask for clarification
305
+ """)
306
+
307
+ # Chat input
308
+ if st.session_state.chat_active and not st.session_state.finished:
309
+ if user_input := st.chat_input("πŸ’­ Describe your symptoms or ask a question..."):
310
+ UIComponents._handle_user_message(user_input, user_config)
311
+
312
+ @staticmethod
313
+ def _start_new_assessment(user_config: UserConfig):
314
+ """Start a new triage assessment"""
315
+ st.session_state.chat_active = True
316
+ st.session_state.messages = []
317
+ st.session_state.finished = False
318
+
319
+ with st.spinner("πŸ”„ Starting your assessment..."):
320
+ success, response = APIService.send_chat_message("", [], user_config.id)
321
+
322
+ if success:
323
+ st.session_state.messages.append({
324
+ "role": "assistant",
325
+ "content": response["response"]
326
+ })
327
+ st.rerun()
328
+ else:
329
+ st.error(f"❌ Failed to start assessment: {response.get('error', 'Unknown error')}")
330
+ st.session_state.chat_active = False
331
+
332
+ @staticmethod
333
+ def _handle_user_message(user_input: str, user_config: UserConfig):
334
+ """Handle user message in chat"""
335
+ # Add user message
336
+ st.session_state.messages.append({"role": "user", "content": user_input})
337
+
338
+ with st.spinner("πŸ€” AI is analyzing your response..."):
339
+ # Convert messages to API format
340
+ history = [{"role": msg["role"], "content": msg["content"]}
341
+ for msg in st.session_state.messages]
342
+
343
+ success, response = APIService.send_chat_message(user_input, history, user_config.id)
344
+
345
+ if success:
346
+ st.session_state.messages.append({
347
+ "role": "assistant",
348
+ "content": response["response"]
349
+ })
350
+
351
+ if response.get("finished", False):
352
+ st.session_state.chat_active = False
353
+ st.session_state.finished = True
354
+ st.success("βœ… Assessment completed successfully!")
355
+ st.balloons()
356
+
357
+ st.rerun()
358
+ else:
359
+ st.error(f"❌ Error: {response.get('error', 'Unknown error')}")
360
+
361
+ class StaffDashboard:
362
+ @staticmethod
363
+ def render_dashboard(user_config: UserConfig):
364
+ """Render staff dashboard"""
365
+ st.markdown("### πŸ“Š Staff Dashboard")
366
+
367
+ # Fetch data
368
+ with st.spinner("πŸ“₯ Loading assessment data..."):
369
+ assessments = APIService.fetch_assessments()
370
+
371
+ if not assessments:
372
+ st.info("πŸ“­ No assessments available yet.")
373
+ return
374
+
375
+ df = pd.DataFrame(assessments)
376
+ df["created_at"] = pd.to_datetime(df["created_at"])
377
+
378
+ # Dashboard metrics
379
+ StaffDashboard._render_metrics(df)
380
+
381
+ # Charts
382
+ col1, col2 = st.columns(2)
383
+ with col1:
384
+ StaffDashboard._render_esi_distribution(df)
385
+ with col2:
386
+ StaffDashboard._render_timeline_chart(df)
387
+
388
+ # Data table
389
+ StaffDashboard._render_assessments_table(df)
390
+
391
+ @staticmethod
392
+ def _render_metrics(df: pd.DataFrame):
393
+ """Render key metrics"""
394
+ col1, col2, col3, col4 = st.columns(4)
395
+
396
+ with col1:
397
+ st.metric(
398
+ "πŸ“‹ Total Assessments",
399
+ len(df),
400
+ delta=None
401
+ )
402
+
403
+ with col2:
404
+ avg_esi = df['esi_level'].mean()
405
+ st.metric(
406
+ "⚑ Avg ESI Level",
407
+ f"{avg_esi:.1f}",
408
+ delta=None
409
+ )
410
+
411
+ with col3:
412
+ emergency_cases = len(df[df['esi_level'] <= 2])
413
+ st.metric(
414
+ "🚨 Emergency Cases",
415
+ emergency_cases,
416
+ delta=f"{(emergency_cases/len(df)*100):.1f}% of total"
417
+ )
418
+
419
+ with col4:
420
+ latest = df['created_at'].max()
421
+ hours_ago = (datetime.now() - latest.replace(tzinfo=None)).total_seconds() / 3600
422
+ st.metric(
423
+ "πŸ• Last Assessment",
424
+ f"{hours_ago:.1f}h ago",
425
+ delta=None
426
+ )
427
+
428
+ @staticmethod
429
+ def _render_esi_distribution(df: pd.DataFrame):
430
+ """Render ESI level distribution chart"""
431
+ st.markdown("**🎯 ESI Level Distribution**")
432
+
433
+ esi_counts = df['esi_level'].value_counts().sort_index()
434
+
435
+ colors = ['#d62728', '#ff7f0e', '#ffbb78', '#2ca02c', '#98df8a']
436
+
437
+ fig = px.bar(
438
+ x=esi_counts.index,
439
+ y=esi_counts.values,
440
+ color=esi_counts.index,
441
+ color_continuous_scale='RdYlGn_r',
442
+ title="Distribution by ESI Level"
443
+ )
444
+
445
+ fig.update_layout(
446
+ xaxis_title="ESI Level",
447
+ yaxis_title="Count",
448
+ showlegend=False,
449
+ height=300
450
+ )
451
+
452
+ st.plotly_chart(fig, use_container_width=True)
453
+
454
+ @staticmethod
455
+ def _render_timeline_chart(df: pd.DataFrame):
456
+ """Render assessments timeline"""
457
+ st.markdown("**πŸ“ˆ Assessment Timeline**")
458
+
459
+ # Group by date
460
+ df_daily = df.groupby(df['created_at'].dt.date).size().reset_index()
461
+ df_daily.columns = ['date', 'count']
462
+
463
+ fig = px.line(
464
+ df_daily,
465
+ x='date',
466
+ y='count',
467
+ title="Daily Assessment Volume",
468
+ markers=True
469
+ )
470
+
471
+ fig.update_layout(
472
+ xaxis_title="Date",
473
+ yaxis_title="Number of Assessments",
474
+ height=300
475
+ )
476
+
477
+ st.plotly_chart(fig, use_container_width=True)
478
+
479
+ @staticmethod
480
+ def _render_assessments_table(df: pd.DataFrame):
481
+ """Render assessments data table"""
482
+ st.markdown("**πŸ“‹ Recent Assessments**")
483
+
484
+ # Prepare display dataframe
485
+ display_df = df.copy()
486
+ display_df['created_at'] = pd.to_datetime(display_df['created_at']).dt.strftime("%Y-%m-%d %H:%M:%S")
487
+
488
+ # Add user information by fetching user details
489
+ if not display_df.empty:
490
+ # Create a dictionary to store user info to avoid multiple API calls
491
+ user_info_cache = {}
492
+ user_names = []
493
+ user_emails = []
494
+
495
+ for user_id in display_df['user_id']:
496
+ if user_id not in user_info_cache:
497
+ success, user_data = APIService.get_user_by_id(user_id)
498
+ if success:
499
+ user_info_cache[user_id] = user_data
500
+ else:
501
+ user_info_cache[user_id] = {"name": "Unknown", "email": "Unknown"}
502
+
503
+ user_info = user_info_cache[user_id]
504
+ user_names.append(user_info.get("name", "Unknown"))
505
+ user_emails.append(user_info.get("email", "Unknown"))
506
+
507
+ display_df['patient_name'] = user_names
508
+ display_df['patient_email'] = user_emails
509
+
510
+ # Select columns to display
511
+ columns = ["id", "patient_name", "patient_email", "esi_level", "diagnosis", "notes", "created_at"]
512
+
513
+ # Sort by creation time (newest first)
514
+ display_df = display_df.sort_values('created_at', ascending=False)
515
+
516
+ st.dataframe(
517
+ display_df[columns],
518
+ use_container_width=True,
519
+ hide_index=True,
520
+ column_config={
521
+ "id": "Assessment ID",
522
+ "patient_name": "Patient Name",
523
+ "patient_email": "Patient Email",
524
+ "esi_level": st.column_config.NumberColumn(
525
+ "ESI Level",
526
+ help="Emergency Severity Index (1=Most urgent, 5=Least urgent)",
527
+ min_value=1,
528
+ max_value=5,
529
+ format="%d"
530
+ ),
531
+ "diagnosis": "Diagnosis",
532
+ "notes": "Notes",
533
+ "created_at": "Created At"
534
+ }
535
+ )
536
+
537
+ # ─────────────────────────────────────────────────────────────────────────────
538
+ # Main Application
539
+ # ─────────────────────────────────────────────────────────────────────────────
540
+
541
+ def main():
542
+ """Main application entry point"""
543
+ # Page configuration
544
+ st.set_page_config(
545
+ page_title=Config.PAGE_TITLE,
546
+ layout="wide",
547
+ page_icon=Config.PAGE_ICON,
548
+ initial_sidebar_state="expanded"
549
+ )
550
+
551
+ # Initialize session state
552
+ SessionStateManager.init_state()
553
+
554
+ # Custom CSS
555
+ st.markdown("""
556
+ <style>
557
+ .main > div {
558
+ padding-top: 2rem;
559
+ }
560
+ .stChatMessage {
561
+ padding: 1rem;
562
+ border-radius: 10px;
563
+ margin-bottom: 1rem;
564
+ }
565
+ .stButton > button {
566
+ border-radius: 20px;
567
+ border: none;
568
+ font-weight: 600;
569
+ }
570
+ .metric-container {
571
+ background: #f8f9fa;
572
+ padding: 1rem;
573
+ border-radius: 10px;
574
+ margin-bottom: 1rem;
575
+ }
576
+ </style>
577
+ """, unsafe_allow_html=True)
578
+
579
+ # Render header
580
+ UIComponents.render_header()
581
+
582
+ # Handle authentication
583
+ if not UIComponents.render_sidebar_auth():
584
+ st.markdown("""
585
+ <div style="text-align: center; padding: 3rem; color: #666;">
586
+ <h3>πŸ‘‹ Welcome to AI Triage System</h3>
587
+ <p>Please sign in using the sidebar to get started with your health assessment.</p>
588
+ </div>
589
+ """, unsafe_allow_html=True)
590
+ st.stop()
591
+
592
+ # Get user configuration
593
+ user_config = SessionStateManager.get_user_config()
594
+
595
+ # Route to appropriate interface
596
+ if user_config.user_type == "patient":
597
+ UIComponents.render_chat_interface(user_config)
598
+ else:
599
+ StaffDashboard.render_dashboard(user_config)
600
 
601
+ if __name__ == "__main__":
602
+ main()