Dash-math_Database / student_login.py
Mochi0622's picture
Update student_login.py
ea68c55 verified
Raw
History Blame Contribute Delete
4.86 kB
"""
student_login.py
----------------
學生登入元件。
用 clientside callback 直接讀 localStorage,避免 Python callback 時序問題。
"""
from __future__ import annotations
import dash
from dash import html, dcc, callback, Input, Output, State, no_update, clientside_callback
import dash_mantine_components as dmc
from dash_iconify import DashIconify
def create_student_store() -> dcc.Store:
return dcc.Store(id="student-id-store", storage_type="local", data="")
def create_login_modal() -> html.Div:
return html.Div([
dmc.Modal(
id="student-login-modal",
opened=False,
centered=True,
closeOnClickOutside=False,
closeOnEscape=False,
withCloseButton=False,
title=dmc.Group([
DashIconify(icon="tabler:school", height=24),
dmc.Text("歡迎使用數學學習平台", fw=700, size="lg"),
], gap="xs"),
children=[
dmc.Text(
"請輸入您的學號,系統將記錄您的學習進度。",
size="sm", c="dimmed", mb="md",
),
dmc.TextInput(
id="student-id-input",
label="學號",
placeholder="例如:B11234567",
leftSection=DashIconify(icon="tabler:id-badge", height=16),
required=True,
mb="md",
),
dmc.Alert(
id="student-login-error",
children="",
color="red",
variant="light",
style={"display": "none"},
mb="sm",
),
dmc.Button(
"開始學習",
id="student-login-btn",
fullWidth=True,
leftSection=DashIconify(icon="tabler:arrow-right", height=16),
),
],
size="sm",
),
])
def create_student_badge() -> html.Div:
return html.Div(
id="student-badge",
style={"display": "none"},
children=dmc.Group([
dmc.Badge(
id="student-badge-text",
leftSection=DashIconify(icon="tabler:user", height=12),
variant="light",
color="teal",
size="sm",
),
dmc.ActionIcon(
DashIconify(icon="tabler:logout", height=14),
id="student-logout-btn",
size="sm",
variant="subtle",
color="gray",
),
], gap=4),
)
@callback(
Output("student-id-store", "data", allow_duplicate=True),
Input("student-logout-btn", "n_clicks"),
prevent_initial_call=True,
)
def logout_student(n_clicks):
if n_clicks:
return ""
return no_update
# ─── Clientside:頁面載入時直接從 localStorage 判斷是否開啟 modal ───────────
# 完全在瀏覽器端執行,不需要等 Python callback,沒有時序問題
clientside_callback(
"""
function(storeData) {
try {
if (storeData && storeData !== "") {
return false;
}
return true;
} catch(e) {
return true;
}
}
""",
Output("student-login-modal", "opened"),
Input("student-id-store", "data"),
prevent_initial_call=False,
)
# ─── Python callback:按下「開始學習」後儲存學號 ──────────────────────────
@callback(
Output("student-id-store", "data"),
Output("student-login-error", "children"),
Output("student-login-error", "style"),
Input("student-login-btn", "n_clicks"),
State("student-id-input", "value"),
prevent_initial_call=True,
)
def save_student_id(n_clicks, input_value):
sid = (input_value or "").strip()
if not sid:
return no_update, "請輸入學號", {"display": "block"}
# 寫入 Firebase
try:
from firebase_progress import upsert_student
upsert_student(sid)
except Exception as e:
print(f"[Firebase] upsert_student 失敗:{e}")
return sid, "", {"display": "none"}
# ─── 學生 badge 更新 ─────────────────────────────────────────────────────
@callback(
Output("student-badge", "style"),
Output("student-badge-text", "children"),
Input("student-id-store", "data"),
)
def update_badge(student_id):
if student_id:
return {"display": "inline-flex", "alignItems": "center"}, f"學號:{student_id}"
return {"display": "none"}, ""