saiganesh2004/Test-fitplan-2
0
1"""2ui.py — Expert-level dark design system with fixed calendar + vivid components.3"""4 5BASE_CSS = """6<style>7@import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600;9..40,700&family=JetBrains+Mono:wght@400;500&display=swap');8 9:root {10 --bg: #07070f;11 --bg2: #0d0d1a;12 --bg3: #13132a;13 --bg4: #1a1a35;14 --border: rgba(120,119,198,0.18);15 --border2: rgba(120,119,198,0.35);16 --accent: #7c7cf7;17 --accent2: #a78bfa;18 --green: #10d9a0;19 --amber: #f59e0b;20 --red: #f43f5e;21 --text: #eeeeff;22 --text2: rgba(238,238,255,0.75);23 --muted: rgba(238,238,255,0.45);24 --muted2: rgba(238,238,255,0.25);25 --muted3: rgba(238,238,255,0.12);26 --radius: 14px;27 --radius-sm:9px;28 --font-head:'Syne', sans-serif;29 --font-body:'DM Sans', sans-serif;30 --font-mono:'JetBrains Mono', monospace;31}32 33#MainMenu,footer,header,34[data-testid="stToolbar"],35[data-testid="stDecoration"],36[data-testid="stSidebarNav"],37section[data-testid="stSidebar"] { display:none!important; }38 39html,body { background:#07070f!important; }40 41.stApp,42[data-testid="stAppViewContainer"],43[data-testid="stAppViewContainer"]>section {44 background:#07070f!important;45 color:#eeeeff!important;46 font-family:'DM Sans',sans-serif!important;47}48 49[data-testid="stAppViewContainer"]>section>div.block-container {50 max-width:1220px!important;51 margin:0 auto!important;52 padding:0 28px 100px!important;53 background:transparent!important;54}55 56h1,h2,h3,h4 { font-family:'Syne',sans-serif!important; color:#eeeeff!important; }57 58[data-testid="stWidgetLabel"] p,59[data-testid="stWidgetLabel"] span,60.stTextInput>label, .stNumberInput>label,61.stSelectbox>label, .stMultiSelect>label,62.stCheckbox>label, .stRadio>label {63 color:rgba(238,238,255,0.55)!important;64 font-size:0.70rem!important; font-weight:600!important;65 letter-spacing:1.8px!important; text-transform:uppercase!important;66}67 68div[data-baseweb="input"]>div,69div[data-baseweb="textarea"]>div {70 background:#0f0f22!important;71 border:1.5px solid rgba(120,119,198,0.22)!important;72 border-radius:9px!important; transition:all 0.2s!important;73}74div[data-baseweb="input"]:focus-within>div,75div[data-baseweb="textarea"]:focus-within>div {76 border-color:#7c7cf7!important;77 box-shadow:0 0 0 3px rgba(124,124,247,0.15)!important;78 background:#13132a!important;79}80div[data-baseweb="input"] input,81div[data-baseweb="textarea"] textarea {82 background:transparent!important; color:#eeeeff!important;83 font-family:'DM Sans',sans-serif!important; font-size:0.92rem!important;84}85div[data-baseweb="input"] input::placeholder { color:rgba(238,238,255,0.22)!important; }86 87div[data-baseweb="select"]>div {88 background:#0f0f22!important;89 border:1.5px solid rgba(120,119,198,0.22)!important;90 border-radius:9px!important; color:#eeeeff!important; transition:all 0.2s!important;91}92div[data-baseweb="select"]>div:focus-within {93 border-color:#7c7cf7!important; box-shadow:0 0 0 3px rgba(124,124,247,0.15)!important;94}95div[data-baseweb="select"] span { color:#eeeeff!important; }96div[data-baseweb="popover"] {97 background:#0d0d1a!important;98 border:1px solid rgba(120,119,198,0.28)!important;99 border-radius:12px!important; box-shadow:0 24px 64px rgba(0,0,0,0.85)!important;100}101li[role="option"] {102 color:rgba(238,238,255,0.45)!important;103 font-family:'DM Sans',sans-serif!important;104 border-radius:6px!important; margin:2px 6px!important;105}106li[role="option"]:hover { background:rgba(124,124,247,0.18)!important; color:#eeeeff!important; }107li[aria-selected="true"] { background:rgba(124,124,247,0.25)!important; color:#eeeeff!important; }108 109div[data-baseweb="multi-select"]>div {110 background:#0f0f22!important;111 border:1.5px solid rgba(120,119,198,0.22)!important;112 border-radius:9px!important; min-height:46px!important;113}114span[data-baseweb="tag"] {115 background:rgba(124,124,247,0.20)!important;116 border:1px solid rgba(124,124,247,0.38)!important; border-radius:5px!important;117}118span[data-baseweb="tag"] span { color:#eeeeff!important; font-size:0.80rem!important; }119 120[data-testid="stCheckbox"] label {121 color:rgba(238,238,255,0.75)!important; font-size:0.88rem!important;122 letter-spacing:0!important; text-transform:none!important; font-weight:400!important;123}124 125.stButton>button {126 background:linear-gradient(135deg,#5b5bd6,#7c3aed)!important;127 border:none!important; color:#fff!important; border-radius:9px!important;128 font-family:'DM Sans',sans-serif!important; font-size:0.87rem!important;129 font-weight:600!important; padding:11px 22px!important;130 letter-spacing:0.3px!important; transition:all 0.2s ease!important;131 box-shadow:0 4px 18px rgba(91,91,214,0.35)!important;132}133.stButton>button:hover {134 transform:translateY(-2px)!important;135 box-shadow:0 10px 34px rgba(91,91,214,0.56)!important;136 filter:brightness(1.10)!important;137}138.stButton>button:active { transform:translateY(0)!important; }139 140.stTabs [data-baseweb="tab-list"] {141 background:#0d0d1a!important; border-radius:9px!important;142 padding:4px!important; gap:3px!important;143 border:1px solid rgba(120,119,198,0.18)!important;144}145.stTabs [data-baseweb="tab"] {146 background:transparent!important; color:rgba(238,238,255,0.45)!important;147 border-radius:6px!important; font-family:'DM Sans',sans-serif!important;148 font-size:0.84rem!important; font-weight:500!important;149 border:none!important; padding:9px 18px!important; transition:all 0.2s!important;150}151.stTabs [aria-selected="true"] {152 background:linear-gradient(135deg,#5b5bd6,#7c3aed)!important;153 color:#fff!important; box-shadow:0 2px 14px rgba(91,91,214,0.45)!important;154}155.stTabs [data-baseweb="tab-highlight"],156.stTabs [data-baseweb="tab-border"] { display:none!important; }157 158.stProgress>div>div {159 background:linear-gradient(90deg,#5b5bd6,#a78bfa)!important; border-radius:100px!important;160}161.stProgress>div { background:rgba(255,255,255,0.06)!important; border-radius:100px!important; }162 163.stAlert { border-radius:9px!important; font-size:0.88rem!important; }164hr { border-color:rgba(120,119,198,0.14)!important; margin:20px 0!important; }165 166::-webkit-scrollbar { width:5px; height:5px; }167::-webkit-scrollbar-track { background:#07070f; }168::-webkit-scrollbar-thumb { background:rgba(124,124,247,0.30); border-radius:100px; }169::-webkit-scrollbar-thumb:hover { background:rgba(124,124,247,0.55); }170 171@keyframes fadeUp {172 from { opacity:0; transform:translateY(10px); }173 to { opacity:1; transform:translateY(0); }174}175</style>176"""177 178 179def inject_css():180 import streamlit as st181 st.markdown(BASE_CSS, unsafe_allow_html=True)182 183 184# ──────────────────────────────────────────────────────────────────────────────185# COMPONENTS186# ──────────────────────────────────────────────────────────────────────────────187 188def stat_card(icon, label, value, sub="", accent="#7c7cf7", glow=True):189 r, g, b = _hex_to_rgb_tuple(accent)190 glow_css = f"box-shadow:0 0 28px rgba({r},{g},{b},0.22);" if glow else ""191 return f"""192<div style="background:#0f0f22;border:1px solid rgba({r},{g},{b},0.25);193 border-radius:14px;padding:22px 16px;text-align:center;position:relative;194 overflow:hidden;animation:fadeUp 0.4s ease;{glow_css}">195 <div style="position:absolute;top:0;left:15%;right:15%;height:2px;196 background:linear-gradient(90deg,transparent,{accent},transparent);opacity:0.8"></div>197 <div style="font-size:1.55rem;margin-bottom:10px;line-height:1">{icon}</div>198 <div style="font-family:'Syne',sans-serif;font-size:2.1rem;font-weight:800;199 color:{accent};line-height:1;letter-spacing:-1.5px">{value}</div>200 <div style="font-size:0.59rem;font-weight:700;letter-spacing:2.5px;201 text-transform:uppercase;color:rgba(238,238,255,0.38);margin-top:8px">{label}</div>202 {f'<div style="font-size:0.72rem;color:rgba(238,238,255,0.22);margin-top:3px">{sub}</div>' if sub else ''}203</div>"""204 205 206def section_header(title, sub="", icon=""):207 return f"""208<div style="display:flex;align-items:center;gap:13px;margin:30px 0 16px;animation:fadeUp 0.35s ease">209 <div style="width:3px;height:22px;background:linear-gradient(180deg,#5b5bd6,#a78bfa);210 border-radius:2px;flex-shrink:0"></div>211 <div>212 <div style="font-family:'Syne',sans-serif;font-size:1.08rem;font-weight:700;213 color:#eeeeff;line-height:1.15">{icon+' ' if icon else ''}{title}</div>214 {f'<div style="font-size:0.74rem;color:rgba(238,238,255,0.42);margin-top:3px">{sub}</div>' if sub else ''}215 </div>216</div>"""217 218 219def badge(text, color="#7c7cf7"):220 return (f'<span style="display:inline-flex;align-items:center;background:{color}1a;'221 f'border:1px solid {color}44;border-radius:5px;padding:2px 10px;'222 f'font-size:0.70rem;font-weight:700;color:{color}">{text}</span>')223 224 225def exercise_card(icon, name, sets, reps, rest, idx=0, desc=""):226 desc_html = (f'<div style="font-size:0.70rem;color:rgba(238,238,255,0.30);'227 f'margin-top:3px;line-height:1.5">{desc}</div>') if desc else ""228 sets_reps = (f'<span style="background:rgba(124,124,247,0.14);border:1px solid rgba(124,124,247,0.26);'229 f'border-radius:6px;padding:3px 10px;font-size:0.70rem;font-weight:700;'230 f'color:#a78bfa">{sets}x{reps}</span>') if sets else ""231 rest_badge = (f'<span style="background:rgba(245,158,11,0.10);border:1px solid rgba(245,158,11,0.22);'232 f'border-radius:6px;padding:3px 10px;font-size:0.70rem;font-weight:600;'233 f'color:#f59e0b">{rest}</span>') if rest and rest != "." else ""234 return f"""235<div style="display:flex;align-items:center;gap:12px;padding:10px;236 border-radius:9px;background:rgba(255,255,255,0.025);237 border:1px solid rgba(120,119,198,0.10);margin-bottom:5px">238 <div style="width:34px;height:34px;border-radius:8px;flex-shrink:0;239 background:rgba(124,124,247,0.12);border:1px solid rgba(124,124,247,0.22);240 display:flex;align-items:center;justify-content:center;font-size:0.85rem">{icon}</div>241 <div style="flex:1;min-width:0">242 <div style="font-size:0.85rem;font-weight:600;color:#eeeeff;243 white-space:nowrap;overflow:hidden;text-overflow:ellipsis">{name}</div>244 {desc_html}245 </div>246 <div style="display:flex;gap:5px;flex-shrink:0">{sets_reps}{rest_badge}</div>247</div>"""248 249 250def progress_ring(pct, size=80, stroke=6, color="#7c7cf7"):251 r = (size - stroke) // 2252 circ = 2 * 3.14159 * r253 offset = circ * (1 - pct / 100)254 return f"""255<svg width="{size}" height="{size}" viewBox="0 0 {size} {size}">256 <circle cx="{size//2}" cy="{size//2}" r="{r}"257 fill="none" stroke="rgba(124,124,247,0.12)" stroke-width="{stroke}"/>258 <circle cx="{size//2}" cy="{size//2}" r="{r}"259 fill="none" stroke="{color}" stroke-width="{stroke}"260 stroke-dasharray="{circ:.1f}" stroke-dashoffset="{offset:.1f}"261 stroke-linecap="round" transform="rotate(-90 {size//2} {size//2})"/>262 <text x="50%" y="50%" text-anchor="middle" dominant-baseline="central"263 font-family="Syne,sans-serif" font-size="{size//5}" font-weight="800"264 fill="{color}">{pct}%</text>265</svg>"""266 267 268def calendar_widget(tracking, num_days, today_str):269 """270 Monthly calendar view showing full current month.271 Each cell displays the date number clearly, colored by workout status.272 """273 from datetime import date, timedelta274 import calendar as cal_mod275 276 today = date.today()277 year = today.year278 month = today.month279 month_name = today.strftime("%B %Y")280 days_in_month = cal_mod.monthrange(year, month)[1]281 first_weekday = date(year, month, 1).weekday() # 0=Mon … 6=Sun282 283 # ── Day-of-week header ────────────────────────────────────────────────────284 day_hdrs = "".join(285 f'<div style="text-align:center;font-size:0.63rem;font-weight:700;'286 f'letter-spacing:1px;text-transform:uppercase;'287 f'color:rgba(238,238,255,0.35);padding-bottom:6px">{d}</div>'288 for d in ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]289 )290 291 # ── Blank leading cells ───────────────────────────────────────────────────292 cells = "".join('<div></div>' for _ in range(first_weekday))293 294 # ── Day cells ─────────────────────────────────────────────────────────────295 for day_num in range(1, days_in_month + 1):296 d = date(year, month, day_num)297 ds = d.isoformat()298 s = tracking.get(ds, {}).get("status", "none")299 is_today = (d == today)300 is_future = (d > today)301 302 # colors303 if is_future:304 bg = "rgba(255,255,255,0.03)"305 num_col = "rgba(238,238,255,0.18)"306 bdr = "rgba(255,255,255,0.05)"307 elif s == "done":308 bg = "#3d3d9e"309 num_col = "#ffffff"310 bdr = "#5b5bd6"311 elif s == "skipped":312 bg = "rgba(245,158,11,0.22)"313 num_col = "#f5c842"314 bdr = "rgba(245,158,11,0.50)"315 elif d < today: # missed316 bg = "rgba(244,63,94,0.10)"317 num_col = "rgba(244,63,94,0.55)"318 bdr = "rgba(244,63,94,0.22)"319 else: # today, pending320 bg = "rgba(124,124,247,0.20)"321 num_col = "#c4b5fd"322 bdr = "rgba(124,124,247,0.60)"323 324 today_ring = (325 "box-shadow:0 0 0 2px #7c7cf7,0 0 14px rgba(124,124,247,0.40);"326 ) if is_today else ""327 328 # small status dot below number329 dot = ""330 if s == "done":331 dot = '<div style="width:4px;height:4px;border-radius:50%;background:rgba(255,255,255,0.65);margin:2px auto 0"></div>'332 elif s == "skipped":333 dot = '<div style="width:4px;height:4px;border-radius:50%;background:#f59e0b;margin:2px auto 0"></div>'334 335 cells += f"""<div title="{ds}"336 style="background:{bg};border:1px solid {bdr};border-radius:7px;337 min-height:40px;display:flex;flex-direction:column;align-items:center;338 justify-content:center;cursor:pointer;padding:4px 2px;{today_ring}339 transition:transform 0.15s,opacity 0.15s"340 onmouseover="this.style.transform='scale(1.08)'"341 onmouseout="this.style.transform='scale(1)'">342 <span style="font-size:0.84rem;font-weight:700;color:{num_col};line-height:1">{day_num}</span>343 {dot}344</div>"""345 346 # ── Legend + container ────────────────────────────────────────────────────347 return f"""348<div>349 <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">350 <span style="font-family:'Syne',sans-serif;font-size:0.90rem;font-weight:700;color:#eeeeff">{month_name}</span>351 <div style="display:flex;gap:12px">352 <span style="display:flex;align-items:center;gap:5px;font-size:0.62rem;color:rgba(238,238,255,0.40)">353 <span style="width:9px;height:9px;border-radius:2px;background:#3d3d9e;display:inline-block"></span>Done354 </span>355 <span style="display:flex;align-items:center;gap:5px;font-size:0.62rem;color:rgba(238,238,255,0.40)">356 <span style="width:9px;height:9px;border-radius:2px;background:rgba(245,158,11,0.55);display:inline-block"></span>Skipped357 </span>358 <span style="display:flex;align-items:center;gap:5px;font-size:0.62rem;color:rgba(238,238,255,0.40)">359 <span style="width:9px;height:9px;border-radius:2px;background:rgba(244,63,94,0.40);display:inline-block"></span>Missed360 </span>361 </div>362 </div>363 <div style="display:grid;grid-template-columns:repeat(7,1fr);gap:4px;margin-bottom:4px">364 {day_hdrs}365 </div>366 <div style="display:grid;grid-template-columns:repeat(7,1fr);gap:4px">367 {cells}368 </div>369</div>"""370 371 372# ── internal helpers ──────────────────────────────────────────────────────────373def _hex_to_rgb_tuple(hex_color):374 h = hex_color.lstrip('#')375 try:376 return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)377 except Exception:378 return 124, 124, 247379 380def _hex_to_rgb(hex_color):381 r, g, b = _hex_to_rgb_tuple(hex_color)382 return f"{r},{g},{b}"383 