File size: 8,536 Bytes
4253e50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# 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"),

                            # Email
                            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)
@callback(
    Output('signup-url-redirect', 'pathname', allow_duplicate=True),
    Output('signup-message', 'children', allow_duplicate=True),
    Input('signup-button', 'n_clicks'),
    State('signup-fullname', 'value'),
    State('signup-username', 'value'),
    State('signup-password', 'value'),
    State('signup-confirm-password', 'value'),
    State('signup-email', 'value'),
    State('signup-role', 'value'),
    prevent_initial_call=True
)
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