import streamlit as st from supabase import create_client from datetime import datetime from zoneinfo import ZoneInfo from supabase_auth.errors import AuthApiError import time # ========== CONFIG ========== st.set_page_config( page_title="Personal Diary", page_icon="📔", layout="wide" ) supabase = create_client( st.secrets["SUPABASE_URL"], st.secrets["SUPABASE_KEY"] ) # ========== STYLE ========== st.markdown(""" """, unsafe_allow_html=True) def signup(email, password): return supabase.auth.sign_up({ "email": email, "password": password }) # ========== HEADER ========== st.title("📔 Personal Diary") st.caption("Ghi chép suy nghĩ mỗi ngày – phiên bản demo") # ========== LOGIN ========== if "user" not in st.session_state: st.subheader("🔐 Tài khoản") tab_login, tab_signup = st.tabs(["Đăng nhập", "Đăng ký"]) # ===== LOGIN ===== with tab_login: email = st.text_input("Email", key="login_email") password = st.text_input("Mật khẩu", type="password", key="login_pw") if st.button("➡️ Đăng nhập", use_container_width=True): res = supabase.auth.sign_in_with_password({ "email": email, "password": password }) if res.user: st.session_state.user = res.user st.success("Đăng nhập thành công") st.rerun() else: st.error("Sai email hoặc mật khẩu") # ===== SIGNUP ===== with tab_signup: email = st.text_input("Email đăng ký", key="signup_email") password = st.text_input( "Mật khẩu (tối thiểu 6 ký tự)", type="password", key="signup_pw" ) # init cooldown if "signup_cooldown_until" not in st.session_state: st.session_state.signup_cooldown_until = 0 now = time.time() cooldown_left = int(st.session_state.signup_cooldown_until - now) if cooldown_left > 0: st.warning(f"⏳ Vui lòng thử lại sau {cooldown_left} giây") signup_disabled = cooldown_left > 0 if st.button( "🆕 Tạo tài khoản", use_container_width=True, disabled=signup_disabled ): try: res = signup(email, password) if res.user: st.session_state.user = res.user st.success("Đăng ký thành công 🎉") st.rerun() else: st.error("Không thể đăng ký") except AuthApiError as e: msg = str(e) # Bắt lỗi rate limit 60s if "only request this after" in msg: # Mặc định 60s nếu parse không được wait_seconds = 60 # cố gắng parse số giây từ message import re match = re.search(r"after (\d+) seconds", msg) if match: wait_seconds = int(match.group(1)) st.session_state.signup_cooldown_until = time.time() + wait_seconds st.error(f"🚫 Bạn đã thử quá nhiều lần. Vui lòng đợi {wait_seconds} giây rồi thử lại.") st.rerun() else: st.error("Lỗi đăng ký: " + msg) # ========== MAIN APP ========== else: user = st.session_state.user # Top bar top_left, top_right = st.columns([4, 1]) with top_left: st.markdown(f"👋 Xin chào **{user.email}**") with top_right: if st.button("🚪 Đăng xuất", use_container_width=True): st.session_state.clear() st.rerun() st.divider() # Main layout left, right = st.columns([2, 3]) # ===== LEFT: WRITE ===== with left: st.subheader("✍️ Viết nhật ký") content = st.text_area( "Hôm nay bạn nghĩ gì?", height=250, placeholder="Viết suy nghĩ của bạn ở đây..." ) if st.button("💾 Lưu nhật ký", use_container_width=True): if content.strip(): supabase.table("journals").insert({ "user_id": user.id, "content": content, "created_at": datetime.utcnow().isoformat() }).execute() st.success("Đã lưu nhật ký ✨") st.rerun() else: st.warning("Nội dung đang trống") # ===== RIGHT: LIST ===== with right: st.subheader("📜 Nhật ký của bạn") data = supabase.table("journals") \ .select("*") \ .eq("user_id", user.id) \ .order("created_at", desc=True) \ .execute() if not data.data: st.info("Chưa có nhật ký nào") else: for row in data.data: # Format ngày cho gọn created_dt = datetime.fromisoformat( row["created_at"].replace("Z", "+00:00") ).astimezone(ZoneInfo("Asia/Ho_Chi_Minh")) date_label = created_dt.strftime("%d/%m/%Y – %H:%M") # Dropdown theo ngày with st.expander(f"📅 {date_label}", expanded=False): new_content = st.text_area( "Nội dung", row["content"], key=row["id"], height=120 ) col_u, col_d = st.columns(2) if col_u.button("✏️ Cập nhật", key=f"u{row['id']}"): supabase.table("journals").update({ "content": new_content, "updated_at": datetime.utcnow().isoformat() }).eq("id", row["id"]).execute() st.rerun() if col_d.button("🗑️ Xoá", key=f"d{row['id']}"): supabase.table("journals").delete() \ .eq("id", row["id"]).execute() st.rerun()