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

-- Таблица 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(15) UNIQUE NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    login VARCHAR(20) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    registration_code VARCHAR(50) UNIQUE 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('Free', 'Busy') DEFAULT 'Free',
    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,
    available BOOLEAN DEFAULT TRUE,
    status ENUM('Available', 'Occupied', 'Maintenance') DEFAULT 'Available',
    price DECIMAL(10,2) NOT NULL,
    bed_type ENUM('Single', 'Double', 'Queen', 'King', 'Twin') NOT NULL,
    room_type VARCHAR(50),
    admin_id INT,  -- Changed to allow NULL
    receptionist_id INT,
    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 SET NULL,
    FOREIGN KEY (receptionist_id) REFERENCES Receptionists(receptionist_id) ON DELETE SET NULL
);

-- Таблица 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('Scheduled', 'In Progress', 'Completed') DEFAULT 'Scheduled',
    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('Male', 'Female', 'Other'),
    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('Booked', 'Checked-in', 'Checked-out', 'Cancelled') DEFAULT 'Booked',
    total_amount DECIMAL(10,2),
    deposit 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('Cash', 'Credit Card', 'Debit Card', 'Online Transfer') 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('Manual Assignment', 'Auto Assignment') NOT NULL,
    scheduled_time DATETIME NOT NULL,
    status ENUM('Scheduled', 'In Progress', 'Completed') DEFAULT 'Scheduled',
    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
);






 Триггер для обновления статуса номера при изменении статуса бронирования
Этот триггер автоматически обновляет статус номера в таблице Rooms в зависимости от статуса бронирования в таблице Reservations.

DELIMITER $$

CREATE TRIGGER trg_update_room_availability
AFTER UPDATE ON Reservations
FOR EACH ROW
BEGIN
    IF NEW.status = 'Booked' OR NEW.status = 'Checked-in' THEN
        UPDATE Rooms
        SET available = FALSE,
            status = 'Occupied'
        WHERE room_id = NEW.room_id;
    ELSEIF NEW.status = 'Cancelled' OR NEW.status = 'Checked-out' THEN
        UPDATE Rooms
        SET available = TRUE,
            status = 'Available'
        WHERE room_id = NEW.room_id;
    END IF;
END$$

DELIMITER ;




Триггер для предотвращения перекрывающихся бронирований
Этот триггер предотвращает создание или обновление бронирования, если выбранный номер уже забронирован на указанные даты.

DELIMITER $$

CREATE TRIGGER trg_prevent_overlapping_reservations
BEFORE INSERT ON Reservations
FOR EACH ROW
BEGIN
    IF EXISTS (
        SELECT 1 FROM Reservations
        WHERE room_id = NEW.room_id
          AND status NOT IN ('Cancelled')
          AND (
              (NEW.check_in BETWEEN check_in AND check_out) OR
              (NEW.check_out BETWEEN check_in AND check_out) OR
              (check_in BETWEEN NEW.check_in AND NEW.check_out)
          )
    ) THEN
        SIGNAL SQLSTATE '45000' 
            SET MESSAGE_TEXT = 'Номер уже забронирован на указанные даты.';
    END IF;
END$$

DELIMITER ;





Триггер для автоматического расчета общей суммы бронирования
Этот триггер автоматически рассчитывает общую сумму бронирования (total_amount) на основе цены номера и длительности пребывания при вставке или обновлении бронирования.
DELIMITER $$

