from __future__ import annotations import time import uuid import re from pathlib import Path import streamlit as st from config import AVATAR_DIR from storage import Profile, ProfileStore from utils import normalize_skill_list, make_prompt_for_matching from groq_client import ask_groq_for_matches from matching import calculate_local_matches from feedback import save_feedback from ui_styles import BASE_CSS # ---------------------------------------------- # Streamlit Config # ---------------------------------------------- st.set_page_config( page_title="AI Skill Swap", page_icon="🤝", layout="wide", ) st.markdown(f"", unsafe_allow_html=True) store = ProfileStore() # ---------------------------------------------- # Header Section # ---------------------------------------------- col_logo, col_title = st.columns([1, 6]) with col_logo: try: st.image("logo.jpg", width=90) except: st.image("https://via.placeholder.com/100x100/4F46E5/FFFFFF?text=🤝", width=90) with col_title: st.title("AI Skill Swap") st.caption("Teach what you know • Learn what you love • Match intelligently") # ---------------------------------------------- # Sidebar: Profile Management # ---------------------------------------------- with st.sidebar: st.header("👤 Profile Manager") profiles = store.load_all() usernames = [p.username for p in profiles] selected_user = st.selectbox( "Select profile", ["— Create New —"] + usernames, key="selected_user" ) st.markdown("### 📝 Profile Details") username = st.text_input( "Username", value="" if selected_user == "— Create New —" else selected_user, key="username_input" ) offers_text = st.text_area( "Skills you can teach", height=90, value=st.session_state.get("offers_text", ""), placeholder="Python, React, Data Science", key="offers_input" ) wants_text = st.text_area( "Skills you want to learn", height=90, value=st.session_state.get("wants_text", ""), placeholder="LLMs, DevOps, UI Design", key="wants_input" ) availability = st.text_input( "Availability", placeholder="Evenings / Weekends", value=st.session_state.get("availability", ""), key="availability_input" ) preferences = st.text_input( "Language / Preferences", placeholder="English, Hindi, Urdu", value=st.session_state.get("preferences", ""), key="preferences_input" ) avatar_file = st.file_uploader( "Upload Avatar (Max 800KB)", type=["png", "jpg", "jpeg"], key="avatar_uploader" ) # Avatar Preview & Size Check if avatar_file: st.image(avatar_file, width=150, caption="Preview Avatar") if avatar_file.size > 800 * 1024: st.error("File exceeds 800KB!") avatar_file = None st.markdown("---") col_create, col_update = st.columns(2) # Create Button with col_create: if st.button("➕ Create", key="create_button", use_container_width=True): if not username.strip(): st.error("Username is required!") elif store.find_by_username(username): st.error("Already exists — use Update!") else: avatar_path = None if avatar_file: safe = re.sub(r"[^A-Za-z0-9_.-]", "_", username) ext = Path(avatar_file.name).suffix avatar_path = str( AVATAR_DIR / f"{safe}_{int(time.time())}{ext}" ) with open(avatar_path, "wb") as f: f.write(avatar_file.getbuffer()) profile = Profile( id=str(uuid.uuid4()), username=username.strip(), offers=normalize_skill_list(offers_text), wants=normalize_skill_list(wants_text), availability=availability.strip(), preferences=preferences.strip(), avatar=avatar_path, ) ok, msg = store.add_or_update(profile) if ok: st.success(msg) # Clear fields on create for key in ["offers_text", "wants_text", "availability", "preferences"]: if key in st.session_state: del st.session_state[key] if "avatar_uploader" in st.session_state: del st.session_state["avatar_uploader"] # Clear previous matches st.session_state["matches"] = [] st.rerun() else: st.error(msg) # Update Button with col_update: if st.button("💾 Update", key="update_button", use_container_width=True): existing = store.find_by_username(username) if not existing: st.error("Profile does not exist.") else: existing.offers = normalize_skill_list(offers_text) existing.wants = normalize_skill_list(wants_text) existing.availability = availability.strip() existing.preferences = preferences.strip() if avatar_file: safe = re.sub(r"[^A-Za-z0-9_.-]", "_", username) ext = Path(avatar_file.name).suffix avatar_path = str( AVATAR_DIR / f"{safe}_{int(time.time())}{ext}" ) with open(avatar_path, "wb") as f: f.write(avatar_file.getbuffer()) existing.avatar = avatar_path store.add_or_update(existing) st.success("Profile updated") if "avatar_uploader" in st.session_state: del st.session_state["avatar_uploader"] st.rerun() # Delete Button if selected_user != "— Create New —": if st.button("🗑️ Delete Profile", key="delete_button", type="secondary"): ok, msg = store.delete(selected_user) if ok: st.warning(msg) # Clear sidebar fields for key in ["username_input", "offers_input", "wants_input", "availability_input", "preferences_input"]: if key in st.session_state: del st.session_state[key] if "avatar_uploader" in st.session_state: del st.session_state["avatar_uploader"] st.rerun() else: st.error(msg) # ---------------------------------------------- # Main Content - Community Profiles # ---------------------------------------------- left, right = st.columns([2, 3]) with left: st.subheader("🌍 Community Profiles") profiles = store.load_all() if not profiles: st.info("No profiles yet. Create one above.") else: for p in profiles: st.markdown("