Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import json | |
| import hashlib | |
| import re | |
| from prompt_builder import build_prompt | |
| from model_api import query_model | |
| # ================= PAGE CONFIG (ONLY ONCE) ================= | |
| st.set_page_config(page_title="FitPlan Pro", layout="wide", initial_sidebar_state="collapsed") | |
| # ================= USER DATABASE ================= | |
| DB_FILE = "users.json" | |
| def load_users(): | |
| try: | |
| with open(DB_FILE, "r") as f: | |
| return json.load(f) | |
| except: | |
| return {} | |
| def save_users(users): | |
| with open(DB_FILE, "w") as f: | |
| json.dump(users, f) | |
| def hash_password(password): | |
| return hashlib.sha256(password.encode()).hexdigest() | |
| def extract_name_from_email(email): | |
| name_part = email.split("@")[0] | |
| name_part = re.sub(r'\d+', '', name_part) | |
| return name_part.capitalize() | |
| # ================= SESSION STATE ================= | |
| if "logged_in" not in st.session_state: | |
| st.session_state.logged_in = False | |
| if "mode" not in st.session_state: | |
| st.session_state.mode = "login" | |
| if "page" not in st.session_state: | |
| st.session_state.page = "welcome" | |
| if "user_details_done" not in st.session_state: | |
| st.session_state.user_details_done = False | |
| if "current_user" not in st.session_state: | |
| st.session_state.current_user = "" | |
| if "name" not in st.session_state: | |
| st.session_state.name = "" | |
| if "age" not in st.session_state: | |
| st.session_state.age = 0 | |
| if "gender" not in st.session_state: | |
| st.session_state.gender = "" | |
| if "height_cm" not in st.session_state: | |
| st.session_state.height_cm = 0 | |
| if "weight_kg" not in st.session_state: | |
| st.session_state.weight_kg = 0 | |
| if "goal" not in st.session_state: | |
| st.session_state.goal = "" | |
| if "level" not in st.session_state: | |
| st.session_state.level = "" | |
| if "equipment" not in st.session_state: | |
| st.session_state.equipment = [] | |
| if "workout_plan" not in st.session_state: | |
| st.session_state.workout_plan = "" | |
| if "bmi_info" not in st.session_state: | |
| st.session_state.bmi_info = "" | |
| if "user_name" not in st.session_state: | |
| st.session_state.user_name = "" | |
| # ================= LOGIN PAGE (UNCHANGED UI) ================= | |
| def apply_pro_styles(): | |
| st.markdown(""" | |
| <style> | |
| header, footer, [data-testid="stToolbar"] {visibility: hidden;} | |
| .block-container { padding: 0 !important; margin: 0 !important; max-width: 100% !important; } | |
| section.main { padding: 0 !important; } | |
| .stApp { background-color: white !important; height: 100vh !important; width: 100vw; overflow: hidden; } | |
| .left-panel { | |
| flex: 1.2; | |
| background: linear-gradient(rgba(139, 0, 0, 0.75), rgba(139, 0, 0, 0.85)), | |
| url("https://images.unsplash.com/photo-1534438327276-14e5300c3a48?q=80&w=2070"); | |
| background-size: cover; | |
| background-position: center; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: space-between; | |
| padding: 5% 5%; | |
| color: white; | |
| height: 100vh; | |
| } | |
| .hero-text { | |
| font-size: 3.5rem; | |
| font-weight: 800; | |
| line-height: 1.1; | |
| text-transform: uppercase; | |
| margin: 30px 0; | |
| } | |
| .stat-row { display: flex; gap: 15px; } | |
| .stat-card { | |
| background: rgba(255, 255, 255, 0.1); | |
| backdrop-filter: blur(10px); | |
| padding: 15px; | |
| border-radius: 10px; | |
| flex: 1; | |
| text-align: center; | |
| } | |
| .form-content { width: 100%; max-width: 400px; } | |
| h1 { color: #D2042D !important; font-weight: 800 !important; font-size: 2.8rem !important; margin:0 !important;} | |
| p.sub { color: #333 !important; margin-bottom: 20px; font-weight: 700; font-size: 1rem;} | |
| label { color: black !important; font-weight: 700 !important; font-size: 0.85rem !important; } | |
| div[data-baseweb="input"] { | |
| border-radius: 8px !important; | |
| border: 1.5px solid #eee !important; | |
| } | |
| div[data-baseweb="input"] input { | |
| background-color: #2b2d3a !important; | |
| color: white !important; | |
| } | |
| div[data-baseweb="input"] input::placeholder { | |
| color: #cccccc !important; | |
| } | |
| div[data-testid="stCheckbox"] label, | |
| div[data-testid="stCheckbox"] label p { | |
| color: black !important; | |
| font-weight: 700 !important; | |
| } | |
| div.stButton > button { | |
| background: #D2042D !important; | |
| color: white !important; | |
| border-radius: 50px !important; | |
| height: 48px !important; | |
| width: 100%; | |
| font-weight: 700 !important; | |
| border: none !important; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| def show_auth_page(): | |
| apply_pro_styles() | |
| l_col, r_col = st.columns([1.2, 1]) | |
| with l_col: | |
| st.markdown(""" | |
| <div class="left-panel"> | |
| <div> | |
| <h3>ποΈ FitPlan Pro</h3> | |
| <p>PERSONALIZED FITNESS</p> | |
| <div class="hero-text">Your Personalized<br>Fitness Journey<br>Starts Here.</div> | |
| </div> | |
| <div class="stat-row"> | |
| <div class="stat-card"><b>12K+</b><br><small>ACTIVE MEMBERS</small></div> | |
| <div class="stat-card"><b>5K+</b><br><small>CUSTOM PLANS</small></div> | |
| <div class="stat-card"><b>94%</b><br><small>SUCCESS RATE</small></div> | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with r_col: | |
| st.markdown('<div class="form-content">', unsafe_allow_html=True) | |
| if st.session_state.mode == "login": | |
| st.markdown('<h1>WELCOME BACK!</h1>', unsafe_allow_html=True) | |
| st.markdown('<p class="sub">LOGIN TO YOUR ACCOUNT.</p>', unsafe_allow_html=True) | |
| email = st.text_input("EMAIL ADDRESS", key="l_email", placeholder="Enter your email") | |
| password = st.text_input("PASSWORD", type="password", key="l_pass", placeholder="Enter your password") | |
| st.checkbox("Remember me", value=True) | |
| if st.button("LOGIN β"): | |
| users = load_users() | |
| if email in users and users[email] == hash_password(password): | |
| st.session_state.logged_in = True | |
| st.session_state.current_user = email | |
| st.session_state.page = "welcome" | |
| st.rerun() | |
| else: | |
| st.error("Invalid Details") | |
| st.markdown("<div style='margin-top:30px;'></div>", unsafe_allow_html=True) | |
| st.markdown("<div style='color:black; font-weight:700; font-size:14px; margin-bottom:15px;'>Don't have an account?</div>", unsafe_allow_html=True) | |
| if st.button("SIGN UP FREE"): | |
| # OR Divider | |
| st.markdown(""" | |
| <div style="display:flex; align-items:center; text-align:center; margin:25px 0;"> | |
| <div style="flex:1; height:1px; background:#ccc;"></div> | |
| <div style="padding:0 10px; font-size:12px; color:#888;">OR</div> | |
| <div style="flex:1; height:1px; background:#ccc;"></div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Google Button (UI only) | |
| st.markdown(""" | |
| <div style=" | |
| border:2px solid #bbb; | |
| border-radius:10px; | |
| padding:12px; | |
| display:flex; | |
| align-items:center; | |
| justify-content:center; | |
| gap:10px; | |
| cursor:pointer; | |
| transition:0.2s; | |
| "> | |
| <img src="https://www.gstatic.com/images/branding/product/1x/gsa_512dp.png" width="18"> | |
| <span style="color:black; font-weight:700;"> | |
| Continue with Google | |
| </span> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| st.session_state.mode = "signup" | |
| st.rerun() | |
| else: | |
| st.markdown('<h1>GET STARTED</h1>', unsafe_allow_html=True) | |
| st.markdown('<p class="sub">CREATE YOUR NEW ACCOUNT.</p>', unsafe_allow_html=True) | |
| new_email = st.text_input("EMAIL", key="s_email") | |
| new_pass = st.text_input("PASSWORD", type="password", key="s_pass") | |
| if st.button("REGISTER"): | |
| users = load_users() | |
| if new_email and new_pass: | |
| users[new_email] = hash_password(new_pass) | |
| save_users(users) | |
| st.success("Account Created!") | |
| st.session_state.mode = "login" | |
| st.rerun() | |
| if st.button("BACK TO LOGIN"): | |
| st.session_state.mode = "login" | |
| st.rerun() | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| def welcome_page(): | |
| apply_pro_styles() | |
| user_name = st.session_state.get("name", "") | |
| st.markdown(""" | |
| <style> | |
| .welcome-card { | |
| background: rgba(255,255,255,0.15); | |
| backdrop-filter: blur(12px); | |
| padding: 50px; | |
| border-radius: 20px; | |
| margin-top: 100px; | |
| text-align: center; | |
| color: white; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| if user_name: | |
| st.markdown(f""" | |
| <div class="welcome-card"> | |
| <h1>Hi {user_name} π</h1> | |
| <h3>Ready to transform your fitness journey?</h3> | |
| <p>Your AI trainer is ready to build a plan tailored just for you.</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| if st.button("START BUILDING MY PLAN"): | |
| st.session_state.page = "dashboard" | |
| st.rerun() | |
| def user_details_page(): | |
| apply_pro_styles() | |
| st.title("Tell Us About Yourself") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| name = st.text_input("NAME") | |
| gender = st.selectbox("GENDER", ["Male", "Female", "Other"]) | |
| height_cm = st.number_input("HEIGHT (CM)", min_value=1.0) | |
| with col2: | |
| age = st.number_input("AGE", min_value=1) | |
| weight_kg = st.number_input("WEIGHT (KG)", min_value=1.0) | |
| if st.button("NEXT β"): | |
| st.session_state.name = name | |
| st.session_state.gender = gender | |
| st.session_state.height_cm = height_cm | |
| st.session_state.weight_kg = weight_kg | |
| st.session_state.age = age | |
| st.session_state.user_details_done = True | |
| st.session_state.page = "goals" | |
| st.rerun() | |
| def goal_page(): | |
| apply_pro_styles() | |
| user_email = st.session_state.get("current_user", "") | |
| user_name = extract_name_from_email(user_email) | |
| st.title(f"Hi {user_name} π") | |
| st.header("Set Your Fitness Goal") | |
| goal = st.selectbox( | |
| "GOAL", | |
| ["Build Muscle", "Weight Loss", "Strength", "Abs", "Flexible"] | |
| ) | |
| level = st.selectbox( | |
| "LEVEL", | |
| ["Beginner", "Intermediate", "Advanced"] | |
| ) | |
| equipment = st.multiselect( | |
| "EQUIPMENT", | |
| ["Dumbbells", "Barbell", "Kettlebells", "Yoga Mat", "Pull-up Bar", "No Equipment"] | |
| ) | |
| if st.button("SUBMIT PROFILE"): | |
| st.session_state.goal = goal | |
| st.session_state.level = level | |
| st.session_state.equipment = equipment | |
| st.session_state.page = "dashboard" | |
| st.rerun() | |
| # ================= DASHBOARD (EXACTLY YOUR ORIGINAL CODE) ================= | |
| def dashboard(): | |
| # Apply SAME style as login | |
| apply_pro_styles() | |
| st.markdown(""" | |
| <style> | |
| .stApp { | |
| background: linear-gradient(rgba(139, 0, 0, 0.85), rgba(139, 0, 0, 0.9)); | |
| } | |
| h1, h2, h3, label, p, span { | |
| color: white !important; | |
| font-weight: 700 !important; | |
| } | |
| .stTabs [role="tab"] { | |
| background: rgba(255,255,255,0.15) !important; | |
| border-radius: 8px !important; | |
| margin-right: 5px !important; | |
| } | |
| .stTabs [aria-selected="true"] { | |
| background: white !important; | |
| } | |
| .stTabs [aria-selected="true"] p { | |
| color: #8B0000 !important; | |
| font-weight: 900 !important; | |
| } | |
| div.stButton > button:first-child { | |
| background: white !important; | |
| color: #8B0000 !important; | |
| font-weight: 900 !important; | |
| font-size: 1.1rem !important; | |
| height: 55px !important; | |
| border-radius: 50px !important; | |
| border: none !important; | |
| } | |
| .workout-container { | |
| background: rgba(255,255,255,0.15); | |
| backdrop-filter: blur(8px); | |
| padding: 25px; | |
| border-radius: 15px; | |
| color: white; | |
| margin-top: 20px; | |
| line-height: 1.6; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| if 'workout_plan' not in st.session_state: | |
| st.session_state.workout_plan = "" | |
| if 'bmi_info' not in st.session_state: | |
| st.session_state.bmi_info = "" | |
| if 'user_name' not in st.session_state: | |
| st.session_state.user_name = "" | |
| st.title("ποΈ PERSONALIZED FITPLAN") | |
| tab1, tab2 = st.tabs(["π ATHLETE PROFILE", "π YOUR WORKOUT PLAN"]) | |
| with tab1: | |
| if st.button("GENERATE MY PLAN"): | |
| with st.spinner("GENERATING YOUR PLAN..."): | |
| prompt, bmi, bmi_status = build_prompt( | |
| st.session_state.name, | |
| st.session_state.gender, | |
| st.session_state.height_cm, | |
| st.session_state.weight_kg, | |
| st.session_state.goal, | |
| st.session_state.level, | |
| st.session_state.equipment, | |
| st.session_state.age | |
| ) | |
| raw_output = query_model(prompt) | |
| clean_text = re.sub(r'\*', '', raw_output) | |
| clean_text = re.sub(r'\n{3,}', '\n\n', clean_text) | |
| clean_text = clean_text.strip() | |
| st.session_state.workout_plan = clean_text | |
| st.session_state.bmi_info = f"BMI: {bmi:.2f} | {bmi_status.upper()}" | |
| st.session_state.user_name = st.session_state.name.upper() | |
| st.success("PLAN GENERATED!") | |
| if st.button("SUBMIT PROFILE"): | |
| if not name or height_cm <= 1 or weight_kg <= 1: | |
| st.error("PLEASE FILL IN ALL FIELDS.") | |
| else: | |
| with st.spinner("GENERATING YOUR PLAN..."): | |
| prompt, bmi, bmi_status = build_prompt( | |
| name, gender, height_cm, weight_kg, | |
| goal, level, equipment, age | |
| ) | |
| raw_output = query_model(prompt) | |
| clean_text = re.sub(r'\*', '', raw_output) | |
| clean_text = re.sub(r'\n{3,}', '\n\n', clean_text) | |
| clean_text = clean_text.strip() | |
| st.session_state.workout_plan = clean_text | |
| st.session_state.bmi_info = f"BMI: {bmi:.2f} | {bmi_status.upper()}" | |
| st.session_state.user_name = name.upper() | |
| st.success("PROFILE SUBMITTED!") | |
| with tab2: | |
| if st.session_state.workout_plan: | |
| st.header(f"π₯ HI {st.session_state.user_name} π") | |
| st.markdown(f""" | |
| <div class="workout-container"> | |
| π {st.session_state.bmi_info} | |
| <hr> | |
| {st.session_state.workout_plan} | |
| </div> | |
| """, unsafe_allow_html=True) | |
| else: | |
| st.warning("PLEASE SUBMIT YOUR PROFILE FIRST.") | |
| # ================= ROUTER ================= | |
| if st.session_state.logged_in: | |
| if st.session_state.page == "welcome": | |
| welcome_page() | |
| elif st.session_state.page == "details": | |
| user_details_page() | |
| elif st.session_state.page == "goals": | |
| goal_page() | |
| elif st.session_state.page == "dashboard": | |
| dashboard() | |
| else: | |
| show_auth_page() | |