# auth/login.py
from dash import html, dcc, Input, Output, State, callback, no_update
import dash_bootstrap_components as dbc # Import dbc
from database import engine, users
from sqlalchemy import select
from werkzeug.security import check_password_hash # PENTING: Import untuk verifikasi hash
login_layout = dbc.Container(
fluid=True,
className="d-flex flex-column justify-content-center align-items-center min-vh-100 p-0 m-0",
style={'background': 'linear-gradient(135deg, #1e2a47, #2c3e50)'},
children=[
dcc.Location(id='login-url-redirect', refresh=True),
dbc.Row(
dbc.Col(
dbc.Card(
dbc.CardBody([
html.H2("Disease Dashboard Login", className="text-center mb-4", style={'color': '#2c3e50'}),
html.Div(id='login-message', className="mb-3 text-center"),
dbc.Form([
dbc.Row([
dbc.Label("Username", width=4, className="text-md-end"),
dbc.Col(
dbc.Input(id='login-username', type='text', placeholder="Masukkan username Anda"),
width=8
)
], className="mb-3 align-items-center"),
dbc.Row([
dbc.Label("Password", width=4, className="text-md-end"),
dbc.Col(
dbc.Input(id='login-password', type='password', placeholder="Masukkan password Anda"),
width=8
)
], className="mb-3 align-items-center"),
dbc.Button("Login", id='login-button', color="primary", className="w-100 mt-4", n_clicks=0, size="lg"),
]),
html.Div(
dcc.Link("Belum punya akun? Daftar di sini", href="/signup", className="d-block mt-3 text-center"),
)
]),
className="shadow-lg",
style={'padding': '2rem'}
),
width=12, sm=10, md=8, lg=5, xl=4 # Sesuaikan lebar card login
),
justify="center",
className="w-100"
)
]
)
layout = login_layout
# Callback untuk handle login
@callback(
Output('login-status', 'data', allow_duplicate=True), # Targetkan store global di app.py
Output('login-url-redirect', 'pathname', allow_duplicate=True),
Output('login-message', 'children', allow_duplicate=True),
Input('login-button', 'n_clicks'),
State('login-username', 'value'),
State('login-password', 'value'),
prevent_initial_call=True
)
def handle_login(n_clicks_login, username, password_input):
if not username or not password_input:
return no_update, no_update, dbc.Alert("Username dan password harus diisi.", color="warning", dismissable=True, duration=4000)
username = username.strip()
with engine.connect() as conn:
# Ambil id_user, username, password (hash), dan nama_lengkap
stmt = select(
users.c.id_user, # Pastikan ini adalah nama PK di tabel users
users.c.username,
users.c.password,
users.c.nama_lengkap
).where(users.c.username == username)
user_record = conn.execute(stmt).fetchone()
print(f"--- LOGIN DEBUG: Mencoba login untuk user: {username} ---")
if user_record:
hashed_password_from_db = user_record.password
# print(f"--- LOGIN DEBUG: Hashed Password dari DB: {str(hashed_password_from_db)[:20]}... ---")
# print(f"--- LOGIN DEBUG: Password Input: '{password_input}' ---")
if check_password_hash(str(hashed_password_from_db), str(password_input).strip()):
session_data = {
'logged_in': True,
'username': user_record.username,
'nama_lengkap': user_record.nama_lengkap,
'id_user': user_record.id_user # <--- TAMBAHKAN BARIS INI
}
print(f"--- LOGIN DEBUG: Login BERHASIL. Session data: {session_data} ---")
return session_data, '/beranda', dbc.Alert(f"Login berhasil, selamat datang {user_record.nama_lengkap}!", color="success", duration=4000)
else:
print(f"--- LOGIN DEBUG: Password SALAH untuk user: {username} ---")
return no_update, no_update, dbc.Alert("Username atau password salah.", color="danger", dismissable=True, duration=4000)
else:
print(f"--- LOGIN DEBUG: Username '{username}' TIDAK DITEMUKAN ---")
return no_update, no_update, dbc.Alert("Username atau password salah.", color="danger", dismissable=True, duration=4000)
# Layout export
layout = login_layout