// 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, ClassNotFoundException {
        // Загрузка драйвера MySQL JDBC
        Class.forName("com.mysql.cj.jdbc.Driver");
        this.connection = DriverManager.getConnection(URL, USER, PASSWORD);
    }

    public Connection getConnection() {
        return connection;
    }

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













// 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());
        tabbedPane.addTab("Финансы", new FinancePanel());

        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;

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

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

    // Панель финансов
    class FinancePanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JLabel totalRevenueLabel;

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

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

            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 loadPayments() {
            tableModel.setRowCount(0);
            String query = "SELECT p.payment_id, p.reservation_id, c.full_name, r.room_number, p.payment_date, p.amount, p.payment_method " +
                    "FROM Payments p " +
                    "JOIN Reservations res ON p.reservation_id = res.reservation_id " +
                    "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("payment_id"),
                            rs.getInt("reservation_id"),
                            rs.getString("full_name"),
                            rs.getInt("room_number"),
                            rs.getDate("payment_date"),
                            rs.getBigDecimal("amount"),
                            rs.getString("payment_method")
                    });
                }

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

        private void calculateTotalRevenue() {
            String query = "SELECT SUM(amount) AS total_revenue FROM Payments";
            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/DashboardReceptionist.java
package h;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import org.jdatepicker.impl.*;
import java.util.Properties;
import java.text.ParseException;
import java.text.SimpleDateFormat;

public class DashboardReceptionist extends JFrame {

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

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

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

        tabbedPane.addTab("Клиенты", new CustomersPanel());
        tabbedPane.addTab("Номера", new RoomsViewPanel());
        tabbedPane.addTab("Бронирования", new ReservationsPanel());
        tabbedPane.addTab("Платежи", new PaymentsPanel());

        add(tabbedPane);
        setVisible(true);
    }

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

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

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

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

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

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

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

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

            add(buttonPanel, BorderLayout.SOUTH);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            setVisible(true);
        }

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

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

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

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == saveButton) {
                String fullName = nameField.getText().trim();
                String phone = phoneField.getText().trim();
                String email = emailField.getText().trim();
                String gender = (String)genderComboBox.getSelectedItem();
                String country = countryField.getText().trim();
                String address = addressField.getText().trim();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        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("Назначить уборщика");
            checkInButton = new JButton("Заселить");
            checkOutButton = new JButton("Выселить");
            refreshButton = new JButton("Обновить");
            buttonPanel.add(addButton);
            buttonPanel.add(editButton);
            buttonPanel.add(deleteButton);
            buttonPanel.add(assignCleanerButton);
            buttonPanel.add(checkInButton);
            buttonPanel.add(checkOutButton);
            buttonPanel.add(refreshButton);

            add(buttonPanel, BorderLayout.SOUTH);

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

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

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

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

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

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

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

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

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

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

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

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

                // Обработка платежей (оплата за все дни)
                handlePayment(reservationId);

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

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

        private void handlePayment(int reservationId) {
            // Получение суммы из бронирования
            String query = "SELECT total_amount FROM Reservations WHERE reservation_id = ?";
            double totalAmount = 0.0;
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {

                pstmt.setInt(1, reservationId);
                try (ResultSet rs = pstmt.executeQuery()) {
                    if (rs.next()) {
                        totalAmount = rs.getDouble("total_amount");
                    }
                }

                // Внесение платежа (предполагается, что клиент оплачивает всю сумму при заселении)
                String insertPayment = "INSERT INTO Payments (reservation_id, amount, payment_date, payment_method, created_at) " +
                        "VALUES (?, ?, CURDATE(), 'Наличные', NOW())";
                try (PreparedStatement pstmtInsert = connection.getConnection().prepareStatement(insertPayment)) {
                    pstmtInsert.setInt(1, reservationId);
                    pstmtInsert.setDouble(2, totalAmount);
                    pstmtInsert.executeUpdate();
                }

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

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

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

                // Добавление в Cleaning_Schedules для назначения уборщика
                assignCleanerForCleaning(reservationId);

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

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

        private void assignCleanerForCleaning(int reservationId) {
            // Получение room_id из бронирования
            String roomQuery = "SELECT room_id FROM Reservations WHERE reservation_id = ?";
            int roomId = -1;
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(roomQuery)) {

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

                if (roomId != -1) {
                    // Проверка доступности уборщиков
                    String cleanersQuery = "SELECT cleaner_id, full_name FROM Cleaners WHERE status = 'Свободен'";
                    JComboBox<String> cleanerComboBox = new JComboBox<>();
                    try (PreparedStatement pstmtCleaners = connection.getConnection().prepareStatement(cleanersQuery);
                         ResultSet rsCleaners = pstmtCleaners.executeQuery()) {

                        while (rsCleaners.next()) {
                            cleanerComboBox.addItem(rsCleaners.getInt("cleaner_id") + " - " + rsCleaners.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 insertCleaning = "INSERT INTO Cleaning_Schedules (room_id, cleaner_id, scheduled_time, status, created_at, updated_at) " +
                                    "VALUES (?, ?, NOW(), 'Назначено', NOW(), NOW())";
                            try (PreparedStatement pstmtInsert = connection.getConnection().prepareStatement(insertCleaning)) {
                                pstmtInsert.setInt(1, roomId);
                                pstmtInsert.setInt(2, cleanerId);
                                pstmtInsert.executeUpdate();
                            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            setVisible(true);
        }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            setVisible(true);
        }

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

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

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

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

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

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

                try (ConnectMySQL connection = new ConnectMySQL()) {
                    String insertQuery = "INSERT INTO Cleaning_Schedules (reservation_id, cleaner_id, scheduled_time, status, created_at, updated_at) " +
                            "VALUES (?, ?, NOW(), 'Назначено', NOW(), NOW())";
                    try (PreparedStatement pstmt = connection.getConnection().prepareStatement(insertQuery)) {
                        pstmt.setInt(1, reservationId);
                        pstmt.setInt(2, cleanerId);
                        pstmt.executeUpdate();
                    }

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

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

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

    // Панель управления платежами
    class PaymentsPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton addPaymentButton, refreshButton;

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

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

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

            // Кнопки управления
            JPanel buttonPanel = new JPanel();
            addPaymentButton = new JButton("Добавить Платеж");
            refreshButton = new JButton("Обновить");
            buttonPanel.add(addPaymentButton);
            buttonPanel.add(refreshButton);

            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчики событий
            addPaymentButton.addActionListener(e -> openPaymentForm());
            refreshButton.addActionListener(e -> loadPayments());
        }

        private void loadPayments() {
            tableModel.setRowCount(0);
            String query = "SELECT p.payment_id, p.reservation_id, c.full_name, r.room_number, p.payment_date, p.amount, p.payment_method " +
                    "FROM Payments p " +
                    "JOIN Reservations res ON p.reservation_id = res.reservation_id " +
                    "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("payment_id"),
                            rs.getInt("reservation_id"),
                            rs.getString("full_name"),
                            rs.getInt("room_number"),
                            rs.getDate("payment_date"),
                            rs.getBigDecimal("amount"),
                            rs.getString("payment_method")
                    });
                }

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

        private void openPaymentForm() {
            new PaymentForm(this);
        }
    }

    // Форма добавления платежа
    class PaymentForm extends JDialog implements ActionListener {
        private JComboBox<String> reservationComboBox;
        private JTextField amountField;
        private JComboBox<String> paymentMethodComboBox;
        private JButton saveButton, cancelButton;
        private PaymentsPanel parentPanel;

        public PaymentForm(PaymentsPanel parentPanel) {
            this.parentPanel = parentPanel;

            setTitle("Добавить Платеж");
            setSize(400, 300);
            setLocationRelativeTo(null);
            setModal(true);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(10,10,10,10);

            // Бронирование
            gbc.gridx = 0;
            gbc.gridy = 0;
            add(new JLabel("Бронирование:"), gbc);
            reservationComboBox = new JComboBox<>();
            loadReservations();
            gbc.gridx = 1;
            add(reservationComboBox, gbc);

            // Сумма
            gbc.gridx = 0;
            gbc.gridy++;
            add(new JLabel("Сумма:"), gbc);
            amountField = new JTextField(20);
            gbc.gridx = 1;
            add(amountField, gbc);

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

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

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

            setVisible(true);
        }

        private void loadReservations() {
            String query = "SELECT reservation_id, CONCAT(full_name, ' - Номер ', room_number) AS reservation_info " +
                    "FROM Reservations res " +
                    "JOIN Customers c ON res.customer_id = c.customer_id " +
                    "JOIN Rooms r ON res.room_id = r.room_id " +
                    "WHERE res.status = 'Заселён'";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    reservationComboBox.addItem(rs.getInt("reservation_id") + " - " + rs.getString("reservation_info"));
                }

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

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == saveButton) {
                String reservationStr = (String)reservationComboBox.getSelectedItem();
                String amountStr = amountField.getText().trim();
                String paymentMethod = (String)paymentMethodComboBox.getSelectedItem();

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

                int reservationId = Integer.parseInt(reservationStr.split(" - ")[0]);
                double amount;
                try {
                    amount = Double.parseDouble(amountStr);
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(this, "Сумма должна быть числовым значением.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                try (ConnectMySQL connection = new ConnectMySQL()) {
                    String insertQuery = "INSERT INTO Payments (reservation_id, amount, payment_date, payment_method, created_at) " +
                            "VALUES (?, ?, CURDATE(), ?, NOW())";
                    try (PreparedStatement pstmt = connection.getConnection().prepareStatement(insertQuery)) {
                        pstmt.setInt(1, reservationId);
                        pstmt.setDouble(2, amount);
                        pstmt.setString(3, paymentMethod);
                        pstmt.executeUpdate();
                    }

                    JOptionPane.showMessageDialog(this, "Платеж успешно добавлен.");
                    parentPanel.loadPayments();
                    dispose();

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

    // Панель управления платежами
    // Уже реализована выше как PaymentsPanel

    // Панель назначений уборщиков после выселения
    // Реализуется через AssignCleanerForm

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

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

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

        @Override
        public String valueToString(Object value) throws ParseException {
            if (value != null) {
                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 (ClassNotFoundException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Драйвер базы данных не найден.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        } catch (SQLException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Ошибка подключения к базе данных.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        }
        return null;
    }

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

        new Login();
    }
}

















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











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

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

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

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

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

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

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

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

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

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

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

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

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

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














DELIMITER $$

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 ;












DELIMITER $$

CREATE TRIGGER trg_after_update_reservation_archive_completed_reservations
AFTER UPDATE ON Reservations
FOR EACH ROW
BEGIN
    IF NEW.status = 'Отменён' OR NEW.status = 'Выселен' THEN
        -- Вставка в архив
        INSERT INTO Archived_Reservations (
            reservation_id,
            customer_id,
            room_id,
            check_in,
            check_out,
            status,
            total_amount,
            created_at,
            updated_at
        )
        VALUES (
            NEW.reservation_id,
            NEW.customer_id,
            NEW.room_id,
            NEW.check_in,
            NEW.check_out,
            NEW.status,
            NEW.total_amount,
            NEW.created_at,
            NEW.updated_at
        );
        
        -- Удаление из таблицы Reservations
        DELETE FROM Reservations WHERE reservation_id = NEW.reservation_id;
    END IF;
END
$$

DELIMITER ;




в компоненте Login.java в этом месте:
catch (ClassNotFoundException e) {
выходит ошибка
Exception 'java.lang.ClassNotFoundException' is never thrown in the corresponding try block