309 lines
13 KiB
Python
309 lines
13 KiB
Python
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QMessageBox, QApplication, QSizePolicy
|
|
from PyQt5.QtCore import Qt
|
|
from PyQt5.QtGui import QIcon
|
|
import os
|
|
from db.database import verify_admin_login_detail
|
|
from gui.register_dialog import RegisterDialog
|
|
from gui.custom_notifications import CustomNotificationDialog
|
|
import config
|
|
|
|
class LoginDialog(QDialog):
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.setWindowTitle('Login')
|
|
|
|
# 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, 420)
|
|
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('Login ke akun Anda')
|
|
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)
|
|
self.input_user.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
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(74)
|
|
self.input_pass.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
self.input_pass.setMinimumWidth(0)
|
|
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.btn_toggle_pass = QPushButton('👁')
|
|
self.btn_toggle_pass.setFixedSize(74, 74)
|
|
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;
|
|
font-family: 'Segoe UI', Arial, sans-serif;
|
|
}
|
|
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)
|
|
|
|
from PyQt5.QtWidgets import QWidget
|
|
self.password_widget = QWidget()
|
|
self.password_widget.setLayout(password_container)
|
|
self.password_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
self.password_widget.setMinimumWidth(0)
|
|
self.password_widget.setMaximumWidth(16777215)
|
|
self.password_widget.setStyleSheet("margin-bottom: 30px;")
|
|
layout.addWidget(self.password_widget)
|
|
|
|
# Login button
|
|
self.btn_login = QPushButton('Login')
|
|
self.btn_login.setFixedHeight(60)
|
|
self.btn_login.setCursor(Qt.PointingHandCursor)
|
|
self.btn_login.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_login.clicked.connect(self.handle_login)
|
|
layout.addWidget(self.btn_login)
|
|
|
|
# Register link container
|
|
register_container = QHBoxLayout()
|
|
register_container.setContentsMargins(0, 0, 0, 0)
|
|
register_container.setSpacing(5)
|
|
register_container.addStretch() # Center the content
|
|
|
|
# Normal text part
|
|
self.label_register_text = QLabel('Belum punya akun?')
|
|
self.label_register_text.setStyleSheet("""
|
|
QLabel {
|
|
font-size: 16px;
|
|
color: #4a5568;
|
|
background-color: transparent;
|
|
font-family: 'Segoe UI', Arial, sans-serif;
|
|
}
|
|
""")
|
|
register_container.addWidget(self.label_register_text)
|
|
|
|
# Clickable register link
|
|
self.btn_register = QPushButton('Register disini')
|
|
self.btn_register.setFixedHeight(36)
|
|
self.btn_register.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_register.clicked.connect(self.show_register_dialog) # Menghubungkan tombol Register dengan dialog registrasi
|
|
self.btn_register.setCursor(Qt.PointingHandCursor)
|
|
register_container.addWidget(self.btn_register)
|
|
register_container.addStretch() # Center the content
|
|
|
|
register_widget = QWidget()
|
|
register_widget.setLayout(register_container)
|
|
layout.addWidget(register_widget)
|
|
|
|
# Enter key triggers login
|
|
self.input_user.returnPressed.connect(self.handle_login)
|
|
self.input_pass.returnPressed.connect(self.handle_login)
|
|
|
|
self.setLayout(layout)
|
|
self.login_success = False
|
|
self.username = None
|
|
self._went_to_register = False
|
|
self.should_show_register = False # Flag untuk menunjukkan bahwa user ingin ke halaman register
|
|
self._sync_password_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
|
|
|
|
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(520, int(available.width() * 0.90)))
|
|
height = min(base_height, max(380, int(available.height() * 0.85)))
|
|
self.setFixedSize(width, height)
|
|
|
|
def _sync_password_row_width(self):
|
|
"""Keep password row width exactly aligned with username input."""
|
|
if hasattr(self, 'input_user') and hasattr(self, 'password_widget'):
|
|
self.password_widget.setFixedWidth(self.input_user.width())
|
|
|
|
def resizeEvent(self, event):
|
|
super().resizeEvent(event)
|
|
self._sync_password_row_width()
|
|
|
|
#sembunyikan/tampilkan password
|
|
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
|
|
|
|
#proses ambil data input
|
|
def handle_login(self):
|
|
username = self.input_user.text().strip()
|
|
password = self.input_pass.text()
|
|
if not username or not password:
|
|
CustomNotificationDialog.show_warning(self, 'Warning!', 'Username dan password harus diisi!', 'OK', compact=True)
|
|
return
|
|
|
|
login_status = verify_admin_login_detail(username, password)
|
|
|
|
if login_status == 'success':
|
|
self.login_success = True
|
|
self.username = username
|
|
CustomNotificationDialog.show_success(self, 'Login Berhasil!', f'Selamat datang, {username}!', 'Lanjutkan', compact=True)
|
|
self.accept()
|
|
elif login_status == 'username_not_found':
|
|
CustomNotificationDialog.show_error(self, 'Login Gagal!', 'Username tidak terdaftar!', 'Coba Lagi', compact=True)
|
|
self.input_user.setFocus()
|
|
elif login_status == 'wrong_password':
|
|
CustomNotificationDialog.show_error(self, 'Login Gagal!', 'Password yang dimasukkan salah!', 'Coba Lagi', compact=True)
|
|
self.input_pass.setFocus()
|
|
elif login_status == 'wrong_username_and_password':
|
|
CustomNotificationDialog.show_error(self, 'Login Gagal!', 'Username dan password salah!', 'Coba Lagi', compact=True)
|
|
self.input_user.setFocus()
|
|
else:
|
|
CustomNotificationDialog.show_error(self, 'Login Gagal!', 'Terjadi kesalahan saat proses login.', 'Coba Lagi', compact=True)
|
|
self.input_user.setFocus()
|
|
|
|
def clear_form(self):
|
|
"""Clear all input fields in the login form"""
|
|
self.input_user.clear()
|
|
self.input_pass.clear()
|
|
# Reset password visibility
|
|
if self.password_visible:
|
|
self.input_pass.setEchoMode(QLineEdit.Password)
|
|
self.password_visible = False
|
|
self.btn_toggle_pass.setText('👁')
|
|
# Set focus to username field
|
|
self.input_user.setFocus()
|
|
|
|
def show_register_dialog(self):
|
|
"""Tutup login dialog dan set flag untuk menampilkan register dialog"""
|
|
self.should_show_register = True
|
|
self.reject() |