import streamlit as st from auth_token import (login, initiate_signup, complete_signup, reset_password_request, reset_password_confirm, STARTUP_WARNINGS) # Show critical startup warnings (missing API keys etc.) if STARTUP_WARNINGS and not __import__('streamlit').session_state.get('_warnings_shown'): for _w in STARTUP_WARNINGS: __import__('streamlit').session_state['_startup_warning'] = _w __import__('streamlit').session_state['_warnings_shown'] = True import streamlit.components.v1 as components import os st.set_page_config(page_title="FitPlan Pro", page_icon="⚡", layout="wide") # ── Already logged in → go to profile ──────────────────────────────────────── if st.session_state.get("logged_in"): st.switch_page("pages/1_Profile.py") # ── State init ──────────────────────────────────────────────────────────────── for k, v in [("page_mode","landing"),("signup_step","form"),("pending_signup",{})]: if k not in st.session_state: st.session_state[k] = v # ── Hide Streamlit chrome, make iframe full screen ─────────────────────────── st.markdown(""" """, unsafe_allow_html=True) # ══════════════════════════════════════════════════════════════════════════════ # ACTION HANDLERS (query params set by JS window.location.href inside iframe) # ══════════════════════════════════════════════════════════════════════════════ params = st.query_params action = params.get("action", "") # ── LOGIN ───────────────────────────────────────────────────────────────────── if action == "login": u = params.get("u", ""); p = params.get("p", "") if u and p: ok, token, real_username, msg = login(u, p) if ok: st.session_state.logged_in = True st.session_state.username = real_username st.session_state.auth_token = token st.query_params.clear() _db_err = None try: from utils.db import get_user_profile, get_active_plan prof = get_user_profile(real_username) if prof: st.session_state.user_data = prof existing = get_active_plan(real_username) if existing and existing.get("days") and len(existing["days"]) > 0: structured = [] for d in existing["days"]: structured.append({ "day": int(d.get("day_number", 1)), "is_rest_day": bool(d.get("is_rest_day", False)), "muscle_group": str(d.get("muscle_group") or "Full Body"), "workout": d.get("workout_json") or [], "dietary": d.get("dietary_json") or {}, "pre_stretch": d.get("pre_stretch_json") or [], "post_stretch": d.get("post_stretch_json") or [], }) structured.sort(key=lambda x: x["day"]) st.session_state.structured_days = structured st.session_state.full_plan_data = structured st.session_state.dietary_type = existing.get("dietary_type", "veg") st.session_state.plan_id = existing.get("plan_id", "") st.session_state.plan_start = existing.get("created_at_date", "") st.session_state.plan_duration = len(structured) st.session_state.workout_plan = "\n".join([ f"## Day {d['day']} - {d['muscle_group']}" for d in structured ]) st.session_state._plan_checked = True st.session_state._db_loaded_dash = True st.switch_page("pages/2_Dashboard.py") except Exception as _e: _db_err = str(_e) import traceback; traceback.print_exc() st.session_state._plan_checked = True if _db_err: st.session_state._login_db_err = _db_err st.switch_page("pages/1_Profile.py") else: st.session_state.login_error = msg st.query_params.clear(); st.rerun() # ── SEND OTP (signup) ───────────────────────────────────────────────────────── if action == "send_otp": su = params.get("u",""); se = params.get("e","") sp = params.get("p",""); sp2 = params.get("p2","") if sp != sp2: st.session_state.signup_error = "Passwords don't match." elif "@" not in se or "." not in se: st.session_state.signup_error = "Enter a valid email address." elif len(sp) < 6: st.session_state.signup_error = "Password must be at least 6 characters." elif not su.strip(): st.session_state.signup_error = "Username is required." else: with st.spinner(""): ok, msg = initiate_signup(su.strip(), se.strip().lower(), sp) if ok: if msg == "__NO_OTP__": st.session_state.signup_success = "✓ Account created! Please sign in." st.session_state.page_mode = "login" st.session_state.signup_step = "form" st.session_state.pending_signup = {} else: st.session_state.pending_signup = {"username": su.strip(), "email": se.strip().lower(), "password": sp} st.session_state.signup_step = "otp" st.session_state.signup_error = "" st.session_state.page_mode = "signup" else: st.session_state.signup_error = msg st.session_state.page_mode = "signup" st.query_params.clear(); st.rerun() # ── VERIFY OTP (signup) ─────────────────────────────────────────────────────── if action == "verify_otp": otp_val = params.get("otp", "") su = params.get("u", "").strip() se = params.get("e", "").strip() sp = params.get("p", "").strip() if not su: pd = st.session_state.get("pending_signup", {}) su = pd.get("username",""); se = pd.get("email",""); sp = pd.get("password","") ok, token, msg = complete_signup(su, se, sp, otp_val) if ok: st.session_state.signup_success = "✓ Account created! Please sign in." st.session_state.signup_step = "form" st.session_state.page_mode = "login" st.session_state.pending_signup = {} else: st.session_state.otp_error = msg st.session_state.signup_step = "otp" st.session_state.page_mode = "signup" st.query_params.clear(); st.rerun() # ── FORGOT PASSWORD — STEP 1: request OTP ──────────────────────────────────── if action == "forgot_request": fp_id = params.get("id", "").strip() if not fp_id: st.session_state.forgot_error = "Please enter your email or username." st.session_state.page_mode = "forgot" else: ok, msg = reset_password_request(fp_id) if ok: st.session_state.forgot_identity = fp_id st.session_state.forgot_skip_otp = (msg == "__NO_OTP__") st.session_state.page_mode = "forgot_otp" st.session_state.forgot_error = "" if msg != "__NO_OTP__": st.session_state.forgot_sent_msg = msg else: st.session_state.forgot_error = msg st.session_state.page_mode = "forgot" st.query_params.clear(); st.rerun() # ── FORGOT PASSWORD — STEP 2: confirm new password ─────────────────────────── if action == "forgot_confirm": fp_id = params.get("id", "").strip() fp_otp = params.get("otp", "").strip() fp_pw = params.get("pw", "").strip() fp_pw2 = params.get("pw2", "").strip() skip = st.session_state.get("forgot_skip_otp", False) if not fp_pw or len(fp_pw) < 6: st.session_state.forgot_otp_error = "Password must be at least 6 characters." st.session_state.page_mode = "forgot_otp" elif fp_pw != fp_pw2: st.session_state.forgot_otp_error = "Passwords don't match." st.session_state.page_mode = "forgot_otp" else: otp_to_use = "000000" if skip else fp_otp ok, msg = reset_password_confirm(fp_id, otp_to_use, fp_pw) if ok: st.session_state.signup_success = "✓ Password updated! Please sign in." st.session_state.page_mode = "login" for k in ["forgot_identity","forgot_skip_otp","forgot_sent_msg","forgot_error","forgot_otp_error"]: st.session_state.pop(k, None) else: st.session_state.forgot_otp_error = msg st.session_state.page_mode = "forgot_otp" st.query_params.clear(); st.rerun() # ── FORGOT: resend OTP ──────────────────────────────────────────────────────── if action == "forgot_resend": fp_id = st.session_state.get("forgot_identity", params.get("id","").strip()) if fp_id: ok, msg = reset_password_request(fp_id) if ok and msg != "__NO_OTP__": st.session_state.forgot_sent_msg = msg st.query_params.clear(); st.rerun() # ── NAV ─────────────────────────────────────────────────────────────────────── if action == "go_signup": st.session_state.page_mode = "signup"; st.session_state.signup_step = "form" st.query_params.clear(); st.rerun() if action == "go_login": st.session_state.page_mode = "login" for k in ["forgot_identity","forgot_skip_otp","forgot_sent_msg","forgot_error","forgot_otp_error"]: st.session_state.pop(k, None) st.query_params.clear(); st.rerun() if action == "go_back": st.session_state.signup_step = "form" st.session_state.pending_signup = {} st.session_state.page_mode = "signup" st.query_params.clear(); st.rerun() if action == "go_forgot": st.session_state.page_mode = "forgot" st.session_state.forgot_error = "" st.query_params.clear(); st.rerun() # ── Pop flash messages ──────────────────────────────────────────────────────── login_error = st.session_state.pop("login_error", "") signup_error = st.session_state.pop("signup_error", "") otp_error = st.session_state.pop("otp_error", "") signup_success = st.session_state.pop("signup_success", "") forgot_error = st.session_state.get("forgot_error", "") forgot_otp_error = st.session_state.get("forgot_otp_error", "") forgot_sent_msg = st.session_state.pop("forgot_sent_msg", "") mode = st.session_state.page_mode signup_step = st.session_state.signup_step pending = st.session_state.pending_signup pending_email = pending.get("email", "") pending_u = pending.get("username", "") pending_e = pending.get("email", "") pending_p = pending.get("password", "") forgot_identity = st.session_state.get("forgot_identity", "") forgot_skip_otp = st.session_state.get("forgot_skip_otp", False) is_signup = mode == "signup" is_landing = mode == "landing" is_forgot = mode == "forgot" is_forgot_otp = mode == "forgot_otp" def err(msg): return f"
Enter your email or username and we'll send a 6-digit reset code.
""" ) # ── FORGOT PASSWORD STEP 2: enter OTP + new password ───────────────────────── elif is_forgot_otp: sent_notice = good(forgot_sent_msg) if forgot_sent_msg else ( "Reset code sent to your email
" "" + forgot_identity + "" "Check your inbox and spam folder
We sent a 6-digit code to
" + pending_email + "Check your inbox and spam folder