CREATE TRIGGER trg_calculate_total_amount_before_insert
BEFORE INSERT 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$$

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 ;
```

Триггер для обновления статуса уборщика при завершении действия уборки

Этот триггер автоматически обновляет статус уборщика в таблице `Cleaners`, когда действие уборки завершается.

DELIMITER $$

CREATE TRIGGER trg_update_cleaner_status_after_action_complete
AFTER UPDATE ON Cleaning_Actions
FOR EACH ROW
BEGIN
    IF NEW.status = 'Completed' THEN
        UPDATE Cleaners
        SET status = 'Free'
        WHERE cleaner_id = NEW.cleaner_id;
    ELSEIF NEW.status = 'In Progress' THEN
        UPDATE Cleaners
        SET status = 'Busy'
        WHERE cleaner_id = NEW.cleaner_id;
    END IF;
END$$

DELIMITER ;


### Примечания

- **DELIMITER**: В MySQL по умолчанию точка с запятой (`;`) используется как разделитель команд. Однако, при создании триггеров, которые содержат несколько SQL-команд внутри, необходимо временно изменить разделитель на другой, например, `$$`, чтобы избежать преждевременного завершения триггера.
  
- **Проверки и ограничения**: Триггеры содержат проверки, такие как обеспечение корректности дат бронирования и предотвращение перекрытия бронирований. Это помогает поддерживать целостность данных на уровне базы.

- **Сообщения об ошибках**: Использование `SIGNAL SQLSTATE` позволяет возвращать понятные сообщения об ошибках, которые можно обработать на уровне приложения.

Эти триггеры помогут автоматизировать основные бизнес-процессы в системе управления гостиницей и обеспечить целостность данных. При необходимости вы можете добавлять дополнительные триггеры в соответствии с другими требованиями вашей системы.













// src/h/ConnectMySQL.java
package h;

import java.sql.*;

public class ConnectMySQL implements AutoCloseable {
    private static final String URL = "jdbc:mysql://localhost:1369/hotelsk?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();
        }
    }
}










package h;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import java.util.Vector;

public class DashboardAdmin extends JFrame {

    private int adminId;
    private JTabbedPane tabbedPane;

    // Components for Receptionists Tab
    private JTable receptionistsTable;
    private DefaultTableModel receptionistsModel;
    private JButton addReceptionistButton, editReceptionistButton, deleteReceptionistButton;

    // Components for Cleaners Tab
    private JTable cleanersTable;
    private DefaultTableModel cleanersModel;
    private JButton addCleanerButton, editCleanerButton, deleteCleanerButton;

    // Components for Rooms Tab
    private JTable roomsTable;
    private DefaultTableModel roomsModel;
    private JButton addRoomButton, editRoomButton, deleteRoomButton;

    // Components for Income Tab
    private JLabel totalIncomeLabel;
    private JTable paymentsTable;
    private DefaultTableModel paymentsModel;

    public DashboardAdmin(int adminId) {
        this.adminId = adminId;
        setTitle("Панель Администратора");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(1000, 700);
        setLocationRelativeTo(null);

        tabbedPane = new JTabbedPane();

        // Инициализируем вкладки
        initReceptionistsTab();
        initCleanersTab();
        initRoomsTab();
        initIncomeTab();

        add(tabbedPane);

        setVisible(true);
    }

    // Инициализация вкладки "Ресепшионисты"
    private void initReceptionistsTab() {
        JPanel panel = new JPanel(new BorderLayout());

        // Верхняя панель с кнопками
        JPanel topPanel = new JPanel();
        addReceptionistButton = new JButton("Добавить");
        editReceptionistButton = new JButton("Редактировать");
        deleteReceptionistButton = new JButton("Удалить");

        topPanel.add(addReceptionistButton);
        topPanel.add(editReceptionistButton);
        topPanel.add(deleteReceptionistButton);

        panel.add(topPanel, BorderLayout.NORTH);

        // Таблица ресепшионистов
        receptionistsModel = new DefaultTableModel();
        receptionistsTable = new JTable(receptionistsModel);
        receptionistsModel.setColumnIdentifiers(new String[]{
                "ID", "Полное имя", "Номер паспорта", "Адрес", "Телефон", "Email", "Логин", "Дата создания", "Дата обновления"
        });

        loadReceptionists();

        JScrollPane scrollPane = new JScrollPane(receptionistsTable);
        panel.add(scrollPane, BorderLayout.CENTER);

        // Обработчики кнопок
        addReceptionistButton.addActionListener(e -> openAddReceptionistDialog());
        editReceptionistButton.addActionListener(e -> openEditReceptionistDialog());
        deleteReceptionistButton.addActionListener(e -> deleteSelectedReceptionist());

        tabbedPane.addTab("Ресепшионисты", panel);
    }

    // Загрузка данных ресепшионистов из базы
    private void loadReceptionists() {
        receptionistsModel.setRowCount(0);
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT receptionist_id, full_name, passport_number, address, phone_number, email, login, created_at, updated_at FROM Receptionists";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);

            while (rs.next()) {
                Vector<Object> row = new Vector<>();
                row.add(rs.getInt("receptionist_id"));
                row.add(rs.getString("full_name"));
                row.add(rs.getString("passport_number"));
                row.add(rs.getString("address"));
                row.add(rs.getString("phone_number"));
                row.add(rs.getString("email"));
                row.add(rs.getString("login"));
                row.add(rs.getTimestamp("created_at"));
                row.add(rs.getTimestamp("updated_at"));
                receptionistsModel.addRow(row);
            }
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка загрузки ресепшионистов.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Диалог для добавления нового ресепшиониста
    private void openAddReceptionistDialog() {
        JDialog dialog = new JDialog(this, "Добавить Ресепшиониста", true);
        dialog.setSize(400, 500);
        dialog.setLocationRelativeTo(this);
        dialog.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5,5,5,5);
        gbc.fill = GridBagConstraints.HORIZONTAL;

        // Поля ввода
        JLabel nameLabel = new JLabel("Полное имя:");
        JTextField nameField = new JTextField(20);
        JLabel passportLabel = new JLabel("Номер паспорта:");
        JTextField passportField = new JTextField(20);
        JLabel addressLabel = new JLabel("Адрес:");
        JTextField addressField = new JTextField(20);
        JLabel phoneLabel = new JLabel("Телефон:");
        JTextField phoneField = new JTextField(20);
        JLabel emailLabel = new JLabel("Email:");
        JTextField emailField = new JTextField(20);
        JLabel loginLabel = new JLabel("Логин:");
        JTextField loginField = new JTextField(20);
        JLabel passwordLabel = new JLabel("Пароль:");
        JPasswordField passwordField = new JPasswordField(20);

        // Кнопки
        JButton saveButton = new JButton("Сохранить");
        JButton cancelButton = new JButton("Отмена");

        // Размещение компонентов
        gbc.gridx = 0;
        gbc.gridy = 0;
        dialog.add(nameLabel, gbc);
        gbc.gridx = 1;
        dialog.add(nameField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(passportLabel, gbc);
        gbc.gridx = 1;
        dialog.add(passportField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(addressLabel, gbc);
        gbc.gridx = 1;
        dialog.add(addressField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(phoneLabel, gbc);
        gbc.gridx = 1;
        dialog.add(phoneField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(emailLabel, gbc);
        gbc.gridx = 1;
        dialog.add(emailField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(loginLabel, gbc);
        gbc.gridx = 1;
        dialog.add(loginField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(passwordLabel, gbc);
        gbc.gridx = 1;
        dialog.add(passwordField, gbc);

        // Панель кнопок
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);
        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 2;
        dialog.add(buttonPanel, gbc);

        // Обработчики кнопок
        saveButton.addActionListener(e -> {
            String fullName = nameField.getText().trim();
            String passport = 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() || passport.isEmpty() || address.isEmpty() ||
                    phone.isEmpty() || email.isEmpty() || login.isEmpty() || password.isEmpty()) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, заполните все поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }

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

                String query = "INSERT INTO Receptionists (full_name, passport_number, address, phone_number, email, login, password, admin_id, created_at, updated_at) "
                        + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
                PreparedStatement pstmt = conn.prepareStatement(query);
                pstmt.setString(1, fullName);
                pstmt.setString(2, passport);
                pstmt.setString(3, address);
                pstmt.setString(4, phone);
                pstmt.setString(5, email);
                pstmt.setString(6, login);
                pstmt.setString(7, hashPassword(password));
                pstmt.setInt(8, adminId); // Привязываем к текущему администратору

                pstmt.executeUpdate();
                conn.close();

                JOptionPane.showMessageDialog(dialog, "Ресепшионист добавлен успешно.");
                dialog.dispose();
                loadReceptionists();
            } catch (SQLIntegrityConstraintViolationException ex) {
                JOptionPane.showMessageDialog(dialog, "Логин, телефон или email уже существуют!", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } catch (Exception ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(dialog, "Ошибка подключения к базе данных.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        });

        cancelButton.addActionListener(e -> dialog.dispose());

        dialog.setVisible(true);
    }

    // Диалог для редактирования выбранного ресепшиониста
    private void openEditReceptionistDialog() {
        int selectedRow = receptionistsTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите ресепшиониста для редактирования.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        int receptionistId = (int) receptionistsModel.getValueAt(selectedRow, 0);
        String currentName = (String) receptionistsModel.getValueAt(selectedRow, 1);
        String currentPassport = (String) receptionistsModel.getValueAt(selectedRow, 2);
        String currentAddress = (String) receptionistsModel.getValueAt(selectedRow, 3);
        String currentPhone = (String) receptionistsModel.getValueAt(selectedRow, 4);
        String currentEmail = (String) receptionistsModel.getValueAt(selectedRow, 5);
        String currentLogin = (String) receptionistsModel.getValueAt(selectedRow, 6);

        JDialog dialog = new JDialog(this, "Редактировать Ресепшиониста", true);
        dialog.setSize(400, 500);
        dialog.setLocationRelativeTo(this);
        dialog.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5,5,5,5);
        gbc.fill = GridBagConstraints.HORIZONTAL;

        // Поля ввода
        JLabel nameLabel = new JLabel("Полное имя:");
        JTextField nameField = new JTextField(currentName, 20);
        JLabel passportLabel = new JLabel("Номер паспорта:");
        JTextField passportField = new JTextField(currentPassport, 20);
        JLabel addressLabel = new JLabel("Адрес:");
        JTextField addressField = new JTextField(currentAddress, 20);
        JLabel phoneLabel = new JLabel("Телефон:");
        JTextField phoneField = new JTextField(currentPhone, 20);
        JLabel emailLabel = new JLabel("Email:");
        JTextField emailField = new JTextField(currentEmail, 20);
        JLabel loginLabel = new JLabel("Логин:");
        JTextField loginField = new JTextField(currentLogin, 20);
        JLabel passwordLabel = new JLabel("Новый Пароль:");
        JPasswordField passwordField = new JPasswordField(20);

        // Кнопки
        JButton saveButton = new JButton("Сохранить");
        JButton cancelButton = new JButton("Отмена");

        // Размещение компонентов
        gbc.gridx = 0;
        gbc.gridy = 0;
        dialog.add(nameLabel, gbc);
        gbc.gridx = 1;
        dialog.add(nameField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(passportLabel, gbc);
        gbc.gridx = 1;
        dialog.add(passportField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(addressLabel, gbc);
        gbc.gridx = 1;
        dialog.add(addressField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(phoneLabel, gbc);
        gbc.gridx = 1;
        dialog.add(phoneField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(emailLabel, gbc);
        gbc.gridx = 1;
        dialog.add(emailField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(loginLabel, gbc);
        gbc.gridx = 1;
        dialog.add(loginField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(passwordLabel, gbc);
        gbc.gridx = 1;
        dialog.add(passwordField, gbc);

        // Панель кнопок
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);
        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 2;
        dialog.add(buttonPanel, gbc);

        // Обработчики кнопок
        saveButton.addActionListener(e -> {
            String fullName = nameField.getText().trim();
            String passport = 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() || passport.isEmpty() || address.isEmpty() ||
                    phone.isEmpty() || email.isEmpty() || login.isEmpty()) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }

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

                String query;
                if (password.isEmpty()) {
                    // Если пароль не изменяется
                    query = "UPDATE Receptionists SET full_name = ?, passport_number = ?, address = ?, phone_number = ?, email = ?, login = ?, updated_at = NOW() WHERE receptionist_id = ?";
                } else {
                    // Если пароль изменяется
                    query = "UPDATE Receptionists SET full_name = ?, passport_number = ?, address = ?, phone_number = ?, email = ?, login = ?, password = ?, updated_at = NOW() WHERE receptionist_id = ?";
                }

                PreparedStatement pstmt = conn.prepareStatement(query);
                pstmt.setString(1, fullName);
                pstmt.setString(2, passport);
                pstmt.setString(3, address);
                pstmt.setString(4, phone);
                pstmt.setString(5, email);
                pstmt.setString(6, login);
                if (password.isEmpty()) {
                    pstmt.setInt(7, receptionistId);
                } else {
                    pstmt.setString(7, hashPassword(password));
                    pstmt.setInt(8, receptionistId);
                }

                pstmt.executeUpdate();
                conn.close();

                JOptionPane.showMessageDialog(dialog, "Ресепшионист обновлен успешно.");
                dialog.dispose();
                loadReceptionists();
            } catch (SQLIntegrityConstraintViolationException ex) {
                JOptionPane.showMessageDialog(dialog, "Логин, телефон или email уже существуют!", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } catch (Exception ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(dialog, "Ошибка подключения к базе данных.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        });

        cancelButton.addActionListener(e -> dialog.dispose());

        dialog.setVisible(true);
    }

    // Удаление выбранного ресепшиониста
    private void deleteSelectedReceptionist() {
        int selectedRow = receptionistsTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите ресепшиониста для удаления.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        int receptionistId = (int) receptionistsModel.getValueAt(selectedRow, 0);
        String receptionistName = (String) receptionistsModel.getValueAt(selectedRow, 1);

        int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить ресепшиониста \"" + receptionistName + "\"?", "Подтверждение", JOptionPane.YES_NO_OPTION);
        if (confirm != JOptionPane.YES_OPTION) {
            return;
        }

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

            String query = "DELETE FROM Receptionists WHERE receptionist_id = ?";
            PreparedStatement pstmt = conn.prepareStatement(query);
            pstmt.setInt(1, receptionistId);
            pstmt.executeUpdate();
            conn.close();

            JOptionPane.showMessageDialog(this, "Ресепшионист удален успешно.");
            loadReceptionists();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка при удалении ресепшиониста.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Инициализация вкладки "Уборщики"
    private void initCleanersTab() {
        JPanel panel = new JPanel(new BorderLayout());

        // Верхняя панель с кнопками
        JPanel topPanel = new JPanel();
        addCleanerButton = new JButton("Добавить");
        editCleanerButton = new JButton("Редактировать");
        deleteCleanerButton = new JButton("Удалить");

        topPanel.add(addCleanerButton);
        topPanel.add(editCleanerButton);
        topPanel.add(deleteCleanerButton);

        panel.add(topPanel, BorderLayout.NORTH);

        // Таблица уборщиков
        cleanersModel = new DefaultTableModel();
        cleanersTable = new JTable(cleanersModel);
        cleanersModel.setColumnIdentifiers(new String[]{
                "ID", "Полное имя", "Телефон", "Email", "Статус", "Смена", "Дата создания", "Дата обновления"
        });

        loadCleaners();

        JScrollPane scrollPane = new JScrollPane(cleanersTable);
        panel.add(scrollPane, BorderLayout.CENTER);

        // Обработчики кнопок
        addCleanerButton.addActionListener(e -> openAddCleanerDialog());
        editCleanerButton.addActionListener(e -> openEditCleanerDialog());
        deleteCleanerButton.addActionListener(e -> deleteSelectedCleaner());

        tabbedPane.addTab("Уборщики", panel);
    }

    // Загрузка данных уборщиков из базы
    private void loadCleaners() {
        cleanersModel.setRowCount(0);
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT c.cleaner_id, c.full_name, c.phone_number, c.email, c.status, s.shift_name, c.created_at, c.updated_at " +
                    "FROM Cleaners c JOIN Shifts s ON c.shift_id = s.shift_id";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);

            while (rs.next()) {
                Vector<Object> row = new Vector<>();
                row.add(rs.getInt("cleaner_id"));
                row.add(rs.getString("full_name"));
                row.add(rs.getString("phone_number"));
                row.add(rs.getString("email"));
                row.add(rs.getString("status"));
                row.add(rs.getString("shift_name"));
                row.add(rs.getTimestamp("created_at"));
                row.add(rs.getTimestamp("updated_at"));
                cleanersModel.addRow(row);
            }
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка загрузки уборщиков.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Диалог для добавления нового уборщика
    private void openAddCleanerDialog() {
        JDialog dialog = new JDialog(this, "Добавить Уборщика", true);
        dialog.setSize(400, 400);
        dialog.setLocationRelativeTo(this);
        dialog.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5,5,5,5);
        gbc.fill = GridBagConstraints.HORIZONTAL;

        // Поля ввода
        JLabel nameLabel = new JLabel("Полное имя:");
        JTextField nameField = new JTextField(20);
        JLabel phoneLabel = new JLabel("Телефон:");
        JTextField phoneField = new JTextField(20);
        JLabel emailLabel = new JLabel("Email:");
        JTextField emailField = new JTextField(20);
        JLabel statusLabel = new JLabel("Статус:");
        JComboBox<String> statusComboBox = new JComboBox<>(new String[]{"Free", "Busy"});
        JLabel shiftLabel = new JLabel("Смена:");
        JComboBox<String> shiftComboBox = new JComboBox<>();

        // Загрузка смен из базы
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String shiftQuery = "SELECT shift_id, shift_name FROM Shifts";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(shiftQuery);
            while (rs.next()) {
                shiftComboBox.addItem(rs.getString("shift_name"));
            }
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Кнопки
        JButton saveButton = new JButton("Сохранить");
        JButton cancelButton = new JButton("Отмена");

        // Размещение компонентов
        gbc.gridx = 0;
        gbc.gridy = 0;
        dialog.add(nameLabel, gbc);
        gbc.gridx = 1;
        dialog.add(nameField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(phoneLabel, gbc);
        gbc.gridx = 1;
        dialog.add(phoneField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(emailLabel, gbc);
        gbc.gridx = 1;
        dialog.add(emailField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(statusLabel, gbc);
        gbc.gridx = 1;
        dialog.add(statusComboBox, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(shiftLabel, gbc);
        gbc.gridx = 1;
        dialog.add(shiftComboBox, gbc);

        // Панель кнопок
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);
        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 2;
        dialog.add(buttonPanel, gbc);

        // Обработчики кнопок
        saveButton.addActionListener(e -> {
            String fullName = nameField.getText().trim();
            String phone = phoneField.getText().trim();
            String email = emailField.getText().trim();
            String status = (String) statusComboBox.getSelectedItem();
            String shiftName = (String) shiftComboBox.getSelectedItem();

            if (fullName.isEmpty() || phone.isEmpty() || email.isEmpty() || shiftName == null) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }

            try {
                int shiftId = getShiftIdByName(shiftName);
                if (shiftId == -1) {
                    JOptionPane.showMessageDialog(dialog, "Выбранная смена не существует.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

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

                String query = "INSERT INTO Cleaners (full_name, phone_number, email, status, shift_id, created_at, updated_at) "
                        + "VALUES (?, ?, ?, ?, ?, NOW(), NOW())";
                PreparedStatement pstmt = conn.prepareStatement(query);
                pstmt.setString(1, fullName);
                pstmt.setString(2, phone);
                pstmt.setString(3, email);
                pstmt.setString(4, status);
                pstmt.setInt(5, shiftId);

                pstmt.executeUpdate();
                conn.close();

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

        cancelButton.addActionListener(e -> dialog.dispose());

        dialog.setVisible(true);
    }

    // Получение ID смены по имени
    private int getShiftIdByName(String shiftName) {
        int shiftId = -1;
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT shift_id FROM Shifts WHERE shift_name = ?";
            PreparedStatement pstmt = conn.prepareStatement(query);
            pstmt.setString(1, shiftName);
            ResultSet rs = pstmt.executeQuery();
            if (rs.next()) {
                shiftId = rs.getInt("shift_id");
            }
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return shiftId;
    }

    // Диалог для редактирования выбранного уборщика
    private void openEditCleanerDialog() {
        int selectedRow = cleanersTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите уборщика для редактирования.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        int cleanerId = (int) cleanersModel.getValueAt(selectedRow, 0);
        String currentName = (String) cleanersModel.getValueAt(selectedRow, 1);
        String currentPhone = (String) cleanersModel.getValueAt(selectedRow, 2);
        String currentEmail = (String) cleanersModel.getValueAt(selectedRow, 3);
        String currentStatus = (String) cleanersModel.getValueAt(selectedRow, 4);
        String currentShift = (String) cleanersModel.getValueAt(selectedRow, 5);

        JDialog dialog = new JDialog(this, "Редактировать Уборщика", true);
        dialog.setSize(400, 400);
        dialog.setLocationRelativeTo(this);
        dialog.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5,5,5,5);
        gbc.fill = GridBagConstraints.HORIZONTAL;

        // Поля ввода
        JLabel nameLabel = new JLabel("Полное имя:");
        JTextField nameField = new JTextField(currentName, 20);
        JLabel phoneLabel = new JLabel("Телефон:");
        JTextField phoneField = new JTextField(currentPhone, 20);
        JLabel emailLabel = new JLabel("Email:");
        JTextField emailField = new JTextField(currentEmail, 20);
        JLabel statusLabel = new JLabel("Статус:");
        JComboBox<String> statusComboBox = new JComboBox<>(new String[]{"Free", "Busy"});
        statusComboBox.setSelectedItem(currentStatus);
        JLabel shiftLabel = new JLabel("Смена:");
        JComboBox<String> shiftComboBox = new JComboBox<>();

        // Загрузка смен из базы
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String shiftQuery = "SELECT shift_id, shift_name FROM Shifts";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(shiftQuery);
            while (rs.next()) {
                shiftComboBox.addItem(rs.getString("shift_name"));
            }
            shiftComboBox.setSelectedItem(currentShift);
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Кнопки
        JButton saveButton = new JButton("Сохранить");
        JButton cancelButton = new JButton("Отмена");

        // Размещение компонентов
        gbc.gridx = 0;
        gbc.gridy = 0;
        dialog.add(nameLabel, gbc);
        gbc.gridx = 1;
        dialog.add(nameField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(phoneLabel, gbc);
        gbc.gridx = 1;
        dialog.add(phoneField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(emailLabel, gbc);
        gbc.gridx = 1;
        dialog.add(emailField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(statusLabel, gbc);
        gbc.gridx = 1;
        dialog.add(statusComboBox, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(shiftLabel, gbc);
        gbc.gridx = 1;
        dialog.add(shiftComboBox, gbc);

        // Панель кнопок
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);
        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 2;
        dialog.add(buttonPanel, gbc);

        // Обработчики кнопок
        saveButton.addActionListener(e -> {
            String fullName = nameField.getText().trim();
            String phone = phoneField.getText().trim();
            String email = emailField.getText().trim();
            String status = (String) statusComboBox.getSelectedItem();
            String shiftName = (String) shiftComboBox.getSelectedItem();

            if (fullName.isEmpty() || phone.isEmpty() || email.isEmpty() || shiftName == null) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }

            try {
                int shiftId = getShiftIdByName(shiftName);
                if (shiftId == -1) {
                    JOptionPane.showMessageDialog(dialog, "Выбранная смена не существует.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

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

                String query = "UPDATE Cleaners SET full_name = ?, phone_number = ?, email = ?, status = ?, shift_id = ?, updated_at = NOW() WHERE cleaner_id = ?";
                PreparedStatement pstmt = conn.prepareStatement(query);
                pstmt.setString(1, fullName);
                pstmt.setString(2, phone);
                pstmt.setString(3, email);
                pstmt.setString(4, status);
                pstmt.setInt(5, shiftId);
                pstmt.setInt(6, cleanerId);

                pstmt.executeUpdate();
                conn.close();

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

        cancelButton.addActionListener(e -> dialog.dispose());

        dialog.setVisible(true);
    }

    // Удаление выбранного уборщика
    private void deleteSelectedCleaner() {
        int selectedRow = cleanersTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите уборщика для удаления.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        int cleanerId = (int) cleanersModel.getValueAt(selectedRow, 0);
        String cleanerName = (String) cleanersModel.getValueAt(selectedRow, 1);

        int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить уборщика \"" + cleanerName + "\"?", "Подтверждение", JOptionPane.YES_NO_OPTION);
        if (confirm != JOptionPane.YES_OPTION) {
            return;
        }

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

            String query = "DELETE FROM Cleaners WHERE cleaner_id = ?";
            PreparedStatement pstmt = conn.prepareStatement(query);
            pstmt.setInt(1, cleanerId);
            pstmt.executeUpdate();
            conn.close();

            JOptionPane.showMessageDialog(this, "Уборщик удален успешно.");
            loadCleaners();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка при удалении уборщика.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Инициализация вкладки "Номера"
    private void initRoomsTab() {
        JPanel panel = new JPanel(new BorderLayout());

        // Верхняя панель с кнопками
        JPanel topPanel = new JPanel();
        addRoomButton = new JButton("Добавить");
        editRoomButton = new JButton("Редактировать");
        deleteRoomButton = new JButton("Удалить");

        topPanel.add(addRoomButton);
        topPanel.add(editRoomButton);
        topPanel.add(deleteRoomButton);

        panel.add(topPanel, BorderLayout.NORTH);

        // Таблица номеров
        roomsModel = new DefaultTableModel();
        roomsTable = new JTable(roomsModel);
        roomsModel.setColumnIdentifiers(new String[]{
                "ID", "Номер", "Доступен", "Статус", "Цена", "Тип кровати", "Тип номера", "Админ ID", "Ресепшионист ID", "Дата создания", "Дата обновления"
        });

        loadRooms();

        JScrollPane scrollPane = new JScrollPane(roomsTable);
        panel.add(scrollPane, BorderLayout.CENTER);

        // Обработчики кнопок
        addRoomButton.addActionListener(e -> openAddRoomDialog());
        editRoomButton.addActionListener(e -> openEditRoomDialog());
        deleteRoomButton.addActionListener(e -> deleteSelectedRoom());

        tabbedPane.addTab("Номера", panel);
    }

    // Загрузка данных номеров из базы
    private void loadRooms() {
        roomsModel.setRowCount(0);
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT room_id, room_number, available, status, price, bed_type, room_type, admin_id, receptionist_id, created_at, updated_at FROM Rooms";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);

            while (rs.next()) {
                Vector<Object> row = new Vector<>();
                row.add(rs.getInt("room_id"));
                row.add(rs.getInt("room_number"));
                row.add(rs.getBoolean("available") ? "Да" : "Нет");
                row.add(rs.getString("status"));
                row.add(rs.getDouble("price")); // Изменено на getDouble
                row.add(rs.getString("bed_type"));
                row.add(rs.getString("room_type"));
                row.add(rs.getObject("admin_id") != null ? rs.getInt("admin_id") : "NULL");
                row.add(rs.getObject("receptionist_id") != null ? rs.getInt("receptionist_id") : "NULL");
                row.add(rs.getTimestamp("created_at"));
                row.add(rs.getTimestamp("updated_at"));
                roomsModel.addRow(row);
            }
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка загрузки номеров.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Диалог для добавления нового номера
    private void openAddRoomDialog() {
        JDialog dialog = new JDialog(this, "Добавить Номер", true);
        dialog.setSize(400, 600);
        dialog.setLocationRelativeTo(this);
        dialog.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5,5,5,5);
        gbc.fill = GridBagConstraints.HORIZONTAL;

        // Поля ввода
        JLabel roomNumberLabel = new JLabel("Номер:");
        JTextField roomNumberField = new JTextField(20);
        JLabel availableLabel = new JLabel("Доступен:");
        JCheckBox availableCheck = new JCheckBox("Да", true);
        JLabel statusLabel = new JLabel("Статус:");
        JComboBox<String> statusComboBox = new JComboBox<>(new String[]{"Available", "Occupied", "Maintenance"});
        JLabel priceLabel = new JLabel("Цена:");
        JTextField priceField = new JTextField(20);
        JLabel bedTypeLabel = new JLabel("Тип кровати:");
        JComboBox<String> bedTypeComboBox = new JComboBox<>(new String[]{"Single", "Double", "Queen", "King", "Twin"});
        JLabel roomTypeLabel = new JLabel("Тип номера:");
        JTextField roomTypeField = new JTextField(20);
        JLabel adminIdLabel = new JLabel("ID Администратора:");
        JTextField adminIdField = new JTextField(20);
        JLabel receptionistIdLabel = new JLabel("ID Ресепшиониста:");
        JTextField receptionistIdField = new JTextField(20);

        // Кнопки
        JButton saveButton = new JButton("Сохранить");
        JButton cancelButton = new JButton("Отмена");

        // Размещение компонентов
        gbc.gridx = 0;
        gbc.gridy = 0;
        dialog.add(roomNumberLabel, gbc);
        gbc.gridx = 1;
        dialog.add(roomNumberField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(availableLabel, gbc);
        gbc.gridx = 1;
        dialog.add(availableCheck, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(statusLabel, gbc);
        gbc.gridx = 1;
        dialog.add(statusComboBox, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(priceLabel, gbc);
        gbc.gridx = 1;
        dialog.add(priceField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(bedTypeLabel, gbc);
        gbc.gridx = 1;
        dialog.add(bedTypeComboBox, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(roomTypeLabel, gbc);
        gbc.gridx = 1;
        dialog.add(roomTypeField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(adminIdLabel, gbc);
        gbc.gridx = 1;
        dialog.add(adminIdField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(receptionistIdLabel, gbc);
        gbc.gridx = 1;
        dialog.add(receptionistIdField, gbc);

        // Панель кнопок
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);
        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 2;
        dialog.add(buttonPanel, gbc);

        // Обработчики кнопок
        saveButton.addActionListener(e -> {
            String roomNumberStr = roomNumberField.getText().trim();
            boolean available = availableCheck.isSelected();
            String status = (String) statusComboBox.getSelectedItem();
            String priceStr = priceField.getText().trim();
            String bedType = (String) bedTypeComboBox.getSelectedItem();
            String roomType = roomTypeField.getText().trim();
            String adminIdStr = adminIdField.getText().trim();
            String receptionistIdStr = receptionistIdField.getText().trim();

            if (roomNumberStr.isEmpty() || priceStr.isEmpty() || roomType.isEmpty()) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }

            try {
                int roomNumber = Integer.parseInt(roomNumberStr);
                double price = Double.parseDouble(priceStr);
                Integer adminId = adminIdStr.isEmpty() ? null : Integer.parseInt(adminIdStr);
                Integer receptionistId = receptionistIdStr.isEmpty() ? null : Integer.parseInt(receptionistIdStr);

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

                String query = "INSERT INTO Rooms (room_number, available, status, price, bed_type, room_type, admin_id, receptionist_id, created_at, updated_at) "
                        + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
                PreparedStatement pstmt = conn.prepareStatement(query);
                pstmt.setInt(1, roomNumber);
                pstmt.setBoolean(2, available);
                pstmt.setString(3, status);
                pstmt.setDouble(4, price);
                pstmt.setString(5, bedType);
                pstmt.setString(6, roomType);
                if (adminId != null) {
                    pstmt.setInt(7, adminId);
                } else {
                    pstmt.setNull(7, Types.INTEGER);
                }
                if (receptionistId != null) {
                    pstmt.setInt(8, receptionistId);
                } else {
                    pstmt.setNull(8, Types.INTEGER);
                }

                pstmt.executeUpdate();
                conn.close();

                JOptionPane.showMessageDialog(dialog, "Номер добавлен успешно.");
                dialog.dispose();
                loadRooms();
            } catch (NumberFormatException ex) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, введите корректные числовые значения для номера, цены и ID.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } catch (SQLIntegrityConstraintViolationException ex) {
                JOptionPane.showMessageDialog(dialog, "Номер комнаты уже существует или указанные admin_id/receptionist_id не существуют!", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } catch (Exception ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(dialog, "Ошибка подключения к базе данных.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        });

        cancelButton.addActionListener(e -> dialog.dispose());

        dialog.setVisible(true);
    }

    // Диалог для редактирования выбранного номера
    private void openEditRoomDialog() {
        int selectedRow = roomsTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для редактирования.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        int roomId = (int) roomsModel.getValueAt(selectedRow, 0);
        int roomNumber = (int) roomsModel.getValueAt(selectedRow, 1);
        String availableStr = (String) roomsModel.getValueAt(selectedRow, 2);
        String status = (String) roomsModel.getValueAt(selectedRow, 3);
        Object priceObj = roomsModel.getValueAt(selectedRow, 4);
        double price = 0.0;
        if (priceObj instanceof Double) {
            price = (Double) priceObj;
        } else if (priceObj instanceof Number) {
            price = ((Number) priceObj).doubleValue();
        } else {
            try {
                price = Double.parseDouble(priceObj.toString());
            } catch (NumberFormatException e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Некорректное значение цены.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }
        }
        String bedType = (String) roomsModel.getValueAt(selectedRow, 5);
        String roomType = (String) roomsModel.getValueAt(selectedRow, 6);
        Object adminIdObj = roomsModel.getValueAt(selectedRow, 7);
        Object receptionistIdObj = roomsModel.getValueAt(selectedRow, 8);

        String adminIdStr = adminIdObj instanceof Integer ? String.valueOf(adminIdObj) : "";
        String receptionistIdStr = receptionistIdObj instanceof Integer ? String.valueOf(receptionistIdObj) : "";

        JDialog dialog = new JDialog(this, "Редактировать Номер", true);
        dialog.setSize(400, 600);
        dialog.setLocationRelativeTo(this);
        dialog.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5,5,5,5);
        gbc.fill = GridBagConstraints.HORIZONTAL;

        // Поля ввода
        JLabel roomNumberLabel = new JLabel("Номер:");
        JTextField roomNumberField = new JTextField(String.valueOf(roomNumber), 20);
        JLabel availableLabel = new JLabel("Доступен:");
        JCheckBox availableCheck = new JCheckBox("Да", availableStr.equals("Да"));
        JLabel statusLabel = new JLabel("Статус:");
        JComboBox<String> statusComboBox = new JComboBox<>(new String[]{"Available", "Occupied", "Maintenance"});
        statusComboBox.setSelectedItem(status);
        JLabel priceLabel = new JLabel("Цена:");
        JTextField priceField = new JTextField(String.valueOf(price), 20);
        JLabel bedTypeLabel = new JLabel("Тип кровати:");
        JComboBox<String> bedTypeComboBox = new JComboBox<>(new String[]{"Single", "Double", "Queen", "King", "Twin"});
        bedTypeComboBox.setSelectedItem(bedType);
        JLabel roomTypeLabel = new JLabel("Тип номера:");
        JTextField roomTypeField = new JTextField(roomType, 20);
        JLabel adminIdLabel = new JLabel("ID Администратора:");
        JTextField adminIdField = new JTextField(adminIdStr, 20);
        JLabel receptionistIdLabel = new JLabel("ID Ресепшиониста:");
        JTextField receptionistIdField = new JTextField(receptionistIdStr, 20);

        // Кнопки
        JButton saveButton = new JButton("Сохранить");
        JButton cancelButton = new JButton("Отмена");

        // Размещение компонентов
        gbc.gridx = 0;
        gbc.gridy = 0;
        dialog.add(roomNumberLabel, gbc);
        gbc.gridx = 1;
        dialog.add(roomNumberField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(availableLabel, gbc);
        gbc.gridx = 1;
        dialog.add(availableCheck, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(statusLabel, gbc);
        gbc.gridx = 1;
        dialog.add(statusComboBox, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(priceLabel, gbc);
        gbc.gridx = 1;
        dialog.add(priceField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(bedTypeLabel, gbc);
        gbc.gridx = 1;
        dialog.add(bedTypeComboBox, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(roomTypeLabel, gbc);
        gbc.gridx = 1;
        dialog.add(roomTypeField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(adminIdLabel, gbc);
        gbc.gridx = 1;
        dialog.add(adminIdField, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(receptionistIdLabel, gbc);
        gbc.gridx = 1;
        dialog.add(receptionistIdField, gbc);

        // Панель кнопок
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);
        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 2;
        dialog.add(buttonPanel, gbc);

        // Обработчики кнопок
        saveButton.addActionListener(e -> {
            String roomNumberStr = roomNumberField.getText().trim();
            boolean available = availableCheck.isSelected();
            String currentStatus = (String) statusComboBox.getSelectedItem();
            String priceStr = priceField.getText().trim();
            String bedTypeNew = (String) bedTypeComboBox.getSelectedItem();
            String roomTypeNew = roomTypeField.getText().trim();
            String adminIdNewStr = adminIdField.getText().trim();
            String receptionistIdNewStr = receptionistIdField.getText().trim();

            if (roomNumberStr.isEmpty() || priceStr.isEmpty() || roomTypeNew.isEmpty()) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }

            try {
                int roomNumberParsed = Integer.parseInt(roomNumberStr);
                double priceParsed = Double.parseDouble(priceStr);
                Integer adminIdNew = adminIdNewStr.isEmpty() ? null : Integer.parseInt(adminIdNewStr);
                Integer receptionistIdNew = receptionistIdNewStr.isEmpty() ? null : Integer.parseInt(receptionistIdNewStr);

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

                String query = "UPDATE Rooms SET room_number = ?, available = ?, status = ?, price = ?, bed_type = ?, room_type = ?, admin_id = ?, receptionist_id = ?, updated_at = NOW() WHERE room_id = ?";
                PreparedStatement pstmt = conn.prepareStatement(query);
                pstmt.setInt(1, roomNumberParsed);
                pstmt.setBoolean(2, available);
                pstmt.setString(3, currentStatus);
                pstmt.setDouble(4, priceParsed);
                pstmt.setString(5, bedTypeNew);
                pstmt.setString(6, roomTypeNew);
                if (adminIdNew != null) {
                    pstmt.setInt(7, adminIdNew);
                } else {
                    pstmt.setNull(7, Types.INTEGER);
                }
                if (receptionistIdNew != null) {
                    pstmt.setInt(8, receptionistIdNew);
                } else {
                    pstmt.setNull(8, Types.INTEGER);
                }
                pstmt.setInt(9, roomId);

                pstmt.executeUpdate();
                conn.close();

                JOptionPane.showMessageDialog(dialog, "Номер обновлен успешно.");
                dialog.dispose();
                loadRooms();
            } catch (NumberFormatException ex) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, введите корректные числовые значения для номера, цены и ID.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } catch (SQLIntegrityConstraintViolationException ex) {
                JOptionPane.showMessageDialog(dialog, "Номер комнаты уже существует или указанные admin_id/receptionist_id не существуют!", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } catch (Exception ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(dialog, "Ошибка подключения к базе данных.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        });

        cancelButton.addActionListener(e -> dialog.dispose());

        dialog.setVisible(true);
    }

    // Удаление выбранного номера
    private void deleteSelectedRoom() {
        int selectedRow = roomsTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для удаления.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        int roomId = (int) roomsModel.getValueAt(selectedRow, 0);
        int roomNumber = (int) roomsModel.getValueAt(selectedRow, 1);

        int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить номер \"" + roomNumber + "\"?", "Подтверждение", JOptionPane.YES_NO_OPTION);
        if (confirm != JOptionPane.YES_OPTION) {
            return;
        }

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

            String query = "DELETE FROM Rooms WHERE room_id = ?";
            PreparedStatement pstmt = conn.prepareStatement(query);
            pstmt.setInt(1, roomId);
            pstmt.executeUpdate();
            conn.close();

            JOptionPane.showMessageDialog(this, "Номер удален успешно.");
            loadRooms();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка при удалении номера.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Инициализация вкладки "Доходы"
    private void initIncomeTab() {
        JPanel panel = new JPanel(new BorderLayout());

        // Верхняя панель с общей суммой дохода
        JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        totalIncomeLabel = new JLabel("Общий доход: ");
        topPanel.add(totalIncomeLabel);

        panel.add(topPanel, BorderLayout.NORTH);

        // Таблица платежей
        paymentsModel = new DefaultTableModel();
        paymentsTable = new JTable(paymentsModel);
        paymentsModel.setColumnIdentifiers(new String[]{
                "ID Платежа", "ID Бронирования", "Сумма", "Дата платежа", "Способ оплаты", "Дата создания"
        });

        loadPayments();

        JScrollPane scrollPane = new JScrollPane(paymentsTable);
        panel.add(scrollPane, BorderLayout.CENTER);

        tabbedPane.addTab("Доходы", panel);
    }

    // Загрузка данных платежей и расчет общего дохода
    private void loadPayments() {
        paymentsModel.setRowCount(0);
        double totalIncome = 0.0;
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT payment_id, reservation_id, amount, payment_date, payment_method, created_at FROM Payments";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);

            while (rs.next()) {
                Vector<Object> row = new Vector<>();
                row.add(rs.getInt("payment_id"));
                row.add(rs.getInt("reservation_id"));
                row.add(rs.getDouble("amount"));
                row.add(rs.getDate("payment_date"));
                row.add(rs.getString("payment_method"));
                row.add(rs.getTimestamp("created_at"));
                paymentsModel.addRow(row);
                totalIncome += rs.getDouble("amount");
            }
            conn.close();

            totalIncomeLabel.setText(String.format("Общий доход: %.2f", totalIncome));
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка загрузки платежей.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Хеширование пароля (тот же метод, что и в других классах)
    private String hashPassword(String password) {
        try {
            java.security.MessageDigest md = java.security.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 (java.security.NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

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

        // Передача реального adminId при запуске. Для тестирования используем adminId = 1
        SwingUtilities.invokeLater(() -> new DashboardAdmin(1));
    }
}






// src/h/DashboardReceptionist.java
package h;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import java.util.Vector;

public class DashboardReceptionist extends JFrame {

    private int receptionistId;
    private JTabbedPane tabbedPane;

    // Components for Reservations Tab
    private JTable reservationsTable;
    private DefaultTableModel reservationsModel;
    private JButton addReservationButton, editReservationButton, cancelReservationButton, checkInButton, checkOutButton;

    // Components for Customers Tab
    private JTable customersTable;
    private DefaultTableModel customersModel;
    private JButton addCustomerButton, editCustomerButton, deleteCustomerButton;

    // Components for Rooms Tab
    private JTable roomsTable;
    private DefaultTableModel roomsModel;
    private JButton refreshRoomsButton;

    // Components for Income Tab
    private JLabel totalIncomeLabel;
    private JTable paymentsTable;
    private DefaultTableModel paymentsModel;

    public DashboardReceptionist(int receptionistId) {
        this.receptionistId = receptionistId;
        setTitle("Панель Ресепшиониста");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(1200, 800);
        setLocationRelativeTo(null);

        tabbedPane = new JTabbedPane();

        // Initialize Tabs
        initReservationsTab();
        initCustomersTab();
        initRoomsTab();
        initIncomeTab();

        add(tabbedPane);

        setVisible(true);
    }

    // Initialization of Reservations Tab
    private void initReservationsTab() {
        JPanel panel = new JPanel(new BorderLayout());

        // Top Panel with Buttons
        JPanel topPanel = new JPanel();
        addReservationButton = new JButton("Добавить Бронирование");
        editReservationButton = new JButton("Редактировать Бронирование");
        cancelReservationButton = new JButton("Отменить Бронирование");
        checkInButton = new JButton("Заселить Клиента");
        checkOutButton = new JButton("Выселить Клиента");

        topPanel.add(addReservationButton);
        topPanel.add(editReservationButton);
        topPanel.add(cancelReservationButton);
        topPanel.add(checkInButton);
        topPanel.add(checkOutButton);

        panel.add(topPanel, BorderLayout.NORTH);

        // Table for Reservations
        reservationsModel = new DefaultTableModel();
        reservationsTable = new JTable(reservationsModel);
        reservationsModel.setColumnIdentifiers(new String[]{
                "ID Бронирования", "Клиент", "Номер", "Дата заезда", "Дата выезда", "Статус", "Сумма", "Депозит", "Ресепшионист", "Дата создания", "Дата обновления"
        });

        loadReservations();

        JScrollPane scrollPane = new JScrollPane(reservationsTable);
        panel.add(scrollPane, BorderLayout.CENTER);

        // Action Listeners for Buttons
        addReservationButton.addActionListener(e -> openAddReservationDialog());
        editReservationButton.addActionListener(e -> openEditReservationDialog());
        cancelReservationButton.addActionListener(e -> cancelSelectedReservation());
        checkInButton.addActionListener(e -> checkInSelectedReservation());
        checkOutButton.addActionListener(e -> checkOutSelectedReservation());

        tabbedPane.addTab("Бронирования", panel);
    }

    // Loading Reservations from Database
    private void loadReservations() {
        reservationsModel.setRowCount(0);
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT r.reservation_id, c.full_name AS customer, ro.room_number, r.check_in, r.check_out, r.status, r.total_amount, r.deposit, " +
                    "rec.full_name AS receptionist, r.created_at, r.updated_at " +
                    "FROM Reservations r " +
                    "JOIN Customers c ON r.customer_id = c.customer_id " +
                    "JOIN Rooms ro ON r.room_id = ro.room_id " +
                    "LEFT JOIN Receptionists rec ON r.receptionist_id = rec.receptionist_id";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);

            while (rs.next()) {
                Vector<Object> row = new Vector<>();
                row.add(rs.getInt("reservation_id"));
                row.add(rs.getString("customer"));
                row.add(rs.getInt("room_number"));
                row.add(rs.getDate("check_in"));
                row.add(rs.getDate("check_out"));
                row.add(rs.getString("status"));
                row.add(rs.getDouble("total_amount"));
                row.add(rs.getDouble("deposit"));
                row.add(rs.getString("receptionist"));
                row.add(rs.getTimestamp("created_at"));
                row.add(rs.getTimestamp("updated_at"));
                reservationsModel.addRow(row);
            }
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка загрузки бронирований.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Dialog to Add New Reservation
    private void openAddReservationDialog() {
        JDialog dialog = new JDialog(this, "Добавить Бронирование", true);
        dialog.setSize(500, 600);
        dialog.setLocationRelativeTo(this);
        dialog.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(10,10,10,10);
        gbc.fill = GridBagConstraints.HORIZONTAL;

        // Customer Selection
        JLabel customerLabel = new JLabel("Клиент:");
        gbc.gridx = 0;
        gbc.gridy = 0;
        dialog.add(customerLabel, gbc);

        JComboBox<String> customerComboBox = new JComboBox<>();
        loadCustomersIntoComboBox(customerComboBox);
        gbc.gridx = 1;
        dialog.add(customerComboBox, gbc);

        // Room Selection
        JLabel roomLabel = new JLabel("Номер:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(roomLabel, gbc);

        JComboBox<String> roomComboBox = new JComboBox<>();
        loadAvailableRoomsIntoComboBox(roomComboBox);
        gbc.gridx = 1;
        dialog.add(roomComboBox, gbc);

        // Check-in Date
        JLabel checkInLabel = new JLabel("Дата заезда:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(checkInLabel, gbc);

        JTextField checkInField = new JTextField("YYYY-MM-DD");
        gbc.gridx = 1;
        dialog.add(checkInField, gbc);

        // Check-out Date
        JLabel checkOutLabel = new JLabel("Дата выезда:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(checkOutLabel, gbc);

        JTextField checkOutField = new JTextField("YYYY-MM-DD");
        gbc.gridx = 1;
        dialog.add(checkOutField, gbc);

        // Deposit
        JLabel depositLabel = new JLabel("Депозит:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(depositLabel, gbc);

        JTextField depositField = new JTextField(20);
        gbc.gridx = 1;
        dialog.add(depositField, gbc);

        // Buttons
        JButton saveButton = new JButton("Сохранить");
        JButton cancelButton = new JButton("Отмена");

        JPanel buttonPanel = new JPanel();
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);

        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 2;
        dialog.add(buttonPanel, gbc);

        // Action Listeners
        saveButton.addActionListener(e -> {
            String selectedCustomer = (String) customerComboBox.getSelectedItem();
            String selectedRoom = (String) roomComboBox.getSelectedItem();
            String checkInDate = checkInField.getText().trim();
            String checkOutDate = checkOutField.getText().trim();
            String depositStr = depositField.getText().trim();

            if (selectedCustomer == null || selectedRoom == null || checkInDate.isEmpty() || checkOutDate.isEmpty()) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }

            double deposit = 0.0;
            if (!depositStr.isEmpty()) {
                try {
                    deposit = Double.parseDouble(depositStr);
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(dialog, "Пожалуйста, введите корректную сумму депозита.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }
            }

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

                // Получение customer_id
                String customerQuery = "SELECT customer_id FROM Customers WHERE full_name = ?";
                PreparedStatement customerStmt = conn.prepareStatement(customerQuery);
                customerStmt.setString(1, selectedCustomer);
                ResultSet customerRs = customerStmt.executeQuery();
                int customerId = -1;
                if (customerRs.next()) {
                    customerId = customerRs.getInt("customer_id");
                }

                // Получение room_id
                String roomQuery = "SELECT room_id, price FROM Rooms WHERE room_number = ?";
                PreparedStatement roomStmt = conn.prepareStatement(roomQuery);
                roomStmt.setString(1, selectedRoom);
                ResultSet roomRs = roomStmt.executeQuery();
                int roomId = -1;
                double roomPrice = 0.0;
                if (roomRs.next()) {
                    roomId = roomRs.getInt("room_id");
                    roomPrice = roomRs.getDouble("price");
                }

                if (customerId == -1 || roomId == -1) {
                    JOptionPane.showMessageDialog(dialog, "Ошибка при получении данных клиента или номера.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    conn.close();
                    return;
                }

                // Расчет количества дней
                long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(
                        java.time.LocalDate.parse(checkInDate),
                        java.time.LocalDate.parse(checkOutDate)
                );

                if (daysBetween <= 0) {
                    JOptionPane.showMessageDialog(dialog, "Дата выезда должна быть позже даты заезда.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    conn.close();
                    return;
                }

                double totalAmount = roomPrice * daysBetween;

                // Вставка бронирования
                String insertQuery = "INSERT INTO Reservations (customer_id, room_id, check_in, check_out, status, total_amount, deposit, created_at, updated_at, receptionist_id) " +
                        "VALUES (?, ?, ?, ?, 'Booked', ?, ?, NOW(), NOW(), ?)";
                PreparedStatement insertStmt = conn.prepareStatement(insertQuery);
                insertStmt.setInt(1, customerId);
                insertStmt.setInt(2, roomId);
                insertStmt.setDate(3, Date.valueOf(checkInDate));
                insertStmt.setDate(4, Date.valueOf(checkOutDate));
                insertStmt.setDouble(5, totalAmount);
                insertStmt.setDouble(6, deposit);
                insertStmt.setInt(7, receptionistId);

                insertStmt.executeUpdate();

                conn.close();

                JOptionPane.showMessageDialog(dialog, "Бронирование успешно добавлено.");
                dialog.dispose();
                loadReservations();
                loadRooms(); // Обновляем статус нумера

            } catch (SQLIntegrityConstraintViolationException ex) {
                JOptionPane.showMessageDialog(dialog, "Номер уже забронирован на указанные даты.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } catch (Exception ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(dialog, "Ошибка при добавлении бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        });

        cancelButton.addActionListener(e -> dialog.dispose());

        dialog.setVisible(true);
    }

    // Load customers into ComboBox
    private void loadCustomersIntoComboBox(JComboBox<String> comboBox) {
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT full_name FROM Customers";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);

            while(rs.next()) {
                comboBox.addItem(rs.getString("full_name"));
            }
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Load available rooms into ComboBox
    private void loadAvailableRoomsIntoComboBox(JComboBox<String> comboBox) {
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT room_number FROM Rooms WHERE available = TRUE AND status = 'Available'";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);

            while(rs.next()) {
                comboBox.addItem(String.valueOf(rs.getInt("room_number")));
            }
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Dialog to Edit Selected Reservation
    private void openEditReservationDialog() {
        int selectedRow = reservationsTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для редактирования.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        int reservationId = (int) reservationsModel.getValueAt(selectedRow, 0);
        String currentCustomer = (String) reservationsModel.getValueAt(selectedRow, 1);
        String currentRoom = String.valueOf(reservationsModel.getValueAt(selectedRow, 2));
        String currentCheckIn = reservationsModel.getValueAt(selectedRow, 3).toString();
        String currentCheckOut = reservationsModel.getValueAt(selectedRow, 4).toString();
        String currentStatus = (String) reservationsModel.getValueAt(selectedRow, 5);
        double currentTotalAmount = (double) reservationsModel.getValueAt(selectedRow, 6);
        double currentDeposit = (double) reservationsModel.getValueAt(selectedRow, 7);

        JDialog dialog = new JDialog(this, "Редактировать Бронирование", true);
        dialog.setSize(500, 600);
        dialog.setLocationRelativeTo(this);
        dialog.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(10,10,10,10);
        gbc.fill = GridBagConstraints.HORIZONTAL;

        // Customer Selection
        JLabel customerLabel = new JLabel("Клиент:");
        gbc.gridx = 0;
        gbc.gridy = 0;
        dialog.add(customerLabel, gbc);

        JComboBox<String> customerComboBox = new JComboBox<>();
        loadCustomersIntoComboBox(customerComboBox);
        customerComboBox.setSelectedItem(currentCustomer);
        gbc.gridx = 1;
        dialog.add(customerComboBox, gbc);

        // Room Selection
        JLabel roomLabel = new JLabel("Номер:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(roomLabel, gbc);

        JComboBox<String> roomComboBox = new JComboBox<>();
        loadAvailableRoomsIntoComboBox(roomComboBox);
        roomComboBox.setSelectedItem(currentRoom);
        gbc.gridx = 1;
        dialog.add(roomComboBox, gbc);

        // Check-in Date
        JLabel checkInLabel = new JLabel("Дата заезда:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(checkInLabel, gbc);

        JTextField checkInField = new JTextField(currentCheckIn, 20);
        gbc.gridx = 1;
        dialog.add(checkInField, gbc);

        // Check-out Date
        JLabel checkOutLabel = new JLabel("Дата выезда:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(checkOutLabel, gbc);

        JTextField checkOutField = new JTextField(currentCheckOut, 20);
        gbc.gridx = 1;
        dialog.add(checkOutField, gbc);

        // Deposit
        JLabel depositLabel = new JLabel("Депозит:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(depositLabel, gbc);

        JTextField depositField = new JTextField(String.valueOf(currentDeposit), 20);
        gbc.gridx = 1;
        dialog.add(depositField, gbc);

        // Status
        JLabel statusLabel = new JLabel("Статус:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(statusLabel, gbc);

        JComboBox<String> statusComboBox = new JComboBox<>(new String[]{"Booked", "Checked-in", "Checked-out", "Cancelled"});
        statusComboBox.setSelectedItem(currentStatus);
        gbc.gridx = 1;
        dialog.add(statusComboBox, gbc);

        // Buttons
        JButton saveButton = new JButton("Сохранить");
        JButton cancelButton = new JButton("Отмена");

        JPanel buttonPanel = new JPanel();
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);

        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 2;
        dialog.add(buttonPanel, gbc);

        // Action Listeners
        saveButton.addActionListener(e -> {
            String selectedCustomer = (String) customerComboBox.getSelectedItem();
            String selectedRoom = (String) roomComboBox.getSelectedItem();
            String checkInDate = checkInField.getText().trim();
            String checkOutDate = checkOutField.getText().trim();
            String depositStr = depositField.getText().trim();
            String status = (String) statusComboBox.getSelectedItem();

            if (selectedCustomer == null || selectedRoom == null || checkInDate.isEmpty() || checkOutDate.isEmpty()) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }

            double deposit = 0.0;
            if (!depositStr.isEmpty()) {
                try {
                    deposit = Double.parseDouble(depositStr);
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(dialog, "Пожалуйста, введите корректную сумму депозита.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }
            }

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

                // Получение customer_id
                String customerQuery = "SELECT customer_id FROM Customers WHERE full_name = ?";
                PreparedStatement customerStmt = conn.prepareStatement(customerQuery);
                customerStmt.setString(1, selectedCustomer);
                ResultSet customerRs = customerStmt.executeQuery();
                int customerId = -1;
                if (customerRs.next()) {
                    customerId = customerRs.getInt("customer_id");
                }

                // Получение room_id
                String roomQuery = "SELECT room_id, price FROM Rooms WHERE room_number = ?";
                PreparedStatement roomStmt = conn.prepareStatement(roomQuery);
                roomStmt.setString(1, selectedRoom);
                ResultSet roomRs = roomStmt.executeQuery();
                int roomId = -1;
                double roomPrice = 0.0;
                if (roomRs.next()) {
                    roomId = roomRs.getInt("room_id");
                    roomPrice = roomRs.getDouble("price");
                }

                if (customerId == -1 || roomId == -1) {
                    JOptionPane.showMessageDialog(dialog, "Ошибка при получении данных клиента или номера.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    conn.close();
                    return;
                }

                // Расчет количества дней
                long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(
                        java.time.LocalDate.parse(checkInDate),
                        java.time.LocalDate.parse(checkOutDate)
                );

                if (daysBetween <= 0) {
                    JOptionPane.showMessageDialog(dialog, "Дата выезда должна быть позже даты заезда.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    conn.close();
                    return;
                }

                double totalAmount = roomPrice * daysBetween;

                // Обновление бронирования
                String updateQuery = "UPDATE Reservations SET customer_id = ?, room_id = ?, check_in = ?, check_out = ?, status = ?, " +
                        "total_amount = ?, deposit = ?, updated_at = ? WHERE reservation_id = ?";
                PreparedStatement updateStmt = conn.prepareStatement(updateQuery);
                updateStmt.setInt(1, customerId);
                updateStmt.setInt(2, roomId);
                updateStmt.setDate(3, Date.valueOf(checkInDate));
                updateStmt.setDate(4, Date.valueOf(checkOutDate));
                updateStmt.setString(5, status);
                updateStmt.setDouble(6, totalAmount);
                updateStmt.setDouble(7, deposit);
                updateStmt.setTimestamp(8, new Timestamp(System.currentTimeMillis()));
                updateStmt.setInt(9, reservationId);

                updateStmt.executeUpdate();
                conn.close();

                JOptionPane.showMessageDialog(dialog, "Бронирование успешно обновлено.");
                dialog.dispose();
                loadReservations();
                loadRooms(); // Обновляем статус номеров

            } catch (SQLIntegrityConstraintViolationException ex) {
                JOptionPane.showMessageDialog(dialog, "Номер уже забронирован на указанные даты.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } catch (Exception ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(dialog, "Ошибка при обновлении бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        });

        cancelButton.addActionListener(e -> dialog.dispose());

        dialog.setVisible(true);
    }

    // Cancel Selected Reservation
    private void cancelSelectedReservation() {
        int selectedRow = reservationsTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для отмены.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        String status = (String) reservationsModel.getValueAt(selectedRow, 5);
        if (status.equals("Cancelled")) {
            JOptionPane.showMessageDialog(this, "Бронирование уже отменено.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        int reservationId = (int) reservationsModel.getValueAt(selectedRow, 0);
        int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите отменить это бронирование?", "Подтверждение", JOptionPane.YES_NO_OPTION);
        if (confirm != JOptionPane.YES_OPTION) {
            return;
        }

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

            String updateQuery = "UPDATE Reservations SET status = 'Cancelled', updated_at = NOW() WHERE reservation_id = ?";
            PreparedStatement updateStmt = conn.prepareStatement(updateQuery);
            updateStmt.setInt(1, reservationId);
            updateStmt.executeUpdate();
            conn.close();

            JOptionPane.showMessageDialog(this, "Бронирование успешно отменено.");
            loadReservations();
            loadRooms(); // Обновляем статус номеров

        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка при отмене бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Check-in Selected Reservation
    private void checkInSelectedReservation() {
        int selectedRow = reservationsTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для заселения.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        String status = (String) reservationsModel.getValueAt(selectedRow, 5);
        if (status.equals("Checked-in")) {
            JOptionPane.showMessageDialog(this, "Клиент уже заселен.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        if (!status.equals("Booked")) {
            JOptionPane.showMessageDialog(this, "Невозможно заселить клиента с текущим статусом.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            return;
        }

        int reservationId = (int) reservationsModel.getValueAt(selectedRow, 0);
        int roomNumber = (int) reservationsModel.getValueAt(selectedRow, 2);

        int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите заселить этого клиента?", "Подтверждение", JOptionPane.YES_NO_OPTION);
        if (confirm != JOptionPane.YES_OPTION) {
            return;
        }

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

            // Обновление статуса бронирования
            String updateReservation = "UPDATE Reservations SET status = 'Checked-in', updated_at = NOW() WHERE reservation_id = ?";
            PreparedStatement pstmt1 = conn.prepareStatement(updateReservation);
            pstmt1.setInt(1, reservationId);
            pstmt1.executeUpdate();

            // Обновление статуса номера
            String updateRoom = "UPDATE Rooms SET available = FALSE, status = 'Occupied' WHERE room_number = ?";
            PreparedStatement pstmt2 = conn.prepareStatement(updateRoom);
            pstmt2.setInt(1, roomNumber);
            pstmt2.executeUpdate();

            conn.close();

            JOptionPane.showMessageDialog(this, "Клиент успешно заселен.");
            loadReservations();
            loadRooms();

        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка при заселении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Check-out Selected Reservation
    private void checkOutSelectedReservation() {
        int selectedRow = reservationsTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для выселения.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        String status = (String) reservationsModel.getValueAt(selectedRow, 5);
        if (!status.equals("Checked-in")) {
            JOptionPane.showMessageDialog(this, "Клиент не заселен или уже выселен.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            return;
        }

        int reservationId = (int) reservationsModel.getValueAt(selectedRow, 0);
        int roomNumber = (int) reservationsModel.getValueAt(selectedRow, 2);

        int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите выселить этого клиента?", "Подтверждение", JOptionPane.YES_NO_OPTION);
        if (confirm != JOptionPane.YES_OPTION) {
            return;
        }

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

            // Обновление статуса бронирования
            String updateReservation = "UPDATE Reservations SET status = 'Checked-out', updated_at = NOW() WHERE reservation_id = ?";
            PreparedStatement pstmt1 = conn.prepareStatement(updateReservation);
            pstmt1.setInt(1, reservationId);
            pstmt1.executeUpdate();

            // Обновление статуса номера
            String updateRoom = "UPDATE Rooms SET available = TRUE, status = 'Available' WHERE room_number = ?";
            PreparedStatement pstmt2 = conn.prepareStatement(updateRoom);
            pstmt2.setInt(1, roomNumber);
            pstmt2.executeUpdate();

            conn.close();

            JOptionPane.showMessageDialog(this, "Клиент успешно выселен.");
            loadReservations();
            loadRooms();

        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка при выселении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Initialization of Customers Tab
    private void initCustomersTab() {
        JPanel panel = new JPanel(new BorderLayout());

        // Top Panel with Buttons
        JPanel topPanel = new JPanel();
        addCustomerButton = new JButton("Добавить Клиента");
        editCustomerButton = new JButton("Редактировать Клиента");
        deleteCustomerButton = new JButton("Удалить Клиента");

        topPanel.add(addCustomerButton);
        topPanel.add(editCustomerButton);
        topPanel.add(deleteCustomerButton);

        panel.add(topPanel, BorderLayout.NORTH);

        // Table for Customers
        customersModel = new DefaultTableModel();
        customersTable = new JTable(customersModel);
        customersModel.setColumnIdentifiers(new String[]{
                "ID Клиента", "Полное имя", "Телефон", "Email", "Пол", "Страна", "Адрес", "Ресепшионист", "Дата создания", "Дата обновления"
        });

        loadCustomers();

        JScrollPane scrollPane = new JScrollPane(customersTable);
        panel.add(scrollPane, BorderLayout.CENTER);

        // Action Listeners for Buttons
        addCustomerButton.addActionListener(e -> openAddCustomerDialog());
        editCustomerButton.addActionListener(e -> openEditCustomerDialog());
        deleteCustomerButton.addActionListener(e -> deleteSelectedCustomer());

        tabbedPane.addTab("Клиенты", panel);
    }

    // Loading Customers from Database
    private void loadCustomers() {
        customersModel.setRowCount(0);
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT c.customer_id, c.full_name, c.phone, c.email, c.gender, c.country, c.address, " +
                    "rec.full_name AS receptionist, c.created_at, c.updated_at " +
                    "FROM Customers c " +
                    "LEFT JOIN Receptionists rec ON c.receptionist_id = rec.receptionist_id";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);

            while (rs.next()) {
                Vector<Object> row = new Vector<>();
                row.add(rs.getInt("customer_id"));
                row.add(rs.getString("full_name"));
                row.add(rs.getString("phone"));
                row.add(rs.getString("email"));
                row.add(rs.getString("gender"));
                row.add(rs.getString("country"));
                row.add(rs.getString("address"));
                row.add(rs.getString("receptionist"));
                row.add(rs.getTimestamp("created_at"));
                row.add(rs.getTimestamp("updated_at"));
                customersModel.addRow(row);
            }
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка загрузки клиентов.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Dialog to Add New Customer
    private void openAddCustomerDialog() {
        JDialog dialog = new JDialog(this, "Добавить Клиента", true);
        dialog.setSize(500, 600);
        dialog.setLocationRelativeTo(this);
        dialog.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(10,10,10,10);
        gbc.fill = GridBagConstraints.HORIZONTAL;

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

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

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

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

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

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

        // Пол
        JLabel genderLabel = new JLabel("Пол:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(genderLabel, gbc);

        JComboBox<String> genderComboBox = new JComboBox<>(new String[]{"Male", "Female", "Other"});
        gbc.gridx = 1;
        dialog.add(genderComboBox, gbc);

        // Страна
        JLabel countryLabel = new JLabel("Страна:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(countryLabel, gbc);

        JTextField countryField = new JTextField(20);
        gbc.gridx = 1;
        dialog.add(countryField, gbc);

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

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

        // Buttons
        JButton saveButton = new JButton("Сохранить");
        JButton cancelButton = new JButton("Отмена");

        JPanel buttonPanel = new JPanel();
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);

        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 2;
        dialog.add(buttonPanel, gbc);

        // Action Listeners
        saveButton.addActionListener(e -> {
            String fullName = nameField.getText().trim();
            String phone = phoneField.getText().trim();
            String email = emailField.getText().trim();
            String gender = (String) genderComboBox.getSelectedItem();
            String country = countryField.getText().trim();
            String address = addressField.getText().trim();

            if (fullName.isEmpty() || phone.isEmpty() || gender.isEmpty()) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }

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

                String query = "INSERT INTO Customers (full_name, phone, email, gender, country, address, receptionist_id, created_at, updated_at) " +
                        "VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
                PreparedStatement pstmt = conn.prepareStatement(query);
                pstmt.setString(1, fullName);
                pstmt.setString(2, phone);
                pstmt.setString(3, email.isEmpty() ? null : email);
                pstmt.setString(4, gender);
                pstmt.setString(5, country.isEmpty() ? null : country);
                pstmt.setString(6, address.isEmpty() ? null : address);
                pstmt.setInt(7, receptionistId);

                pstmt.executeUpdate();
                conn.close();

                JOptionPane.showMessageDialog(dialog, "Клиент успешно добавлен.");
                dialog.dispose();
                loadCustomers();
            } catch (SQLIntegrityConstraintViolationException ex) {
                JOptionPane.showMessageDialog(dialog, "Такой телефон или email уже существуют!", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } catch (Exception ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(dialog, "Ошибка при добавлении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        });

        cancelButton.addActionListener(e -> dialog.dispose());

        dialog.setVisible(true);
    }

    // Dialog to Edit Selected Customer
    private void openEditCustomerDialog() {
        int selectedRow = customersTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите клиента для редактирования.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        int customerId = (int) customersModel.getValueAt(selectedRow, 0);
        String currentName = (String) customersModel.getValueAt(selectedRow, 1);
        String currentPhone = (String) customersModel.getValueAt(selectedRow, 2);
        String currentEmail = (String) customersModel.getValueAt(selectedRow, 3);
        String currentGender = (String) customersModel.getValueAt(selectedRow, 4);
        String currentCountry = (String) customersModel.getValueAt(selectedRow, 5);
        String currentAddress = (String) customersModel.getValueAt(selectedRow, 6);

        JDialog dialog = new JDialog(this, "Редактировать Клиента", true);
        dialog.setSize(500, 600);
        dialog.setLocationRelativeTo(this);
        dialog.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(10,10,10,10);
        gbc.fill = GridBagConstraints.HORIZONTAL;

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

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

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

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

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

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

        // Пол
        JLabel genderLabel = new JLabel("Пол:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(genderLabel, gbc);

        JComboBox<String> genderComboBox = new JComboBox<>(new String[]{"Male", "Female", "Other"});
        genderComboBox.setSelectedItem(currentGender);
        gbc.gridx = 1;
        dialog.add(genderComboBox, gbc);

        // Страна
        JLabel countryLabel = new JLabel("Страна:");
        gbc.gridx = 0;
        gbc.gridy++;
        dialog.add(countryLabel, gbc);

        JTextField countryField = new JTextField(currentCountry, 20);
        gbc.gridx = 1;
        dialog.add(countryField, gbc);

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

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

        // Buttons
        JButton saveButton = new JButton("Сохранить");
        JButton cancelButton = new JButton("Отмена");

        JPanel buttonPanel = new JPanel();
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);

        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 2;
        dialog.add(buttonPanel, gbc);

        // Action Listeners
        saveButton.addActionListener(e -> {
            String fullName = nameField.getText().trim();
            String phone = phoneField.getText().trim();
            String email = emailField.getText().trim();
            String gender = (String) genderComboBox.getSelectedItem();
            String country = countryField.getText().trim();
            String address = addressField.getText().trim();

            if (fullName.isEmpty() || phone.isEmpty() || gender.isEmpty()) {
                JOptionPane.showMessageDialog(dialog, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }

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

                String query = "UPDATE Customers SET full_name = ?, phone = ?, email = ?, gender = ?, country = ?, address = ?, updated_at = NOW() WHERE customer_id = ?";
                PreparedStatement pstmt = conn.prepareStatement(query);
                pstmt.setString(1, fullName);
                pstmt.setString(2, phone);
                pstmt.setString(3, email.isEmpty() ? null : email);
                pstmt.setString(4, gender);
                pstmt.setString(5, country.isEmpty() ? null : country);
                pstmt.setString(6, address.isEmpty() ? null : address);
                pstmt.setInt(7, customerId);

                pstmt.executeUpdate();
                conn.close();

                JOptionPane.showMessageDialog(dialog, "Клиент успешно обновлен.");
                dialog.dispose();
                loadCustomers();
            } catch (SQLIntegrityConstraintViolationException ex) {
                JOptionPane.showMessageDialog(dialog, "Такой телефон или email уже существуют!", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } catch (Exception ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(dialog, "Ошибка при обновлении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        });

        cancelButton.addActionListener(e -> dialog.dispose());

        dialog.setVisible(true);
    }

    // Delete Selected Customer
    private void deleteSelectedCustomer() {
        int selectedRow = customersTable.getSelectedRow();
        if (selectedRow == -1) {
            JOptionPane.showMessageDialog(this, "Пожалуйста, выберите клиента для удаления.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        int customerId = (int) customersModel.getValueAt(selectedRow, 0);
        String customerName = (String) customersModel.getValueAt(selectedRow, 1);

        int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить клиента \"" + customerName + "\"?", "Подтверждение", JOptionPane.YES_NO_OPTION);
        if (confirm != JOptionPane.YES_OPTION) {
            return;
        }

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

            String query = "DELETE FROM Customers WHERE customer_id = ?";
            PreparedStatement pstmt = conn.prepareStatement(query);
            pstmt.setInt(1, customerId);
            pstmt.executeUpdate();
            conn.close();

            JOptionPane.showMessageDialog(this, "Клиент успешно удален.");
            loadCustomers();
        } catch (SQLIntegrityConstraintViolationException ex) {
            JOptionPane.showMessageDialog(this, "Невозможно удалить клиента, так как существуют связанные бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка при удалении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Initialization of Rooms Tab
    private void initRoomsTab() {
        JPanel panel = new JPanel(new BorderLayout());

        // Top Panel with Refresh Button
        JPanel topPanel = new JPanel();
        refreshRoomsButton = new JButton("Обновить");
        topPanel.add(refreshRoomsButton);

        panel.add(topPanel, BorderLayout.NORTH);

        // Table for Rooms
        roomsModel = new DefaultTableModel();
        roomsTable = new JTable(roomsModel);
        roomsModel.setColumnIdentifiers(new String[]{
                "ID Номера", "Номер", "Доступен", "Статус", "Цена", "Тип кровати", "Тип номера", "Админ ID", "Ресепшионист ID", "Дата создания", "Дата обновления"
        });

        loadRooms();

        JScrollPane scrollPane = new JScrollPane(roomsTable);
        panel.add(scrollPane, BorderLayout.CENTER);

        // Action Listener for Refresh Button
        refreshRoomsButton.addActionListener(e -> loadRooms());

        tabbedPane.addTab("Номера", panel);
    }

    // Loading Rooms from Database
    private void loadRooms() {
        roomsModel.setRowCount(0);
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT room_id, room_number, available, status, price, bed_type, room_type, admin_id, receptionist_id, created_at, updated_at FROM Rooms";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);

            while (rs.next()) {
                Vector<Object> row = new Vector<>();
                row.add(rs.getInt("room_id"));
                row.add(rs.getInt("room_number"));
                row.add(rs.getBoolean("available") ? "Да" : "Нет");
                row.add(rs.getString("status"));
                row.add(rs.getDouble("price"));
                row.add(rs.getString("bed_type"));
                row.add(rs.getString("room_type"));
                row.add(rs.getObject("admin_id") != null ? rs.getInt("admin_id") : "NULL");
                row.add(rs.getObject("receptionist_id") != null ? rs.getInt("receptionist_id") : "NULL");
                row.add(rs.getTimestamp("created_at"));
                row.add(rs.getTimestamp("updated_at"));
                roomsModel.addRow(row);
            }
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка загрузки номеров.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Initialization of Income Tab
    private void initIncomeTab() {
        JPanel panel = new JPanel(new BorderLayout());

        // Top Panel with Total Income
        JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        totalIncomeLabel = new JLabel("Общий доход: ");
        topPanel.add(totalIncomeLabel);

        panel.add(topPanel, BorderLayout.NORTH);

        // Table for Payments
        paymentsModel = new DefaultTableModel();
        paymentsTable = new JTable(paymentsModel);
        paymentsModel.setColumnIdentifiers(new String[]{
                "ID Платежа", "ID Бронирования", "Сумма", "Дата платежа", "Метод оплаты", "Дата создания"
        });

        loadPayments();

        JScrollPane scrollPane = new JScrollPane(paymentsTable);
        panel.add(scrollPane, BorderLayout.CENTER);

        tabbedPane.addTab("Доходы", panel);
    }

    // Loading Payments and Calculating Total Income
    private void loadPayments() {
        paymentsModel.setRowCount(0);
        double totalIncome = 0.0;
        try {
            ConnectMySQL connection = new ConnectMySQL();
            Connection conn = connection.getConnection();
            String query = "SELECT payment_id, reservation_id, amount, payment_date, payment_method, created_at FROM Payments";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);

            while (rs.next()) {
                Vector<Object> row = new Vector<>();
                row.add(rs.getInt("payment_id"));
                row.add(rs.getInt("reservation_id"));
                row.add(rs.getDouble("amount"));
                row.add(rs.getDate("payment_date"));
                row.add(rs.getString("payment_method"));
                row.add(rs.getTimestamp("created_at"));
                paymentsModel.addRow(row);
                totalIncome += rs.getDouble("amount");
            }
            conn.close();

            totalIncomeLabel.setText(String.format("Общий доход: %.2f", totalIncome));
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка загрузки платежей.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
    }

    // Dialog to Add New Customer
    // (Same as in Reservations tab, can be refactored to avoid duplication)

    // Hashing Password (if needed)
    private String hashPassword(String password) {
        try {
            java.security.MessageDigest md = java.security.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 (java.security.NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    // Main Method to Launch Receptionist Dashboard
    public static void main(String[] args) {
        // Set system look and feel for modern UI
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ignored) {}

        // For testing purposes, pass a sample receptionistId
        SwingUtilities.invokeLater(() -> new DashboardReceptionist(1));
    }
}












// 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 JTextField usernameField;
    private JPasswordField passwordField;
    private JButton loginButton, cancelButton;
    private JCheckBox showPasswordCheck;
    private JButton signupLink;

    public Login() {
        setTitle("Авторизация");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(400, 300);
        setLocationRelativeTo(null);

        // Панель с отступами
        JPanel panel = new JPanel(new GridBagLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;

        // Метка и поле ввода для логина
        JLabel usernameLabel = new JLabel("Имя пользователя:");
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.insets = new Insets(5,5,5,5);
        panel.add(usernameLabel, gbc);

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

        // Метка и поле ввода для пароля
        JLabel passwordLabel = new JLabel("Пароль:");
        gbc.gridx = 0;
        gbc.gridy = 1;
        panel.add(passwordLabel, gbc);

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

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

        // Панель для кнопок
        JPanel buttonPanel = new JPanel();
        loginButton = new JButton("Войти");
        cancelButton = new JButton("Отмена");
        buttonPanel.add(loginButton);
        buttonPanel.add(cancelButton);

        gbc.gridx = 1;
        gbc.gridy = 3;
        panel.add(buttonPanel, gbc);

        // Ссылка на окно регистрации
        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.addActionListener(this);

        gbc.gridx = 1;
        gbc.gridy = 4;
        panel.add(signupLink, 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) {
        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);
    }

    /**
     * Метод для аутентификации пользователя в заданной роли.
     *
     * @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(); // Используем try-with-resources
             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();
    }
}












// src/h/User.java
package h;

public class Pr {
    private int id;
    private String fullName;
    private String role; // "Administrator" or "Receptionist"

    public Pr(int id, String fullName, String role) {
        this.id = id;
        this.fullName = fullName;
        this.role = role;
    }

    // Геттеры и сеттеры
    public int getId() {
        return id;
    }

    public String getFullName() {
        return fullName;
    }

    public String getRole() {
        return role;
    }
}














// 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 JComboBox<String> roleComboBox;
    private JTextField nameField, passportField, addressField, phoneField, emailField, loginField;
    private JPasswordField passwordField;
    private JTextField regCodeField;
    private JLabel regCodeLabel; // Объявляем как поле класса
    private JCheckBox showPasswordCheck;
    private JButton signupButton, cancelButton;
    private JButton loginLink;

    // Предопределенный код регистрации для администратора
    private static final String ADMIN_REG_CODE = "ADMIN2023"; // Измените этот код на нужный вам

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

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

        // Выбор роли
        JLabel roleLabel = new JLabel("Роль:");
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.insets = new Insets(5,5,5,5);
        panel.add(roleLabel, gbc);

        roleComboBox = new JComboBox<>(new String[]{"Ресепшионист", "Администратор"});
        gbc.gridx = 1;
        panel.add(roleComboBox, gbc);
        roleComboBox.addActionListener(this);

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

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

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

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

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

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

        // Телефон
        JLabel phoneLabel = new JLabel("Телефон:");
        gbc.gridx = 0;
        gbc.gridy = 4;
        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 = 5;
        panel.add(emailLabel, gbc);

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

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

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

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

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

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

        // Поле для кода регистрации (только для администратора)
        regCodeLabel = new JLabel("Код регистрации:");
        gbc.gridx = 0;
        gbc.gridy = 9;
        regCodeLabel.setVisible(false); // Изначально скрыто
        panel.add(regCodeLabel, gbc);

        regCodeField = new JTextField(20);
        gbc.gridx = 1;
        regCodeField.setVisible(false); // Изначально скрыто
        panel.add(regCodeField, gbc);

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

        cancelButton = new JButton("Отмена");
        gbc.gridy = 11;
        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 = 12;
        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 == roleComboBox) {
            String selectedRole = (String) roleComboBox.getSelectedItem();
            boolean isAdmin = selectedRole.equals("Администратор");

            // Показать или скрыть поле кода регистрации
            regCodeLabel.setVisible(isAdmin);
            regCodeField.setVisible(isAdmin);
            this.revalidate();
            this.repaint();
        }

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

        if (source == signupButton) {
            String role = (String) roleComboBox.getSelectedItem();
            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();
            String regCode = regCodeField.getText().trim();

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

            if (role.equals("Администратор")) {
                if (regCode.isEmpty()) {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, введите код регистрации для администратора.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }
                if (!regCode.equals(ADMIN_REG_CODE)) {
                    JOptionPane.showMessageDialog(this, "Неверный код регистрации.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }
            }

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

                String tableName = role.equals("Администратор") ? "Administrators" : "Receptionists";
                String query;
                if (role.equals("Администратор")) {
                    query = "INSERT INTO Administrators (full_name, passport_number, address, phone_number, email, login, password, registration_code, created_at, updated_at) "
                            + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
                } else {
                    // Для ресепшиониста admin_id должен быть указан администратором
                    // Здесь мы предполагаем, что администратор с admin_id = 1 существует
                    // В реальной системе лучше выбирать или определять администратора динамически
                    query = "INSERT INTO Receptionists (full_name, passport_number, address, phone_number, email, login, password, admin_id, 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));

                if (role.equals("Администратор")) {
                    pstmt.setString(8, regCode);
                } else {
                    // Устанавливаем admin_id. Предполагаем, что администратор с admin_id = 1 существует
                    pstmt.setInt(8, 1); // Измените это значение в соответствии с вашей логикой
                }

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

            } 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();
    }
}



















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




также нужно чтобы ресепшионист и администратор могли просматривать информацию о доходах


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

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

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