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

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

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

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

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

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

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

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

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

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

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

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








DELIMITER $$

CREATE TRIGGER trg_after_insert_reservation_update_room_status
AFTER INSERT ON Reservations
FOR EACH ROW
BEGIN
    IF NEW.status = 'Забронирован' OR NEW.status = 'Заселён' THEN
        UPDATE Rooms
        SET status = 'Недоступен'
        WHERE room_id = NEW.room_id;
    END IF;
END
$$

DELIMITER ;












DELIMITER $$

CREATE TRIGGER trg_after_update_reservation_update_room_status
AFTER UPDATE ON Reservations
FOR EACH ROW
BEGIN
    IF NEW.status = 'Забронирован' OR NEW.status = 'Заселён' THEN
        UPDATE Rooms
        SET status = 'Недоступен'
        WHERE room_id = NEW.room_id;
    ELSEIF NEW.status = 'Отменён' OR NEW.status = 'Выселен' THEN
        UPDATE Rooms
        SET status = 'Доступен'
        WHERE room_id = NEW.room_id;
    END IF;
END
$$

DELIMITER ;












DELIMITER $$

CREATE TRIGGER trg_before_insert_reservation_prevent_overlapping
BEFORE INSERT ON Reservations
FOR EACH ROW
BEGIN
    IF EXISTS (
        SELECT 1 FROM Reservations
        WHERE room_id = NEW.room_id
          AND status NOT IN ('Отменён')
          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 ;













DELIMITER $$

CREATE TRIGGER trg_before_update_reservation_prevent_overlapping
BEFORE UPDATE ON Reservations
FOR EACH ROW
BEGIN
    IF EXISTS (
        SELECT 1 FROM Reservations
        WHERE room_id = NEW.room_id
          AND reservation_id != NEW.reservation_id
          AND status NOT IN ('Отменён')
          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 ;



















DELIMITER $$

CREATE TRIGGER trg_before_insert_reservation_calculate_total_amount
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
$$

DELIMITER ;
















DELIMITER $$

CREATE TRIGGER trg_before_update_reservation_calculate_total_amount
BEFORE UPDATE ON Reservations
FOR EACH ROW
BEGIN
    DECLARE roomPrice DECIMAL(10,2);
    DECLARE days INT;
    
    -- Получение цены номера
    SELECT price INTO roomPrice FROM Rooms WHERE room_id = NEW.room_id;
    
    -- Вычисление количества дней
    SET days = DATEDIFF(NEW.check_out, NEW.check_in);
    
    -- Проверка валидности дат
    IF days <= 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'Дата выезда должна быть позже даты заезда.';
    END IF;
    
    -- Установка общей суммы
    SET NEW.total_amount = roomPrice * days;
END
$$

DELIMITER ;











DELIMITER $$

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

DELIMITER ;

















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

import java.sql.*;

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

    private Connection connection;

    public ConnectMySQL() throws SQLException {
        // Класс загрузки драйвера MySQL JDBC теперь не нужен
        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();
        }
    }
}














// src/h/DashboardAdmin.java
package h;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;
import org.jdatepicker.impl.*;

public class DashboardAdmin extends JFrame {

    private int adminId; // Идентификатор администратора

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

        // Создание вкладок
        JTabbedPane tabbedPane = new JTabbedPane();

        tabbedPane.addTab("Ресепшионисты", new ReceptionistsPanel());
        tabbedPane.addTab("Уборщики", new CleanersPanel());
        tabbedPane.addTab("Номера", new RoomsPanel());
        tabbedPane.addTab("Архивы", new ArchivesPanel());

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

    // Панель управления ресепшионистами
    class ReceptionistsPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton addButton, editButton, deleteButton;
        private JTextField searchField;

