File size: 6,795 Bytes
863b325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
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("""

<style>

.diary-card {

    padding: 1rem;

    border-radius: 12px;

    background-color: #f8f9fa;

    margin-bottom: 1rem;

}

.small-text {

    font-size: 0.8rem;

    color: #6c757d;

}

</style>

""", 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()