""" FocusTrack - Page 3: Activity Log Searchable, filterable, paginated activity table. """ import streamlit as st import pandas as pd from datetime import datetime, timedelta, date from ui_utils import ( fmt_duration, privacy_badge, page_header, CATEGORY_COLORS, get_db, get_date_range ) PAGE_SIZE = 50 def render(): db = get_db() privacy_badge() page_header("Activity Log", "Browse and search all tracked sessions") # ── Filters ─────────────────────────────────────────────────────────────── col1, col2, col3, col4 = st.columns([1.5, 1, 1, 1]) with col1: search = st.text_input("🔍 Search", placeholder="App, title, or category…") with col2: range_sel = st.selectbox("Range", ["Today", "Yesterday", "Last 7 days", "Last 30 days", "Custom"], index=2) start, end = get_date_range(range_sel) if range_sel == "Custom": with col3: start_d = st.date_input("From", value=date.today() - timedelta(days=6)) with col4: end_d = st.date_input("To", value=date.today()) start = datetime.combine(start_d, datetime.min.time()) end = datetime.combine(end_d, datetime.max.time()) # Filter by category cats = [c["name"] for c in db.get_categories()] with col3 if range_sel != "Custom" else st.columns(1)[0]: pass cat_filter_col, show_idle_col = st.columns([2, 1]) with cat_filter_col: selected_cats = st.multiselect("Filter by category", cats, default=[]) with show_idle_col: show_idle = st.checkbox("Include idle", value=False) # ── Count & Pagination ──────────────────────────────────────────────────── total_count = db.get_activity_count(start, end, search) if "log_page" not in st.session_state: st.session_state.log_page = 0 page = st.session_state.log_page offset = page * PAGE_SIZE total_pages = max(1, (total_count + PAGE_SIZE - 1) // PAGE_SIZE) # ── Fetch Data ──────────────────────────────────────────────────────────── rows = db.get_activities(start, end, search=search, limit=PAGE_SIZE, offset=offset) # Apply local filters if selected_cats: rows = [r for r in rows if r.get("category") in selected_cats] if not show_idle: rows = [r for r in rows if not r.get("is_idle")] # ── Stats bar ───────────────────────────────────────────────────────────── st.markdown( f"
" f"Showing {len(rows)} of {total_count:,} records | " f"Page {page + 1} of {total_pages}" f"
", unsafe_allow_html=True, ) # ── Table ───────────────────────────────────────────────────────────────── if rows: html_rows = "" for r in rows: cat = r.get("category", "uncategorized") color = CATEGORY_COLORS.get(cat, "#6366f1") ts = r.get("timestamp", "")[:19].replace("T", " ") is_idle_icon = "💤" if r.get("is_idle") else "●" idle_color = "#374151" if r.get("is_idle") else "#10b981" dur = fmt_duration(r.get("duration_seconds", 0)) app = str(r.get("app_name", ""))[:30] title = str(r.get("window_title", ""))[:60] html_rows += f"""| Time | App | Window Title | Duration | Category | Status |
|---|