        public ReceptionistsPanel() {
            setLayout(new BorderLayout());

            // Панель поиска
            JPanel searchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
            JLabel searchLabel = new JLabel("Поиск по имени:");
            searchField = new JTextField(20);
            searchPanel.add(searchLabel);
            searchPanel.add(searchField);
            add(searchPanel, BorderLayout.NORTH);

            // Создание модели таблицы
            tableModel = new DefaultTableModel();
            tableModel.setColumnIdentifiers(new String[] {"ID", "Полное имя", "Паспорт", "Адрес", "Телефон", "Email", "Логин"});
            table = new JTable(tableModel);
            loadReceptionists("");

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Кнопки управления
            JPanel buttonPanel = new JPanel();
            addButton = new JButton("Добавить");
            editButton = new JButton("Редактировать");
            deleteButton = new JButton("Удалить");
            buttonPanel.add(addButton);
            buttonPanel.add(editButton);
            buttonPanel.add(deleteButton);

            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчики событий
            addButton.addActionListener(e -> openReceptionistForm(null));
            editButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int receptionistId = (int)tableModel.getValueAt(selectedRow, 0);
                    openReceptionistForm(receptionistId);
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите ресепшиониста для редактирования.");
                }
            });
            deleteButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int receptionistId = (int)tableModel.getValueAt(selectedRow, 0);
                    int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить этого ресепшиониста?", "Подтверждение", JOptionPane.YES_NO_OPTION);
                    if (confirm == JOptionPane.YES_OPTION) {
                        deleteReceptionist(receptionistId);
                        loadReceptionists(searchField.getText().trim());
                    }
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите ресепшиониста для удаления.");
                }
            });

            // Добавляем слушатель изменений для поля поиска
            searchField.getDocument().addDocumentListener(new DocumentListener() {
                public void insertUpdate(DocumentEvent e) {
                    loadReceptionists(searchField.getText().trim());
                }
                public void removeUpdate(DocumentEvent e) {
                    loadReceptionists(searchField.getText().trim());
                }
                public void changedUpdate(DocumentEvent e) {
                    loadReceptionists(searchField.getText().trim());
                }
            });
        }

        private void loadReceptionists(String search) {
            tableModel.setRowCount(0);
            String query = "SELECT receptionist_id, full_name, passport_number, address, phone_number, email, login FROM Receptionists";
            if (search != null && !search.isEmpty()) {
                query += " WHERE full_name LIKE ?";
            }
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                if (search != null && !search.isEmpty()) {
                    pstmt.setString(1, "%" + search + "%");
                }

                try (ResultSet rs = pstmt.executeQuery()) {
                    while (rs.next()) {
                        tableModel.addRow(new Object[] {
                                rs.getInt("receptionist_id"),
                                rs.getString("full_name"),
                                rs.getString("passport_number"),
                                rs.getString("address"),
                                rs.getString("phone_number"),
                                rs.getString("email"),
                                rs.getString("login")
                        });
                    }
                }

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

        private void openReceptionistForm(Integer receptionistId) {
            new ReceptionistForm(receptionistId, this);
        }

        private void deleteReceptionist(int receptionistId) {
            String query = "DELETE FROM Receptionists WHERE receptionist_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, receptionistId);
                pstmt.executeUpdate();

                JOptionPane.showMessageDialog(this, "Ресепшионист успешно удален.");

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

    // Форма добавления/редактирования ресепшиониста
    class ReceptionistForm extends JDialog implements ActionListener {
        private JTextField nameField, passportField, addressField, phoneField, emailField, loginField;
        private JPasswordField passwordField;
        private JButton saveButton, cancelButton;
        private Integer receptionistId; // Null для добавления, не null для редактирования
        private ReceptionistsPanel parentPanel;

        public ReceptionistForm(Integer receptionistId, ReceptionistsPanel parentPanel) {
            this.receptionistId = receptionistId;
            this.parentPanel = parentPanel;

            setTitle(receptionistId == null ? "Добавить Ресепшиониста" : "Редактировать Ресепшиониста");
            setSize(400, 500);
            setLocationRelativeTo(null);
            setModal(true);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(5,5,5,5);

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

            // Паспорт
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Номер паспорта:"), gbc);
            passportField = new JTextField(20);
            gbc.gridx = 1;
            add(passportField, gbc);

            // Адрес
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Адрес:"), gbc);
            addressField = new JTextField(20);
            gbc.gridx = 1;
            add(addressField, gbc);

            // Телефон
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Телефон:"), gbc);
            phoneField = new JTextField(20);
            gbc.gridx = 1;
            add(phoneField, gbc);

            // Email
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Email:"), gbc);
            emailField = new JTextField(20);
            gbc.gridx = 1;
            add(emailField, gbc);

            // Логин
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Логин:"), gbc);
            loginField = new JTextField(20);
            gbc.gridx = 1;
            add(loginField, gbc);

            // Пароль
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Пароль:"), gbc);
            passwordField = new JPasswordField(20);
            gbc.gridx = 1;
            add(passwordField, gbc);

            // Кнопки
            gbc.gridx = 0;
            gbc.gridy++;
            gbc.gridwidth = 2;
            JPanel buttonPanel = new JPanel();
            saveButton = new JButton("Сохранить");
            cancelButton = new JButton("Отмена");
            buttonPanel.add(saveButton);
            buttonPanel.add(cancelButton);
            add(buttonPanel, gbc);

            // Загрузка данных для редактирования
            if (receptionistId != null) {
                loadReceptionistData();
            }

            // Обработчики
            saveButton.addActionListener(this);
            cancelButton.addActionListener(e -> dispose());

            setVisible(true);
        }

        private void loadReceptionistData() {
            String query = "SELECT * FROM Receptionists WHERE receptionist_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, receptionistId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        nameField.setText(rs.getString("full_name"));
                        passportField.setText(rs.getString("passport_number"));
                        addressField.setText(rs.getString("address"));
                        phoneField.setText(rs.getString("phone_number"));
                        emailField.setText(rs.getString("email"));
                        loginField.setText(rs.getString("login"));
                        // Пароль не загружается для безопасности
                    }
                }

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

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == saveButton) {
                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() ||
                        (receptionistId == null && password.isEmpty())) {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                try (ConnectMySQL connection = new ConnectMySQL()) {
                    Connection conn = connection.getConnection();
                    PreparedStatement pstmt;
                    if (receptionistId == null) {
                        // Добавление нового ресепшиониста
                        String query = "INSERT INTO Receptionists (full_name, passport_number, address, phone_number, email, login, password, admin_id, created_at, updated_at) " +
                                "VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
                        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); // текущий администратор
                    } else {
                        // Обновление существующего ресепшиониста
                        String query = "UPDATE Receptionists SET full_name = ?, passport_number = ?, address = ?, phone_number = ?, email = ?, login = ?" +
                                (password.isEmpty() ? "" : ", password = ?") +
                                ", updated_at = NOW() WHERE receptionist_id = ?";
                        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);
                        int paramIndex = 7;
                        if (!password.isEmpty()) {
                            pstmt.setString(paramIndex++, hashPassword(password));
                        }
                        pstmt.setInt(paramIndex, receptionistId);
                    }

                    pstmt.executeUpdate();
                    JOptionPane.showMessageDialog(this, "Данные успешно сохранены.");
                    parentPanel.loadReceptionists(parentPanel.searchField.getText().trim());
                    dispose();

                } catch (SQLIntegrityConstraintViolationException ex) {
                    JOptionPane.showMessageDialog(this, "Дублирование логина, паспорта или телефона.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                } catch (Exception ex) {
                    ex.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Ошибка при сохранении данных ресепшиониста.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    }

    // Панель управления уборщиками
    class CleanersPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton addButton, editButton, deleteButton;
        private JTextField searchField;

        public CleanersPanel() {
            setLayout(new BorderLayout());

            // Панель поиска
            JPanel searchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
            JLabel searchLabel = new JLabel("Поиск по имени:");
            searchField = new JTextField(20);
            searchPanel.add(searchLabel);
            searchPanel.add(searchField);
            add(searchPanel, BorderLayout.NORTH);

            // Создание модели таблицы
            tableModel = new DefaultTableModel();
            tableModel.setColumnIdentifiers(new String[] {"ID", "Полное имя", "Телефон", "Email", "Статус", "Смена"});
            table = new JTable(tableModel);
            loadCleaners("");

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Кнопки управления
            JPanel buttonPanel = new JPanel();
            addButton = new JButton("Добавить");
            editButton = new JButton("Редактировать");
            deleteButton = new JButton("Удалить");
            buttonPanel.add(addButton);
            buttonPanel.add(editButton);
            buttonPanel.add(deleteButton);

            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчики событий
            addButton.addActionListener(e -> openCleanerForm(null));
            editButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int cleanerId = (int)tableModel.getValueAt(selectedRow, 0);
                    openCleanerForm(cleanerId);
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите уборщика для редактирования.");
                }
            });
            deleteButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int cleanerId = (int)tableModel.getValueAt(selectedRow, 0);
                    int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить этого уборщика?", "Подтверждение", JOptionPane.YES_NO_OPTION);
                    if (confirm == JOptionPane.YES_OPTION) {
                        deleteCleaner(cleanerId);
                        loadCleaners(searchField.getText().trim());
                    }
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите уборщика для удаления.");
                }
            });

            // Добавляем слушатель изменений поля поиска
            searchField.getDocument().addDocumentListener(new DocumentListener() {
                public void insertUpdate(DocumentEvent e) {
                    loadCleaners(searchField.getText().trim());
                }
                public void removeUpdate(DocumentEvent e) {
                    loadCleaners(searchField.getText().trim());
                }
                public void changedUpdate(DocumentEvent e) {
                    loadCleaners(searchField.getText().trim());
                }
            });
        }

        private void loadCleaners(String search) {
            tableModel.setRowCount(0);
            String query = "SELECT c.cleaner_id, c.full_name, c.phone_number, c.email, c.status, s.shift_name " +
                    "FROM Cleaners c LEFT JOIN Shifts s ON c.shift_id = s.shift_id";
            if (search != null && !search.isEmpty()) {
                query += " WHERE c.full_name LIKE ?";
            }
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                if (search != null && !search.isEmpty()) {
                    pstmt.setString(1, "%" + search + "%");
                }

                try (ResultSet rs = pstmt.executeQuery()) {
                    while (rs.next()) {
                        tableModel.addRow(new Object[] {
                                rs.getInt("cleaner_id"),
                                rs.getString("full_name"),
                                rs.getString("phone_number"),
                                rs.getString("email"),
                                rs.getString("status"),
                                rs.getString("shift_name")
                        });
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке уборщиков.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        private void openCleanerForm(Integer cleanerId) {
            new CleanerForm(cleanerId, this);
        }

        private void deleteCleaner(int cleanerId) {
            String query = "DELETE FROM Cleaners WHERE cleaner_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, cleanerId);
                pstmt.executeUpdate();

                JOptionPane.showMessageDialog(this, "Уборщик успешно удален.");

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

    // Форма добавления/редактирования уборщика
    class CleanerForm extends JDialog implements ActionListener {
        private JTextField nameField, phoneField, emailField;
        private JComboBox<String> shiftComboBox, statusComboBox;
        private JButton saveButton, cancelButton;
        private Integer cleanerId;
        private CleanersPanel parentPanel;

        public CleanerForm(Integer cleanerId, CleanersPanel parentPanel) {
            this.cleanerId = cleanerId;
            this.parentPanel = parentPanel;

            setTitle(cleanerId == null ? "Добавить Уборщика" : "Редактировать Уборщика");
            setSize(400, 400);
            setLocationRelativeTo(null);
            setModal(true);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(5,5,5,5);

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

            // Телефон
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Телефон:"), gbc);
            phoneField = new JTextField(20);
            gbc.gridx = 1;
            add(phoneField, gbc);

            // Email
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Email:"), gbc);
            emailField = new JTextField(20);
            gbc.gridx = 1;
            add(emailField, gbc);

            // Смена
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Смена:"), gbc);
            shiftComboBox = new JComboBox<>();
            loadShifts();
            gbc.gridx = 1;
            add(shiftComboBox, gbc);

            // Статус
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Статус:"), gbc);
            statusComboBox = new JComboBox<>(new String[] {"Свободен", "Занят"});
            gbc.gridx = 1;
            add(statusComboBox, gbc);

            // Кнопки
            gbc.gridx = 0;
            gbc.gridy++;
            gbc.gridwidth = 2;
            JPanel buttonPanel = new JPanel();
            saveButton = new JButton("Сохранить");
            cancelButton = new JButton("Отмена");
            buttonPanel.add(saveButton);
            buttonPanel.add(cancelButton);
            add(buttonPanel, gbc);

            // Загрузка данных для редактирования
            if (cleanerId != null) {
                loadCleanerData();
            }

            // Обработчики
            saveButton.addActionListener(this);
            cancelButton.addActionListener(e -> dispose());

            setVisible(true);
        }

        private void loadShifts() {
            String query = "SELECT shift_name FROM Shifts";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    shiftComboBox.addItem(rs.getString("shift_name"));
                }

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

        private void loadCleanerData() {
            String query = "SELECT * FROM Cleaners WHERE cleaner_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, cleanerId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        nameField.setText(rs.getString("full_name"));
                        phoneField.setText(rs.getString("phone_number"));
                        emailField.setText(rs.getString("email"));
                        shiftComboBox.setSelectedItem(getShiftNameById(rs.getInt("shift_id")));
                        statusComboBox.setSelectedItem(rs.getString("status"));
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке данных уборщика.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        private String getShiftNameById(int shiftId) {
            String shiftName = "";
            String query = "SELECT shift_name FROM Shifts WHERE shift_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, shiftId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        shiftName = rs.getString("shift_name");
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return shiftName;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == saveButton) {
                String fullName = nameField.getText().trim();
                String phone = phoneField.getText().trim();
                String email = emailField.getText().trim();
                String shiftName = (String)shiftComboBox.getSelectedItem();
                String status = (String)statusComboBox.getSelectedItem();

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

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

                    // Получение shift_id по shift_name
                    int shiftId = getShiftIdByName(shiftName);

                    if (cleanerId == null) {
                        // Добавление нового уборщика
                        String query = "INSERT INTO Cleaners (full_name, phone_number, email, status, shift_id, created_at, updated_at) " +
                                "VALUES (?, ?, ?, ?, ?, NOW(), NOW())";
                        pstmt = conn.prepareStatement(query);
                        pstmt.setString(1, fullName);
                        pstmt.setString(2, phone);
                        pstmt.setString(3, email);
                        pstmt.setString(4, status);
                        pstmt.setInt(5, shiftId);
                    } else {
                        // Обновление существующего уборщика
                        String query = "UPDATE Cleaners SET full_name = ?, phone_number = ?, email = ?, status = ?, shift_id = ?, updated_at = NOW() WHERE cleaner_id = ?";
                        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();
                    JOptionPane.showMessageDialog(this, "Данные успешно сохранены.");
                    parentPanel.loadCleaners(parentPanel.searchField.getText().trim());
                    dispose();

                } catch (SQLIntegrityConstraintViolationException ex) {
                    JOptionPane.showMessageDialog(this, "Дублирование телефона или email.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                } catch (Exception ex) {
                    ex.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Ошибка при сохранении данных уборщика.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            }
        }

        private int getShiftIdByName(String shiftName) {
            int shiftId = -1;
            String query = "SELECT shift_id FROM Shifts WHERE shift_name = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setString(1, shiftName);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        shiftId = rs.getInt("shift_id");
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return shiftId;
        }
    }

    // Панель управления номерами
    class RoomsPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton addButton, editButton, deleteButton;
        private JTextField searchField;

        public RoomsPanel() {
            setLayout(new BorderLayout());

            // Панель поиска
            JPanel searchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
            JLabel searchLabel = new JLabel("Поиск по номеру:");
            searchField = new JTextField(10);
            searchPanel.add(searchLabel);
            searchPanel.add(searchField);
            add(searchPanel, BorderLayout.NORTH);

            // Создание модели таблицы
            tableModel = new DefaultTableModel();
            tableModel.setColumnIdentifiers(new String[] {"ID", "Номер", "Статус", "Цена", "Тип кровати", "Тип номера"});
            table = new JTable(tableModel);
            loadRooms("");

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Кнопки управления
            JPanel buttonPanel = new JPanel();
            addButton = new JButton("Добавить");
            editButton = new JButton("Редактировать");
            deleteButton = new JButton("Удалить");
            buttonPanel.add(addButton);
            buttonPanel.add(editButton);
            buttonPanel.add(deleteButton);

            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчики событий
            addButton.addActionListener(e -> openRoomForm(null));
            editButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int roomId = (int)tableModel.getValueAt(selectedRow, 0);
                    openRoomForm(roomId);
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для редактирования.");
                }
            });
            deleteButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int roomId = (int)tableModel.getValueAt(selectedRow, 0);
                    int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить этот номер?", "Подтверждение", JOptionPane.YES_NO_OPTION);
                    if (confirm == JOptionPane.YES_OPTION) {
                        deleteRoom(roomId);
                        loadRooms(searchField.getText().trim());
                    }
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для удаления.");
                }
            });

            // Добавляем слушатель изменений для поля поиска
            searchField.getDocument().addDocumentListener(new DocumentListener() {
                public void insertUpdate(DocumentEvent e) {
                    loadRooms(searchField.getText().trim());
                }
                public void removeUpdate(DocumentEvent e) {
                    loadRooms(searchField.getText().trim());
                }
                public void changedUpdate(DocumentEvent e) {
                    loadRooms(searchField.getText().trim());
                }
            });
        }

        private void loadRooms(String search) {
            tableModel.setRowCount(0);
            String query = "SELECT room_id, room_number, status, price, bed_type, room_type FROM Rooms";
            if (search != null && !search.isEmpty()) {
                query += " WHERE room_number LIKE ?";
            }
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                if (search != null && !search.isEmpty()) {
                    pstmt.setString(1, "%" + search + "%");
                }

                try (ResultSet rs = pstmt.executeQuery()) {
                    while (rs.next()) {
                        tableModel.addRow(new Object[] {
                                rs.getInt("room_id"),
                                rs.getInt("room_number"),
                                rs.getString("status"),
                                rs.getBigDecimal("price"),
                                rs.getString("bed_type"),
                                rs.getString("room_type")
                        });
                    }
                }

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

        private void openRoomForm(Integer roomId) {
            new RoomForm(roomId, this);
        }

        private void deleteRoom(int roomId) {
            String query = "DELETE FROM Rooms WHERE room_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, roomId);
                pstmt.executeUpdate();

                JOptionPane.showMessageDialog(this, "Номер успешно удален.");

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

    // Форма добавления/редактирования номера
    class RoomForm extends JDialog implements ActionListener {
        private JTextField roomNumberField, priceField;
        private JComboBox<String> statusComboBox, bedTypeComboBox, roomTypeComboBox;
        private JButton saveButton, cancelButton;
        private Integer roomId;
        private RoomsPanel parentPanel;

        public RoomForm(Integer roomId, RoomsPanel parentPanel) {
            this.roomId = roomId;
            this.parentPanel = parentPanel;

            setTitle(roomId == null ? "Добавить Номер" : "Редактировать Номер");
            setSize(400, 400);
            setLocationRelativeTo(null);
            setModal(true);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(5,5,5,5);

            // Номер комнаты
            gbc.gridx = 0;
            gbc.gridy = 0;
            add(new JLabel("Номер комнаты:"), gbc);
            roomNumberField = new JTextField(20);
            gbc.gridx = 1;
            add(roomNumberField, gbc);

            // Статус
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Статус:"), gbc);
            statusComboBox = new JComboBox<>(new String[] {"Доступен", "Недоступен"});
            gbc.gridx = 1;
            add(statusComboBox, gbc);

            // Цена
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Цена:"), gbc);
            priceField = new JTextField(20);
            gbc.gridx = 1;
            add(priceField, gbc);

            // Тип кровати
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Тип кровати:"), gbc);
            bedTypeComboBox = new JComboBox<>(new String[] {"Одноместный", "Двухместный", "Трехместный", "Четырехместный"});
            gbc.gridx = 1;
            add(bedTypeComboBox, gbc);

            // Тип номера
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Тип номера:"), gbc);
            roomTypeComboBox = new JComboBox<>(new String[] {"Стандарт", "Люкс", "Делюкс"});
            gbc.gridx = 1;
            add(roomTypeComboBox, gbc);

            // Кнопки
            gbc.gridx = 0;
            gbc.gridy++;
            gbc.gridwidth = 2;
            JPanel buttonPanel = new JPanel();
            saveButton = new JButton("Сохранить");
            cancelButton = new JButton("Отмена");
            buttonPanel.add(saveButton);
            buttonPanel.add(cancelButton);
            add(buttonPanel, gbc);

            // Загрузка данных для редактирования
            if (roomId != null) {
                loadRoomData();
            }

            // Обработчики
            saveButton.addActionListener(this);
            cancelButton.addActionListener(e -> dispose());

            setVisible(true);
        }

        private void loadRoomData() {
            String query = "SELECT * FROM Rooms WHERE room_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, roomId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        roomNumberField.setText(String.valueOf(rs.getInt("room_number")));
                        statusComboBox.setSelectedItem(rs.getString("status"));
                        priceField.setText(rs.getBigDecimal("price").toString());
                        bedTypeComboBox.setSelectedItem(rs.getString("bed_type"));
                        roomTypeComboBox.setSelectedItem(rs.getString("room_type"));
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке данных номера.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == saveButton) {
                String roomNumberStr = roomNumberField.getText().trim();
                String status = (String)statusComboBox.getSelectedItem();
                String priceStr = priceField.getText().trim();
                String bedType = (String)bedTypeComboBox.getSelectedItem();
                String roomType = (String)roomTypeComboBox.getSelectedItem();

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

                int roomNumber;
                double price;
                try {
                    roomNumber = Integer.parseInt(roomNumberStr);
                    price = Double.parseDouble(priceStr);
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(this, "Номер комнаты и цена должны быть числовыми значениями.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

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

                    if (roomId == null) {
                        // Добавление нового номера
                        String query = "INSERT INTO Rooms (room_number, status, price, bed_type, room_type, created_at, updated_at) " +
                                "VALUES (?, ?, ?, ?, ?, NOW(), NOW())";
                        pstmt = conn.prepareStatement(query);
                        pstmt.setInt(1, roomNumber);
                        pstmt.setString(2, status);
                        pstmt.setDouble(3, price);
                        pstmt.setString(4, bedType);
                        pstmt.setString(5, roomType);
                    } else {
                        // Обновление существующего номера
                        String query = "UPDATE Rooms SET room_number = ?, status = ?, price = ?, bed_type = ?, room_type = ?, updated_at = NOW() WHERE room_id = ?";
                        pstmt = conn.prepareStatement(query);
                        pstmt.setInt(1, roomNumber);
                        pstmt.setString(2, status);
                        pstmt.setDouble(3, price);
                        pstmt.setString(4, bedType);
                        pstmt.setString(5, roomType);
                        pstmt.setInt(6, roomId);
                    }

                    pstmt.executeUpdate();
                    JOptionPane.showMessageDialog(this, "Данные успешно сохранены.");
                    parentPanel.loadRooms(parentPanel.searchField.getText().trim());
                    dispose();

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

    // Панель архивов резервирований с отображением общей выручки
    class ArchivesPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JLabel totalRevenueLabel; // Метка для общей выручки
        private JTextField searchField;

        public ArchivesPanel() {
            setLayout(new BorderLayout());

            // Панель поиска
            JPanel searchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
            JLabel searchLabel = new JLabel("Поиск по имени клиента:");
            searchField = new JTextField(20);
            searchPanel.add(searchLabel);
            searchPanel.add(searchField);
            add(searchPanel, BorderLayout.NORTH);

            // Создание модели таблицы
            tableModel = new DefaultTableModel();
            tableModel.setColumnIdentifiers(new String[] {"Архив ID", "Бронирование ID", "Клиент", "Номер", "Дата заезда", "Дата выезда", "Статус", "Сумма"});
            table = new JTable(tableModel);
            loadArchivedReservations("");

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Панель для отображения общей выручки
            JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
            totalRevenueLabel = new JLabel("Общая выручка: 0.00");
            bottomPanel.add(totalRevenueLabel);
            add(bottomPanel, BorderLayout.SOUTH);

            calculateTotalRevenue("");

            // Добавляем слушатель изменений поля поиска
            searchField.getDocument().addDocumentListener(new DocumentListener() {
                public void insertUpdate(DocumentEvent e) {
                    String search = searchField.getText().trim();
                    loadArchivedReservations(search);
                    calculateTotalRevenue(search);
                }
                public void removeUpdate(DocumentEvent e) {
                    String search = searchField.getText().trim();
                    loadArchivedReservations(search);
                    calculateTotalRevenue(search);
                }
                public void changedUpdate(DocumentEvent e) {
                    String search = searchField.getText().trim();
                    loadArchivedReservations(search);
                    calculateTotalRevenue(search);
                }
            });
        }

        private void loadArchivedReservations(String search) {
            tableModel.setRowCount(0);
            String query = "SELECT ar.archive_id, ar.reservation_id, c.full_name, r.room_number, ar.check_in, ar.check_out, ar.status, ar.total_amount " +
                    "FROM Archived_Reservations ar " +
                    "JOIN Customers c ON ar.customer_id = c.customer_id " +
                    "JOIN Rooms r ON ar.room_id = r.room_id";
            if (search != null && !search.isEmpty()) {
                query += " WHERE c.full_name LIKE ?";
            }
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                if (search != null && !search.isEmpty()) {
                    pstmt.setString(1, "%" + search + "%");
                }

                try (ResultSet rs = pstmt.executeQuery()) {
                    while (rs.next()) {
                        tableModel.addRow(new Object[] {
                                rs.getInt("archive_id"),
                                rs.getInt("reservation_id"),
                                rs.getString("full_name"),
                                rs.getInt("room_number"),
                                rs.getDate("check_in"),
                                rs.getDate("check_out"),
                                rs.getString("status"),
                                rs.getBigDecimal("total_amount")
                        });
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке архивных резервирований.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        private void calculateTotalRevenue(String search) {
            String query = "SELECT SUM(ar.total_amount) AS total_revenue FROM Archived_Reservations ar " +
                    "JOIN Customers c ON ar.customer_id = c.customer_id";
            if (search != null && !search.isEmpty()) {
                query += " WHERE c.full_name LIKE ?";
            }
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                if (search != null && !search.isEmpty()) {
                    pstmt.setString(1, "%" + search + "%");
                }

                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        double totalRevenue = rs.getDouble("total_revenue");
                        totalRevenueLabel.setText("Общая выручка: " + String.format("%.2f", totalRevenue));
                    }
                }

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



    class CleaningPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton assignButton;

        public CleaningPanel() {
            setLayout(new BorderLayout());

            // Создание модели таблицы
            tableModel = new DefaultTableModel();
            tableModel.setColumnIdentifiers(new String[] {"Номер", "Статус Уборки", "Уборщик"});
            table = new JTable(tableModel);
            loadCleaningRooms();

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Кнопка назначения уборщика
            JPanel buttonPanel = new JPanel();
            assignButton = new JButton("Назначить Уборщика");
            buttonPanel.add(assignButton);
            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчик события
            assignButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int roomNumber = (int)tableModel.getValueAt(selectedRow, 0);
                    int roomId = getRoomIdByNumber(roomNumber);
                    assignCleaner(roomId);
                    loadCleaningRooms();
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для назначения уборщика.");
                }
            });
        }

        private void loadCleaningRooms() {
            tableModel.setRowCount(0);
            String query = "SELECT r.room_number, cs.status, cl.full_name " +
                    "FROM Cleaning_Schedules cs " +
                    "JOIN Rooms r ON cs.room_id = r.room_id " +
                    "LEFT JOIN Cleaners cl ON cs.cleaner_id = cl.cleaner_id " +
                    "WHERE cs.status IN ('Назначено', 'В процессе')";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    tableModel.addRow(new Object[] {
                            rs.getInt("room_number"),
                            rs.getString("status"),
                            rs.getString("full_name")
                    });
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке данных уборки.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        private int getRoomIdByNumber(int roomNumber) {
            int roomId = -1;
            String query = "SELECT room_id FROM Rooms WHERE room_number = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, roomNumber);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        roomId = rs.getInt("room_id");
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return roomId;
        }

        private void assignCleaner(int roomId) {
            String cleanerQuery = "SELECT cleaner_id, full_name FROM Cleaners WHERE status = 'Свободен'";
            JComboBox<String> cleanerComboBox = new JComboBox<>();
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(cleanerQuery);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    cleanerComboBox.addItem(rs.getInt("cleaner_id") + " - " + rs.getString("full_name"));
                }

                if (cleanerComboBox.getItemCount() == 0) {
                    JOptionPane.showMessageDialog(this, "Нет доступных уборщиков.", "Информация", JOptionPane.INFORMATION_MESSAGE);
                    return;
                }

                int option = JOptionPane.showConfirmDialog(this, cleanerComboBox, "Выберите уборщика", JOptionPane.OK_CANCEL_OPTION);
                if (option == JOptionPane.OK_OPTION) {
                    String selectedCleaner = (String)cleanerComboBox.getSelectedItem();
                    int cleanerId = Integer.parseInt(selectedCleaner.split(" - ")[0]);

                    // Назначение уборщика
                    String insertQuery = "INSERT INTO Cleaning_Schedules (room_id, cleaner_id, scheduled_time, status, created_at, updated_at) " +
                            "VALUES (?, ?, NOW(), 'Назначено', NOW(), NOW())";
                    try (ConnectMySQL connectionUpdate = new ConnectMySQL();
                         PreparedStatement insertPstmt = connectionUpdate.getConnection().prepareStatement(insertQuery)) {

                        insertPstmt.setInt(1, roomId);
                        insertPstmt.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
                        insertPstmt.executeUpdate();

                    } catch (Exception ex) {
                        ex.printStackTrace();
                        JOptionPane.showMessageDialog(this, "Ошибка при назначении уборщика.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                        return;
                    }

                    // Обновление статуса уборщика
                    String updateCleanerQuery = "UPDATE Cleaners SET status = 'Занят' WHERE cleaner_id = ?";
                    try (ConnectMySQL connectionUpdate = new ConnectMySQL();
                         PreparedStatement updatePstmt = connectionUpdate.getConnection().prepareStatement(updateCleanerQuery)) {

                        updatePstmt.setInt(1, cleanerId);
                        updatePstmt.executeUpdate();

                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }

                    JOptionPane.showMessageDialog(this, "Уборщик успешно назначен.");
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке уборщиков.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    // Панель управления бронированиями (опционально)
    // Можно интегрировать into DashboardAdmin или оставить как отдельную вкладку
    // Здесь представлен базовый пример
    class ReservationsPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton addButton, editButton, deleteButton, assignCleanerButton;

        public ReservationsPanel() {
            setLayout(new BorderLayout());

            // Создание модели таблицы
            tableModel = new DefaultTableModel();
            tableModel.setColumnIdentifiers(new String[] {"ID", "Клиент", "Номер", "Дата заезда", "Дата выезда", "Статус", "Сумма"});
            table = new JTable(tableModel);
            loadReservations();

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Кнопки управления
            JPanel buttonPanel = new JPanel();
            addButton = new JButton("Добавить");
            editButton = new JButton("Редактировать");
            deleteButton = new JButton("Удалить");
            assignCleanerButton = new JButton("Назначить Уборщика");
            buttonPanel.add(addButton);
            buttonPanel.add(editButton);
            buttonPanel.add(deleteButton);
            buttonPanel.add(assignCleanerButton);

            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчики событий
            addButton.addActionListener(e -> openReservationForm(null));
            editButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
                    openReservationForm(reservationId);
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для редактирования.");
                }
            });
            deleteButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
                    int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить это бронирование?", "Подтверждение", JOptionPane.YES_NO_OPTION);
                    if (confirm == JOptionPane.YES_OPTION) {
                        deleteReservation(reservationId);
                        loadReservations();
                    }
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для удаления.");
                }
            });
            assignCleanerButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
                    assignCleaner(reservationId);
                    loadReservations();
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для назначения уборщика.");
                }
            });
        }

        private void loadReservations() {
            tableModel.setRowCount(0);
            String query = "SELECT res.reservation_id, c.full_name, r.room_number, res.check_in, res.check_out, res.status, res.total_amount " +
                    "FROM Reservations res " +
                    "JOIN Customers c ON res.customer_id = c.customer_id " +
                    "JOIN Rooms r ON res.room_id = r.room_id";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    tableModel.addRow(new Object[] {
                            rs.getInt("reservation_id"),
                            rs.getString("full_name"),
                            rs.getInt("room_number"),
                            rs.getDate("check_in"),
                            rs.getDate("check_out"),
                            rs.getString("status"),
                            rs.getBigDecimal("total_amount")
                    });
                }

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

        private void openReservationForm(Integer reservationId) {
            new ReservationForm(reservationId, this, adminId);
        }

        private void deleteReservation(int reservationId) {
            String query = "DELETE FROM Reservations WHERE reservation_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, reservationId);
                pstmt.executeUpdate();

                JOptionPane.showMessageDialog(this, "Бронирование успешно удалено.");

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

        private void assignCleaner(int reservationId) {
            new AssignCleanerForm(reservationId, this);
        }
    }

    // Форма добавления/редактирования бронирования
    class ReservationForm extends JDialog implements ActionListener {
        private JComboBox<String> customerComboBox, roomComboBox;
        private JDatePickerImpl checkInDatePicker, checkOutDatePicker;
        private JButton saveButton, cancelButton;
        private Integer reservationId;
        private ReservationsPanel parentPanel;
        private int adminId;

        public ReservationForm(Integer reservationId, ReservationsPanel parentPanel, int adminId) {
            this.reservationId = reservationId;
            this.parentPanel = parentPanel;
            this.adminId = adminId;

            setTitle(reservationId == null ? "Добавить Бронирование" : "Редактировать Бронирование");
            setSize(500, 400);
            setLocationRelativeTo(null);
            setModal(true);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(5,5,5,5);

            // Клиент
            gbc.gridx = 0;
            gbc.gridy = 0;
            add(new JLabel("Клиент:"), gbc);
            customerComboBox = new JComboBox<>();
            loadCustomers();
            gbc.gridx = 1;
            add(customerComboBox, gbc);

            // Номер
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Номер:"), gbc);
            roomComboBox = new JComboBox<>();
            loadAvailableRooms();
            gbc.gridx = 1;
            add(roomComboBox, gbc);

            // Дата заезда
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Дата заезда:"), gbc);
            checkInDatePicker = createDatePicker();
            gbc.gridx = 1;
            add(checkInDatePicker, gbc);

            // Дата выезда
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Дата выезда:"), gbc);
            checkOutDatePicker = createDatePicker();
            gbc.gridx = 1;
            add(checkOutDatePicker, gbc);

            // Кнопки
            gbc.gridx = 0;
            gbc.gridy++;
            gbc.gridwidth = 2;
            JPanel buttonPanel = new JPanel();
            saveButton = new JButton("Сохранить");
            cancelButton = new JButton("Отмена");
            buttonPanel.add(saveButton);
            buttonPanel.add(cancelButton);
            add(buttonPanel, gbc);

            // Загрузка данных для редактирования
            if (reservationId != null) {
                loadReservationData();
            }

            // Обработчики
            saveButton.addActionListener(this);
            cancelButton.addActionListener(e -> dispose());

            setVisible(true);
        }

        private void loadCustomers() {
            String query = "SELECT customer_id, full_name FROM Customers";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    customerComboBox.addItem(rs.getInt("customer_id") + " - " + rs.getString("full_name"));
                }

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

        private void loadAvailableRooms() {
            String query = "SELECT room_id, room_number FROM Rooms WHERE status = 'Доступен'";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    roomComboBox.addItem(rs.getInt("room_id") + " - " + rs.getInt("room_number"));
                }

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

        private JDatePickerImpl createDatePicker() {
            UtilDateModel model = new UtilDateModel();
            Properties p = new Properties();
            p.put("text.today", "Сегодня");
            p.put("text.month", "Месяц");
            p.put("text.year", "Год");
            JDatePanelImpl datePanel = new JDatePanelImpl(model, p);
            return new JDatePickerImpl(datePanel, new DateLabelFormatter());
        }

        private void loadReservationData() {
            String query = "SELECT * FROM Reservations WHERE reservation_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, reservationId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        customerComboBox.setSelectedItem(rs.getInt("customer_id") + " - " + getCustomerName(rs.getInt("customer_id")));
                        roomComboBox.setSelectedItem(rs.getInt("room_id") + " - " + getRoomNumber(rs.getInt("room_id")));
                        checkInDatePicker.getModel().setDate(rs.getDate("check_in").getYear() + 1900, rs.getDate("check_in").getMonth(), rs.getDate("check_in").getDate());
                        checkInDatePicker.getModel().setSelected(true);
                        checkOutDatePicker.getModel().setDate(rs.getDate("check_out").getYear() + 1900, rs.getDate("check_out").getMonth(), rs.getDate("check_out").getDate());
                        checkOutDatePicker.getModel().setSelected(true);
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке данных бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        private String getCustomerName(int customerId) {
            String name = "";
            String query = "SELECT full_name FROM Customers WHERE customer_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, customerId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        name = rs.getString("full_name");
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return name;
        }

        private int getRoomNumber(int roomId) {
            int roomNumber = -1;
            String query = "SELECT room_number FROM Rooms WHERE room_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, roomId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        roomNumber = rs.getInt("room_number");
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return roomNumber;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == saveButton) {
                String customerStr = (String)customerComboBox.getSelectedItem();
                String roomStr = (String)roomComboBox.getSelectedItem();
                java.util.Date checkInDate = (java.util.Date)checkInDatePicker.getModel().getValue();
                java.util.Date checkOutDate = (java.util.Date)checkOutDatePicker.getModel().getValue();

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

                int customerId = Integer.parseInt(customerStr.split(" - ")[0]);
                int roomId = Integer.parseInt(roomStr.split(" - ")[0]);

                java.sql.Date checkInSqlDate = new java.sql.Date(checkInDate.getTime());
                java.sql.Date checkOutSqlDate = new java.sql.Date(checkOutDate.getTime());

                // Валидация дат
                if (!validateDates(checkInSqlDate, checkOutSqlDate)) {
                    JOptionPane.showMessageDialog(this, "Дата выезда должна быть позже даты заезда.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                // Проверка доступности номера
                if (!isRoomAvailable(roomId, checkInSqlDate, checkOutSqlDate)) {
                    JOptionPane.showMessageDialog(this, "Номер уже забронирован на указанные даты.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

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

                    if (reservationId == null) {
                        // Добавление нового бронирования
                        String query = "INSERT INTO Reservations (customer_id, room_id, check_in, check_out, status, total_amount, created_at, updated_at, receptionist_id) " +
                                "VALUES (?, ?, ?, ?, 'Забронирован', ?, NOW(), NOW(), ?)";
                        pstmt = conn.prepareStatement(query);

                        // Вычисление total_amount
                        double totalAmount = calculateTotalAmount(roomId, checkInSqlDate, checkOutSqlDate);

                        pstmt.setInt(1, customerId);
                        pstmt.setInt(2, roomId);
                        pstmt.setDate(3, checkInSqlDate);
                        pstmt.setDate(4, checkOutSqlDate);
                        pstmt.setDouble(5, totalAmount);
                        pstmt.setInt(6, adminId);
                    } else {
                        // Обновление существующего бронирования
                        String query = "UPDATE Reservations SET customer_id = ?, room_id = ?, check_in = ?, check_out = ?, status = 'Забронирован', total_amount = ?, updated_at = NOW(), receptionist_id = ? WHERE reservation_id = ?";
                        pstmt = conn.prepareStatement(query);

                        // Вычисление total_amount
                        double totalAmount = calculateTotalAmount(roomId, checkInSqlDate, checkOutSqlDate);

                        pstmt.setInt(1, customerId);
                        pstmt.setInt(2, roomId);
                        pstmt.setDate(3, checkInSqlDate);
                        pstmt.setDate(4, checkOutSqlDate);
                        pstmt.setDouble(5, totalAmount);
                        pstmt.setInt(6, adminId);
                        pstmt.setInt(7, reservationId);
                    }

                    pstmt.executeUpdate();
                    JOptionPane.showMessageDialog(this, "Бронирование успешно сохранено.");
                    parentPanel.loadReservations();
                    dispose();

                } catch (SQLException ex) {
                    ex.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Ошибка при сохранении бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            }
        }

        private boolean validateDates(java.sql.Date checkIn, java.sql.Date checkOut) {
            return checkOut.after(checkIn);
        }

        private boolean isRoomAvailable(int roomId, java.sql.Date checkIn, java.sql.Date checkOut) {
            String query = "SELECT COUNT(*) AS count FROM Reservations WHERE room_id = ? AND status NOT IN ('Отменён') AND (" +
                    "(check_in <= ? AND check_out > ?) OR " +
                    "(check_in < ? AND check_out >= ?) OR " +
                    "(check_in >= ? AND check_out <= ?))";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, roomId);
                pstmt.setDate(2, checkOut);
                pstmt.setDate(3, checkIn);
                pstmt.setDate(4, checkIn);
                pstmt.setDate(5, checkOut);
                pstmt.setDate(6, checkIn);
                pstmt.setDate(7, checkOut);

                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        return rs.getInt("count") == 0;
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }

        private double calculateTotalAmount(int roomId, java.sql.Date checkIn, java.sql.Date checkOut) {
            String query = "SELECT price FROM Rooms WHERE room_id = ?";
            double price = 0.0;
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, roomId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        price = rs.getDouble("price");
                    }
                }

                long diffInMillies = checkOut.getTime() - checkIn.getTime();
                long days = diffInMillies / (1000 * 60 * 60 * 24);
                return price * days;

            } catch (Exception e) {
                e.printStackTrace();
            }
            return price;
        }
    }

    // Форма назначения уборщика
    class AssignCleanerForm extends JDialog implements ActionListener {
        private JComboBox<String> cleanerComboBox;
        private JButton assignButton, cancelButton;
        private int reservationId;
        private ReservationsPanel parentPanel;

        public AssignCleanerForm(int reservationId, ReservationsPanel parentPanel) {
            this.reservationId = reservationId;
            this.parentPanel = parentPanel;

            setTitle("Назначить Уборщика");
            setSize(400, 200);
            setLocationRelativeTo(null);
            setModal(true);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(5,5,5,5);

            // Выбор уборщика
            gbc.gridx = 0;
            gbc.gridy = 0;
            add(new JLabel("Уборщик:"), gbc);
            cleanerComboBox = new JComboBox<>();
            loadAvailableCleaners();
            gbc.gridx = 1;
            add(cleanerComboBox, gbc);

            // Кнопки
            gbc.gridx = 0;
            gbc.gridy++;
            gbc.gridwidth = 2;
            JPanel buttonPanel = new JPanel();
            assignButton = new JButton("Назначить");
            cancelButton = new JButton("Отмена");
            buttonPanel.add(assignButton);
            buttonPanel.add(cancelButton);
            add(buttonPanel, gbc);

            // Обработчики
            assignButton.addActionListener(this);
            cancelButton.addActionListener(e -> dispose());

            setVisible(true);
        }

        private void loadAvailableCleaners() {
            String query = "SELECT cleaner_id, full_name FROM Cleaners WHERE status = 'Свободен'";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    cleanerComboBox.addItem(rs.getInt("cleaner_id") + " - " + rs.getString("full_name"));
                }

                if (cleanerComboBox.getItemCount() == 0) {
                    JOptionPane.showMessageDialog(this, "Нет доступных уборщиков.", "Информация", JOptionPane.INFORMATION_MESSAGE);
                    dispose();
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке уборщиков.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == assignButton) {
                String cleanerStr = (String)cleanerComboBox.getSelectedItem();
                if (cleanerStr == null) {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите уборщика.");
                    return;
                }

                int cleanerId = Integer.parseInt(cleanerStr.split(" - ")[0]);

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

                    // Назначение уборщика в Cleaning_Schedules
                    String insertQuery = "INSERT INTO Cleaning_Schedules (reservation_id, cleaner_id, scheduled_time, status, created_at, updated_at) " +
                            "VALUES (?, ?, NOW(), 'Назначено', NOW(), NOW())";
                    try (PreparedStatement pstmt = conn.prepareStatement(insertQuery)) {
                        pstmt.setInt(1, reservationId);
                        pstmt.setInt(2, cleanerId);
                        pstmt.executeUpdate();
                    }

                    // Обновление статуса уборщика
                    String updateCleanerQuery = "UPDATE Cleaners SET status = 'Занят' WHERE cleaner_id = ?";
                    try (PreparedStatement pstmt = conn.prepareStatement(updateCleanerQuery)) {
                        pstmt.setInt(1, cleanerId);
                        pstmt.executeUpdate();
                    }

                    JOptionPane.showMessageDialog(this, "Уборщик успешно назначен.");
                    parentPanel.loadReservations();
                    dispose();

                } catch (Exception ex) {
                    ex.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Ошибка при назначении уборщика.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    }

    // Класс для форматирования даты в JDatePicker
    class DateLabelFormatter extends JFormattedTextField.AbstractFormatter {

        private String datePattern = "yyyy-MM-dd";
        private java.text.SimpleDateFormat dateFormatter = new java.text.SimpleDateFormat(datePattern);

        @Override
        public Object stringToValue(String text) throws java.text.ParseException {
            if (text == null || text.isEmpty()) {
                return null;
            }
            return dateFormatter.parse(text);
        }

        @Override
        public String valueToString(Object value) throws java.text.ParseException {
            if (value != null) {
                java.util.Date date = (java.util.Date) value;
                return dateFormatter.format(date);
            }

            return "";
        }
    }

    // Дополнительные классы и панели можно добавить здесь
    // Например, форма для управления платежами, загрузки изображений и т.д.
}



























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

