"""
FocusTrack - Shared UI utilities, CSS themes, and helper functions.
"""
import streamlit as st
from datetime import datetime, timedelta, date
from typing import Optional
import pandas as pd
# ─── CSS Theme ───────────────────────────────────────────────────────────────
DARK_CSS = """
"""
LIGHT_CSS = """
"""
CATEGORY_COLORS = {
"coding": "#22d3ee",
"browsing": "#f59e0b",
"communication": "#10b981",
"design": "#a78bfa",
"documents": "#fb923c",
"media": "#f43f5e",
"system": "#64748b",
"idle": "#374151",
"uncategorized": "#6366f1",
}
# ─── Helpers ─────────────────────────────────────────────────────────────────
def fmt_duration(seconds: Optional[float]) -> str:
"""Format seconds to human-readable HH:MM or Xh Ym."""
if not seconds:
return "0m"
seconds = int(seconds)
h, m = divmod(seconds // 60, 60) if seconds >= 3600 else (0, seconds // 60)
if seconds >= 3600:
return f"{h}h {m:02d}m"
return f"{m}m {seconds % 60:02d}s" if m == 0 else f"{m}m"
def fmt_duration_long(seconds: Optional[float]) -> str:
if not seconds:
return "0 min"
seconds = int(seconds)
h = seconds // 3600
m = (seconds % 3600) // 60
parts = []
if h: parts.append(f"{h}h")
if m: parts.append(f"{m}m")
return " ".join(parts) or "< 1m"
def get_focus_score(focus_sec: float, total_sec: float) -> float:
if not total_sec:
return 0.0
return round((focus_sec / total_sec) * 100, 1)
def privacy_badge():
"""Render the privacy badge present on every screen."""
st.markdown("""
●
100% private
• All data stays on your device
""", unsafe_allow_html=True)
def page_header(title: str, subtitle: str = ""):
"""Render a styled page header."""
st.markdown(f"""
{title}
{"" if not subtitle else f'
{subtitle}
'}
""", unsafe_allow_html=True)
def status_dot(status: str) -> str:
colors = {"running": "#10b981", "paused": "#f59e0b", "stopped": "#f43f5e"}
labels = {"running": "Tracking", "paused": "Paused", "stopped": "Stopped"}
c = colors.get(status, "#6366f1")
l = labels.get(status, status.title())
return f'● {l}'
def category_pill(category: str) -> str:
color = CATEGORY_COLORS.get(category, "#6366f1")
return (
f'{category}'
)
def get_db():
"""Get cached database instance via session state."""
from database import Database
if "db" not in st.session_state:
db = Database()
db.initialize()
st.session_state.db = db
return st.session_state.db
def get_date_range(selection: str):
today = date.today()
if selection == "Today":
start = datetime.combine(today, datetime.min.time())
end = datetime.combine(today, datetime.max.time())
elif selection == "Yesterday":
y = today - timedelta(days=1)
start = datetime.combine(y, datetime.min.time())
end = datetime.combine(y, datetime.max.time())
elif selection == "Last 7 days":
start = datetime.combine(today - timedelta(days=6), datetime.min.time())
end = datetime.combine(today, datetime.max.time())
elif selection == "Last 30 days":
start = datetime.combine(today - timedelta(days=29), datetime.min.time())
end = datetime.combine(today, datetime.max.time())
else: # Custom
return None, None
return start, end