-- Создание базы данных
CREATE DATABASE IF NOT EXISTS hotel;
USE hotel;

-- Таблица Administrators
CREATE TABLE Administrators (
admin_id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(150) NOT NULL,
passport_number VARCHAR(20) UNIQUE NOT NULL,
address TEXT NOT NULL,
phone_number VARCHAR(30) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
login VARCHAR(30) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- Таблица Receptionists
CREATE TABLE Receptionists (
receptionist_id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(150) NOT NULL,
passport_number VARCHAR(20) UNIQUE NOT NULL,
address TEXT NOT NULL,
phone_number VARCHAR(15) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
login VARCHAR(20) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
admin_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (admin_id) REFERENCES Administrators(admin_id) ON DELETE CASCADE
);

-- Таблица Shifts
CREATE TABLE Shifts (
shift_id INT AUTO_INCREMENT PRIMARY KEY,
shift_name VARCHAR(50) NOT NULL UNIQUE,
start_time TIME NOT NULL,
end_time TIME NOT NULL
);

-- Вставка фиксированных смен
INSERT INTO Shifts (shift_name, start_time, end_time) VALUES
('Ночь', '00:00:00', '08:00:00'),
('Утро', '08:00:00', '16:00:00'),
('Вечер', '16:00:00', '00:00:00');

-- Таблица Cleaners
CREATE TABLE Cleaners (
cleaner_id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(150) NOT NULL,
phone_number VARCHAR(15) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
status ENUM('Свободен', 'Занят') DEFAULT 'Свободен',
shift_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (shift_id) REFERENCES Shifts(shift_id) ON DELETE RESTRICT ON UPDATE CASCADE
);

-- Таблица Rooms
CREATE TABLE Rooms (
room_id INT AUTO_INCREMENT PRIMARY KEY,
room_number INT UNIQUE NOT NULL,
status ENUM('Доступен', 'Недоступен') DEFAULT 'Доступен',
price DECIMAL(10,2) NOT NULL,
bed_type ENUM('Одноместный', 'Двухместный', 'Трехместный', 'Четырехместный') NOT NULL,
room_type ENUM('Стандарт', 'Люкс', 'Делюкс') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- Таблица Cleaning_Schedules
CREATE TABLE Cleaning_Schedules (
schedule_id INT AUTO_INCREMENT PRIMARY KEY,
room_id INT NOT NULL,
cleaner_id INT NOT NULL,
scheduled_time DATETIME NOT NULL,
status ENUM('Назначено', 'В процессе', 'Завершено') DEFAULT 'Назначено',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (room_id) REFERENCES Rooms(room_id) ON DELETE CASCADE,
FOREIGN KEY (cleaner_id) REFERENCES Cleaners(cleaner_id) ON DELETE CASCADE
);

-- Таблица Room_Images
CREATE TABLE Room_Images (
image_id INT AUTO_INCREMENT PRIMARY KEY,
room_id INT NOT NULL,
image_url VARCHAR(255) NOT NULL,
is_primary BOOLEAN DEFAULT FALSE,
FOREIGN KEY (room_id) REFERENCES Rooms(room_id) ON DELETE CASCADE
);

-- Таблица Customers
CREATE TABLE Customers (
customer_id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(100) NOT NULL,
phone VARCHAR(15) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE,
gender ENUM('Мужской', 'Женский'),
country VARCHAR(50),
address TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
receptionist_id INT,
FOREIGN KEY (receptionist_id) REFERENCES Receptionists(receptionist_id) ON DELETE SET NULL
);

-- Таблица Reservations
CREATE TABLE Reservations (
reservation_id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT NOT NULL,
room_id INT NOT NULL,
check_in DATE NOT NULL,
check_out DATE NOT NULL,
status ENUM('Забронирован', 'Заселён', 'Выселен', 'Отменён') DEFAULT 'Забронирован',
total_amount DECIMAL(10,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
receptionist_id INT,
FOREIGN KEY (customer_id) REFERENCES Customers(customer_id) ON DELETE CASCADE,
FOREIGN KEY (room_id) REFERENCES Rooms(room_id) ON DELETE CASCADE,
FOREIGN KEY (receptionist_id) REFERENCES Receptionists(receptionist_id) ON DELETE SET NULL
);

-- Таблица Payments
CREATE TABLE Payments (
payment_id INT AUTO_INCREMENT PRIMARY KEY,
reservation_id INT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
payment_date DATE NOT NULL,
payment_method ENUM('Наличные', 'Кредитная карта', 'Дебетовая карта', 'Онлайн перевод') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (reservation_id) REFERENCES Reservations(reservation_id) ON DELETE CASCADE
);

-- Таблица Cleaning_Actions
CREATE TABLE Cleaning_Actions (
action_id INT AUTO_INCREMENT PRIMARY KEY,
reservation_id INT NOT NULL,
room_id INT NOT NULL,
cleaner_id INT NOT NULL,
action_type ENUM('Ручное назначение', 'Автоматическое назначение') NOT NULL,
scheduled_time DATETIME NOT NULL,
status ENUM('Запланировано', 'В процессе', 'Завершено') DEFAULT 'Запланировано',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (reservation_id) REFERENCES Reservations(reservation_id) ON DELETE CASCADE,
FOREIGN KEY (room_id) REFERENCES Rooms(room_id) ON DELETE CASCADE,
FOREIGN KEY (cleaner_id) REFERENCES Cleaners(cleaner_id) ON DELETE CASCADE
);

-- Таблица Archived_Reservations
CREATE TABLE IF NOT EXISTS Archived_Reservations (
archive_id INT AUTO_INCREMENT PRIMARY KEY,
reservation_id INT NOT NULL,
customer_id INT NOT NULL,
room_id INT NOT NULL,
check_in DATE NOT NULL,
check_out DATE NOT NULL,
status ENUM('Заселён', 'Выселен', 'Отменён') NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES Customers(customer_id) ON DELETE CASCADE,
FOREIGN KEY (room_id) REFERENCES Rooms(room_id) ON DELETE CASCADE
);

DELIMITER
CREATETRIGGERtrgupdateroomavailabilityAFTERUPDATEONReservationsFOREACHROWBEGINIFNEW.status=′Забронирован′ORNEW.status=′Заселён′THENUPDATERoomsSETavailable=FALSE,status=′Недоступен′WHEREroomid=NEW.roomid;ELSEIFNEW.status=′Отменён′ORNEW.status=′Выселен′THENUPDATERoomsSETavailable=TRUE,status=′Доступен′WHEREroomid=NEW.roomid;ENDIF;END
CREATETRIGGERtrgu​pdater​ooma​vailabilityAFTERUPDATEONReservationsFOREACHROWBEGINIFNEW.status=′Забронирован′ORNEW.status=′Заселён′THENUPDATERoomsSETavailable=FALSE,status=′Недоступен′WHEREroomi​d=NEW.roomi​d;ELSEIFNEW.status=′Отменён′ORNEW.status=′Выселен′THENUPDATERoomsSETavailable=TRUE,status=′Доступен′WHEREroomi​d=NEW.roomi​d;ENDIF;END

DELIMITER ;

DELIMITER
CREATETRIGGERtrgpreventoverlappingreservationsBEFOREINSERTONReservationsFOREACHROWBEGINIFEXISTS(SELECT1FROMReservationsWHEREroomid=NEW.roomidANDstatusNOTIN(′Отменён′)AND((NEW.checkinBETWEENcheckinANDcheckout)OR(NEW.checkoutBETWEENcheckinANDcheckout)OR(checkinBETWEENNEW.checkinANDNEW.checkout)))THENSIGNALSQLSTATE′45000′SETMESSAGETEXT=′Номеружезабронированнауказанныедаты.′;ENDIF;END
CREATETRIGGERtrgp​revento​verlappingr​eservationsBEFOREINSERTONReservationsFOREACHROWBEGINIFEXISTS(SELECT1FROMReservationsWHEREroomi​d=NEW.roomi​dANDstatusNOTIN(′Отменён′)AND((NEW.checki​nBETWEENchecki​nANDchecko​ut)OR(NEW.checko​utBETWEENchecki​nANDchecko​ut)OR(checki​nBETWEENNEW.checki​nANDNEW.checko​ut)))THENSIGNALSQLSTATE′45000′SETMESSAGET​EXT=′Номеружезабронированнауказанныедаты.′;ENDIF;END

DELIMITER ;

DELIMITER
CREATETRIGGERtrgcalculatetotalamountbeforeinsertBEFOREINSERTONReservationsFOREACHROWBEGINDECLAREroomPriceDECIMAL(10,2);DECLAREdaysINT;SELECTpriceINTOroomPriceFROMRoomsWHEREroomid=NEW.roomid;SETdays=DATEDIFF(NEW.checkout,NEW.checkin);IFdays<=0THENSIGNALSQLSTATE′45000′SETMESSAGETEXT=′Датавыездадолжнабытьпозжедатызаезда.′;ENDIF;SETNEW.totalamount=roomPrice∗days;END
CREATETRIGGERtrgc​alculatet​otala​mountb​eforei​nsertBEFOREINSERTONReservationsFOREACHROWBEGINDECLAREroomPriceDECIMAL(10,2);DECLAREdaysINT;SELECTpriceINTOroomPriceFROMRoomsWHEREroomi​d=NEW.roomi​d;SETdays=DATEDIFF(NEW.checko​ut,NEW.checki​n);IFdays<=0THENSIGNALSQLSTATE′45000′SETMESSAGET​EXT=′Датавыездадолжнабытьпозжедатызаезда.′;ENDIF;SETNEW.totala​mount=roomPrice∗days;END

CREATE TRIGGER trg_calculate_total_amount_before_update
BEFORE UPDATE ON Reservations
FOR EACH ROW
BEGIN
DECLARE roomPrice DECIMAL(10,2);
DECLARE days INT;

SELECT price INTO roomPrice FROM Rooms WHERE room_id = NEW.room_id;
SET days = DATEDIFF(NEW.check_out, NEW.check_in);
IF days <= 0 THEN
    SIGNAL SQLSTATE '45000' 
        SET MESSAGE_TEXT = 'Дата выезда должна быть позже даты заезда.';
END IF;
SET NEW.total_amount = roomPrice * days;

END
DELIMITER;DELIMITER
DELIMITER;DELIMITER

CREATE TRIGGER trg_update_cleaner_status_after_action_complete
AFTER UPDATE ON Cleaning_Actions
FOR EACH ROW
BEGIN
IF NEW.status = 'Завершено' THEN
UPDATE Cleaners
SET status = 'Свободен'
WHERE cleaner_id = NEW.cleaner_id;
ELSEIF NEW.status = 'В процессе' THEN
UPDATE Cleaners
SET status = 'Занят'
WHERE cleaner_id = NEW.cleaner_id;
END IF;
END
$$

DELIMITER ;

я хочу разработать десктопное приложение на java база данных у меня mysql.
в приложении должна быть возможность регистрации только у администраторов у меня еже в принципе есть код
// src/h/SignUp.java
package h;

import java.awt.;
import java.awt.event.;
import javax.swing.;
import java.sql.;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class SignUp extends JFrame implements ActionListener {

private JTextField nameField, passportField, addressField, phoneField, emailField, loginField;
private JPasswordField passwordField;
private JCheckBox showPasswordCheck;
private JButton signupButton, cancelButton;
private JButton loginLink;

public SignUp() {
    setTitle("Регистрация Администратора");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(500, 500);
    setLocationRelativeTo(null);

    JPanel panel = new JPanel(new GridBagLayout());
    panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
    GridBagConstraints gbc = new GridBagConstraints();
    gbc.fill = GridBagConstraints.HORIZONTAL;

    gbc.insets = new Insets(5,5,5,5);

    // Полное имя
    JLabel nameLabel = new JLabel("Полное имя:");
    gbc.gridx = 0;
    gbc.gridy = 0;
    panel.add(nameLabel, gbc);

    nameField = new JTextField(20);
    gbc.gridx = 1;
    panel.add(nameField, gbc);

    // Номер паспорта
    JLabel passportLabel = new JLabel("Номер паспорта:");
    gbc.gridx = 0;
    gbc.gridy++;
    panel.add(passportLabel, gbc);

    passportField = new JTextField(20);
    gbc.gridx = 1;
    panel.add(passportField, gbc);

    // Адрес
    JLabel addressLabel = new JLabel("Адрес:");
    gbc.gridx = 0;
    gbc.gridy++;
    panel.add(addressLabel, gbc);

    addressField = new JTextField(20);
    gbc.gridx = 1;
    panel.add(addressField, gbc);

    // Телефон
    JLabel phoneLabel = new JLabel("Телефон:");
    gbc.gridx = 0;
    gbc.gridy++;
    panel.add(phoneLabel, gbc);

    phoneField = new JTextField(20);
    gbc.gridx = 1;
    panel.add(phoneField, gbc);

    // Email
    JLabel emailLabel = new JLabel("Email:");
    gbc.gridx = 0;
    gbc.gridy++;
    panel.add(emailLabel, gbc);

    emailField = new JTextField(20);
    gbc.gridx = 1;
    panel.add(emailField, gbc);

    // Логин
    JLabel loginLabel = new JLabel("Логин:");
    gbc.gridx = 0;
    gbc.gridy++;
    panel.add(loginLabel, gbc);

    loginField = new JTextField(20);
    gbc.gridx = 1;
    panel.add(loginField, gbc);

    // Пароль
    JLabel passwordLabel = new JLabel("Пароль:");
    gbc.gridx = 0;
    gbc.gridy++;
    panel.add(passwordLabel, gbc);

    passwordField = new JPasswordField(20);
    gbc.gridx = 1;
    panel.add(passwordField, gbc);

    // Чекбокс для показа пароля
    showPasswordCheck = new JCheckBox("Показать пароль");
    gbc.gridx = 1;
    gbc.gridy++;
    panel.add(showPasswordCheck, gbc);
    showPasswordCheck.addActionListener(this);

    // Кнопки регистрации и отмены
    signupButton = new JButton("Зарегистрироваться");
    gbc.gridx = 0;
    gbc.gridy++;
    gbc.gridwidth = 2;
    gbc.insets = new Insets(20,5,5,5);
    panel.add(signupButton, gbc);
    signupButton.addActionListener(this);

    cancelButton = new JButton("Отмена");
    gbc.gridy++;
    panel.add(cancelButton, gbc);
    cancelButton.addActionListener(this);

    // Ссылка на окно входа
    loginLink = new JButton("<HTML><U>Уже есть аккаунт? Войти</U></HTML>");
    loginLink.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    loginLink.setBorderPainted(false);
    loginLink.setForeground(Color.BLUE.darker());
    loginLink.setBackground(Color.WHITE);
    loginLink.setFocusPainted(false);
    loginLink.addActionListener(this);
    gbc.gridy++;
    panel.add(loginLink, gbc);

    add(panel);
    setVisible(true);
}

// Метод для хеширования пароля
private String hashPassword(String password) {
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] hashBytes = md.digest(password.getBytes());

        StringBuilder sb = new StringBuilder();
        for(byte b : hashBytes){
            sb.append(String.format("%02x", b));
        }

        return sb.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return null;
    }
}

// Обработка событий
@Override
public void actionPerformed(ActionEvent ae) {
    Object source = ae.getSource();

    if (source == showPasswordCheck) {
        if (showPasswordCheck.isSelected()) {
            passwordField.setEchoChar((char)0);
        } else {
            passwordField.setEchoChar('•');
        }
    }

    if (source == signupButton) {
        String fullName = nameField.getText().trim();
        String passportNumber = passportField.getText().trim();
        String address = addressField.getText().trim();
        String phone = phoneField.getText().trim();
        String email = emailField.getText().trim();
        String login = loginField.getText().trim();
        String password = new String(passwordField.getPassword()).trim();

        // Валидация
        if (fullName.isEmpty() || passportNumber.isEmpty() || address.isEmpty() ||
                phone.isEmpty() || email.isEmpty() || login.isEmpty() || password.isEmpty()) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            return;
        }

        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();

            String query = "INSERT INTO Administrators (full_name, passport_number, address, phone_number, email, login, password, created_at, updated_at) "
                    + "VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";

            PreparedStatement pstmt = conn.prepareStatement(query);
            pstmt.setString(1, fullName);
            pstmt.setString(2, passportNumber);
            pstmt.setString(3, address);
            pstmt.setString(4, phone);
            pstmt.setString(5, email);
            pstmt.setString(6, login);
            pstmt.setString(7, hashPassword(password));

            int rowsAffected = pstmt.executeUpdate();
            if (rowsAffected > 0) {
                JOptionPane.showMessageDialog(this, "Аккаунт администратора успешно создан!");
                new Login().setVisible(true);
                this.dispose();
            } else {
                JOptionPane.showMessageDialog(this, "Ошибка при создании аккаунта. Пожалуйста, попробуйте снова.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }

            conn.close();

        } catch (SQLIntegrityConstraintViolationException e) {
            JOptionPane.showMessageDialog(this, "Такой логин, номер паспорта, телефон или email уже существуют!", "Ошибка", JOptionPane.ERROR_MESSAGE);
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка подключения к базе данных.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    if (source == cancelButton) {
        int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите отменить регистрацию?", "Подтверждение", JOptionPane.YES_NO_OPTION);
        if (confirm == JOptionPane.YES_OPTION) {
            new Login().setVisible(true);
            this.dispose();
        }
    }

    if (source == loginLink) {
        new Login().setVisible(true);
        this.dispose();
    }
}

public static void main(String[] args) {
    // Установка системного вида для современного оформления
    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (Exception ignored) {}

    new SignUp();
}

}

и зарегистрировавшись администратор может в своей панели создать новые комнаты, ресепшионистов, уборщиков, просматривать архивы резервирований номеров и просмотр доходов с каждого бронирования и общий доход для каждого пункта должны быть вкладки персонал в котором можно выбрать панель с ресепшионистами и отдельную с уборщиками в этих панелях администратор сможет добавлять новых сотрудников или редактировать или удалять существующих, потом панель номера в которой администартор сможет добавлять новые комнаты или редактировать и удалять существующие, потом вкладка архивы там должны быть архивы и вкладка финансы в котором должны быть показываться сколько денег принесло каждое бронирование

у меня также есть компонент для логина:
// src/h/Login.java
package h;

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.sql.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Login extends JFrame implements ActionListener {

    private JLabel usernameLabel, passwordLabel;
    private JTextField usernameField;
    private JPasswordField passwordField;
    private JButton loginButton, cancelButton;
    private JCheckBox showPasswordCheck;
    private JButton signupLink;

    public Login() {
        setTitle("Авторизация");
        setBounds(700, 300, 400, 300);
        setLayout(null);

        // Метка и поле ввода для логина
        usernameLabel = new JLabel("Имя пользователя:");
        usernameLabel.setBounds(50, 50, 120, 30);
        add(usernameLabel);

        usernameField = new JTextField();
        usernameField.setBounds(180, 50, 150, 30);
        add(usernameField);

        // Метка и поле ввода для пароля
        passwordLabel = new JLabel("Пароль:");
        passwordLabel.setBounds(50, 100, 120, 30);
        add(passwordLabel);

        passwordField = new JPasswordField();
        passwordField.setBounds(180, 100, 150, 30);
        add(passwordField);

        // Чекбокс для показа пароля
        showPasswordCheck = new JCheckBox("Показать пароль");
        showPasswordCheck.setBounds(180, 140, 150, 25);
        showPasswordCheck.setBackground(Color.WHITE);
        showPasswordCheck.addActionListener(this);
        add(showPasswordCheck);

        // Кнопка входа
        loginButton = new JButton("Войти");
        loginButton.setBounds(50, 180, 120, 30);
        loginButton.setForeground(Color.BLACK);
        loginButton.setBackground(Color.BLACK);
        loginButton.addActionListener(this);
        add(loginButton);

        // Кнопка отмены
        cancelButton = new JButton("Выход");
        cancelButton.setBounds(210, 180, 120, 30);
        cancelButton.setForeground(Color.BLACK);
        cancelButton.setBackground(Color.BLACK);
        cancelButton.addActionListener(this);
        add(cancelButton);

        // Ссылка на регистрацию
        signupLink = new JButton("<HTML><U>Нет аккаунта? Зарегистрироваться</U></HTML>");
        signupLink.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        signupLink.setBorderPainted(false);
        signupLink.setForeground(Color.BLUE.darker());
        signupLink.setBackground(Color.WHITE);
        signupLink.setFocusPainted(false);
        signupLink.setBounds(100, 220, 200, 25);
        signupLink.addActionListener(this);
        add(signupLink);

        getContentPane().setBackground(Color.WHITE);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    // Метод для хеширования пароля
    private String hashPassword(String password) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] hash = md.digest(password.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : hash) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    // Обработка событий
    public void actionPerformed(ActionEvent ae) {
        // Обработка чекбокса показа пароля
        if (ae.getSource() == showPasswordCheck) {
            if (showPasswordCheck.isSelected()) {
                passwordField.setEchoChar((char) 0);
            } else {
                passwordField.setEchoChar('•');
            }
        }

        // Обработка кнопки "Войти"
        if (ae.getSource() == loginButton) {
            authenticateUser();
        }

        // Обработка кнопки "Отмена"
        if (ae.getSource() == cancelButton) {
            int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите выйти?", "Подтверждение", JOptionPane.YES_NO_OPTION);
            if (confirm == JOptionPane.YES_OPTION) {
                System.exit(0);
            }
        }

        // Обработка ссылки на регистрацию
        if (ae.getSource() == signupLink) {
            new SignUp().setVisible(true);
            this.dispose();
        }
    }

    // Метод аутентификации пользователя
    private void authenticateUser() {
        String username = usernameField.getText().trim();
        String password = new String(passwordField.getPassword()).trim();

        if (username.isEmpty() || password.isEmpty()) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, введите имя пользователя и пароль.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            return;
        }

        String hashedPassword = hashPassword(password);
        if (hashedPassword == null) {
            JOptionPane.showMessageDialog(this, "Ошибка хеширования пароля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            return;
        }

        // Попытка авторизоваться как Администратор
        Pr user = authenticateInRole("Administrators", "admin_id", "login", "password");
        if (user != null) {
            JOptionPane.showMessageDialog(this, "Добро пожаловать, Администратор!");
            new DashboardAdmin(user.getId()).setVisible(true);
            this.dispose();
            return;
        }

        // Попытка авторизоваться как Ресепшионист
        user = authenticateInRole("Receptionists", "receptionist_id", "login", "password");
        if (user != null) {
            JOptionPane.showMessageDialog(this, "Добро пожаловать, Ресепшионист!");
            new DashboardReceptionist(user.getId()).setVisible(true);
            this.dispose();
            return;
        }

        // Если пользователь не найден
        JOptionPane.showMessageDialog(this, "Неверные имя пользователя или пароль.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        usernameField.setText("");
        passwordField.setText("");
    }

    /**
     * Метод для аутентификации пользователя в заданной роли.
     *
     * @param tableName      Название таблицы (Administrators или Receptionists)
     * @param idColumn       Название столбца ID (admin_id или receptionist_id)
     * @param loginColumn    Название столбца с логином
     * @param passwordColumn Название столбца с паролем
     * @return Объект Pr с информацией о пользователе или null, если не найден
     */
    private Pr authenticateInRole(String tableName, String idColumn, String loginColumn, String passwordColumn) {
        String query = "SELECT * FROM " + tableName + " WHERE " + loginColumn + " = ? AND " + passwordColumn + " = ?";
        try (ConnectMySQL connection = new ConnectMySQL();
             PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

            pstmt.setString(1, usernameField.getText().trim());
            pstmt.setString(2, hashPassword(new String(passwordField.getPassword()).trim()));

            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    int id = rs.getInt(idColumn);
                    String fullName = rs.getString("full_name");
                    String role = tableName.equals("Administrators") ? "Administrator" : "Receptionist";
                    return new Pr(id, fullName, role);
                }
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Драйвер базы данных не найден.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        } catch (SQLException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка подключения к базе данных.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
        return null;
    }

    public static void main(String[] args) {
        // Установка системного вида для современного оформления
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ignored) {}

        new Login();
    }
}
в компоненте логина смогут авторизоваться администраторы с введенными при создании аккаунта логинами и паролями и ресепшионисты если администратор создал для них аккаунт. После входа у ресепшионистов и администраторов должны открываться разные панели управления

Про панель управления администартора я уже рассказал теперь расскажу что я хочу видеть у ресепшиониста.Во первых ресепшионист должен регистрировать клиентов с нужными данными в базе данных также у него должна быть вкладка для просмотра номеров и их состояний, и вкладка для бронирований в которых ресепшионист может выбирать номер и будет открываться календарь на ближайшие семь дней и он может забронировать номер на какие то определенные дни для клиентов но при этом другой клиент не может забронировать номер на дни на которые он уже занят он может забронировать номер на дни которые он не занят но с ограничением как я сказал в семь ближайших дней если ему нужен номер прямо сейчас то он может заселиться в незанятый номер, после того как бронирование номера истекает данное бронирование должно перейти в архив но номер не должен сразу становится доступен после выселения клиента комната попадает во вкладку уборка и ресепшионист должен назначить уборщика чтобы он убрался и после 30 минут он автоматически становится доступен и убирается из вкладки уборка также ресепшионист может просто в ручную назначить уборку определннеого номера если это нужно, клиент сразу при вьезде в номер должен оплатить полную сумму аренды номера за все дни остановки в нем. но при этом ресепшионист может в ручную выселить клиента при желании деньги при этом не возвращаются и комната попадает во вкладку уборка после которой она снова считается доступной.

вот еще код соединения с базой данных

package h;

import java.sql.*;

public class ConnectMySQL implements AutoCloseable {
private static final String URL = "jdbc:mysql://localhost:1369/hotel?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASSWORD = "1369sukAnigghaD1ck";

private Connection connection;

public ConnectMySQL() throws SQLException, ClassNotFoundException {
    // Загрузка драйвера MySQL JDBC
    Class.forName("com.mysql.cj.jdbc.Driver");
    this.connection = DriverManager.getConnection(URL, USER, PASSWORD);
}

public Connection getConnection() {
    return connection;
}

@Override
public void close() throws SQLException {
    if (connection != null && !connection.isClosed()) {
        connection.close();
    }
}

}



возможно я упустил что то сам додумай и добавь все что надо и напиши все что я должен создать и полные коды для всего чтобы все работало