import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import org.jdatepicker.impl.*;
import java.util.Properties;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.Locale;
import java.time.LocalTime;
import java.sql.Time;
public class DashboardReceptionist extends JFrame {

    private int receptionistId; // Идентификатор ресепшиониста

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

        // Создание вкладок
        JTabbedPane tabbedPane = new JTabbedPane();

        tabbedPane.addTab("Клиенты", new CustomersPanel());
        tabbedPane.addTab("Номера", new RoomsViewPanel());
        tabbedPane.addTab("Бронирования", new ReservationsPanel());
        tabbedPane.addTab("Архив", new ArchivePanel()); // Добавили вкладку Архив
        tabbedPane.addTab("Уборка", new CleaningPanel()); // Добавили вкладку Уборка

        add(tabbedPane);
        setVisible(true);
    }

    // Метод для хеширования пароля
    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;
        }
    }

    // Панель управления клиентами
    class CustomersPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton addButton, editButton, deleteButton, refreshButton;

        public CustomersPanel() {
            setLayout(new BorderLayout());

            // Создание модели таблицы
            tableModel = new DefaultTableModel();
            tableModel.setColumnIdentifiers(new String[] {"ID", "Полное имя", "Телефон", "Email", "Пол", "Страна"});
            table = new JTable(tableModel);
            loadCustomers();

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Кнопки управления
            JPanel buttonPanel = new JPanel();
            addButton = new JButton("Добавить");
            editButton = new JButton("Редактировать");
            deleteButton = new JButton("Удалить");
            refreshButton = new JButton("Обновить");
            buttonPanel.add(addButton);
            buttonPanel.add(editButton);
            buttonPanel.add(deleteButton);
            buttonPanel.add(refreshButton);

            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчики событий
            addButton.addActionListener(e -> openCustomerForm(null));
            editButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int customerId = (int)tableModel.getValueAt(selectedRow, 0);
                    openCustomerForm(customerId);
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите клиента для редактирования.");
                }
            });
            deleteButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int customerId = (int)tableModel.getValueAt(selectedRow, 0);
                    int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить этого клиента?", "Подтверждение", JOptionPane.YES_NO_OPTION);
                    if (confirm == JOptionPane.YES_OPTION) {
                        deleteCustomer(customerId);
                        loadCustomers();
                    }
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите клиента для удаления.");
                }
            });
            refreshButton.addActionListener(e -> loadCustomers());
        }

        private void loadCustomers() {
            tableModel.setRowCount(0);
            String query = "SELECT customer_id, full_name, phone, email, gender, country FROM Customers";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    tableModel.addRow(new Object[] {
                            rs.getInt("customer_id"),
                            rs.getString("full_name"),
                            rs.getString("phone"),
                            rs.getString("email"),
                            rs.getString("gender"),
                            rs.getString("country"),
                    });
                }

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

        private void openCustomerForm(Integer customerId) {
            new CustomerForm(customerId, this);
        }

        private void deleteCustomer(int customerId) {
            String query = "DELETE FROM Customers WHERE customer_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, customerId);
                pstmt.executeUpdate();

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

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

    // Форма добавления/редактирования клиента
    class CustomerForm extends JDialog implements ActionListener {
        private JTextField nameField, phoneField, emailField, countryField;
        private JComboBox<String> genderComboBox;
        private JButton saveButton, cancelButton;
        private Integer customerId; // Null для добавления, не null для редактирования
        private CustomersPanel parentPanel;

        public CustomerForm(Integer customerId, CustomersPanel parentPanel) {
            this.customerId = customerId;
            this.parentPanel = parentPanel;

            setTitle(customerId == null ? "Добавить Клиента" : "Редактировать Клиента");
            setSize(500, 500);
            setLocationRelativeTo(null);
            setModal(true);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(10,10,10,10);

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

            // Телефон
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Телефон:"), gbc);
            phoneField = new JTextField(20);
            gbc.gridx = 1;
            add(phoneField, gbc);

            // Email
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Email:"), gbc);
            emailField = new JTextField(20);
            gbc.gridx = 1;
            add(emailField, gbc);

            // Пол
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Пол:"), gbc);
            genderComboBox = new JComboBox<>(new String[] {"Мужской", "Женский"});
            gbc.gridx = 1;
            add(genderComboBox, gbc);

            // Страна
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Страна:"), gbc);
            countryField = new JTextField(20);
            gbc.gridx = 1;
            add(countryField, gbc);


            // Кнопки
            gbc.gridx = 0;
            gbc.gridy++;
            gbc.gridwidth = 2;
            JPanel buttonPanel = new JPanel();
            saveButton = new JButton("Сохранить");
            cancelButton = new JButton("Отмена");
            buttonPanel.add(saveButton);
            buttonPanel.add(cancelButton);
            add(buttonPanel, gbc);

            // Загрузка данных для редактирования
            if (customerId != null) {
                loadCustomerData();
            }

            // Обработчики
            saveButton.addActionListener(this);
            cancelButton.addActionListener(e -> dispose());

            setVisible(true);
        }

        private void loadCustomerData() {
            String query = "SELECT * FROM Customers WHERE customer_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, customerId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        nameField.setText(rs.getString("full_name"));
                        phoneField.setText(rs.getString("phone"));
                        emailField.setText(rs.getString("email"));
                        genderComboBox.setSelectedItem(rs.getString("gender"));
                        countryField.setText(rs.getString("country"));
                    }
                }

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

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == saveButton) {
                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();

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

                try (ConnectMySQL connection = new ConnectMySQL()) {
                    Connection conn = connection.getConnection();
                    PreparedStatement pstmt;
                    if (customerId == null) {
                        // Добавление нового клиента
                        String query = "INSERT INTO Customers (full_name, phone, email, gender, country, created_at, updated_at, receptionist_id) " +
                                "VALUES (?, ?, ?, ?, ?, NOW(), NOW(), ?)";
                        pstmt = conn.prepareStatement(query);
                        pstmt.setString(1, fullName);
                        pstmt.setString(2, phone);
                        pstmt.setString(3, email);
                        pstmt.setString(4, gender);
                        pstmt.setString(5, country);
                        pstmt.setInt(6, receptionistId);
                    } else {
                        // Обновление существующего клиента
                        String query = "UPDATE Customers SET full_name = ?, phone = ?, email = ?, gender = ?, country = ?, updated_at = NOW() WHERE customer_id = ?";
                        pstmt = conn.prepareStatement(query);
                        pstmt.setString(1, fullName);
                        pstmt.setString(2, phone);
                        pstmt.setString(3, email);
                        pstmt.setString(4, gender);
                        pstmt.setString(5, country);
                        pstmt.setInt(6, customerId);
                    }

                    pstmt.executeUpdate();
                    JOptionPane.showMessageDialog(this, "Данные успешно сохранены.");
                    parentPanel.loadCustomers();
                    dispose();

                } catch (SQLIntegrityConstraintViolationException ex) {
                    JOptionPane.showMessageDialog(this, "Дублирование телефона или email.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                } catch (Exception ex) {
                    ex.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Ошибка при сохранении данных клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    }

    // Панель просмотра номеров
    class RoomsViewPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton refreshButton;

        public RoomsViewPanel() {
            setLayout(new BorderLayout());

            // Создание модели таблицы
            tableModel = new DefaultTableModel();
            tableModel.setColumnIdentifiers(new String[] {"Номер ID", "Номер комнаты", "Статус", "Цена", "Тип кровати", "Тип номера"});
            table = new JTable(tableModel);
            loadRooms();

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Кнопка обновления
            JPanel buttonPanel = new JPanel();
            refreshButton = new JButton("Обновить");
            buttonPanel.add(refreshButton);
            add(buttonPanel, BorderLayout.SOUTH);

            refreshButton.addActionListener(e -> loadRooms());
        }

        private void loadRooms() {
            tableModel.setRowCount(0);
            String query = "SELECT room_id, room_number, status, price, bed_type, room_type FROM Rooms";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    tableModel.addRow(new Object[] {
                            rs.getInt("room_id"),
                            rs.getInt("room_number"),
                            rs.getString("status"),
                            rs.getBigDecimal("price"),
                            rs.getString("bed_type"),
                            rs.getString("room_type")
                    });
                }

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

    // Панель управления бронированиями
    class ReservationsPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton addButton, editButton, refreshButton, checkInButton, checkOutButton;

        public ReservationsPanel() {
            setLayout(new BorderLayout());

            // Создание модели таблицы
            tableModel = new DefaultTableModel() {
                @Override
                public boolean isCellEditable(int row, int column) {
                    return false; // Запретить редактирование ячеек напрямую
                }
            };
            tableModel.setColumnIdentifiers(new String[] {"ID", "Клиент", "Номер", "Дата заезда", "Дата выезда", "Статус", "Сумма"});
            table = new JTable(tableModel);

            // Добавление кастомного рендера для строк
            table.setDefaultRenderer(Object.class, new ReservationCellRenderer());

            loadReservations();

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Кнопки управления
            JPanel buttonPanel = new JPanel();
            addButton = new JButton("Добавить");
            editButton = new JButton("Редактировать");
            checkInButton = new JButton("Заселить");
            checkOutButton = new JButton("Выселить");
            refreshButton = new JButton("Обновить");
            buttonPanel.add(addButton);
            buttonPanel.add(editButton);
            buttonPanel.add(checkInButton);
            buttonPanel.add(checkOutButton);
            buttonPanel.add(refreshButton);

            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчики событий
            addButton.addActionListener(e -> openReservationForm(null));
            editButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
                    openReservationForm(reservationId);
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для редактирования.");
                }
            });
            checkInButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
                    openPaymentForm(reservationId);
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для заселения.");
                }
            });
            checkOutButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
                    checkOut(reservationId);
                    loadReservations();
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для выселения.");
                }
            });
            refreshButton.addActionListener(e -> loadReservations());
        }

        private void loadReservations() {
            tableModel.setRowCount(0);
            String query = "SELECT res.reservation_id, c.full_name, r.room_number, res.check_in, res.check_out, res.status, res.total_amount " +
                    "FROM Reservations res " +
                    "JOIN Customers c ON res.customer_id = c.customer_id " +
                    "JOIN Rooms r ON res.room_id = r.room_id";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    tableModel.addRow(new Object[] {
                            rs.getInt("reservation_id"),
                            rs.getString("full_name"),
                            rs.getInt("room_number"),
                            rs.getDate("check_in"),
                            rs.getDate("check_out"),
                            rs.getString("status"),
                            rs.getBigDecimal("total_amount")
                    });
                }

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

        private void openReservationForm(Integer reservationId) {
            new ReservationForm(reservationId, this, receptionistId);
        }

        private void openPaymentForm(int reservationId) {
            new PaymentForm(reservationId, this);
        }

        private void deleteReservation(int reservationId) {
            String query = "DELETE FROM Reservations WHERE reservation_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, reservationId);
                pstmt.executeUpdate();

                JOptionPane.showMessageDialog(this, "Бронирование успешно удалено.");

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

        private void assignCleaner(int reservationId) {
            new AssignCleanerForm(reservationId, this);
        }

        private void checkIn(int reservationId) {
            // Проверка и обновление статуса бронирования
            String updateQuery = "UPDATE Reservations SET status = 'Заселён', updated_at = NOW() WHERE reservation_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(updateQuery)) {

                pstmt.setInt(1, reservationId);
                pstmt.executeUpdate();

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

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

        private void checkOut(int reservationId) {
            String selectQuery = "SELECT * FROM Reservations WHERE reservation_id = ?";
            String updateQuery = "UPDATE Reservations SET status = 'Выселен', updated_at = NOW() WHERE reservation_id = ?";
            String insertArchiveQuery = "INSERT INTO Archived_Reservations (reservation_id, customer_id, room_id, check_in, check_out, status, total_amount, created_at, updated_at) " +
                    "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
            String deleteQuery = "DELETE FROM Reservations WHERE reservation_id = ?";

            Connection conn = null;
            PreparedStatement selectStmt = null;
            PreparedStatement updateStmt = null;
            PreparedStatement insertArchiveStmt = null;
            PreparedStatement deleteStmt = null;

            try {
                ConnectMySQL connection = new ConnectMySQL();
                conn = connection.getConnection();
                conn.setAutoCommit(false); // Начало транзакции

                // Получение данных бронирования
                selectStmt = conn.prepareStatement(selectQuery);
                selectStmt.setInt(1, reservationId);
                ResultSet rs = selectStmt.executeQuery();

                if (!rs.next()) {
                    JOptionPane.showMessageDialog(this, "Бронирование не найдено.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                int customerId = rs.getInt("customer_id");
                int roomId = rs.getInt("room_id");
                Date checkIn = rs.getDate("check_in");
                Date checkOut = rs.getDate("check_out");
                String status = rs.getString("status");
                double totalAmount = rs.getDouble("total_amount");
                Timestamp createdAt = rs.getTimestamp("created_at");
                Timestamp updatedAt = rs.getTimestamp("updated_at");

                // Обновление статуса бронирования
                updateStmt = conn.prepareStatement(updateQuery);
                updateStmt.setInt(1, reservationId);
                updateStmt.executeUpdate();

                // Вставка в архив
                insertArchiveStmt = conn.prepareStatement(insertArchiveQuery);
                insertArchiveStmt.setInt(1, reservationId);
                insertArchiveStmt.setInt(2, customerId);
                insertArchiveStmt.setInt(3, roomId);
                insertArchiveStmt.setDate(4, new java.sql.Date(checkIn.getTime()));
                insertArchiveStmt.setDate(5, new java.sql.Date(checkOut.getTime()));
                insertArchiveStmt.setString(6, "Выселен");
                insertArchiveStmt.setDouble(7, totalAmount);
                insertArchiveStmt.setTimestamp(8, createdAt);
                insertArchiveStmt.setTimestamp(9, updatedAt);
                insertArchiveStmt.executeUpdate();

                // Удаление из текущих бронирований
                deleteStmt = conn.prepareStatement(deleteQuery);
                deleteStmt.setInt(1, reservationId);
                deleteStmt.executeUpdate();

                // Обновление статуса комнаты на "Требует уборки"
                String updateRoomQuery = "UPDATE Rooms SET status = 'Требует уборки' WHERE room_id = ?";
                try (PreparedStatement pstmt = conn.prepareStatement(updateRoomQuery)) {
                    pstmt.setInt(1, roomId);
                    pstmt.executeUpdate();
                }

                conn.commit(); // Коммит транзакции
                JOptionPane.showMessageDialog(this, "Клиент успешно выселен.");

            } catch (SQLException e) {
                e.printStackTrace();
                if (conn != null) {
                    try {
                        conn.rollback(); // Откат транзакции при ошибке
                    } catch (SQLException ex) {
                        ex.printStackTrace();
                    }
                }
                JOptionPane.showMessageDialog(this, "Ошибка при выселении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            } finally {
                try {
                    if (selectStmt != null) selectStmt.close();
                    if (updateStmt != null) updateStmt.close();
                    if (insertArchiveStmt != null) insertArchiveStmt.close();
                    if (deleteStmt != null) deleteStmt.close();
                    if (conn != null) conn.setAutoCommit(true);
                    if (conn != null) conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        // Кастомный рендерер для выделения строк с датой выезда сегодня
        // Кастомный рендерер для выделения строк с учетом текущих клиентов
        class ReservationCellRenderer extends DefaultTableCellRenderer {

            // Метод для обрезки времени из даты
            private Date removeTime(Date date) {
                Calendar cal = Calendar.getInstance();
                cal.setTime(date);
                cal.set(Calendar.HOUR_OF_DAY, 0);
                cal.set(Calendar.MINUTE, 0);
                cal.set(Calendar.SECOND, 0);
                cal.set(Calendar.MILLISECOND, 0);
                return cal.getTime();
            }

            @Override
            public Component getTableCellRendererComponent(JTable table, Object value,
                                                           boolean isSelected, boolean hasFocus,
                                                           int row, int column) {
                Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

                // Получаем текущую дату без времени
                Date today = removeTime(new Date());

                String status = tableModel.getValueAt(row, 5).toString(); // Статус бронирования
                Object checkInObj = tableModel.getValueAt(row, 3); // Дата заезда
                Object checkOutObj = tableModel.getValueAt(row, 4); // Дата выезда

                if (status.equals("Заселён") && checkInObj != null && checkOutObj != null) {
                    Date checkInDate = removeTime((Date) checkInObj);
                    Date checkOutDate = removeTime((Date) checkOutObj);

                    if (!checkOutDate.after(today)) {
                        // Если дата выезда сегодня или раньше, выделяем строку красным
                        c.setBackground(Color.PINK);
                        if (c instanceof JComponent) {
                            ((JComponent) c).setToolTipText("Клиенту пора выезжать");
                        }
                    } else if (!checkInDate.after(today) && today.before(checkOutDate)) {
                        // Если сегодня между датой заезда и датой выезда, выделяем строку зелёным
                        c.setBackground(Color.GREEN);
                        if (c instanceof JComponent) {
                            ((JComponent) c).setToolTipText("Клиент проживает в номере");
                        }
                    } else {
                        c.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
                        if (c instanceof JComponent) {
                            ((JComponent) c).setToolTipText(null);
                        }
                    }
                } else {
                    c.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
                    if (c instanceof JComponent) {
                        ((JComponent) c).setToolTipText(null);
                    }
                }

                return c;
            }
        }

        class PaymentForm extends JDialog implements ActionListener {
            private JTextField amountField;
            private JComboBox<String> paymentMethodComboBox;
            private JButton payButton, cancelButton;
            private int reservationId;
            private ReservationsPanel parentPanel;

            public PaymentForm(int reservationId, ReservationsPanel parentPanel) {
                this.reservationId = reservationId;
                this.parentPanel = parentPanel;

                setTitle("Оплата");
                setSize(400, 300);
                setLocationRelativeTo(null);
                setModal(true);
                setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.insets = new Insets(10,10,10,10);

                // Получение данных бронирования
                double totalAmount = 0.0;
                int customerId = 0;
                int roomId = 0;
                try (ConnectMySQL connection = new ConnectMySQL()) {
                    String query = "SELECT total_amount, customer_id, room_id FROM Reservations WHERE reservation_id = ?";
                    PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                    pstmt.setInt(1, reservationId);
                    ResultSet rs = pstmt.executeQuery();
                    if (rs.next()) {
                        totalAmount = rs.getDouble("total_amount");
                        customerId = rs.getInt("customer_id");
                        roomId = rs.getInt("room_id");
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Ошибка при получении данных бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    dispose();
                    return;
                }

                // Сумма
                gbc.gridx = 0;
                gbc.gridy = 0;
                add(new JLabel("Сумма к оплате:"), gbc);

                // Format amount using locale
                NumberFormat format = NumberFormat.getInstance(Locale.getDefault());
                String amountStr = format.format(totalAmount);

                amountField = new JTextField(amountStr);
                amountField.setEditable(false);
                gbc.gridx = 1;
                add(amountField, gbc);

                // Способ оплаты
                gbc.gridx = 0;
                gbc.gridy++;
                add(new JLabel("Способ оплаты:"), gbc);
                paymentMethodComboBox = new JComboBox<>(new String[] {"Наличные", "Кредитная карта", "Дебетовая карта", "Онлайн перевод"});
                gbc.gridx = 1;
                add(paymentMethodComboBox, gbc);

                // Кнопки
                gbc.gridx = 0;
                gbc.gridy++;
                gbc.gridwidth = 2;
                JPanel buttonPanel = new JPanel();
                payButton = new JButton("Оплатить");
                cancelButton = new JButton("Отмена");
                buttonPanel.add(payButton);
                buttonPanel.add(cancelButton);
                add(buttonPanel, gbc);

                // Обработчики
                payButton.addActionListener(this);
                cancelButton.addActionListener(e -> dispose());

                setVisible(true);
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                if (e.getSource() == payButton) {
                    String paymentMethod = (String) paymentMethodComboBox.getSelectedItem();

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

                        // Start transaction
                        conn.setAutoCommit(false);

                        // Parse amount using NumberFormat
                        NumberFormat format = NumberFormat.getInstance(Locale.getDefault());
                        Number number = format.parse(amountField.getText());
                        double amount = number.doubleValue();

                        // Update reservation status
                        String updateReservationQuery = "UPDATE Reservations SET status = 'Заселён', updated_at = NOW() WHERE reservation_id = ?";
                        try (PreparedStatement pstmt = conn.prepareStatement(updateReservationQuery)) {
                            pstmt.setInt(1, reservationId);
                            pstmt.executeUpdate();
                        }

                        // Insert into Payments table
                        String insertPaymentQuery = "INSERT INTO Payments (reservation_id, amount, payment_date, payment_method, created_at) " +
                                "VALUES (?, ?, CURDATE(), ?, NOW())";
                        try (PreparedStatement pstmt = conn.prepareStatement(insertPaymentQuery)) {
                            pstmt.setInt(1, reservationId);
                            pstmt.setDouble(2, amount);
                            pstmt.setString(3, paymentMethod);
                            pstmt.executeUpdate();
                        }

                        // Commit transaction
                        conn.commit();

                        JOptionPane.showMessageDialog(this, "Оплата успешно проведена. Клиент заселён.");
                        parentPanel.loadReservations();
                        dispose();

                    } catch (SQLException | ParseException ex) {
                        ex.printStackTrace();
                        JOptionPane.showMessageDialog(this, "Ошибка при проведении оплаты.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                        try {
                            if (conn != null) {
                                conn.rollback();
                            }
                        } catch (SQLException rollbackEx) {
                            rollbackEx.printStackTrace();
                        }
                    } finally {
                        try {
                            if (conn != null) {
                                conn.setAutoCommit(true);
                            }
                            if (connection != null) {
                                connection.close();
                            }
                        } catch (SQLException ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            }
        }

        // Класс Reservation для хранения деталей бронирования
        class Reservation {
            private int reservationId;
            private int customerId;
            private int roomId;
            private java.sql.Date checkIn;
            private java.sql.Date checkOut;
            private String status;
            private double totalAmount;

            public Reservation(int reservationId, int customerId, int roomId, java.sql.Date checkIn, java.sql.Date checkOut, String status, double totalAmount) {
                this.reservationId = reservationId;
                this.customerId = customerId;
                this.roomId = roomId;
                this.checkIn = checkIn;
                this.checkOut = checkOut;
                this.status = status;
                this.totalAmount = totalAmount;
            }

            // Геттеры
            public int getReservationId() { return reservationId; }
            public int getCustomerId() { return customerId; }
            public int getRoomId() { return roomId; }
            public java.sql.Date getCheckIn() { return checkIn; }
            public java.sql.Date getCheckOut() { return checkOut; }
            public String getStatus() { return status; }
            public double getTotalAmount() { return totalAmount; }
        }

        private void archiveAndDeleteReservation(int reservationId, Connection conn) throws SQLException {
            // Получение данных бронирования
            String selectQuery = "SELECT * FROM Reservations WHERE reservation_id = ?";
            try (PreparedStatement selectStmt = conn.prepareStatement(selectQuery)) {
                selectStmt.setInt(1, reservationId);
                try (ResultSet rs = selectStmt.executeQuery()) {
                    if (rs.next()) {
                        // Вставка в архив
                        String insertArchiveQuery = "INSERT INTO Archived_Reservations (reservation_id, customer_id, room_id, check_in, check_out, status, total_amount, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
                        try (PreparedStatement insertStmt = conn.prepareStatement(insertArchiveQuery)) {
                            insertStmt.setInt(1, rs.getInt("reservation_id"));
                            insertStmt.setInt(2, rs.getInt("customer_id"));
                            insertStmt.setInt(3, rs.getInt("room_id"));
                            insertStmt.setDate(4, rs.getDate("check_in"));
                            insertStmt.setDate(5, rs.getDate("check_out"));
                            insertStmt.setString(6, rs.getString("status"));
                            insertStmt.setBigDecimal(7, rs.getBigDecimal("total_amount"));
                            insertStmt.setTimestamp(8, rs.getTimestamp("created_at"));
                            insertStmt.setTimestamp(9, rs.getTimestamp("updated_at"));
                            insertStmt.executeUpdate();
                        }

                        // Удаление из Reservations
                        String deleteQuery = "DELETE FROM Reservations WHERE reservation_id = ?";
                        try (PreparedStatement deleteStmt = conn.prepareStatement(deleteQuery)) {
                            deleteStmt.setInt(1, reservationId);
                            deleteStmt.executeUpdate();
                        }
                    }
                }
            }
        }
    }

    // Форма добавления/редактирования бронирования
    // (Код ReservationForm остается в основном без изменений, за исключением добавления обработки платежей при бронировании)
    class ReservationForm extends JDialog implements ActionListener {
        private JComboBox<String> customerComboBox, roomComboBox;
        private JDatePickerImpl checkInDatePicker, checkOutDatePicker;
        private JButton saveButton, cancelButton;
        private Integer reservationId; // Null для добавления, не null для редактирования
        private ReservationsPanel parentPanel;
        private int receptionistId;

        public ReservationForm(Integer reservationId, ReservationsPanel parentPanel, int receptionistId) {
            this.reservationId = reservationId;
            this.parentPanel = parentPanel;
            this.receptionistId = receptionistId;

            setTitle(reservationId == null ? "Добавить Бронирование" : "Редактировать Бронирование");
            setSize(500, 400);
            setLocationRelativeTo(null);
            setModal(true);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(10,10,10,10);

            // Клиент
            gbc.gridx = 0;
            gbc.gridy = 0;
            add(new JLabel("Клиент:"), gbc);
            customerComboBox = new JComboBox<>();
            loadCustomers();
            gbc.gridx = 1;
            add(customerComboBox, gbc);

            // Номер
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Номер:"), gbc);
            roomComboBox = new JComboBox<>();
            loadAvailableRooms();
            gbc.gridx = 1;
            add(roomComboBox, gbc);

            // Дата заезда
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Дата заезда:"), gbc);
            checkInDatePicker = createDatePicker();
            gbc.gridx = 1;
            add(checkInDatePicker, gbc);

            // Дата выезда
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Дата выезда:"), gbc);
            checkOutDatePicker = createDatePicker();
            gbc.gridx = 1;
            add(checkOutDatePicker, gbc);

            // Кнопки
            gbc.gridx = 0;
            gbc.gridy++;
            gbc.gridwidth = 2;
            JPanel buttonPanel = new JPanel();
            saveButton = new JButton("Сохранить");
            cancelButton = new JButton("Отмена");
            buttonPanel.add(saveButton);
            buttonPanel.add(cancelButton);
            add(buttonPanel, gbc);

            // Загрузка данных для редактирования
            if (reservationId != null) {
                loadReservationData();
            }

            // Обработчики
            saveButton.addActionListener(this);
            cancelButton.addActionListener(e -> dispose());

            setVisible(true);
        }

        private void loadCustomers() {
            String query = "SELECT customer_id, full_name FROM Customers";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    customerComboBox.addItem(rs.getInt("customer_id") + " - " + rs.getString("full_name"));
                }

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

        private void loadAvailableRooms() {
            // Получение текущей даты
            java.util.Date today = new java.util.Date();
            java.sql.Date todaySqlDate = new java.sql.Date(today.getTime());

            // Загрузка номеров, доступных на ближайшие 7 дней
            String query = "SELECT r.room_id, r.room_number FROM Rooms r " +
                    "WHERE r.status = 'Доступен' AND r.room_id NOT IN (" +
                    "SELECT room_id FROM Reservations WHERE status IN ('Забронирован', 'Заселён') AND (" +
                    "(check_in BETWEEN ? AND DATE_ADD(?, INTERVAL 7 DAY)) OR " +
                    "(check_out BETWEEN ? AND DATE_ADD(?, INTERVAL 7 DAY)) OR " +
                    "(? BETWEEN check_in AND check_out)" +
                    "))";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setDate(1, todaySqlDate);
                pstmt.setDate(2, todaySqlDate);
                pstmt.setDate(3, todaySqlDate);
                pstmt.setDate(4, todaySqlDate);
                pstmt.setDate(5, todaySqlDate);

                try (ResultSet rs = pstmt.executeQuery()) {
                    while (rs.next()) {
                        roomComboBox.addItem(rs.getInt("room_id") + " - " + rs.getInt("room_number"));
                    }
                }

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

        private JDatePickerImpl createDatePicker() {
            UtilDateModel model = new UtilDateModel();
            Properties p = new Properties();
            p.put("text.today", "Сегодня");
            p.put("text.month", "Месяц");
            p.put("text.year", "Год");
            JDatePanelImpl datePanel = new JDatePanelImpl(model, p);
            return new JDatePickerImpl(datePanel, new DateLabelFormatter());
        }

        private void loadReservationData() {
            String query = "SELECT * FROM Reservations WHERE reservation_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, reservationId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        customerComboBox.setSelectedItem(rs.getInt("customer_id") + " - " + getCustomerName(rs.getInt("customer_id")));
                        roomComboBox.setSelectedItem(rs.getInt("room_id") + " - " + getRoomNumber(rs.getInt("room_id")));
                        checkInDatePicker.getModel().setDate(rs.getDate("check_in").getYear() + 1900, rs.getDate("check_in").getMonth(), rs.getDate("check_in").getDate());
                        checkInDatePicker.getModel().setSelected(true);
                        checkOutDatePicker.getModel().setDate(rs.getDate("check_out").getYear() + 1900, rs.getDate("check_out").getMonth(), rs.getDate("check_out").getDate());
                        checkOutDatePicker.getModel().setSelected(true);
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке данных бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        private String getCustomerName(int customerId) {
            String name = "";
            String query = "SELECT full_name FROM Customers WHERE customer_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, customerId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        name = rs.getString("full_name");
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return name;
        }

        private int getRoomNumber(int roomId) {
            int roomNumber = -1;
            String query = "SELECT room_number FROM Rooms WHERE room_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, roomId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        roomNumber = rs.getInt("room_number");
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return roomNumber;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == saveButton) {
                String customerStr = (String)customerComboBox.getSelectedItem();
                String roomStr = (String)roomComboBox.getSelectedItem();
                java.util.Date checkInDate = (java.util.Date)checkInDatePicker.getModel().getValue();
                java.util.Date checkOutDate = (java.util.Date)checkOutDatePicker.getModel().getValue();

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

                int customerId = Integer.parseInt(customerStr.split(" - ")[0]);
                int roomId = Integer.parseInt(roomStr.split(" - ")[0]);

                java.sql.Date checkInSqlDate = new java.sql.Date(checkInDate.getTime());
                java.sql.Date checkOutSqlDate = new java.sql.Date(checkOutDate.getTime());

                // Валидация дат
                if (!validateDates(checkInSqlDate, checkOutSqlDate)) {
                    JOptionPane.showMessageDialog(this, "Дата выезда должна быть позже даты заезда.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                // Ограничение бронирования до 7 дней вперед
                java.util.Calendar cal = java.util.Calendar.getInstance();
                cal.setTime(new java.util.Date());
                cal.add(java.util.Calendar.DAY_OF_MONTH, 7);
                java.sql.Date sevenDaysFromNow = new java.sql.Date(cal.getTimeInMillis());

                if (checkInSqlDate.after(sevenDaysFromNow)) {
                    JOptionPane.showMessageDialog(this, "Бронирование может быть сделано только на ближайшие 7 дней.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                // Проверка доступности номера
                if (!isRoomAvailable(roomId, checkInSqlDate, checkOutSqlDate, reservationId)) {
                    JOptionPane.showMessageDialog(this, "Номер уже забронирован на указанные даты.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                // Вычисление общей суммы
                double totalAmount = calculateTotalAmount(roomId, checkInSqlDate, checkOutSqlDate);

                try (ConnectMySQL connection = new ConnectMySQL()) {
                    Connection conn = connection.getConnection();
                    PreparedStatement pstmt;
                    if (reservationId == null) {
                        // Добавление нового бронирования
                        String query = "INSERT INTO Reservations (customer_id, room_id, check_in, check_out, status, total_amount, created_at, updated_at, receptionist_id) " +
                                "VALUES (?, ?, ?, ?, 'Забронирован', ?, NOW(), NOW(), ?)";
                        pstmt = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
                        pstmt.setInt(1, customerId);
                        pstmt.setInt(2, roomId);
                        pstmt.setDate(3, checkInSqlDate);
                        pstmt.setDate(4, checkOutSqlDate);
                        pstmt.setDouble(5, totalAmount);
                        pstmt.setInt(6, receptionistId);

                        pstmt.executeUpdate();

                        // Получение сгенерированного reservation_id
                        int reservationId = -1;
                        try (ResultSet generatedKeys = pstmt.getGeneratedKeys()) {
                            if (generatedKeys.next()) {
                                reservationId = generatedKeys.getInt(1);
                            }
                        }

                        if (reservationId != -1) {
                            // Вставка записи в Payments
                            String paymentQuery = "INSERT INTO Payments (reservation_id, amount, payment_date, payment_method, created_at) " +
                                    "VALUES (?, ?, CURDATE(), 'Наличные', NOW())";
                            try (PreparedStatement paymentStmt = conn.prepareStatement(paymentQuery)) {
                                paymentStmt.setInt(1, reservationId);
                                paymentStmt.setDouble(2, totalAmount);
                                paymentStmt.executeUpdate();
                            }
                        }

                    } else {
                        // Обновление существующего бронирования
                        String query = "UPDATE Reservations SET customer_id = ?, room_id = ?, check_in = ?, check_out = ?, status = ?," +
                                " total_amount = ?, updated_at = NOW(), receptionist_id = ? WHERE reservation_id = ?";
                        pstmt = conn.prepareStatement(query);
                        pstmt.setInt(1, customerId);
                        pstmt.setInt(2, roomId);
                        pstmt.setDate(3, checkInSqlDate);
                        pstmt.setDate(4, checkOutSqlDate);
                        pstmt.setString(5, "Забронирован");
                        pstmt.setDouble(6, totalAmount);
                        pstmt.setInt(7, receptionistId);
                        pstmt.setInt(8, reservationId);

                        pstmt.executeUpdate();

                        // Обновление платежа (если необходимо)
                        // В данном случае можно добавить логику для обновления платежа, если сумма изменилась
                    }

                    JOptionPane.showMessageDialog(this, "Бронирование успешно сохранено.");
                    parentPanel.loadReservations();
                    dispose();

                } catch (SQLException ex) {
                    ex.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Ошибка при сохранении бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            }
        }

        private boolean validateDates(java.sql.Date checkIn, java.sql.Date checkOut) {
            return checkOut.after(checkIn);
        }

        private boolean isRoomAvailable(int roomId, java.sql.Date checkIn, java.sql.Date checkOut, Integer currentReservationId) {
            String query = "SELECT COUNT(*) AS count FROM Reservations WHERE room_id = ? AND status NOT IN ('Отменён') AND (" +
                    "(check_in <= ? AND check_out > ?) OR " +
                    "(check_in < ? AND check_out >= ?) OR " +
                    "(check_in >= ? AND check_out <= ?))";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, roomId);
                pstmt.setDate(2, checkOut);
                pstmt.setDate(3, checkIn);
                pstmt.setDate(4, checkIn);
                pstmt.setDate(5, checkOut);
                pstmt.setDate(6, checkIn);
                pstmt.setDate(7, checkOut);

                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        int count = rs.getInt("count");
                        if (currentReservationId != null && count > 0) {
                            // Если редактируемое бронирование, и оно совпадает с одним из возвратов, не учитывать его
                            String excludeQuery = "SELECT COUNT(*) AS exclude_count FROM Reservations WHERE reservation_id = ? AND " +
                                    "room_id = ? AND status NOT IN ('Отменён') AND (" +
                                    "(check_in <= ? AND check_out > ?) OR " +
                                    "(check_in < ? AND check_out >= ?) OR " +
                                    "(check_in >= ? AND check_out <= ?))";
                            try (PreparedStatement pstmtExclude = connection.getConnection().prepareStatement(excludeQuery)) {
                                pstmtExclude.setInt(1, currentReservationId);
                                pstmtExclude.setInt(2, roomId);
                                pstmtExclude.setDate(3, checkOut);
                                pstmtExclude.setDate(4, checkIn);
                                pstmtExclude.setDate(5, checkIn);
                                pstmtExclude.setDate(6, checkOut);
                                pstmtExclude.setDate(7, checkIn);
                                pstmtExclude.setDate(8, checkOut);

                                try (ResultSet rsExclude = pstmtExclude.executeQuery()) {
                                    if (rsExclude.next()) {
                                        int excludeCount = rsExclude.getInt("exclude_count");
                                        if (excludeCount > 0) {
                                            count -= excludeCount;
                                        }
                                    }
                                }
                            }
                        }
                        return count == 0;
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }

        private double calculateTotalAmount(int roomId, java.sql.Date checkIn, java.sql.Date checkOut) {
            String query = "SELECT price FROM Rooms WHERE room_id = ?";
            double price = 0.0;
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, roomId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        price = rs.getDouble("price");
                    }
                }

                long diffInMillies = checkOut.getTime() - checkIn.getTime();
                long days = diffInMillies / (1000 * 60 * 60 * 24);
                return price * days;

            } catch (Exception e) {
                e.printStackTrace();
            }
            return price;
        }
    }

    // Форма назначения уборщика
    class AssignCleanerForm extends JDialog implements ActionListener {
        private JComboBox<String> cleanerComboBox;
        private JButton assignButton, cancelButton;
        private int reservationId;
        private ReservationsPanel parentPanel;

        public AssignCleanerForm(int reservationId, ReservationsPanel parentPanel) {
            this.reservationId = reservationId;
            this.parentPanel = parentPanel;

            setTitle("Назначить Уборщика");
            setSize(400, 200);
            setLocationRelativeTo(null);
            setModal(true);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(10,10,10,10);

            // Выбор уборщика
            gbc.gridx = 0;
            gbc.gridy = 0;
            add(new JLabel("Уборщик:"), gbc);
            cleanerComboBox = new JComboBox<>();
            loadAvailableCleaners();
            gbc.gridx = 1;
            add(cleanerComboBox, gbc);

            // Кнопки
            gbc.gridx = 0;
            gbc.gridy++;
            gbc.gridwidth = 2;
            JPanel buttonPanel = new JPanel();
            assignButton = new JButton("Назначить");
            cancelButton = new JButton("Отмена");
            buttonPanel.add(assignButton);
            buttonPanel.add(cancelButton);
            add(buttonPanel, gbc);

            // Обработчики
            assignButton.addActionListener(this);
            cancelButton.addActionListener(e -> dispose());

            setVisible(true);
        }

        private void loadAvailableCleaners() {
            String query = "SELECT cleaner_id, full_name FROM Cleaners WHERE status = 'Свободен'";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    cleanerComboBox.addItem(rs.getInt("cleaner_id") + " - " + rs.getString("full_name"));
                }

                if (cleanerComboBox.getItemCount() == 0) {
                    JOptionPane.showMessageDialog(this, "Нет доступных уборщиков.", "Информация", JOptionPane.INFORMATION_MESSAGE);
                    dispose();
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке уборщиков.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == assignButton) {
                String cleanerStr = (String)cleanerComboBox.getSelectedItem();
                if (cleanerStr == null) {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите уборщика.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                int cleanerId = Integer.parseInt(cleanerStr.split(" - ")[0]);

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

                    // Получение room_id по reservation_id
                    String getRoomIdQuery = "SELECT room_id FROM Reservations WHERE reservation_id = ?";
                    int roomId = -1;
                    try (PreparedStatement pstmtGetRoomId = conn.prepareStatement(getRoomIdQuery)) {
                        pstmtGetRoomId.setInt(1, reservationId);
                        try (ResultSet rs = pstmtGetRoomId.executeQuery()) {
                            if (rs.next()) {
                                roomId = rs.getInt("room_id");
                            } else {
                                // Если бронирование не найдено в Reservations, ищем в Archived_Reservations
                                String getRoomIdArchivedQuery = "SELECT room_id FROM Archived_Reservations WHERE reservation_id = ?";
                                try (PreparedStatement pstmtGetRoomIdArchived = conn.prepareStatement(getRoomIdArchivedQuery)) {
                                    pstmtGetRoomIdArchived.setInt(1, reservationId);
                                    try (ResultSet rsArchived = pstmtGetRoomIdArchived.executeQuery()) {
                                        if (rsArchived.next()) {
                                            roomId = rsArchived.getInt("room_id");
                                        } else {
                                            JOptionPane.showMessageDialog(this, "Не удалось найти бронирование с данным ID.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                                            return;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    // Назначение уборщика в Cleaning_Schedules
                    String insertQuery = "INSERT INTO Cleaning_Schedules (room_id, cleaner_id, scheduled_time, status, created_at, updated_at) " +
                            "VALUES (?, ?, NOW(), 'Назначено', NOW(), NOW())";
                    try (PreparedStatement pstmt = conn.prepareStatement(insertQuery)) {
                        pstmt.setInt(1, roomId);
                        pstmt.setInt(2, cleanerId);
                        pstmt.executeUpdate();
                    }

                    // Обновление статуса уборщика
                    String updateCleanerQuery = "UPDATE Cleaners SET status = 'Занят' WHERE cleaner_id = ?";
                    try (PreparedStatement pstmtUpdate = conn.prepareStatement(updateCleanerQuery)) {
                        pstmtUpdate.setInt(1, cleanerId);
                        pstmtUpdate.executeUpdate();
                    }

                    JOptionPane.showMessageDialog(this, "Уборщик успешно назначен.");
                    parentPanel.loadReservations();
                    dispose();

                } catch (Exception ex) {
                    ex.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Ошибка при назначении уборщика.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    }





    // Панель архивов бронирований с добавлением общей выручки
    class ArchivePanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton refreshButton;
        private JLabel totalRevenueLabel; // Добавили метку для общей выручки

        public ArchivePanel() {
            setLayout(new BorderLayout());

            // Создание модели таблицы
            tableModel = new DefaultTableModel();
            tableModel.setColumnIdentifiers(new String[] {"Архив ID", "Бронирование ID", "Клиент", "Номер", "Дата заезда", "Дата выезда", "Статус", "Сумма"});
            table = new JTable(tableModel);
            loadArchivedReservations();

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Кнопка обновления и панель общей выручки
            JPanel bottomPanel = new JPanel(new BorderLayout());
            refreshButton = new JButton("Обновить");
            bottomPanel.add(refreshButton, BorderLayout.WEST);

            totalRevenueLabel = new JLabel("Общая выручка: 0.00");
            JPanel revenuePanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
            revenuePanel.add(totalRevenueLabel);
            bottomPanel.add(revenuePanel, BorderLayout.EAST);

            add(bottomPanel, BorderLayout.SOUTH);

            refreshButton.addActionListener(e -> {
                loadArchivedReservations();
                calculateTotalRevenue();
            });

            calculateTotalRevenue();
        }

        private void loadArchivedReservations() {
            tableModel.setRowCount(0);
            String query = "SELECT ar.archive_id, ar.reservation_id, c.full_name, r.room_number, ar.check_in, ar.check_out, ar.status, ar.total_amount " +
                    "FROM Archived_Reservations ar " +
                    "JOIN Customers c ON ar.customer_id = c.customer_id " +
                    "JOIN Rooms r ON ar.room_id = r.room_id";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    tableModel.addRow(new Object[] {
                            rs.getInt("archive_id"),
                            rs.getInt("reservation_id"),
                            rs.getString("full_name"),
                            rs.getInt("room_number"),
                            rs.getDate("check_in"),
                            rs.getDate("check_out"),
                            rs.getString("status"),
                            rs.getBigDecimal("total_amount")
                    });
                }

            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке архивных бронирований.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        private void calculateTotalRevenue() {
            String query = "SELECT SUM(total_amount) AS total_revenue FROM Archived_Reservations";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                if (rs.next()) {
                    double totalRevenue = rs.getDouble("total_revenue");
                    totalRevenueLabel.setText("Общая выручка: " + String.format("%.2f", totalRevenue));
                }

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


    // Панель управления уборкой
    class CleaningPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton assignCleanerButton, refreshButton;
        private JButton startCleaningButton, finishCleaningButton;
        private JButton showCleanersButton; // Добавили кнопку "Список уборщиков"

        public CleaningPanel() {
            setLayout(new BorderLayout());

            // Создание модели таблицы с дополнительными столбцами для уборщика и статуса уборки
            tableModel = new DefaultTableModel();
            tableModel.setColumnIdentifiers(new String[] {"ID Номера", "Номер", "Статус", "Уборщик", "Статус уборки"});
            table = new JTable(tableModel);
            loadRoomsNeedingCleaning();

            add(new JScrollPane(table), BorderLayout.CENTER);

            // Кнопки управления
            JPanel buttonPanel = new JPanel();
            assignCleanerButton = new JButton("Назначить уборщика");
            startCleaningButton = new JButton("Начать уборку");
            finishCleaningButton = new JButton("Завершить уборку");
            refreshButton = new JButton("Обновить");
            showCleanersButton = new JButton("Список уборщиков"); // Инициализируем кнопку

            buttonPanel.add(assignCleanerButton);
            buttonPanel.add(startCleaningButton);
            buttonPanel.add(finishCleaningButton);
            buttonPanel.add(refreshButton);
            buttonPanel.add(showCleanersButton); // Добавляем кнопку на панель

            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчики событий
            assignCleanerButton.addActionListener(e -> assignCleanerToRoom());
            startCleaningButton.addActionListener(e -> startCleaning());
            finishCleaningButton.addActionListener(e -> finishCleaning());
            refreshButton.addActionListener(e -> loadRoomsNeedingCleaning());
            showCleanersButton.addActionListener(e -> showCleanersList()); // Обработчик события

        }

        private void loadRoomsNeedingCleaning() {
            tableModel.setRowCount(0);
            String query = "SELECT r.room_id, r.room_number, r.status, ca.cleaner_id, c.full_name as cleaner_name, ca.status as cleaning_status " +
                    "FROM Rooms r " +
                    "LEFT JOIN Cleaning_Actions ca ON r.room_id = ca.room_id AND ca.status != 'Завершено' " +
                    "LEFT JOIN Cleaners c ON ca.cleaner_id = c.cleaner_id " +
                    "WHERE r.status = 'Требует уборки'";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {
                while (rs.next()) {
                    int roomId = rs.getInt("room_id");
                    int roomNumber = rs.getInt("room_number");
                    String roomStatus = rs.getString("status");
                    String cleanerName = rs.getString("cleaner_name");
                    String cleaningStatus = rs.getString("cleaning_status");

                    if (cleaningStatus == null) {
                        cleaningStatus = "Не назначено";
                    }

                    tableModel.addRow(new Object[] {
                            roomId,
                            roomNumber,
                            roomStatus,
                            cleanerName != null ? cleanerName : "Не назначено",
                            cleaningStatus
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Ошибка при загрузке номеров, требующих уборки.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
        }

        private void assignCleanerToRoom() {
            int selectedRow = table.getSelectedRow();
            if (selectedRow >= 0) {
                int roomId = (int) tableModel.getValueAt(selectedRow, 0);
                // Проверяем, есть ли уже назначенный уборщик
                String cleaningStatus = (String) tableModel.getValueAt(selectedRow, 4);
                if (!"Не назначено".equals(cleaningStatus) && !"Завершено".equals(cleaningStatus)) {
                    JOptionPane.showMessageDialog(this, "Уборщик уже назначен на этот номер.", "Информация", JOptionPane.INFORMATION_MESSAGE);
                    return;
                }
                // Открываем форму назначения уборщика
                new AssignCleanerToRoomForm(roomId, this);
            } else {
                JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для назначения уборщика.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            }
        }

        private void startCleaning() {
            int selectedRow = table.getSelectedRow();
            if (selectedRow >= 0) {
                int roomId = (int) tableModel.getValueAt(selectedRow, 0);
                String cleaningStatus = (String) tableModel.getValueAt(selectedRow, 4);
                if ("Назначено".equals(cleaningStatus)) {
                    // Обновляем статус уборки на "В процессе"
                    try (ConnectMySQL connection = new ConnectMySQL()) {
                        Connection conn = connection.getConnection();
                        String updateQuery = "UPDATE Cleaning_Actions SET status = 'В процессе', updated_at = NOW() " +
                                "WHERE room_id = ? AND status = 'Назначено'";
                        try (PreparedStatement pstmt = conn.prepareStatement(updateQuery)) {
                            pstmt.setInt(1, roomId);
                            int rowsUpdated = pstmt.executeUpdate();
                            if (rowsUpdated > 0) {
                                JOptionPane.showMessageDialog(this, "Уборка начата.");
                                loadRoomsNeedingCleaning();
                            } else {
                                JOptionPane.showMessageDialog(this, "Не удалось начать уборку.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                            }
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        JOptionPane.showMessageDialog(this, "Ошибка при обновлении статуса уборки.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    }
                } else {
                    JOptionPane.showMessageDialog(this, "Уборку нельзя начать в текущем статусе.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            } else {
                JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для начала уборки.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            }
        }

        private void finishCleaning() {
            int selectedRow = table.getSelectedRow();
            if (selectedRow >= 0) {
                int roomId = (int) tableModel.getValueAt(selectedRow, 0);
                String cleaningStatus = (String) tableModel.getValueAt(selectedRow, 4);
                if ("В процессе".equals(cleaningStatus)) {
                    // Завершаем уборку, обновляем статусы комнаты и уборщика
                    try (ConnectMySQL connection = new ConnectMySQL()) {
                        Connection conn = connection.getConnection();

                        // Получаем ID уборщика
                        String selectCleanerQuery = "SELECT cleaner_id FROM Cleaning_Actions WHERE room_id = ? AND status = 'В процессе'";
                        int cleanerId = -1;
                        try (PreparedStatement pstmt = conn.prepareStatement(selectCleanerQuery)) {
                            pstmt.setInt(1, roomId);
                            try (ResultSet rs = pstmt.executeQuery()) {
                                if (rs.next()) {
                                    cleanerId = rs.getInt("cleaner_id");
                                } else {
                                    JOptionPane.showMessageDialog(this, "Не удалось найти данные уборки.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                                    return;
                                }
                            }
                        }

                        // Обновляем статус уборки
                        String updateCleaningActionQuery = "UPDATE Cleaning_Actions SET status = 'Завершено', updated_at = NOW() " +
                                "WHERE room_id = ? AND status = 'В процессе'";
                        try (PreparedStatement pstmt = conn.prepareStatement(updateCleaningActionQuery)) {
                            pstmt.setInt(1, roomId);
                            pstmt.executeUpdate();
                        }

                        // Обновляем статус комнаты на "Доступен"
                        String updateRoomStatusQuery = "UPDATE Rooms SET status = 'Доступен', updated_at = NOW() WHERE room_id = ?";
                        try (PreparedStatement pstmt = conn.prepareStatement(updateRoomStatusQuery)) {
                            pstmt.setInt(1, roomId);
                            pstmt.executeUpdate();
                        }

                        // Обновляем статус уборщика на "Свободен"
                        String updateCleanerStatusQuery = "UPDATE Cleaners SET status = 'Свободен', updated_at = NOW() WHERE cleaner_id = ?";
                        try (PreparedStatement pstmt = conn.prepareStatement(updateCleanerStatusQuery)) {
                            pstmt.setInt(1, cleanerId);
                            pstmt.executeUpdate();
                        }

                        JOptionPane.showMessageDialog(this, "Уборка завершена.");
                        loadRoomsNeedingCleaning();

                    } catch (Exception ex) {
                        ex.printStackTrace();
                        JOptionPane.showMessageDialog(this, "Ошибка при завершении уборки.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    }

                } else {
                    JOptionPane.showMessageDialog(this, "Уборку нельзя завершить в текущем статусе.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            } else {
                JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для завершения уборки.", "Информация", JOptionPane.INFORMATION_MESSAGE);
            }
        }

        // Метод для отображения списка уборщиков
        private void showCleanersList() {
            new CleanersListDialog();
        }

        // Внутренний класс для отображения списка уборщиков
        class CleanersListDialog extends JDialog {
            public CleanersListDialog() {
                setTitle("Список уборщиков");
                setSize(600, 400);
                setLocationRelativeTo(null);
                setModal(true);
                setLayout(new BorderLayout());

                // Создание модели таблицы
                DefaultTableModel tableModel = new DefaultTableModel();
                tableModel.setColumnIdentifiers(new String[] {"ID", "Полное имя", "Телефон", "Email", "Статус", "Смена"});

                // Создание таблицы
                JTable cleanersTable = new JTable(tableModel);

                // Загрузка данных уборщиков
                loadCleaners(tableModel);

                // Добавление таблицы в прокрутку
                JScrollPane scrollPane = new JScrollPane(cleanersTable);
                add(scrollPane, BorderLayout.CENTER);

                // Кнопка закрытия
                JButton closeButton = new JButton("Закрыть");
                closeButton.addActionListener(e -> dispose());
                JPanel buttonPanel = new JPanel();
                buttonPanel.add(closeButton);
                add(buttonPanel, BorderLayout.SOUTH);

                setVisible(true);
            }

            private void loadCleaners(DefaultTableModel tableModel) {
                String query = "SELECT c.cleaner_id, c.full_name, c.phone_number, c.email, c.status, s.shift_name " +
                        "FROM Cleaners c LEFT JOIN Shifts s ON c.shift_id = s.shift_id";
                try (ConnectMySQL connection = new ConnectMySQL();
                     PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                     ResultSet rs = pstmt.executeQuery()) {

                    while (rs.next()) {
                        tableModel.addRow(new Object[] {
                                rs.getInt("cleaner_id"),
                                rs.getString("full_name"),
                                rs.getString("phone_number"),
                                rs.getString("email"),
                                rs.getString("status"),
                                rs.getString("shift_name")
                        });
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Ошибка при загрузке уборщиков.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            }
        }

        // Остальной код класса CleaningPanel остается без изменений

        // Класс для назначения уборщика на комнату
        class AssignCleanerToRoomForm extends JDialog implements ActionListener {
            private JComboBox<String> cleanerComboBox;
            private JButton assignButton, cancelButton;
            private int roomId;
            private CleaningPanel parentPanel;

            public AssignCleanerToRoomForm(int roomId, CleaningPanel parentPanel) {
                this.roomId = roomId;
                this.parentPanel = parentPanel;

                setTitle("Назначить Уборщика");
                setSize(400, 200);
                setLocationRelativeTo(null);
                setModal(true);
                setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.insets = new Insets(10,10,10,10);

                // Выбор уборщика
                gbc.gridx = 0;
                gbc.gridy = 0;
                add(new JLabel("Уборщик:"), gbc);
                cleanerComboBox = new JComboBox<>();
                loadAvailableCleaners();
                gbc.gridx = 1;
                add(cleanerComboBox, gbc);

                // Кнопки
                gbc.gridx = 0;
                gbc.gridy++;
                gbc.gridwidth = 2;
                JPanel buttonPanel = new JPanel();
                assignButton = new JButton("Назначить");
                cancelButton = new JButton("Отмена");
                buttonPanel.add(assignButton);
                buttonPanel.add(cancelButton);
                add(buttonPanel, gbc);

                // Обработчики
                assignButton.addActionListener(this);
                cancelButton.addActionListener(e -> dispose());

                setVisible(true);
            }

            private void loadAvailableCleaners() {
                String query = "SELECT c.cleaner_id, c.full_name, s.start_time, s.end_time " +
                        "FROM Cleaners c " +
                        "JOIN Shifts s ON c.shift_id = s.shift_id " +
                        "WHERE c.status = 'Свободен'";

                try (ConnectMySQL connection = new ConnectMySQL();
                     PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                     ResultSet rs = pstmt.executeQuery()) {

                    while (rs.next()) {
                        int cleanerId = rs.getInt("cleaner_id");
                        String fullName = rs.getString("full_name");
                        Time startTime = rs.getTime("start_time");
                        Time endTime = rs.getTime("end_time");

                        if (isCleanerAvailableNow(startTime, endTime)) {
                            cleanerComboBox.addItem(cleanerId + " - " + fullName);
                        }
                    }

                    if (cleanerComboBox.getItemCount() == 0) {
                        JOptionPane.showMessageDialog(this, "Нет доступных уборщиков в текущий момент.", "Информация", JOptionPane.INFORMATION_MESSAGE);
                        dispose();
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Ошибка при загрузке уборщиков.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }
            }

            private boolean isCleanerAvailableNow(Time startTime, Time endTime) {
                LocalTime now = LocalTime.now();
                LocalTime shiftStart = startTime.toLocalTime();
                LocalTime shiftEnd = endTime.toLocalTime();

                if (shiftEnd.isBefore(shiftStart)) {
                    // Смена пересекает полночь
                    if (now.isAfter(shiftStart) || now.isBefore(shiftEnd)) {
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    return !now.isBefore(shiftStart) && !now.isAfter(shiftEnd);
                }
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                if (e.getSource() == assignButton) {
                    String cleanerStr = (String) cleanerComboBox.getSelectedItem();
                    if (cleanerStr == null) {
                        JOptionPane.showMessageDialog(this, "Пожалуйста, выберите уборщика.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                        return;
                    }

                    int cleanerId = Integer.parseInt(cleanerStr.split(" - ")[0]);

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

                        // Назначаем уборщика в Cleaning_Actions
                        String insertQuery = "INSERT INTO Cleaning_Actions (reservation_id, room_id, cleaner_id, scheduled_time, status, created_at, updated_at) " +
                                "VALUES (NULL, ?, ?, NOW(), 'Назначено', NOW(), NOW())";
                        try (PreparedStatement pstmt = conn.prepareStatement(insertQuery)) {
                            pstmt.setInt(1, roomId);
                            pstmt.setInt(2, cleanerId);
                            pstmt.executeUpdate();
                        }

                        // Обновляем статус уборщика на "Занят"
                        String updateCleanerStatus = "UPDATE Cleaners SET status = 'Занят' WHERE cleaner_id = ?";
                        try (PreparedStatement pstmt = conn.prepareStatement(updateCleanerStatus)) {
                            pstmt.setInt(1, cleanerId);
                            pstmt.executeUpdate();
                        }

                        JOptionPane.showMessageDialog(this, "Уборщик успешно назначен.");
                        parentPanel.loadRoomsNeedingCleaning();
                        dispose();

                    } catch (Exception ex) {
                        ex.printStackTrace();
                        JOptionPane.showMessageDialog(this, "Ошибка при назначении уборщика.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    }
                }
            }
        }
    }

    // Класс для форматирования даты в JDatePicker
    class DateLabelFormatter extends JFormattedTextField.AbstractFormatter {

        private String datePattern = "yyyy-MM-dd";
        private SimpleDateFormat dateFormatter = new SimpleDateFormat(datePattern);

        @Override
        public Object stringToValue(String text) throws ParseException {
            if (text == null || text.isEmpty()) {
                return null;
            }
            return dateFormatter.parse(text);
        }

        @Override
        public String valueToString(Object value) throws ParseException {
            if (value != null) {
                if (value instanceof java.util.Date) {
                    java.util.Date date = (java.util.Date) value;
                    return dateFormatter.format(date);
                } else if (value instanceof java.util.Calendar) {
                    java.util.Calendar cal = (java.util.Calendar) value;
                    return dateFormatter.format(cal.getTime());
                }
            }
            return "";
        }
    }
}
























// src/h/Login.java
package h;

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

public class Login extends JFrame implements ActionListener {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        } catch (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/Pr.java
package h;

public class Pr {
    private int id;
    private String fullName;
    private String role;

    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 JTextField nameField, passportField, addressField, phoneField, emailField, loginField;
    private JPasswordField passwordField;
    private JCheckBox showPasswordCheck;
    private JButton signupButton, cancelButton;
    private JButton loginLink;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        add(panel);
        setVisible(true);
    }

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

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

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

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

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

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

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

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

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

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

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

                conn.close();

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

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

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

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

        new SignUp();
    }
}

















вот что вышло когда я попытался назначить уборщика во вкладке номера для уборки номера
D:\21-jdk\bin\java.exe "-javaagent:D:\IntelliJ IDEA Community Edition 2023.2.5\lib\idea_rt.jar=57463:D:\IntelliJ IDEA Community Edition 2023.2.5\bin" -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath D:\Hotels\HotelF\F\out\production\F;D:\Hotels\mysql-connector-j-9.1.0.jar;D:\Hotels\rs2xml.jar;D:\Hotels\jdatepicker-1.3.4.jar h.Login
java.sql.SQLIntegrityConstraintViolationException: Column 'reservation_id' cannot be null
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:109)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:114)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:988)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1166)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1101)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1467)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1084)
	at h.DashboardReceptionist$CleaningPanel$AssignCleanerToRoomForm.actionPerformed(DashboardReceptionist.java:1891)
	at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
	at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2314)
	at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:407)
	at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
	at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
	at java.desktop/java.awt.Component.processMouseEvent(Component.java:6621)
	at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3398)
	at java.desktop/java.awt.Component.processEvent(Component.java:6386)
	at java.desktop/java.awt.Container.processEvent(Container.java:2266)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4996)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4575)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
	at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:98)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117)
	at java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:191)
	at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:236)
	at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:234)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:319)
	at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:234)
	at java.desktop/java.awt.Dialog.show(Dialog.java:1079)
	at java.desktop/java.awt.Component.show(Component.java:1728)
	at java.desktop/java.awt.Component.setVisible(Component.java:1675)
	at java.desktop/java.awt.Window.setVisible(Window.java:1036)
	at java.desktop/java.awt.Dialog.setVisible(Dialog.java:1015)
	at h.DashboardReceptionist$CleaningPanel$AssignCleanerToRoomForm.<init>(DashboardReceptionist.java:1819)
	at h.DashboardReceptionist$CleaningPanel.assignCleanerToRoom(DashboardReceptionist.java:1608)
	at h.DashboardReceptionist$CleaningPanel.lambda$new$0(DashboardReceptionist.java:1554)
	at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
	at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2314)
	at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:407)
	at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
	at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
	at java.desktop/java.awt.Component.processMouseEvent(Component.java:6621)
	at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3398)
	at java.desktop/java.awt.Component.processEvent(Component.java:6386)
	at java.desktop/java.awt.Container.processEvent(Container.java:2266)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4996)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4575)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
	at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:98)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)





возможно дело в том что я удалил таблицу 


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



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






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

напиши полный окончательный финальный код для всего