393 lines
16 KiB
Python
393 lines
16 KiB
Python
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QMessageBox, QWidget, QApplication
|
|
from PyQt5.QtCore import Qt
|
|
from PyQt5.QtGui import QIcon
|
|
import os
|
|
from db.database import register_admin
|
|
from gui.custom_notifications import CustomNotificationDialog
|
|
import config
|
|
|
|
class RegisterDialog(QDialog):
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.setWindowTitle('Register')
|
|
# Ensure this dialog is a real top-level window so taskbar uses app icon.
|
|
self.setWindowFlags((self.windowFlags() | Qt.Window) & ~Qt.WindowContextHelpButtonHint | Qt.WindowMinimizeButtonHint)
|
|
icon_path = config.get_app_icon_path()
|
|
if icon_path:
|
|
self.setWindowIcon(QIcon(icon_path))
|
|
self._apply_responsive_size(600, 500)
|
|
if icon_path:
|
|
self.setWindowIcon(QIcon(icon_path))
|
|
|
|
# Set background color
|
|
self.setStyleSheet("QDialog { background-color: #fafafa; }")
|
|
|
|
# Main layout
|
|
layout = QVBoxLayout()
|
|
layout.setContentsMargins(40, 40, 40, 40)
|
|
layout.setSpacing(5)
|
|
|
|
# Title
|
|
title = QLabel('Sistem Klasifikasi Jurusan')
|
|
title.setStyleSheet("font-size: 28px; font-weight: bold; color: #1a202c; margin-bottom: 5px; margin-top: 0px;font-family: 'Segoe UI', Arial, sans-serif;")
|
|
title.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
|
|
title.setWordWrap(True)
|
|
layout.addWidget(title)
|
|
|
|
subtitle = QLabel('Registrasi Akun Baru')
|
|
subtitle.setStyleSheet("font-size: 18px; color: #4a5568; margin-bottom: 30px;font-family: 'Segoe UI', Arial, sans-serif;")
|
|
subtitle.setAlignment(Qt.AlignCenter)
|
|
layout.addWidget(subtitle)
|
|
|
|
# Username
|
|
self.label_user = QLabel('Username')
|
|
self.label_user.setStyleSheet("font-size: 18px; color: #2d3748; font-weight: bold; margin-bottom: 0px;font-family: 'Segoe UI', Arial, sans-serif;")
|
|
layout.addWidget(self.label_user)
|
|
|
|
self.input_user = QLineEdit()
|
|
self.input_user.setFixedHeight(64)
|
|
from PyQt5.QtWidgets import QSizePolicy
|
|
self.input_user.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
#self.input_user.setFixedWidth(520)
|
|
self.input_user.setPlaceholderText('Masukkan username')
|
|
self.input_user.setStyleSheet("font-size: 20px; padding: 0px 12px; margin-bottom: 20px; color: #2d3748; background-color: white; border: 1px solid #e2e8f0; border-radius: 6px;font-family: 'Segoe UI', Arial, sans-serif;")
|
|
layout.addWidget(self.input_user)
|
|
|
|
# Password
|
|
self.label_pass = QLabel('Password')
|
|
self.label_pass.setStyleSheet("font-size: 18px; color: #2d3748; font-weight: bold; margin-bottom: 0px;font-family: 'Segoe UI', Arial, sans-serif;")
|
|
layout.addWidget(self.label_pass)
|
|
|
|
# Password container with toggle button
|
|
password_container = QHBoxLayout()
|
|
password_container.setSpacing(0)
|
|
password_container.setContentsMargins(0, 0, 0, 0)
|
|
|
|
self.input_pass = QLineEdit()
|
|
self.input_pass.setFixedHeight(64)
|
|
from PyQt5.QtWidgets import QSizePolicy
|
|
self.input_pass.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
#self.input_pass.setFixedWidth(465)
|
|
self.input_pass.setPlaceholderText('Masukkan password')
|
|
|
|
self.input_pass.setEchoMode(QLineEdit.Password)
|
|
self.input_pass.setStyleSheet("font-size: 20px; padding: 0px 12px; color: #2d3748; background-color: white; border: 1px solid #e2e8f0; border-top-left-radius: 6px; border-bottom-left-radius: 6px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-right: none;font-family: 'Segoe UI', Arial, sans-serif;")
|
|
self.input_pass.setMinimumWidth(0)
|
|
|
|
self.btn_toggle_pass = QPushButton('👁')
|
|
self.btn_toggle_pass.setFixedSize(64, 64)
|
|
self.btn_toggle_pass.setStyleSheet("""
|
|
QPushButton {
|
|
font-size: 18px;
|
|
border: 1px solid #e2e8f0;
|
|
border-left: none;
|
|
border-top-left-radius: 0px;
|
|
border-bottom-left-radius: 0px;
|
|
border-top-right-radius: 6px;
|
|
border-bottom-right-radius: 6px;
|
|
background-color: white;
|
|
color: #718096;
|
|
margin-left: -8px;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #f7fafc;
|
|
color: #4a5568;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #edf2f7;
|
|
}
|
|
""")
|
|
self.btn_toggle_pass.setCursor(Qt.PointingHandCursor)
|
|
self.btn_toggle_pass.clicked.connect(self.toggle_password_visibility)
|
|
self.password_visible = False
|
|
|
|
password_container.addWidget(self.input_pass)
|
|
password_container.addWidget(self.btn_toggle_pass)
|
|
password_container.setStretch(0, 1)
|
|
|
|
password_widget = QWidget()
|
|
password_widget.setLayout(password_container)
|
|
password_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
password_widget.setStyleSheet("margin-bottom: 20px;")
|
|
layout.addWidget(password_widget)
|
|
|
|
|
|
# Confirm Password
|
|
self.label_confirm = QLabel('Konfirmasi Password')
|
|
self.label_confirm.setStyleSheet("font-size: 18px; color: #2d3748; font-weight: bold; margin-bottom: 0px;font-family: 'Segoe UI', Arial, sans-serif;")
|
|
layout.addWidget(self.label_confirm)
|
|
|
|
# Confirm password container with toggle button
|
|
confirm_container = QHBoxLayout()
|
|
confirm_container.setSpacing(0)
|
|
confirm_container.setContentsMargins(0, 0, 0, 0)
|
|
|
|
self.input_confirm = QLineEdit()
|
|
self.input_confirm.setFixedHeight(74)
|
|
self.input_confirm.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
#self.input_confirm.setFixedWidth(465)
|
|
self.input_confirm.setPlaceholderText('Masukkan password kembali')
|
|
self.input_confirm.setEchoMode(QLineEdit.Password)
|
|
self.input_confirm.setStyleSheet("font-size: 20px; padding: 0px 12px; color: #2d3748; background-color: white; border: 1px solid #e2e8f0; border-top-left-radius: 6px; border-bottom-left-radius: 6px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-right: none;font-family: 'Segoe UI', Arial, sans-serif;")
|
|
self.input_confirm.setMinimumWidth(0)
|
|
|
|
self.btn_toggle_confirm = QPushButton('👁')
|
|
self.btn_toggle_confirm.setFixedSize(74, 74)
|
|
self.btn_toggle_confirm.setStyleSheet("""
|
|
QPushButton {
|
|
font-size: 18px;
|
|
border: 1px solid #e2e8f0;
|
|
border-left: none;
|
|
border-top-left-radius: 0px;
|
|
border-bottom-left-radius: 0px;
|
|
border-top-right-radius: 6px;
|
|
border-bottom-right-radius: 6px;
|
|
background-color: white;
|
|
color: #718096;
|
|
margin-left: 0px;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #f7fafc;
|
|
color: #4a5568;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #edf2f7;
|
|
}
|
|
""")
|
|
self.btn_toggle_confirm.setCursor(Qt.PointingHandCursor)
|
|
self.btn_toggle_confirm.clicked.connect(self.toggle_confirm_visibility)
|
|
self.confirm_visible = False
|
|
|
|
confirm_container.addWidget(self.input_confirm)
|
|
confirm_container.addWidget(self.btn_toggle_confirm)
|
|
confirm_container.setStretch(0, 1)
|
|
|
|
self.confirm_widget = QWidget()
|
|
self.confirm_widget.setLayout(confirm_container)
|
|
self.confirm_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
self.confirm_widget.setMinimumWidth(0)
|
|
self.confirm_widget.setMaximumWidth(16777215)
|
|
self.confirm_widget.setStyleSheet("margin-bottom: 30px;")
|
|
layout.addWidget(self.confirm_widget)
|
|
|
|
|
|
# Register button
|
|
self.btn_register = QPushButton('Register')
|
|
self.btn_register.setFixedHeight(60)
|
|
self.btn_register.setCursor(Qt.PointingHandCursor)
|
|
self.btn_register.setStyleSheet("""
|
|
QPushButton {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
|
stop:0 #667eea, stop:1 #764ba2);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 6px;
|
|
margin-bottom: 20px;
|
|
font-family: 'Segoe UI', Arial, sans-serif;
|
|
}
|
|
QPushButton:hover {
|
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
|
stop:0 #5568d3, stop:1 #6a3b91);
|
|
}
|
|
QPushButton:pressed {
|
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
|
stop:0 #4a5abc, stop:1 #5a3080);
|
|
}
|
|
""")
|
|
self.btn_register.clicked.connect(self.handle_register)
|
|
layout.addWidget(self.btn_register)
|
|
|
|
# Back to login link container
|
|
login_container = QHBoxLayout()
|
|
login_container.setContentsMargins(0, 0, 0, 0)
|
|
login_container.setSpacing(5)
|
|
login_container.addStretch() # Center the content
|
|
|
|
# Normal text part
|
|
self.label_login_text = QLabel('Sudah punya akun?')
|
|
self.label_login_text.setStyleSheet("""
|
|
QLabel {
|
|
font-size: 16px;
|
|
color: #4a5568;
|
|
background-color: transparent;
|
|
font-family: 'Segoe UI', Arial, sans-serif;
|
|
}
|
|
""")
|
|
login_container.addWidget(self.label_login_text)
|
|
|
|
# Clickable login link
|
|
self.btn_back = QPushButton('Login disini')
|
|
self.btn_back.setFixedHeight(36)
|
|
self.btn_back.setStyleSheet("""
|
|
QPushButton {
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
color: #667eea;
|
|
background-color: transparent;
|
|
border: none;
|
|
text-decoration: underline;
|
|
font-family: 'Segoe UI', Arial, sans-serif;
|
|
}
|
|
QPushButton:hover {
|
|
color: #764ba2;
|
|
}
|
|
""")
|
|
self.btn_back.clicked.connect(self.back_to_login)
|
|
self.btn_back.setCursor(Qt.PointingHandCursor)
|
|
login_container.addWidget(self.btn_back)
|
|
login_container.addStretch() # Center the content
|
|
|
|
login_widget = QWidget()
|
|
login_widget.setLayout(login_container)
|
|
layout.addWidget(login_widget)
|
|
|
|
# Enter key triggers register
|
|
self.input_user.returnPressed.connect(self.handle_register)
|
|
self.input_pass.returnPressed.connect(self.handle_register)
|
|
self.input_confirm.returnPressed.connect(self.handle_register)
|
|
|
|
self.setLayout(layout)
|
|
self.register_success = False
|
|
self.back_to_login_clicked = False
|
|
self.should_show_login = False # Flag untuk menunjukkan bahwa user ingin kembali ke halaman login
|
|
self._sync_confirm_row_width()
|
|
self._native_icon_applied = False
|
|
|
|
def _apply_native_windows_icon(self):
|
|
"""Set native HWND icons to avoid default taskbar icon on first window show."""
|
|
if os.name != 'nt':
|
|
return
|
|
icon_path = config.get_app_icon_path()
|
|
if not icon_path or not os.path.exists(icon_path):
|
|
return
|
|
try:
|
|
import ctypes
|
|
WM_SETICON = 0x0080
|
|
ICON_SMALL = 0
|
|
ICON_BIG = 1
|
|
IMAGE_ICON = 1
|
|
LR_LOADFROMFILE = 0x0010
|
|
LR_DEFAULTSIZE = 0x0040
|
|
|
|
hicon = ctypes.windll.user32.LoadImageW(
|
|
None,
|
|
icon_path,
|
|
IMAGE_ICON,
|
|
0,
|
|
0,
|
|
LR_LOADFROMFILE | LR_DEFAULTSIZE,
|
|
)
|
|
if hicon:
|
|
hwnd = int(self.winId())
|
|
ctypes.windll.user32.SendMessageW(hwnd, WM_SETICON, ICON_SMALL, hicon)
|
|
ctypes.windll.user32.SendMessageW(hwnd, WM_SETICON, ICON_BIG, hicon)
|
|
except Exception:
|
|
pass
|
|
|
|
def showEvent(self, event):
|
|
super().showEvent(event)
|
|
if not self._native_icon_applied:
|
|
self._apply_native_windows_icon()
|
|
self._native_icon_applied = True
|
|
# Clear form setiap kali dialog ditampilkan
|
|
self.clear_form()
|
|
|
|
def clear_form(self):
|
|
"""Clear all input fields in the register form"""
|
|
self.input_user.clear()
|
|
self.input_pass.clear()
|
|
self.input_confirm.clear()
|
|
# Reset password visibility
|
|
if self.password_visible:
|
|
self.input_pass.setEchoMode(QLineEdit.Password)
|
|
self.password_visible = False
|
|
if self.confirm_visible:
|
|
self.input_confirm.setEchoMode(QLineEdit.Password)
|
|
self.confirm_visible = False
|
|
self.btn_toggle_pass.setText('👁')
|
|
self.btn_toggle_confirm.setText('👁')
|
|
# Set focus to username field
|
|
self.input_user.setFocus()
|
|
|
|
def _apply_responsive_size(self, base_width, base_height):
|
|
"""Apply fixed size that adapts for smaller screens."""
|
|
screen = QApplication.primaryScreen()
|
|
if not screen:
|
|
self.setFixedSize(base_width, base_height)
|
|
return
|
|
|
|
available = screen.availableGeometry()
|
|
width = min(base_width, max(540, int(available.width() * 0.92)))
|
|
height = min(base_height, max(430, int(available.height() * 0.90)))
|
|
self.setFixedSize(width, height)
|
|
|
|
def _sync_confirm_row_width(self):
|
|
"""Keep confirm-password row width exactly aligned with username input."""
|
|
if hasattr(self, 'input_user') and hasattr(self, 'confirm_widget'):
|
|
self.confirm_widget.setFixedWidth(self.input_user.width())
|
|
|
|
def resizeEvent(self, event):
|
|
super().resizeEvent(event)
|
|
self._sync_confirm_row_width()
|
|
|
|
def toggle_password_visibility(self):
|
|
"""Toggle password visibility"""
|
|
if self.password_visible:
|
|
self.input_pass.setEchoMode(QLineEdit.Password)
|
|
self.password_visible = False
|
|
else:
|
|
self.input_pass.setEchoMode(QLineEdit.Normal)
|
|
self.password_visible = True
|
|
|
|
def toggle_confirm_visibility(self):
|
|
"""Toggle confirm password visibility"""
|
|
if self.confirm_visible:
|
|
self.input_confirm.setEchoMode(QLineEdit.Password)
|
|
self.confirm_visible = False
|
|
else:
|
|
self.input_confirm.setEchoMode(QLineEdit.Normal)
|
|
self.confirm_visible = True
|
|
|
|
#proses ambil data input
|
|
def handle_register(self):
|
|
username = self.input_user.text().strip()
|
|
password = self.input_pass.text()
|
|
confirm_password = self.input_confirm.text()
|
|
|
|
# Validasi input
|
|
if not username or not password or not confirm_password:
|
|
CustomNotificationDialog.show_warning(self, 'Warning!', 'Semua field harus diisi!', 'OK', compact=True)
|
|
return
|
|
|
|
if len(username) < 3:
|
|
CustomNotificationDialog.show_warning(self, 'Warning!', 'Username minimal 3 karakter!', 'OK', compact=True)
|
|
return
|
|
|
|
if len(password) < 6:
|
|
CustomNotificationDialog.show_warning(self, 'Warning!', 'Password minimal 6 karakter!', 'OK', compact=True)
|
|
return
|
|
|
|
if password != confirm_password:
|
|
CustomNotificationDialog.show_warning(self, 'Warning!', 'Password dan konfirmasi password tidak cocok!', 'OK', compact=True)
|
|
return
|
|
|
|
# Coba register
|
|
try:
|
|
register_admin(username, password)
|
|
self.register_success = True
|
|
CustomNotificationDialog.show_success(self, 'Registrasi Berhasil!', f'Akun {username} berhasil dibuat!\nSilakan login untuk melanjutkan.', 'OK', compact=True)
|
|
self.accept()
|
|
except Exception as e:
|
|
error_msg = str(e)
|
|
if 'duplicate' in error_msg.lower() or 'unique' in error_msg.lower():
|
|
CustomNotificationDialog.show_error(self, 'Error!', 'Username sudah digunakan!\nPilih username lain.', 'Coba Lagi', compact=True)
|
|
else:
|
|
CustomNotificationDialog.show_error(self, 'Error!', f'Gagal mendaftar:\n{error_msg}', 'Coba Lagi', compact=True)
|
|
|
|
def back_to_login(self):
|
|
"""Tutup register dialog dan set flag untuk menampilkan login dialog"""
|
|
self.should_show_login = True
|
|
self.reject()
|