Spaces:
Sleeping
Sleeping
# auth/signup.py | |
from dash import html, dcc, Input, Output, State, callback, no_update | |
import dash_bootstrap_components as dbc | |
from database import engine, users | |
from sqlalchemy import select, insert | |
from werkzeug.security import generate_password_hash | |
# Opsi untuk Jabatan | |
role_options = [ | |
{'label': 'Petugas Kesehatan Dasar', 'value': 'petugas_kesdas'}, | |
{'label': 'Petugas Rekam Medis Puskesmas', 'value': 'pmik'}, | |
{'label': 'Peneliti', 'value': 'peneliti'}, | |
{'label': 'Lainnya', 'value': 'lainnya'} | |
] | |
signup_layout = dbc.Container( | |
fluid=True, # Mengambil lebar penuh viewport | |
className="d-flex flex-column justify-content-center align-items-center min-vh-100 p-0 m-0", # Center vertikal & horizontal, tinggi minimal 100% viewport, hapus padding/margin default container | |
style={'background': 'linear-gradient(135deg, #1e2a47, #2c3e50)'}, | |
children=[ | |
dcc.Location(id='signup-url-redirect', refresh=True), | |
dbc.Row( # Baris untuk menengahkan Card | |
dbc.Col( # Kolom untuk Card, batasi lebar | |
dbc.Card( | |
dbc.CardBody([ | |
html.H2("Daftar Akun Baru", className="text-center mb-4", style={'color': '#2c3e50'}), | |
# Pesan error/sukses di atas form | |
html.Div(id='signup-message', className="mb-3 text-center"), | |
dbc.Form([ | |
# Nama Lengkap | |
dbc.Row([ | |
dbc.Label("Nama Lengkap", width=4, className="text-md-end"), # Label di kiri pada layar medium+ | |
dbc.Col( | |
dbc.Input(id='signup-fullname', type='text', placeholder='Masukkan nama lengkap Anda'), | |
width=8 | |
) | |
], className="mb-3 align-items-center"), | |
# Username | |
dbc.Row([ | |
dbc.Label("Username", width=4, className="text-md-end"), | |
dbc.Col( | |
dbc.Input(id='signup-username', type='text', placeholder='Pilih username (unik)'), | |
width=8 | |
) | |
], className="mb-3 align-items-center"), | |
dbc.Row([ | |
dbc.Label("Email (Opsional)", width=4, className="text-md-end"), | |
dbc.Col( | |
dbc.Input(id='signup-email', type='email', placeholder='Masukkan alamat email'), | |
width=8 | |
) | |
], className="mb-3 align-items-center"), | |
# Password | |
dbc.Row([ | |
dbc.Label("Password", width=4, className="text-md-end"), | |
dbc.Col( | |
dbc.Input(id='signup-password', type='password', placeholder='Buat password (minimal 6 karakter)'), | |
width=8 | |
) | |
], className="mb-3 align-items-center"), | |
# Konfirmasi Password | |
dbc.Row([ | |
dbc.Label("Konfirmasi Password", width=4, className="text-md-end"), | |
dbc.Col( | |
dbc.Input(id='signup-confirm-password', type='password', placeholder='Ulangi password'), | |
width=8 | |
) | |
], className="mb-3 align-items-center"), | |
# Jabatan | |
dbc.Row([ | |
dbc.Label("Jabatan", width=4, className="text-md-end"), | |
dbc.Col( | |
dcc.Dropdown( | |
id='signup-role', | |
options=role_options, | |
placeholder='Pilih jabatan Anda', | |
), | |
width=8 | |
) | |
], className="mb-3 align-items-center"), | |
dbc.Button("Daftar", id='signup-button', color="success", className="w-100 mt-4", n_clicks=0, size="lg"), | |
]), | |
html.Div( | |
dcc.Link("Sudah punya akun? Login di sini", href="/login", className="d-block mt-3 text-center"), | |
) | |
]), | |
className="shadow-lg", # Shadow pada card | |
style={'padding': '2rem'} # Tambahkan padding dalam card | |
), | |
width=12, # Lebar kolom untuk layar kecil | |
sm=10, # Sedikit lebih sempit di layar small | |
md=8, # Lebih sempit lagi di layar medium | |
lg=6, # Paling ideal di layar large | |
xl=5 # Mungkin terlalu sempit, bisa disesuaikan | |
), | |
justify="center", # Tengahkan kolom di dalam baris | |
className="w-100" # Pastikan baris mengambil lebar penuh | |
) | |
] | |
) | |
# Callback untuk handle signup (TETAP SAMA SEPERTI SEBELUMNYA) | |
def handle_signup(n_clicks_signup, fullname, username, password, confirm_password, email, role): | |
if not fullname or not username or not password or not confirm_password or not role: | |
return no_update, dbc.Alert("Semua field (Nama Lengkap, Username, Password, Konfirmasi, Jabatan) harus diisi.", color="danger", dismissable=True, duration=4000) | |
if len(password) < 6: | |
return no_update, dbc.Alert("Password minimal 6 karakter.", color="danger", dismissable=True, duration=4000) | |
if password != confirm_password: | |
return no_update, dbc.Alert("Password dan konfirmasi password tidak cocok.", color="danger", dismissable=True, duration=4000) | |
username = username.strip() | |
email = email.strip() if email else None | |
with engine.connect() as conn: | |
stmt_check_username = select(users.c.id_user).where(users.c.username == username) | |
if conn.execute(stmt_check_username).fetchone(): | |
return no_update, dbc.Alert(f"Username '{username}' sudah digunakan.", color="warning", dismissable=True, duration=4000) | |
if email: | |
stmt_check_email = select(users.c.id_user).where(users.c.email == email) | |
if conn.execute(stmt_check_email).fetchone(): | |
return no_update, dbc.Alert(f"Email '{email}' sudah terdaftar.", color="warning", dismissable=True, duration=4000) | |
hashed_password_to_store = generate_password_hash(password) | |
# print(f"--- SIGNUP DEBUG: Username: {username}, Password Asli: '{password}', Hashed untuk DB: {hashed_password_to_store[:20]}... ---") | |
try: | |
new_user_values = { | |
'nama_lengkap': fullname.strip(), | |
'username': username, | |
'password': hashed_password_to_store, | |
'jabatan': role | |
} | |
if email: | |
new_user_values['email'] = email | |
insert_stmt = users.insert().values(**new_user_values) | |
conn.execute(insert_stmt) | |
conn.commit() | |
return '/login', dbc.Alert("Registrasi berhasil! Silakan login.", color="success", duration=5000) | |
except Exception as e: | |
conn.rollback() | |
# print(f"--- SIGNUP ERROR: {e} ---") | |
# import traceback | |
# traceback.print_exc() | |
return no_update, dbc.Alert(f"Terjadi kesalahan saat registrasi.", color="danger", dismissable=True, duration=4000) | |
return no_update, "" | |
layout = signup_layout |