FitPlan_3 / app.py
Sreehitha-V's picture
Update app.py
32c57ad verified
import streamlit as st
from auth_token import login, initiate_signup, complete_signup
st.set_page_config(page_title="FitPlan Pro", page_icon="πŸ‹οΈ", layout="wide")
if st.session_state.get("logged_in"):
st.switch_page("pages/1_Profile.py")
if "page_mode" not in st.session_state:
st.session_state.page_mode = "login"
if "signup_step" not in st.session_state:
st.session_state.signup_step = "form"
if "pending_signup" not in st.session_state:
st.session_state.pending_signup = {}
st.markdown("""
<style>
@import url('https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@700;800;900&family=Barlow:wght@400;500;600&display=swap');
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body, [class*="css"], .stApp {
font-family: 'Barlow', sans-serif !important;
background: #e0e0e0 !important;
}
#MainMenu, footer, header,
[data-testid="stToolbar"], [data-testid="stDecoration"],
[data-testid="stSidebarNav"], section[data-testid="stSidebar"] { display:none !important; }
[data-testid="stAppViewContainer"] > section > div.block-container {
padding: 0 !important;
max-width: 100% !important;
width: 100% !important;
}
[data-testid="stAppViewContainer"] { background: #e0e0e0 !important; }
.shell {
display: flex;
align-items: stretch;
width: 95%;
max-width: 1080px;
min-height: 620px;
margin: 40px auto;
border-radius: 18px;
overflow: hidden;
border: 5px solid #cc0000;
box-shadow: 0 20px 60px rgba(0,0,0,0.22);
}
.lp {
width: 42%;
flex-shrink: 0;
background:
linear-gradient(158deg, rgba(140,0,0,0.76) 0%, rgba(45,0,0,0.94) 100%),
url('https://images.unsplash.com/photo-1534438327276-14e5300c3a48?w=900&q=80')
center top / cover no-repeat;
padding: 36px 30px;
display: flex;
flex-direction: column;
justify-content: space-between;
color: white;
}
.brand { display:flex; align-items:center; gap:10px; }
.bicon { width:40px;height:40px;border-radius:9px;background:rgba(255,255,255,0.14);display:flex;align-items:center;justify-content:center;font-size:1.2rem; }
.bname { font-weight:700; font-size:0.98rem; }
.bsub { font-size:0.6rem; letter-spacing:2.5px; text-transform:uppercase; opacity:0.55; }
.pill { display:inline-flex;align-items:center;gap:7px;background:rgba(255,255,255,0.11);border:1px solid rgba(255,255,255,0.24);border-radius:50px;padding:5px 13px;font-size:0.78rem;margin-bottom:12px;width:fit-content; }
.dot { width:7px;height:7px;border-radius:50%;background:#ff3333;flex-shrink:0; }
.htitle { font-family:'Barlow Condensed',sans-serif;font-size:2.6rem;font-weight:900;line-height:1.04;text-transform:uppercase;margin-bottom:10px; }
.hdesc { font-size:0.85rem;line-height:1.65;opacity:0.72;margin-bottom:22px; }
.srow { display:flex;gap:8px; }
.sbox { flex:1;border-radius:11px;padding:11px 5px;text-align:center;background:rgba(255,255,255,0.09);border:1px solid rgba(255,255,255,0.17); }
.sn { font-family:'Barlow Condensed',sans-serif;font-size:1.4rem;font-weight:800;display:block; }
.sl { font-size:0.58rem;letter-spacing:1.5px;text-transform:uppercase;opacity:0.55;margin-top:2px;display:block; }
.rp {
flex: 1;
background: white;
padding: 44px 52px;
display: flex;
flex-direction: column;
justify-content: center;
overflow-y: auto;
}
.ftitle { font-family:'Barlow Condensed',sans-serif;font-size:2.65rem;font-weight:900;color:#cc0000;text-transform:uppercase;line-height:1;margin-bottom:4px; }
.fsub { font-size:0.78rem;font-weight:700;color:#111;text-transform:uppercase;letter-spacing:1.5px;margin-bottom:20px; }
.soc-row { display:flex;gap:9px;margin-bottom:16px; }
.soc-btn { flex:1;display:flex;align-items:center;justify-content:center;gap:6px;border:1.5px solid #e0e0e0;border-radius:9px;padding:10px 5px;font-size:0.82rem;color:#222;background:white; }
.or-line { display:flex;align-items:center;gap:10px;color:#ccc;font-size:0.77rem;margin-bottom:14px; }
.or-line::before, .or-line::after { content:'';flex:1;height:1px;background:#ebebeb; }
.flbl { font-size:0.68rem;font-weight:700;letter-spacing:1.8px;text-transform:uppercase;color:#222;margin-bottom:4px;display:block; }
.ext-row { display:flex;justify-content:space-between;align-items:center;margin:10px 0 18px; }
.rem { display:flex;align-items:center;gap:6px;font-size:0.8rem;color:#444; }
.fgt { color:#cc0000;font-size:0.8rem;font-weight:600; }
.terms { text-align:center;font-size:0.72rem;color:#bbb;margin-top:16px; }
.terms a { color:#555;font-weight:600;text-decoration:underline; }
.otp-info { background:#fff5f5;border:1.5px solid #ffcccc;border-radius:10px;padding:14px 16px;font-size:0.85rem;color:#555;margin-bottom:16px;border-left:4px solid #cc0000; }
.otp-info strong { color:#cc0000; }
.stTextInput label { display:none !important; }
div[data-baseweb="input"] { background:white !important;border:1.5px solid #e0e0e0 !important;border-radius:9px !important; }
div[data-baseweb="input"]:focus-within { border-color:#cc0000 !important;box-shadow:0 0 0 3px rgba(204,0,0,0.08) !important; }
div[data-baseweb="input"] > div { background:transparent !important; }
div[data-baseweb="input"] input { background:transparent !important;color:#111 !important;border:none !important;box-shadow:none !important;font-family:'Barlow',sans-serif !important;font-size:0.9rem !important; }
div[data-baseweb="input"] input::placeholder { color:#c0c0c0 !important; }
[data-testid="column"]:nth-child(1) .stButton > button {
background:#cc0000 !important;color:white !important;border:none !important;
border-radius:50px !important;font-family:'Barlow Condensed',sans-serif !important;
font-size:1rem !important;font-weight:800 !important;letter-spacing:2px !important;
text-transform:uppercase !important;padding:13px !important;width:100% !important;
}
[data-testid="column"]:nth-child(1) .stButton > button:hover {
background:#aa0000 !important;box-shadow:0 5px 18px rgba(204,0,0,0.38) !important;
transform:translateY(-1px) !important;
}
[data-testid="column"]:nth-child(2) .stButton > button {
background:white !important;color:#cc0000 !important;
border:2px solid #cc0000 !important;border-radius:50px !important;
font-family:'Barlow Condensed',sans-serif !important;
font-size:1rem !important;font-weight:800 !important;letter-spacing:2px !important;
text-transform:uppercase !important;padding:13px !important;width:100% !important;
}
[data-testid="column"]:nth-child(2) .stButton > button:hover {
background:#fff0f0 !important;transform:translateY(-1px) !important;
}
.stAlert { border-radius:9px !important; }
</style>
""", unsafe_allow_html=True)
# ── LAYOUT: open shell + left panel ──────────────────────────────────────────
st.markdown("""
<div class="shell">
<div class="lp">
<div class="brand">
<div class="bicon">πŸ‹οΈ</div>
<div>
<div class="bname">FitPlan Pro</div>
<div class="bsub">Personalized Fitness</div>
</div>
</div>
<div>
<div class="pill"><span class="dot"></span>&nbsp;Join 12,000+ members</div>
<div class="htitle">Your Personalized<br>Fitness Journey<br>Starts Here.</div>
<div class="hdesc">Custom workout plans, progress tracking, and expert guidance β€” all tailored to your goals and fitness level.</div>
<div class="srow">
<div class="sbox"><span class="sn">12K+</span><span class="sl">Active Members</span></div>
<div class="sbox"><span class="sn">5K+</span><span class="sl">Custom Plans</span></div>
<div class="sbox"><span class="sn">94%</span><span class="sl">Success Rate</span></div>
</div>
</div>
</div>
<div class="rp">
""", unsafe_allow_html=True)
# ══════════════════════ LOGIN ══════════════════════════════════════════════════
if st.session_state.page_mode == "login":
st.markdown("<div class='ftitle'>Welcome Back!</div>", unsafe_allow_html=True)
st.markdown("<div class='fsub'>Login to your account.</div>", unsafe_allow_html=True)
st.markdown("""
<div class="soc-row">
<div class="soc-btn">
<svg width="15" height="15" viewBox="0 0 48 48"><path fill="#4285F4" d="M44.5 20H24v8.5h11.8C34.7 33.9 29.8 37 24 37c-7.2 0-13-5.8-13-13s5.8-13 13-13c3.1 0 5.9 1.1 8.1 2.9l6.4-6.4C34.6 4.1 29.6 2 24 2 11.8 2 2 11.8 2 24s9.8 22 22 22c11 0 21-8 21-22 0-1.3-.2-2.7-.5-4z"/></svg>
Google
</div>
<div class="soc-btn">
<svg width="15" height="15" viewBox="0 0 814 1000"><path d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76 0-103.7 40.8-165.9 40.8s-105-37.5-165.9-123.1C46.6 763.3-.5 653.8-.5 543.4c0-222.9 145.5-340.9 289-340.9 73.4 0 134.4 42.1 179.6 42.1 43.3 0 111.5-44.7 192.3-44.7zm-318.3-64.1c-12.8-60.7 8-122.8 44.7-166 41.5-48.1 108.9-80.3 171.8-80.3 5.1 66.3-19.2 131.2-57.8 179.4-35.3 44.7-100.6 79.3-158.7 66.9z"/></svg>
Apple
</div>
<div class="soc-btn">
<svg width="15" height="15" viewBox="0 0 48 48"><path fill="#1877F2" d="M48 24C48 10.7 37.3 0 24 0S0 10.7 0 24c0 12 8.8 21.9 20.3 23.7V30.9h-6.1V24h6.1v-5.3c0-6 3.6-9.3 9-9.3 2.6 0 5.4.5 5.4.5v5.9h-3c-3 0-3.9 1.9-3.9 3.8V24h6.6l-1.1 6.9h-5.6v16.8C39.2 45.9 48 36 48 24z"/></svg>
Facebook
</div>
</div>
<div class="or-line">OR</div>
<span class="flbl">Email / Username</span>
""", unsafe_allow_html=True)
username = st.text_input("li_u", placeholder="βœ‰ Enter your Email Address", key="li_u", label_visibility="collapsed")
st.markdown('<span class="flbl">Password</span>', unsafe_allow_html=True)
password = st.text_input("li_p", placeholder="πŸ”’ Enter your password", type="password", key="li_p", label_visibility="collapsed")
st.markdown("""
<div class="ext-row">
<label class="rem"><input type="checkbox" checked style="accent-color:#cc0000;width:14px;height:14px;"> Remember me</label>
<span class="fgt">Forgot password?</span>
</div>
""", unsafe_allow_html=True)
c1, c2 = st.columns(2)
with c1:
if st.button("LOGIN β†’", key="btn_login"):
if username and password:
ok, token, msg = login(username, password)
if ok:
st.session_state.logged_in = True
st.session_state.username = username.strip()
st.session_state.auth_token = token
st.switch_page("pages/1_Profile.py")
else:
st.error(msg)
else:
st.warning("Please fill in both fields.")
with c2:
if st.button("SIGN UP", key="btn_go_signup"):
st.session_state.page_mode = "signup"
st.session_state.signup_step = "form"
st.session_state.pending_signup = {}
st.rerun()
st.markdown("<div class='terms'>By continuing, you accept our <a href='#'>Terms of Use</a> and our <a href='#'>Privacy Policy</a>.</div>", unsafe_allow_html=True)
# ══════════════════════ SIGNUP STEP 1 ═════════════════════════════════════════
elif st.session_state.page_mode == "signup" and st.session_state.signup_step == "form":
st.markdown("<div class='ftitle'>Create Account</div>", unsafe_allow_html=True)
st.markdown("<div class='fsub'>Start your fitness journey today.</div>", unsafe_allow_html=True)
st.markdown('<span class="flbl">Username</span>', unsafe_allow_html=True)
su = st.text_input("su_u", placeholder="πŸ‘€ Choose a username", key="su_u", label_visibility="collapsed")
st.markdown('<span class="flbl">Email Address</span>', unsafe_allow_html=True)
se = st.text_input("su_e", placeholder="βœ‰ Enter your email", key="su_e", label_visibility="collapsed")
st.markdown('<span class="flbl">Password</span>', unsafe_allow_html=True)
sp = st.text_input("su_p", placeholder="πŸ”’ Min 6 characters", type="password", key="su_p", label_visibility="collapsed")
st.markdown('<span class="flbl">Confirm Password</span>', unsafe_allow_html=True)
sp2 = st.text_input("su_p2", placeholder="πŸ”’ Repeat password", type="password", key="su_p2", label_visibility="collapsed")
st.markdown("<br>", unsafe_allow_html=True)
c1, c2 = st.columns(2)
with c1:
if st.button("SEND OTP β†’", key="btn_send_otp"):
if su and se and sp and sp2:
if sp != sp2:
st.error("Passwords do not match.")
elif "@" not in se or "." not in se:
st.error("Please enter a valid email address.")
else:
with st.spinner("Sending OTP to " + se + "..."):
ok, msg = initiate_signup(su, se, sp)
if ok:
st.session_state.pending_signup = {
"username": su.strip(),
"email": se.strip(),
"password": sp
}
st.session_state.signup_step = "otp"
st.rerun()
else:
st.error(msg)
else:
st.warning("Please fill all fields.")
with c2:
if st.button("← BACK TO LOGIN", key="btn_back_su"):
st.session_state.page_mode = "login"
st.session_state.signup_step = "form"
st.rerun()
st.markdown("<div class='terms'>By signing up, you accept our <a href='#'>Terms of Use</a> and <a href='#'>Privacy Policy</a>.</div>", unsafe_allow_html=True)
# ══════════════════════ SIGNUP STEP 2 β€” OTP ═══════════════════════════════════
elif st.session_state.page_mode == "signup" and st.session_state.signup_step == "otp":
pending = st.session_state.pending_signup
email = pending.get("email", "")
st.markdown("<div class='ftitle'>Verify Email</div>", unsafe_allow_html=True)
st.markdown("<div class='fsub'>Enter the OTP sent to your inbox.</div>", unsafe_allow_html=True)
st.markdown(
"<div class='otp-info'>A 6-digit OTP was sent to <strong>" + email + "</strong>.<br>"
"Check your inbox and spam folder.</div>",
unsafe_allow_html=True
)
st.markdown('<span class="flbl">Enter 6-Digit OTP</span>', unsafe_allow_html=True)
otp_input = st.text_input("otp_val", placeholder="e.g. 482910", key="otp_val", label_visibility="collapsed", max_chars=6)
st.markdown("<br>", unsafe_allow_html=True)
c1, c2 = st.columns(2)
with c1:
if st.button("VERIFY & CREATE β†’", key="btn_verify"):
if otp_input:
ok, token, msg = complete_signup(
pending["username"],
pending["email"],
pending["password"],
otp_input
)
if ok:
st.success(msg)
st.session_state.page_mode = "login"
st.session_state.signup_step = "form"
st.session_state.pending_signup = {}
st.rerun()
else:
st.error(msg)
else:
st.warning("Please enter the OTP.")
with c2:
if st.button("RESEND OTP", key="btn_resend"):
with st.spinner("Resending..."):
ok, msg = initiate_signup(
pending["username"],
pending["email"],
pending["password"]
)
if ok:
st.success("New OTP sent to " + email)
else:
st.error(msg)
st.markdown("<br>", unsafe_allow_html=True)
if st.button("← Change Email / Start Over", key="btn_restart"):
st.session_state.signup_step = "form"
st.session_state.pending_signup = {}
st.rerun()
# close .rp + .shell
st.markdown("</div></div>", unsafe_allow_html=True)