Spaces:
Sleeping
Sleeping
| 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> 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> | |
| </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> | |
| </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) |