Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -353,6 +353,17 @@ def inject_custom_css():
|
|
| 353 |
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
| 354 |
}
|
| 355 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
/* Animations */
|
| 357 |
@keyframes fadeIn {
|
| 358 |
from { opacity: 0; transform: translateY(20px); }
|
|
@@ -370,31 +381,6 @@ def inject_custom_css():
|
|
| 370 |
margin-top: 1rem;
|
| 371 |
}
|
| 372 |
|
| 373 |
-
.action-btn {
|
| 374 |
-
padding: 0.4rem 0.8rem;
|
| 375 |
-
border: none;
|
| 376 |
-
border-radius: 8px;
|
| 377 |
-
font-size: 0.8rem;
|
| 378 |
-
font-weight: 500;
|
| 379 |
-
cursor: pointer;
|
| 380 |
-
transition: all 0.3s ease;
|
| 381 |
-
}
|
| 382 |
-
|
| 383 |
-
.edit-btn {
|
| 384 |
-
background: #28a745;
|
| 385 |
-
color: white;
|
| 386 |
-
}
|
| 387 |
-
|
| 388 |
-
.delete-btn {
|
| 389 |
-
background: #dc3545;
|
| 390 |
-
color: white;
|
| 391 |
-
}
|
| 392 |
-
|
| 393 |
-
.action-btn:hover {
|
| 394 |
-
transform: translateY(-1px);
|
| 395 |
-
opacity: 0.9;
|
| 396 |
-
}
|
| 397 |
-
|
| 398 |
/* Responsive design */
|
| 399 |
@media (max-width: 768px) {
|
| 400 |
.main-header {
|
|
@@ -407,6 +393,16 @@ def inject_custom_css():
|
|
| 407 |
padding: 6px 12px;
|
| 408 |
}
|
| 409 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
</style>
|
| 411 |
""", unsafe_allow_html=True)
|
| 412 |
|
|
@@ -512,38 +508,61 @@ def render_create_account_tab():
|
|
| 512 |
|
| 513 |
store = ProfileStore()
|
| 514 |
|
| 515 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 516 |
col1, col2 = st.columns(2)
|
| 517 |
|
| 518 |
with col1:
|
| 519 |
username = st.text_input("π€ Username*",
|
| 520 |
-
|
|
|
|
|
|
|
|
|
|
| 521 |
offers_text = st.text_area("π― Skills I Can Teach*",
|
|
|
|
| 522 |
placeholder="Python programming\nWeb design\nPublic speaking\nData analysis",
|
| 523 |
height=120,
|
| 524 |
help="Enter one skill per line")
|
|
|
|
| 525 |
availability = st.text_input("β° Availability",
|
|
|
|
| 526 |
placeholder="Weekends, Evenings after 6 PM")
|
| 527 |
|
| 528 |
with col2:
|
| 529 |
# Show current username if editing
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
st.info(f"Currently editing: **{current_username}**")
|
| 533 |
|
| 534 |
wants_text = st.text_area("π Skills I Want to Learn*",
|
|
|
|
| 535 |
placeholder="Machine learning\nGraphic design\nDigital marketing\nSpanish language",
|
| 536 |
height=120,
|
| 537 |
help="Enter one skill per line")
|
|
|
|
| 538 |
preferences = st.text_input("π« Preferences",
|
|
|
|
| 539 |
placeholder="Online sessions, English language")
|
| 540 |
|
| 541 |
col1, col2 = st.columns(2)
|
| 542 |
with col1:
|
| 543 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 544 |
with col2:
|
| 545 |
-
if
|
| 546 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 547 |
|
| 548 |
if submit_button:
|
| 549 |
if not username.strip():
|
|
@@ -568,25 +587,31 @@ def render_create_account_tab():
|
|
| 568 |
st.session_state["wants_text"] = wants_text
|
| 569 |
st.session_state["availability"] = availability
|
| 570 |
st.session_state["preferences"] = preferences
|
|
|
|
| 571 |
st.success("β
" + msg)
|
| 572 |
# Auto-redirect to Community tab
|
| 573 |
-
st.session_state.current_tab = "Community"
|
| 574 |
st.rerun()
|
| 575 |
else:
|
| 576 |
st.error("β " + msg)
|
| 577 |
|
| 578 |
-
if
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
|
| 588 |
-
|
| 589 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 590 |
|
| 591 |
def render_community_tab():
|
| 592 |
"""Community tab with modern card layout"""
|
|
@@ -625,65 +650,75 @@ def render_community_tab():
|
|
| 625 |
render_profile_card(profile, store)
|
| 626 |
|
| 627 |
def render_profile_card(profile: Profile, store: ProfileStore):
|
| 628 |
-
"""Render a single profile card"""
|
| 629 |
current_user = st.session_state.get("username", "")
|
| 630 |
is_current_user = current_user.lower() == profile.username.lower()
|
| 631 |
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
<div style="margin-bottom: 1rem;">
|
| 640 |
-
<div style="margin-bottom: 0.8rem;">
|
| 641 |
-
<strong style="color: #667eea;">π― Teaches:</strong><br>
|
| 642 |
-
<div style="margin-top: 0.5rem;">
|
| 643 |
-
{" ".join([f'<span class="skill-tag">{skill}</span>' for skill in profile.offers]) if profile.offers else "<span style='color: #999;'>No skills listed</span>"}
|
| 644 |
-
</div>
|
| 645 |
-
</div>
|
| 646 |
-
<div>
|
| 647 |
-
<strong style="color: #667eea;">π Wants to Learn:</strong><br>
|
| 648 |
-
<div style="margin-top: 0.5rem;">
|
| 649 |
-
{" ".join([f'<span class="skill-tag skill-tag-want">{skill}</span>' for skill in profile.wants]) if profile.wants else "<span style='color: #999;'>No skills listed</span>"}
|
| 650 |
-
</div>
|
| 651 |
</div>
|
| 652 |
</div>
|
|
|
|
| 653 |
|
| 654 |
-
|
| 655 |
-
{f"β° <strong>Available:</strong> {profile.availability}" if profile.availability else ""}
|
| 656 |
-
{f"<br>π« <strong>Preferences:</strong> {profile.preferences}" if profile.preferences else ""}
|
| 657 |
-
</div>
|
| 658 |
-
</div>
|
| 659 |
-
""", unsafe_allow_html=True)
|
| 660 |
-
|
| 661 |
-
# Action buttons for current user's profile
|
| 662 |
-
if is_current_user:
|
| 663 |
col1, col2 = st.columns(2)
|
| 664 |
with col1:
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
|
| 670 |
-
st.
|
| 671 |
-
|
| 672 |
-
st.rerun()
|
| 673 |
with col2:
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 684 |
st.rerun()
|
| 685 |
-
|
| 686 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 687 |
|
| 688 |
def render_matches_tab():
|
| 689 |
"""AI Matches tab"""
|
|
@@ -789,6 +824,8 @@ def main():
|
|
| 789 |
# Initialize session state
|
| 790 |
if "current_tab" not in st.session_state:
|
| 791 |
st.session_state.current_tab = "Dashboard"
|
|
|
|
|
|
|
| 792 |
|
| 793 |
# Inject custom CSS
|
| 794 |
inject_custom_css()
|
|
|
|
| 353 |
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
| 354 |
}
|
| 355 |
|
| 356 |
+
/* Secondary button */
|
| 357 |
+
.stButton button[kind="secondary"] {
|
| 358 |
+
background: #6c757d;
|
| 359 |
+
border: 1px solid #6c757d;
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
.stButton button[kind="secondary"]:hover {
|
| 363 |
+
background: #5a6268;
|
| 364 |
+
border-color: #545b62;
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
/* Animations */
|
| 368 |
@keyframes fadeIn {
|
| 369 |
from { opacity: 0; transform: translateY(20px); }
|
|
|
|
| 381 |
margin-top: 1rem;
|
| 382 |
}
|
| 383 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
/* Responsive design */
|
| 385 |
@media (max-width: 768px) {
|
| 386 |
.main-header {
|
|
|
|
| 393 |
padding: 6px 12px;
|
| 394 |
}
|
| 395 |
}
|
| 396 |
+
|
| 397 |
+
/* Modal-like form styling */
|
| 398 |
+
.edit-form-container {
|
| 399 |
+
background: white;
|
| 400 |
+
padding: 2rem;
|
| 401 |
+
border-radius: 15px;
|
| 402 |
+
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
| 403 |
+
border: 1px solid #e0e0e0;
|
| 404 |
+
margin: 1rem 0;
|
| 405 |
+
}
|
| 406 |
</style>
|
| 407 |
""", unsafe_allow_html=True)
|
| 408 |
|
|
|
|
| 508 |
|
| 509 |
store = ProfileStore()
|
| 510 |
|
| 511 |
+
# Check if we're in edit mode
|
| 512 |
+
edit_mode = st.session_state.get("edit_profile", False)
|
| 513 |
+
current_profile = None
|
| 514 |
+
if edit_mode and st.session_state.get("username"):
|
| 515 |
+
current_profile = store.find_by_username(st.session_state["username"])
|
| 516 |
+
|
| 517 |
+
with st.form("create_account_form", clear_on_submit=not edit_mode):
|
| 518 |
col1, col2 = st.columns(2)
|
| 519 |
|
| 520 |
with col1:
|
| 521 |
username = st.text_input("π€ Username*",
|
| 522 |
+
value=st.session_state.get("username", ""),
|
| 523 |
+
placeholder="Enter your unique username",
|
| 524 |
+
disabled=edit_mode) # Cannot change username in edit mode
|
| 525 |
+
|
| 526 |
offers_text = st.text_area("π― Skills I Can Teach*",
|
| 527 |
+
value=st.session_state.get("offers_text", ""),
|
| 528 |
placeholder="Python programming\nWeb design\nPublic speaking\nData analysis",
|
| 529 |
height=120,
|
| 530 |
help="Enter one skill per line")
|
| 531 |
+
|
| 532 |
availability = st.text_input("β° Availability",
|
| 533 |
+
value=st.session_state.get("availability", ""),
|
| 534 |
placeholder="Weekends, Evenings after 6 PM")
|
| 535 |
|
| 536 |
with col2:
|
| 537 |
# Show current username if editing
|
| 538 |
+
if edit_mode and current_profile:
|
| 539 |
+
st.info(f"βοΈ Editing profile: **{current_profile.username}**")
|
|
|
|
| 540 |
|
| 541 |
wants_text = st.text_area("π Skills I Want to Learn*",
|
| 542 |
+
value=st.session_state.get("wants_text", ""),
|
| 543 |
placeholder="Machine learning\nGraphic design\nDigital marketing\nSpanish language",
|
| 544 |
height=120,
|
| 545 |
help="Enter one skill per line")
|
| 546 |
+
|
| 547 |
preferences = st.text_input("π« Preferences",
|
| 548 |
+
value=st.session_state.get("preferences", ""),
|
| 549 |
placeholder="Online sessions, English language")
|
| 550 |
|
| 551 |
col1, col2 = st.columns(2)
|
| 552 |
with col1:
|
| 553 |
+
if edit_mode:
|
| 554 |
+
submit_button = st.form_submit_button("πΎ Update Profile", use_container_width=True)
|
| 555 |
+
else:
|
| 556 |
+
submit_button = st.form_submit_button("π Create Profile", use_container_width=True)
|
| 557 |
+
|
| 558 |
with col2:
|
| 559 |
+
if edit_mode:
|
| 560 |
+
cancel_button = st.form_submit_button("β Cancel Edit", use_container_width=True, type="secondary")
|
| 561 |
+
else:
|
| 562 |
+
# Only show delete button if user has a profile
|
| 563 |
+
current_username = st.session_state.get("username", "")
|
| 564 |
+
if current_username and store.find_by_username(current_username):
|
| 565 |
+
delete_button = st.form_submit_button("ποΈ Delete Profile", use_container_width=True, type="secondary")
|
| 566 |
|
| 567 |
if submit_button:
|
| 568 |
if not username.strip():
|
|
|
|
| 587 |
st.session_state["wants_text"] = wants_text
|
| 588 |
st.session_state["availability"] = availability
|
| 589 |
st.session_state["preferences"] = preferences
|
| 590 |
+
st.session_state["edit_profile"] = False # Exit edit mode
|
| 591 |
st.success("β
" + msg)
|
| 592 |
# Auto-redirect to Community tab
|
|
|
|
| 593 |
st.rerun()
|
| 594 |
else:
|
| 595 |
st.error("β " + msg)
|
| 596 |
|
| 597 |
+
if edit_mode and cancel_button:
|
| 598 |
+
st.session_state["edit_profile"] = False
|
| 599 |
+
st.rerun()
|
| 600 |
+
|
| 601 |
+
if not edit_mode and 'delete_button' in locals() and delete_button:
|
| 602 |
+
current_username = st.session_state.get("username", "")
|
| 603 |
+
if current_username:
|
| 604 |
+
ok, msg = store.delete(current_username)
|
| 605 |
+
if ok:
|
| 606 |
+
st.session_state["username"] = ""
|
| 607 |
+
st.session_state["offers_text"] = ""
|
| 608 |
+
st.session_state["wants_text"] = ""
|
| 609 |
+
st.session_state["availability"] = ""
|
| 610 |
+
st.session_state["preferences"] = ""
|
| 611 |
+
st.success("β
" + msg)
|
| 612 |
+
st.rerun()
|
| 613 |
+
else:
|
| 614 |
+
st.error("β " + msg)
|
| 615 |
|
| 616 |
def render_community_tab():
|
| 617 |
"""Community tab with modern card layout"""
|
|
|
|
| 650 |
render_profile_card(profile, store)
|
| 651 |
|
| 652 |
def render_profile_card(profile: Profile, store: ProfileStore):
|
| 653 |
+
"""Render a single profile card with proper data display"""
|
| 654 |
current_user = st.session_state.get("username", "")
|
| 655 |
is_current_user = current_user.lower() == profile.username.lower()
|
| 656 |
|
| 657 |
+
# Create the card content using Streamlit components instead of raw HTML
|
| 658 |
+
with st.container():
|
| 659 |
+
st.markdown(f"""
|
| 660 |
+
<div class="profile-card fade-in">
|
| 661 |
+
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 1rem;">
|
| 662 |
+
<h3 style="margin: 0; color: #333;">π€ {profile.username}</h3>
|
| 663 |
+
{'<span style="background: #667eea; color: white; padding: 0.3rem 0.8rem; border-radius: 15px; font-size: 0.8rem;">You</span>' if is_current_user else ''}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 664 |
</div>
|
| 665 |
</div>
|
| 666 |
+
""", unsafe_allow_html=True)
|
| 667 |
|
| 668 |
+
# Skills section
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 669 |
col1, col2 = st.columns(2)
|
| 670 |
with col1:
|
| 671 |
+
st.write("**π― Teaches:**")
|
| 672 |
+
if profile.offers:
|
| 673 |
+
for skill in profile.offers:
|
| 674 |
+
st.markdown(f'<span class="skill-tag">{skill}</span>', unsafe_allow_html=True)
|
| 675 |
+
else:
|
| 676 |
+
st.write("No skills listed")
|
| 677 |
+
|
|
|
|
| 678 |
with col2:
|
| 679 |
+
st.write("**π Wants to Learn:**")
|
| 680 |
+
if profile.wants:
|
| 681 |
+
for skill in profile.wants:
|
| 682 |
+
st.markdown(f'<span class="skill-tag skill-tag-want">{skill}</span>', unsafe_allow_html=True)
|
| 683 |
+
else:
|
| 684 |
+
st.write("No skills listed")
|
| 685 |
+
|
| 686 |
+
# Additional details
|
| 687 |
+
if profile.availability or profile.preferences:
|
| 688 |
+
st.write("---")
|
| 689 |
+
if profile.availability:
|
| 690 |
+
st.write(f"**β° Available:** {profile.availability}")
|
| 691 |
+
if profile.preferences:
|
| 692 |
+
st.write(f"**π« Preferences:** {profile.preferences}")
|
| 693 |
+
|
| 694 |
+
# Action buttons for current user's profile
|
| 695 |
+
if is_current_user:
|
| 696 |
+
st.write("---")
|
| 697 |
+
col1, col2 = st.columns(2)
|
| 698 |
+
with col1:
|
| 699 |
+
if st.button("βοΈ Edit", key=f"edit_{profile.id}", use_container_width=True):
|
| 700 |
+
st.session_state["username"] = profile.username
|
| 701 |
+
st.session_state["offers_text"] = "\n".join(profile.offers)
|
| 702 |
+
st.session_state["wants_text"] = "\n".join(profile.wants)
|
| 703 |
+
st.session_state["availability"] = profile.availability
|
| 704 |
+
st.session_state["preferences"] = profile.preferences
|
| 705 |
+
st.session_state["edit_profile"] = True
|
| 706 |
st.rerun()
|
| 707 |
+
with col2:
|
| 708 |
+
if st.button("ποΈ Delete", key=f"delete_{profile.id}", use_container_width=True, type="secondary"):
|
| 709 |
+
ok, msg = store.delete(profile.username)
|
| 710 |
+
if ok:
|
| 711 |
+
if st.session_state.get("username") == profile.username:
|
| 712 |
+
st.session_state["username"] = ""
|
| 713 |
+
st.session_state["offers_text"] = ""
|
| 714 |
+
st.session_state["wants_text"] = ""
|
| 715 |
+
st.session_state["availability"] = ""
|
| 716 |
+
st.session_state["preferences"] = ""
|
| 717 |
+
st.session_state["edit_profile"] = False
|
| 718 |
+
st.success("β
" + msg)
|
| 719 |
+
st.rerun()
|
| 720 |
+
else:
|
| 721 |
+
st.error("β " + msg)
|
| 722 |
|
| 723 |
def render_matches_tab():
|
| 724 |
"""AI Matches tab"""
|
|
|
|
| 824 |
# Initialize session state
|
| 825 |
if "current_tab" not in st.session_state:
|
| 826 |
st.session_state.current_tab = "Dashboard"
|
| 827 |
+
if "edit_profile" not in st.session_state:
|
| 828 |
+
st.session_state.edit_profile = False
|
| 829 |
|
| 830 |
# Inject custom CSS
|
| 831 |
inject_custom_css()
|