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

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

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

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

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

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

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

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

-- Таблица 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,
action_type ENUM('Ручное назначение', 'Автоматическое назначение') NOT NULL,
scheduled_time DATETIME NOT NULL,
status ENUM('Запланировано', 'В процессе', 'Завершено') DEFAULT 'Запланировано',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (reservation_id) REFERENCES Reservations(reservation_id) ON DELETE CASCADE,
FOREIGN KEY (room_id) REFERENCES Rooms(room_id) ON DELETE CASCADE,
FOREIGN KEY (cleaner_id) REFERENCES Cleaners(cleaner_id) ON DELETE CASCADE
);

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








DELIMITER $$

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

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

            // Создание модели таблицы
            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();
                    }
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите ресепшиониста для удаления.");
                }
            });
        }

        private void loadReceptionists() {
            tableModel.setRowCount(0);
            String query = "SELECT receptionist_id, full_name, passport_number, address, phone_number, email, login FROM Receptionists";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 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();
                    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;

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

            // Создание модели таблицы
            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();
                    }
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите уборщика для удаления.");
                }
            });
        }

        private void loadCleaners() {
            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";
            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);
            }
        }

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

        public RoomsPanel() {
            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();
            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();
                    }
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для удаления.");
                }
            });
        }

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

        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();
                    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; // Добавили метку для общей выручки

        public ArchivesPanel() {
            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 FlowLayout(FlowLayout.RIGHT));
            totalRevenueLabel = new JLabel("Общая выручка: 0.00");
            bottomPanel.add(totalRevenueLabel);
            add(bottomPanel, BorderLayout.SOUTH);

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












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

при этом не убирай возможности которые есть сейчас 

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