"""
ui.py — Expert-level dark design system with fixed calendar + vivid components.
"""
BASE_CSS = """
"""
def inject_css():
import streamlit as st
st.markdown(BASE_CSS, unsafe_allow_html=True)
# ──────────────────────────────────────────────────────────────────────────────
# COMPONENTS
# ──────────────────────────────────────────────────────────────────────────────
def stat_card(icon, label, value, sub="", accent="#7c7cf7", glow=True):
r, g, b = _hex_to_rgb_tuple(accent)
glow_css = f"box-shadow:0 0 28px rgba({r},{g},{b},0.22);" if glow else ""
return f"""
{icon}
{value}
{label}
{f'
{sub}
' if sub else ''}
"""
def section_header(title, sub="", icon=""):
return f"""
{icon+' ' if icon else ''}{title}
{f'
{sub}
' if sub else ''}
"""
def badge(text, color="#7c7cf7"):
return (f'{text}')
def exercise_card(icon, name, sets, reps, rest, idx=0, desc=""):
desc_html = (f'{desc}
') if desc else ""
sets_reps = (f'{sets}x{reps}') if sets else ""
rest_badge = (f'{rest}') if rest and rest != "." else ""
return f"""
{icon}
{sets_reps}{rest_badge}
"""
def progress_ring(pct, size=80, stroke=6, color="#7c7cf7"):
r = (size - stroke) // 2
circ = 2 * 3.14159 * r
offset = circ * (1 - pct / 100)
return f"""
"""
def calendar_widget(tracking, num_days, today_str):
"""
Monthly calendar view showing full current month.
Each cell displays the date number clearly, colored by workout status.
"""
from datetime import date, timedelta
import calendar as cal_mod
today = date.today()
year = today.year
month = today.month
month_name = today.strftime("%B %Y")
days_in_month = cal_mod.monthrange(year, month)[1]
first_weekday = date(year, month, 1).weekday() # 0=Mon … 6=Sun
# ── Day-of-week header ────────────────────────────────────────────────────
day_hdrs = "".join(
f'{d}
'
for d in ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]
)
# ── Blank leading cells ───────────────────────────────────────────────────
cells = "".join('' for _ in range(first_weekday))
# ── Day cells ─────────────────────────────────────────────────────────────
for day_num in range(1, days_in_month + 1):
d = date(year, month, day_num)
ds = d.isoformat()
s = tracking.get(ds, {}).get("status", "none")
is_today = (d == today)
is_future = (d > today)
# colors
if is_future:
bg = "rgba(255,255,255,0.03)"
num_col = "rgba(238,238,255,0.18)"
bdr = "rgba(255,255,255,0.05)"
elif s == "done":
bg = "#3d3d9e"
num_col = "#ffffff"
bdr = "#5b5bd6"
elif s == "skipped":
bg = "rgba(245,158,11,0.22)"
num_col = "#f5c842"
bdr = "rgba(245,158,11,0.50)"
elif d < today: # missed
bg = "rgba(244,63,94,0.10)"
num_col = "rgba(244,63,94,0.55)"
bdr = "rgba(244,63,94,0.22)"
else: # today, pending
bg = "rgba(124,124,247,0.20)"
num_col = "#c4b5fd"
bdr = "rgba(124,124,247,0.60)"
today_ring = (
"box-shadow:0 0 0 2px #7c7cf7,0 0 14px rgba(124,124,247,0.40);"
) if is_today else ""
# small status dot below number
dot = ""
if s == "done":
dot = ''
elif s == "skipped":
dot = ''
cells += f"""
{day_num}
{dot}
"""
# ── Legend + container ────────────────────────────────────────────────────
return f"""
{month_name}
Done
Skipped
Missed
{day_hdrs}
{cells}
"""
# ── internal helpers ──────────────────────────────────────────────────────────
def _hex_to_rgb_tuple(hex_color):
h = hex_color.lstrip('#')
try:
return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
except Exception:
return 124, 124, 247
def _hex_to_rgb(hex_color):
r, g, b = _hex_to_rgb_tuple(hex_color)
return f"{r},{g},{b}"