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 |