LifeFlow-AI / ui /renderers.py
Marco310's picture
feat: Complete UI/UX redesign and fix critical workflow bugs
6b71d3d
raw
history blame
6.91 kB
# ui/renderers.py
from datetime import datetime
from config import AGENTS_INFO
def _format_iso_time(iso_str: str) -> str:
if not iso_str or iso_str == "N/A": return ""
try:
dt = datetime.fromisoformat(iso_str)
return dt.strftime("%H:%M")
except Exception:
if "T" in iso_str: return iso_str.split("T")[1][:5]
return iso_str
def create_agent_stream_output(text: str = None) -> str:
return f'<div style="font-family: monospace; color: #334155;">{text}</div>'
def create_agent_dashboard(status_dict: dict) -> str:
leader_key = 'team'
member_keys = ['scout', 'weatherman', 'optimizer', 'navigator', 'presenter']
def _render_card(key):
info = AGENTS_INFO.get(key, {})
state = status_dict.get(key, {})
status = state.get('status', 'idle')
msg = state.get('message', 'Standby')
active_class = "working" if status == "working" else ""
icon = info.get('icon', 'πŸ€–')
name = info.get('name', key.title())
role = info.get('role', 'Agent')
color = info.get('color', '#6366f1')
return f"""
<div class="agent-card-wrap {active_class}">
<div class="agent-card-inner" style="border-top: 3px solid {color}">
<div class="agent-avatar">{icon}</div>
<div class="agent-name">{name}</div>
<div class="agent-role">{role}</div>
<div class="status-badge">{msg}</div>
</div>
</div>
"""
html = f"""
<div class="agent-war-room">
<div class="org-chart">
<div class="org-level">
{_render_card(leader_key)}
</div>
<div class="connector-line"></div>
<div class="connector-horizontal"></div>
<div class="org-level">
{''.join([_render_card(k) for k in member_keys])}
</div>
</div>
</div>
"""
return html
def create_summary_card(total_tasks: int, high_priority: int, total_time: int, location: str = "Taipei City",
date: str = "Today") -> str:
return f"""
<div style="background: #f8fafc; border-radius: 12px; padding: 16px; margin-bottom: 20px; border: 1px solid #e2e8f0;">
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px;">
<div>
<div style="font-size: 0.8rem; color: #64748b; font-weight: 600;">TRIP SUMMARY</div>
<div style="font-size: 1.1rem; font-weight: 700; color: #1e293b;">{location}</div>
<div style="font-size: 0.9rem; color: #6366f1;">πŸ“… {date}</div>
</div>
<div style="text-align: right;">
<div style="font-size: 2rem; font-weight: 800; color: #6366f1; line-height: 1;">{total_tasks}</div>
<div style="font-size: 0.8rem; color: #64748b;">Tasks</div>
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
<div style="background: white; padding: 8px; border-radius: 8px; text-align: center; border: 1px solid #e2e8f0;">
<div style="font-size: 0.8rem; color: #64748b;">Duration</div>
<div style="font-weight: 600; color: #334155;">{total_time} min</div>
</div>
<div style="background: white; padding: 8px; border-radius: 8px; text-align: center; border: 1px solid #e2e8f0;">
<div style="font-size: 0.8rem; color: #64748b;">High Prio</div>
<div style="font-weight: 600; color: #ef4444;">{high_priority}</div>
</div>
</div>
</div>
"""
def create_task_card(task_num: int, task_title: str, priority: str, time_window: dict, duration: str, location: str,
icon: str = "πŸ“‹") -> str:
p_color = {"HIGH": "#ef4444", "MEDIUM": "#f59e0b", "LOW": "#10b981"}.get(priority.upper(), "#94a3b8")
display_time = "Anytime"
if isinstance(time_window, dict):
s_clean = _format_iso_time(time_window.get('earliest_time', ''))
e_clean = _format_iso_time(time_window.get('latest_time', ''))
if s_clean and e_clean:
display_time = f"{s_clean} - {e_clean}"
elif s_clean:
display_time = f"After {s_clean}"
elif time_window:
display_time = str(time_window)
return f"""
<div class="task-card-item" style="border-left: 4px solid {p_color};">
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<div style="font-weight: 700; color: #334155; display: flex; align-items: center; gap: 8px;">
<span style="background:#f1f5f9; padding:4px; border-radius:6px;">{icon}</span>
{task_title}
</div>
<span style="font-size: 0.7rem; font-weight: 700; color: {p_color}; background: {p_color}15; padding: 2px 8px; border-radius: 12px; height: fit-content;">{priority}</span>
</div>
<div style="font-size: 0.85rem; color: #64748b; display: flex; flex-direction: column; gap: 4px;">
<div style="display: flex; align-items: center; gap: 6px;"><span>πŸ“</span> {location}</div>
<div style="display: flex; align-items: center; gap: 6px;"><span>πŸ•’</span> {display_time} <span style="color:#cbd5e1">|</span> ⏳ {duration}</div>
</div>
</div>
"""
# πŸ”₯πŸ”₯πŸ”₯ Updated Timeline Function
def create_timeline_html_enhanced(timeline):
if not timeline:
return """
<div style="text-align: center; padding: 20px; color: #94a3b8;">
No timeline data available yet.
</div>
"""
html = '<div class="timeline-container">'
for i, stop in enumerate(timeline):
location = stop.get('location', 'Unknown')
time_str = stop.get('time', '')
if not time_str or time_str == "N/A": time_str = "Flexible Time"
weather_str = stop.get('weather', '')
weather_html = ""
if weather_str and weather_str != "N/A":
weather_html = f'<div class="timeline-meta">🌀️ {weather_str}</div>'
html += f"""
<div class="timeline-stop">
<div class="timeline-left">
<div class="timeline-marker"></div>
</div>
<div class="timeline-card">
<div class="timeline-header">
<div class="timeline-location">{location}</div>
<div class="timeline-time-badge">πŸ•’ {time_str}</div>
</div>
{weather_html}
</div>
</div>
"""
html += '</div>'
return html
# Empty Placeholders
def create_metrics_cards(metrics, traffic): return ""
def create_result_visualization(tasks, data): return ""
def generate_chat_history_html_bubble(session): return ""