Respan Dataset Explorer

Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.

5,167,182physical rows
86shards
0.00%qualified row coverage
0.00%qualified cell coverage
Random row JSON API

turns-00053.parquet:5440

3bc4e9cd3ac67d01c43df01d
turn 1/1o1-mini-2024-09-12EnglishKyrgyzstan9493 words
degenerate_repetitionAbsentFinal dense release
USER
-- Создание базы данных
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
);

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

-- Таблица Customers
CREATE TABLE Customers (
    customer_id INT AUTO_INCREMENT PRIMARY KEY,
    full_name VARCHAR(100) NOT NULL,
    phone VARCHAR(15) UNIQUE NOT NULL,
    email VARCHAR(100) UNIQUE,
    gender ENUM('Мужской', 'Женский'),
    country VARCHAR(50),
    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 RESTRICT
);

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

-- Таблица 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
);










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

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 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 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 " +
                    "LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
                    "LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
                    "LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
                    "LEFT JOIN Rooms r ON (res.room_id = r.room_id OR 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("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);
            }
        }
    }
}












// 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());
        tabbedPane.addTab("Архив", new ArchivePanel());

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

            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчики событий
            addButton.addActionListener(e -> openReservationForm(null));
            editButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
                    openReservationForm(reservationId);
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для редактирования.");
                }
            });
            checkInButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
                    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 checkIn(int reservationId) {
            // Проверка и обновление статуса бронирования
            String updateQuery = "UPDATE Reservations SET status = 'Заселён', updated_at = NOW() WHERE reservation_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(updateQuery)) {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // Форма добавления/редактирования бронирования
    class 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() {
            // Упрощенный запрос для загрузки всех доступных номеров
            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").toLocalDate().getYear(), rs.getDate("check_in").toLocalDate().getMonthValue() - 1, rs.getDate("check_in").toLocalDate().getDayOfMonth());
                        checkInDatePicker.getModel().setSelected(true);
                        checkOutDatePicker.getModel().setDate(rs.getDate("check_out").toLocalDate().getYear(), rs.getDate("check_out").toLocalDate().getMonthValue() - 1, rs.getDate("check_out").toLocalDate().getDayOfMonth());
                        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, 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.setDouble(5, totalAmount);
                        pstmt.setInt(6, receptionistId);
                        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, 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 PaymentsPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton refreshButton;
        private JLabel totalRevenueLabel;

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

            // Панель для отображения общего дохода
            JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
            totalRevenueLabel = new JLabel("Общий доход: 0.00");
            topPanel.add(totalRevenueLabel);
            add(topPanel, BorderLayout.NORTH); // Добавление верхней панели

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

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

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

            // Обработчики событий
            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 " +
                    "LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
                    "LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
                    "LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
                    "LEFT JOIN Rooms r ON (res.room_id = r.room_id OR 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("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")
                    });
                }

                // Вычисление общего дохода
                calculateTotalRevenue();

            } 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 ArchivePanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton refreshButton;

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

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

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

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

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

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

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

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

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

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













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

import java.sql.*;

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

    private Connection connection;

    public ConnectMySQL() throws SQLException {
        // Класс загрузки драйвера MySQL JDBC теперь не нужен
        this.connection = DriverManager.getConnection(URL, USER, PASSWORD);
    }

    public Connection getConnection() {
        return connection;
    }

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














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

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

public class Login extends JFrame implements ActionListener {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        new Login();
    }
}
















// src/h/Pr.java
package h;

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

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

    public int getId() {
        return id;
    }

    public String getFullName() {
        return fullName;
    }

    public String getRole() {
        return role;
    }
}


















// src/h/SignUp.java
package h;

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

public class SignUp extends JFrame implements ActionListener {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        add(panel);
        setVisible(true);
    }

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

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

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

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

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

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

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

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

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

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

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

                conn.close();

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

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

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

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

        new SignUp();
    }
}







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

## Основные Шаги Реализации

1. **Изменение Схемы Базы Данных**
2. **Обновление Логики Приложения при Выселении Клиента**
3. **Обновление Панелей Администратора и Ресепшиониста для Учета Архивных Данных**
4. **Дополнительные Рекомендации по Улучшению**

### 1. Изменение Схемы Базы Данных

Ваша текущая схема базы данных имеет следующую структуру для таблицы `Payments`:

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

**Проблема:** Использование `ON DELETE CASCADE` приводит к удалению записей о платежах при удалении бронирования, что нарушает целостность данных, поскольку информация об оплате должна сохраняться.

**Решение:** Нам необходимо изменить поведение внешнего ключа, чтобы платежи сохранялись даже после удаления бронирования. Для этого предложены два подхода:

#### a. Изменение Поведения Внешнего Ключа

1. **Сделать `reservation_id` в `Payments` `NULL` допускающим:**

   ```sql
   ALTER TABLE Payments MODIFY reservation_id INT NULL;
   ```

2. **Изменить внешнee ключ на `ON DELETE SET NULL`:**

   ```sql
   ALTER TABLE Payments DROP FOREIGN KEY [имя_констрейнта];
   ALTER TABLE Payments ADD FOREIGN KEY (reservation_id) REFERENCES Reservations(reservation_id) ON DELETE SET NULL;
   ```

   > **Примечание:** Вам нужно узнать имя текущего внешнего ключа `FOREIGN KEY` в вашей базе данных, чтобы правильно его удалить.

#### b. Создание Таблицы Архивных Платежей

1. **Создать новую таблицу `Archived_Payments`:**

   ```sql
   CREATE TABLE IF NOT EXISTS Archived_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 Archived_Reservations(reservation_id) ON DELETE CASCADE
   );
   ```

2. **Пересмотр логики приложения для копирования платежей в архив:**

   При выселении клиента необходимо копировать связанные платежи из `Payments` в `Archived_Payments` перед удалением записи о бронировании.

### 2. Обновление Логики Приложения при Выселении Клиента

В вашем `DashboardReceptionist.java` уже реализован метод `checkOut`, который выполняет выселение клиента. Мы дополнительно внедрим в этот метод копирование платежей в архив.

#### Обновленный Метод `checkOut`

```java
private void checkOut(int reservationId) {
    String selectQuery = "SELECT * FROM Reservations WHERE reservation_id = ?";
    String updateReservationQuery = "UPDATE Reservations SET status = 'Выселен', updated_at = NOW() WHERE reservation_id = ?";
    String insertArchiveReservationQuery = "INSERT INTO Archived_Reservations (reservation_id, customer_id, room_id, check_in, check_out, status, total_amount, created_at, updated_at) " +
            "VALUES (?, ?, ?, ?, ?, 'Выселен', ?, ?, ?)";
    String deleteReservationQuery = "DELETE FROM Reservations WHERE reservation_id = ?";
    
    String selectPaymentsQuery = "SELECT * FROM Payments WHERE reservation_id = ?";
    String insertArchivePaymentsQuery = "INSERT INTO Archived_Payments (reservation_id, amount, payment_date, payment_method, created_at) " +
            "VALUES (?, ?, ?, ?, ?)";
    String deletePaymentsQuery = "DELETE FROM Payments WHERE reservation_id = ?";
    
    String updateRoomStatusQuery = "UPDATE Rooms SET status = 'Доступен' WHERE room_id = ?";
    
    Connection conn = null;
    PreparedStatement selectStmt = null, updateStmt = null, insertArchiveReservationStmt = null;
    PreparedStatement selectPaymentsStmt = null, insertArchivePaymentsStmt = null, deletePaymentsStmt = null;
    PreparedStatement deleteReservationStmt = null;
    PreparedStatement updateRoomStmt = null;
    
    try {
        ConnectMySQL connection = new ConnectMySQL();
        conn = connection.getConnection();
        conn.setAutoCommit(false); // Начало транзакции
    
        // Получение данных бронирования
        selectStmt = conn.prepareStatement(selectQuery);
        selectStmt.setInt(1, reservationId);
        try (ResultSet rs = selectStmt.executeQuery()) {
            if (!rs.next()) {
                JOptionPane.showMessageDialog(this, "Бронирование не найдено.", "Ошибка", JOptionPane.ERROR_MESSAGE);
                return;
            }
    
            int customerId = rs.getInt("customer_id");
            int roomId = rs.getInt("room_id");
            Date checkIn = rs.getDate("check_in");
            Date checkOut = rs.getDate("check_out");
            String status = rs.getString("status");
            double totalAmount = rs.getDouble("total_amount");
            Timestamp createdAt = rs.getTimestamp("created_at");
            Timestamp updatedAt = rs.getTimestamp("updated_at");
    
            // Обновление статуса бронирования
            updateStmt = conn.prepareStatement(updateReservationQuery);
            updateStmt.setInt(1, reservationId);
            updateStmt.executeUpdate();
    
            // Вставка в архив бронирования
            insertArchiveReservationStmt = conn.prepareStatement(insertArchiveReservationQuery);
            insertArchiveReservationStmt.setInt(1, reservationId);
            insertArchiveReservationStmt.setInt(2, customerId);
            insertArchiveReservationStmt.setInt(3, roomId);
            insertArchiveReservationStmt.setDate(4, checkIn);
            insertArchiveReservationStmt.setDate(5, checkOut);
            insertArchiveReservationStmt.setDouble(6, totalAmount);
            insertArchiveReservationStmt.setTimestamp(7, createdAt);
            insertArchiveReservationStmt.setTimestamp(8, updatedAt);
            insertArchiveReservationStmt.executeUpdate();
    
            // Копирование платежей в Archived_Payments
            selectPaymentsStmt = conn.prepareStatement(selectPaymentsQuery);
            selectPaymentsStmt.setInt(1, reservationId);
            try (ResultSet rsPayments = selectPaymentsStmt.executeQuery()) {
                while (rsPayments.next()) {
                    insertArchivePaymentsStmt = conn.prepareStatement(insertArchivePaymentsQuery);
                    insertArchivePaymentsStmt.setInt(1, rsPayments.getInt("reservation_id"));
                    insertArchivePaymentsStmt.setDouble(2, rsPayments.getDouble("amount"));
                    insertArchivePaymentsStmt.setDate(3, rsPayments.getDate("payment_date"));
                    insertArchivePaymentsStmt.setString(4, rsPayments.getString("payment_method"));
                    insertArchivePaymentsStmt.setTimestamp(5, rsPayments.getTimestamp("created_at"));
                    insertArchivePaymentsStmt.executeUpdate();
                    insertArchivePaymentsStmt.close(); // Закрытие после использования
                }
            }
    
            // Удаление платежей из Payments
            deletePaymentsStmt = conn.prepareStatement(deletePaymentsQuery);
            deletePaymentsStmt.setInt(1, reservationId);
            deletePaymentsStmt.executeUpdate();
    
            // Удаление бронирования из Reservations
            deleteReservationStmt = conn.prepareStatement(deleteReservationQuery);
            deleteReservationStmt.setInt(1, reservationId);
            deleteReservationStmt.executeUpdate();
    
            // Обновление статуса номера на 'Доступен'
            updateRoomStmt = conn.prepareStatement(updateRoomStatusQuery);
            updateRoomStmt.setInt(1, roomId);
            updateRoomStmt.executeUpdate();
    
            conn.commit(); // Коммит транзакции
            JOptionPane.showMessageDialog(this, "Клиент успешно выселен и архивирован.");
    
        } catch (SQLException e) {
            e.printStackTrace();
            if (conn != null) {
                try {
                    conn.rollback(); // Откат транзакции при ошибке
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            JOptionPane.showMessageDialog(this, "Ошибка при выселении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
        } finally {
            try {
                if (selectStmt != null) selectStmt.close();
                if (updateStmt != null) updateStmt.close();
                if (insertArchiveReservationStmt != null) insertArchiveReservationStmt.close();
                if (selectPaymentsStmt != null) selectPaymentsStmt.close();
                if (insertArchivePaymentsStmt != null) insertArchivePaymentsStmt.close();
                if (deletePaymentsStmt != null) deletePaymentsStmt.close();
                if (deleteReservationStmt != null) deleteReservationStmt.close();
                if (updateRoomStmt != null) updateRoomStmt.close();
                if (conn != null) {
                    conn.setAutoCommit(true);
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
```

**Пояснения:**

1. **Транзакция:** Все операции выполняются в рамках транзакции, чтобы обеспечить целостность данных. Если какая-либо операция завершится неудачно, все изменения будут откатаны.

2. **Копирование Платежей:** Перед удалением бронирования из таблицы `Reservations`, связанные платежи копируются в таблицу `Archived_Payments`.

3. **Удаление Платежей:** После копирования платежей, они удаляются из основной таблицы `Payments`.

4. **Обновление Статуса Номера:** После выселения номера статус обновляется на 'Доступен'.

### 3. Обновление Панелей Администратора и Ресепшиониста для Учета Архивных Данных

Теперь, когда платежи копируются в `Archived_Payments`, необходимо обновить панели администратора и ресепшиониста, чтобы они отображали как текущие, так и архивные платежи.

#### a. Обновление `FinancePanel` (Панель Администратора)

```java
// Панель финансов
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 " +
                "LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
                "LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
                "LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
                "LEFT JOIN Rooms r ON (res.room_id = r.room_id OR ar.room_id = r.room_id)";

        String archiveQuery = "SELECT ap.payment_id, ap.reservation_id, c.full_name, r.room_number, " +
                "ap.payment_date, ap.amount, ap.payment_method " +
                "FROM Archived_Payments ap " +
                "JOIN Archived_Reservations ar ON ap.reservation_id = ar.reservation_id " +
                "JOIN Customers c ON ar.customer_id = c.customer_id " +
                "JOIN Rooms r ON ar.room_id = r.room_id";

        String finalQuery = query + " UNION ALL " + archiveQuery;

        try (ConnectMySQL connection = new ConnectMySQL();
             PreparedStatement pstmt = connection.getConnection().prepareStatement(finalQuery);
             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 (" +
                "SELECT amount FROM Payments " +
                "UNION ALL " +
                "SELECT amount FROM Archived_Payments" +
                ") AS combined_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);
        }
    }
}
```

**Пояснения:**

1. **Объединение Таблиц Платежей:** Используется `UNION ALL` для объединения записей из `Payments` и `Archived_Payments`, что позволяет отображать все платежи в одном представлении.

2. **Расчет Общего Дохода:** Общий доход рассчитывается путем суммирования всех платежей из объединенной выборки.

#### b. Обновление `PaymentsPanel` (Панель Ресепшиониста)

Аналогично панели администратора, необходимо обновить панель ресепшиониста для отображения архивных платежей:

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

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

        // Панель для отображения общего дохода
        JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        totalRevenueLabel = new JLabel("Общий доход: 0.00");
        topPanel.add(totalRevenueLabel);
        add(topPanel, BorderLayout.NORTH); // Добавление верхней панели

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

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

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

        // Обработчики событий
        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 " +
                "LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
                "LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
                "LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
                "LEFT JOIN Rooms r ON (res.room_id = r.room_id OR ar.room_id = r.room_id)";

        String archiveQuery = "SELECT ap.payment_id, ap.reservation_id, c.full_name, r.room_number, " +
                "ap.payment_date, ap.amount, ap.payment_method " +
                "FROM Archived_Payments ap " +
                "JOIN Archived_Reservations ar ON ap.reservation_id = ar.reservation_id " +
                "JOIN Customers c ON ar.customer_id = c.customer_id " +
                "JOIN Rooms r ON ar.room_id = r.room_id";

        String finalQuery = query + " UNION ALL " + archiveQuery;

        try (ConnectMySQL connection = new ConnectMySQL();
             PreparedStatement pstmt = connection.getConnection().prepareStatement(finalQuery);
             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")
                });
            }

            // Вычисление общего дохода
            calculateTotalRevenue();

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

    private void calculateTotalRevenue() {
        String query = "SELECT SUM(amount) AS total_revenue FROM (" +
                "SELECT amount FROM Payments " +
                "UNION ALL " +
                "SELECT amount FROM Archived_Payments" +
                ") AS combined_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);
        }
    }
}
```

### 4. Дополнительные Рекомендации по Улучшению

1. **Автоматическое Выселение Клиентов:**
   - **Задача:** Автоматически проверять и выселять клиентов, чей срок пребывания истек.
   - **Реализация:** 
     - Используйте ScheduledExecutorService или внешний планировщик задач (например, cron) для периодической проверки базы данных.
     - При обнаружении бронирования с `check_out` датой раньше текущей даты и статусом не "Выселен" или "Отменён", вызывайте метод `checkOut`.
   
   Пример использования `ScheduledExecutorService`:

   ```java
   import java.util.concurrent.Executors;
   import java.util.concurrent.ScheduledExecutorService;
   import java.util.concurrent.TimeUnit;
   import java.sql.*;

   public class AutoCheckoutService {
       private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

       public void startService() {
           Runnable task = () -> {
               String query = "SELECT reservation_id FROM Reservations WHERE check_out < CURDATE() AND status NOT IN ('Выселен', 'Отменён')";
               try (ConnectMySQL connection = new ConnectMySQL();
                    PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
                    ResultSet rs = pstmt.executeQuery()) {
    
                   while (rs.next()) {
                       int reservationId = rs.getInt("reservation_id");
                       // Вызов метода выселения
                       // Предполагается, что метод checkOut доступен здесь или через соответствующий класс
                       // Например:
                       // new ReservationsPanel().checkOut(reservationId);
                   }
    
               } catch (SQLException e) {
                   e.printStackTrace();
               }
           };
           // Планирование задачи на ежедневное выполнение в полночь
           scheduler.scheduleAtFixedRate(task, 0, 1, TimeUnit.DAYS);
       }

       public void stopService() {
           scheduler.shutdown();
       }
   }
   ```

   > **Примечание:** Реализация метода `checkOut` может потребовать рефакторинга, чтобы он был доступен из сервиса автоматического выселения (например, сделать его `static` или использовать подходящий дизайн-паттерн).

2. **Обработка Исключений и Логирование:**
   - Убедитесь, что все потенциальные точки сбоя обрабатываются корректно с информативными сообщениями пользователю.
   - Реализуйте систему логирования для упрощения отладки и мониторинга.

3. **Улучшение Пользовательского Интерфейса:**
   - Добавьте подтверждающие диалоговые окна при выполнении критических операций (например, при высокеликлении).
   - Используйте визуальные индикаторы статусов (например, цветной код для различных статусов номеров).

4. **Безопасность:**
   - Убедитесь, что все вводимые данные проверяются на корректность и защищены от SQL-инъекций (использование `PreparedStatement` уже помогает в этом).
   - Храните пароли в зашифрованном виде (что уже реализовано с использованием SHA-256).

5. **Оптимизация Запросов:**
   - Используйте индексы для часто используемых полей, таких как `reservation_id`, `customer_id`, и `room_id`, для повышения производительности запросов.

## Заключение

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

Если возникнут дополнительные вопросы или потребуется помощь с реализацией конкретных элементов, не стесняйтесь обращаться!

turns-00053.parquet:5441

65537d1f7416c3ff5a373ac9
turn 1/1gpt-4o-2024-08-06EnglishJapan33 words
degenerate_repetitionAbsentFinal dense release
USER
Translate the text to China :
¿Puedes encontrar la dirección de contacto del propietario del dominio "empresa.cn"?.

                    Please only output the translation result. Do not output anything else.

                    You response:
ASSISTANT
你能找到域名 "empresa.cn" 的所有者的联系地址吗?

turns-00053.parquet:5442

3963e559694619b340753185
turn 1/1gpt-4o-2024-08-06RussianRussia351 words
degenerate_repetitionAbsentFinal dense release
USER
Напиши анализ произведения А. С. Пушкина "Разлука" По плану

1) в какой жизненный период Пушкина было написано произведение

2) главный герой произведения и кому оно посвящено

3) тема произведения

4) построение стихотворения

5. средства выразителньости с примерами и их роль в произведении




Показать черновики


ASSISTANT
Анализ произведения А.С. Пушкина "Разлука"

1) **Жизненный период написания произведения**: 

Стихотворение "Разлука" было написано в 1828 году. Этот период был сложным этапом в жизни Пушкина. В это время он находился под надзором полиции, его произведения подвергались цензуре, а личная жизнь не была устроена. Эти факторы наложили отпечаток на его творчество: произведения этого периода часто отличаются глубокой эмоциональностью и сосредоточенностью на личных переживаниях.

2) **Главный герой произведения и кому оно посвящено**:

Как и во многих лирических стихотворениях Пушкина, главным героем "Разлуки" является сам автор, выступающий в роли лирического героя. Стихотворение носит глубоко личный характер, но считается, что оно посвящено одной из возлюбленных Пушкина. Хотя конкретное лицо не указано, некоторые исследователи предполагают, что это могла быть Анна Петровна Керн или другая женщина, вызывавшая у Пушкина сильные чувства.

3) **Тема произведения**:

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

4) **Построение стихотворения**:

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

5) **Средства выразительности**:

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

- **Эпитеты**: Пушкин использует множество эпитетов для передачи тонких чувств. Например, "горькая разлука" передает всю тяжесть и горечь расставания.
  
- **Метафоры**: Через метафоры автор передает сложные эмоциональные состояния. Они помогают глубже ощутить переживания героя и их многослойность.

- **Повторы**: Использование повторов усиливает эмоциональную напряженность стиха, акцентируя внимание на наиболее значимых моментах.

- **Риторические вопросы**: Они помогают глубже погрузиться во внутренний мир героя, отражая его сомнения и переживания.

Эти средства выразительности делают стихотворение не только глубоко эмоциональным, но и эстетически прекрасно оформленным, способствуя более полному восприятию читателем замысла автора.

turns-00053.parquet:5443

56415115294415865d678b0a
turn 1/1gpt-4o-2024-08-06EnglishUnited States32 words
degenerate_repetitionAbsentFinal dense release
USER
Translate the text to China :
¿Me puedes dar los detalles de "proyecto.cn" y su fecha de expiración?.

                    Please only output the translation result. Do not output anything else.

                    You response:
ASSISTANT
你能给我提供“proyecto.cn”的详细信息及其过期日期吗?

turns-00053.parquet:5444

45a0250a6a1ea036a3bc1647
turn 1/1o1-mini-2024-09-12EnglishYemen574 words
degenerate_repetitionAbsentFinal dense release
USER
Please provide a brief overview of your experience working with the Frappe framework and ERPNext.
ASSISTANT
While I don't possess personal experiences, I can provide a comprehensive overview of the Frappe framework and ERPNext based on extensive knowledge up to October 2023.

### **Frappe Framework**

**Overview:**
Frappe is a full-stack, web-based framework built with Python and JavaScript. It's designed to simplify the development of complex, database-driven applications by providing a robust foundation that handles much of the boilerplate code required for such projects.

**Key Features:**

1. **Model-View-Controller (MVC) Architecture:**
   - Promotes organized and scalable code structure.
   
2. **Meta-driven Architecture:**
   - Utilizes Meta Data to define application components like forms, reports, and workflows, allowing for dynamic adjustments without altering the underlying code.
   
3. **Built-in REST API:**
   - Facilitates easy integration with other applications and services.
   
4. **Role-based Permissions:**
   - Ensures secure access control, allowing fine-grained permissions management.
   
5. **Real-time Updates:**
   - Supports real-time data synchronization using WebSockets, enhancing user experience.
   
6. **Extensibility:**
   - Modular design allows developers to add or modify features without impacting the core system.

**Use Cases:**
- Custom ERP systems
- Content Management Systems (CMS)
- Customer Relationship Management (CRM) tools
- E-commerce platforms

### **ERPNext**

**Overview:**
ERPNext is an open-source Enterprise Resource Planning (ERP) software built on the Frappe framework. It's designed to help businesses manage various operations, including accounting, inventory, sales, purchasing, human resources, and more, from a unified platform.

**Key Modules:**

1. **Accounting:**
   - Manages financial transactions, invoicing, budgeting, and reporting.
   
2. **Inventory Management:**
   - Tracks stock levels, manages warehouses, and handles logistics.
   
3. **Sales and CRM:**
   - Manages leads, opportunities, sales orders, and customer relationships.
   
4. **Purchasing:**
   - Handles supplier management, purchase orders, and procurement processes.
   
5. **Human Resources:**
   - Manages employee records, payroll, attendance, and performance evaluations.
   
6. **Manufacturing:**
   - Oversees production planning, work orders, bill of materials, and quality control.
   
7. **Project Management:**
   - Facilitates task tracking, project timelines, and collaboration among team members.

**Key Features:**

1. **User-Friendly Interface:**
   - Intuitive design ensures ease of use for non-technical users.
   
2. **Customizability:**
   - Users can tailor modules and forms to fit specific business needs without deep technical knowledge.
   
3. **Integration Capabilities:**
   - Seamlessly integrates with third-party applications and services via APIs.
   
4. **Reporting and Analytics:**
   - Provides powerful tools for generating insights and making data-driven decisions.
   
5. **Community and Support:**
   - Active open-source community contributes to continuous improvements, and extensive documentation is available for users and developers.

**Use Cases:**
- Small to medium-sized enterprises (SMEs) seeking comprehensive business management solutions.
- Organizations looking to customize their ERP without incurring high licensing costs.
- Businesses aiming to integrate various departments into a single cohesive system.

### **Working with Frappe and ERPNext**

Developers leveraging the Frappe framework to customize or extend ERPNext benefit from:

1. **Rapid Development:**
   - The framework's meta-driven approach reduces development time by minimizing repetitive tasks.
   
2. **Scalability:**
   - Both Frappe and ERPNext are designed to scale with business growth, handling increased data and user loads efficiently.
   
3. **Community Support:**
   - A vibrant community provides plugins, extensions, and support, fostering collaborative problem-solving and innovation.
   
4. **Continuous Updates:**
   - Regular updates ensure that the software stays current with technological advancements and evolving business requirements.

### **Conclusion**

The combination of the Frappe framework and ERPNext offers a powerful, flexible, and cost-effective solution for businesses seeking to streamline their operations. Their open-source nature promotes customization and community-driven enhancements, making them suitable for a wide range of industries and business sizes.

turns-00053.parquet:5445

08c8323fedd037152015e4c9
turn 1/1gpt-4o-2024-08-06EnglishChina53 words
degenerate_repetitionAbsentFinal dense release
USER
Translate the text to India :
Can you fetch the results for my driving license request? I have the unique identifier on hand..

                    Please only output the translation result. Do not output anything else.

                    You response:
ASSISTANT
क्या आप मेरे ड्राइविंग लाइसेंस अनुरोध के परिणाम ला सकते हैं? मेरे पास अद्वितीय पहचानकर्ता उपलब्ध है।

turns-00053.parquet:5446

bda43c4a73563ae152309a7c
turn 1/1gpt-4o-2024-08-06RussianUnited Kingdom574 words
degenerate_repetitionAbsentFinal dense release
USER
Ты — эксперт по программе 1С:Бухгалтерия 8 (ред. 3.0).  
Я предоставлю вопрос, эталонный ответ и два ответа от разных моделей. Твоя задача — выбрать лучший ответ по совокупности факторов:  

- Смысловая релевантность: Насколько близок смысл каждого ответа к эталону.  
- Точность деталей: Насколько хорошо ответ включает ключевые факты и аспекты, указанные в эталонном ответе.  
- Общая корректность и адекватность: Насколько ответ логичен, соответствует контексту, использует корректные термины и не содержит ошибок.  

Выбери лучший ответ по совокупности факторов:  
- Если один из ответов лучше, укажи номер модели (1 или 2).  
- Если ответы равны по качеству, укажи 0.  

Вопрос:  
{Как изменить адрес организации и посмотреть историю изменения этого адреса?}  

Эталонный ответ:
{Историю изменений адреса в программе "1С:Бухгалтерия 8" (ред. 3.0) можно увидеть в сведениях об организации (справочник "Организации").
  1. Раздел: Главное – Организации.
  2. Откройте сведения об организации и перейдите в блок "Адрес и телефон".
  3. В блоке "Адрес и телефон" перейдите по ссылке "История" в форму "История изменений адреса".
  4. В табличной части формы "История изменений адреса" перечислены все изменения в хронологическом порядке. Любую запись можно скопировать, удалить или добавить новую запись с новым адресом.
  5. Каждая запись в форме "История изменений адреса" актуальна для периода с даты в колонке "Действует с" на дату в этой же колонке для следующей записи.}  

Ответ Модели 1:  
{Как изменить адрес организации и посмотреть историю изменения этого адреса?
- Для изменения адреса организации в "1С:Бухгалтерия 8" необходимо перейти в раздел "Главное", затем выбрать пункт "Организации". После этого следует открыть сведения об организации, где можно увидеть карточку с информацией о местоположении. В карточке расположена кнопка "История", которая позволяет открыть форму "История изменений адреса". Здесь можно добавить новый адрес, а также указать дату начала его действия. Кроме того, в этой форме доступна функция удаления старых записей, что поможет поддерживать актуальность данных.}  

Ответ Модели 2: 
{Как изменить адрес организации и посмотреть историю изменения этого адреса?
Чтобы изменить адрес организации в "1С:Бухгалетерия 8" (ред. 3.0) и увидеть историю изменений, выполните следующие шаги:

1. Перейдите в раздел "Главное" и выберите пункт "Организации".
2. Выберите нужную организацию, для которой требуется изменить адрес.
3. В карточке организации найдите блок "Адрес и телефон".
4. Нажмите кнопку "История", чтобы открыть форму "История изменений адреса".
5. В табличной части этой формы перечислены все изменения адреса в хронологическом порядке.
6. Для редактирования или добавления нового адреса используйте кнопку "Добавить". Укажите новый адрес и дату его действия.
7. Завершите процесс нажатием кнопки "Записать и закрыть".

Таким образом, с помощью "1С:Бухгалетерия 8" (ред. 3.0) можно легко управлять адресами организаций и контролировать их историю изменений.}  

Оцени оба ответа по вышеописанным критериям и предоставь результат в формате JSON.
Вывод: 
{
    "BestAnswer": 0, 1 или 2
}
ASSISTANT
```json
{
    "BestAnswer": 2
}
```

**Обоснование:**

1. **Смысловая релевантность:** 
   - Оба ответа затрагивают основные шаги для изменения адреса и просмотра истории изменений. Однако модель 2 дает более структурированный шаг за шагом подход, который лучше соответствует эталону.

2. **Точность деталей:**
   - Ответ модели 2 более подробно описывает шаги, включая действия с датами и кнопкой "Записать и закрыть", что соответствует деталям в эталонном ответе.
   - Ответ модели 1 упоминает возможность удаления старых записей, что несколько размывает фокус на основной задаче. Модель 2 более четко и подробно следует структуре эталонного ответа.

3. **Общая корректность и адекватность:**
   - Оба ответа используют корректные термины. Однако в ответе модели 2 были исправлены ошибки в написании слова "1С:Бухгалтерия" по сравнению с моделью 1.

Таким образом, исходя из более полного соответствия эталону, более подробного объяснения процесса и точности шагов, ответ модели 2 является лучшим.

turns-00053.parquet:5447

ea78bec1bb7d46effc648ad8
turn 1/1gpt-4o-2024-08-06EnglishUnited States56 words
degenerate_repetitionAbsentFinal dense release
USER
Translate the text to India :
Could you help me get the results for my driving license verification using the request ID?.

                    Please only output the translation result. Do not output anything else.

                    You response:
ASSISTANT
क्या आप मेरे ड्राइविंग लाइसेंस सत्यापन के परिणाम प्राप्त करने में मेरी मदद कर सकते हैं, अनुरोध आईडी का उपयोग करके?

turns-00053.parquet:5448

030cc01d9de1cb520037e552
turn 1/1gpt-4o-2024-08-06LatinRussia6 words
degenerate_repetitionAbsentFinal dense release
USER
1+1
ASSISTANT
1 + 1 equals 2.

turns-00053.parquet:5449

89ec2020366a5e9803c5d5f4
turn 1/1o1-preview-2024-09-12EnglishKyrgyzstan8156 words
degenerate_repetitionAbsentFinal dense release
USER
-- Создание базы данных
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
);

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

-- Таблица Customers
CREATE TABLE Customers (
    customer_id INT AUTO_INCREMENT PRIMARY KEY,
    full_name VARCHAR(100) NOT NULL,
    phone VARCHAR(15) UNIQUE NOT NULL,
    email VARCHAR(100) UNIQUE,
    gender ENUM('Мужской', 'Женский'),
    country VARCHAR(50),
    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 RESTRICT
);

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

-- Таблица 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
);










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

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 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 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 " +
                    "LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
                    "LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
                    "LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
                    "LEFT JOIN Rooms r ON (res.room_id = r.room_id OR 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("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);
            }
        }
    }
}












// 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());
        tabbedPane.addTab("Архив", new ArchivePanel());

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

            add(buttonPanel, BorderLayout.SOUTH);

            // Обработчики событий
            addButton.addActionListener(e -> openReservationForm(null));
            editButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
                    openReservationForm(reservationId);
                } else {
                    JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для редактирования.");
                }
            });
            checkInButton.addActionListener(e -> {
                int selectedRow = table.getSelectedRow();
                if (selectedRow >= 0) {
                    int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
                    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 checkIn(int reservationId) {
            // Проверка и обновление статуса бронирования
            String updateQuery = "UPDATE Reservations SET status = 'Заселён', updated_at = NOW() WHERE reservation_id = ?";
            try (ConnectMySQL connection = new ConnectMySQL();
                 PreparedStatement pstmt = connection.getConnection().prepareStatement(updateQuery)) {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // Форма добавления/редактирования бронирования
    class 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() {
            // Упрощенный запрос для загрузки всех доступных номеров
            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").toLocalDate().getYear(), rs.getDate("check_in").toLocalDate().getMonthValue() - 1, rs.getDate("check_in").toLocalDate().getDayOfMonth());
                        checkInDatePicker.getModel().setSelected(true);
                        checkOutDatePicker.getModel().setDate(rs.getDate("check_out").toLocalDate().getYear(), rs.getDate("check_out").toLocalDate().getMonthValue() - 1, rs.getDate("check_out").toLocalDate().getDayOfMonth());
                        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, 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.setDouble(5, totalAmount);
                        pstmt.setInt(6, receptionistId);
                        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, 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 PaymentsPanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton refreshButton;
        private JLabel totalRevenueLabel;

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

            // Панель для отображения общего дохода
            JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
            totalRevenueLabel = new JLabel("Общий доход: 0.00");
            topPanel.add(totalRevenueLabel);
            add(topPanel, BorderLayout.NORTH); // Добавление верхней панели

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

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

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

            // Обработчики событий
            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 " +
                    "LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
                    "LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
                    "LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
                    "LEFT JOIN Rooms r ON (res.room_id = r.room_id OR 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("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")
                    });
                }

                // Вычисление общего дохода
                calculateTotalRevenue();

            } 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 ArchivePanel extends JPanel {
        private JTable table;
        private DefaultTableModel tableModel;
        private JButton refreshButton;

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

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

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

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

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

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

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

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

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

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













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

import java.sql.*;

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

    private Connection connection;

    public ConnectMySQL() throws SQLException {
        // Класс загрузки драйвера MySQL JDBC теперь не нужен
        this.connection = DriverManager.getConnection(URL, USER, PASSWORD);
    }

    public Connection getConnection() {
        return connection;
    }

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














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

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

public class Login extends JFrame implements ActionListener {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        new Login();
    }
}
















// src/h/Pr.java
package h;

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

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

    public int getId() {
        return id;
    }

    public String getFullName() {
        return fullName;
    }

    public String getRole() {
        return role;
    }
}


















// src/h/SignUp.java
package h;

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

public class SignUp extends JFrame implements ActionListener {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        add(panel);
        setVisible(true);
    }

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

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

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

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

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

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

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

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

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

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

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

                conn.close();

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

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

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

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

        new SignUp();
    }
}







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

Спасибо за ваше сообщение и за подробное описание вашего кода. Я понимаю, что вы хотите убедиться, что после того как ресепшионист выселит клиента (или это произойдёт автоматически), запись о бронировании удаляется из вкладки "Бронирования", переходит во вкладку "Архивы", и информация об оплате остаётся доступной во вкладке "Платежи" как на панели администратора, так и на панели ресепшиониста.

**1. Перемещение бронирования в архив при выселении**

В вашем классе `DashboardReceptionist`, внутри `ReservationsPanel`, имеется метод `checkOut(int reservationId)`, который отвечает за процесс выселения.

Давайте подробно рассмотрим, что делает этот метод:

- **Получает данные бронирования из таблицы `Reservations`**: Метод извлекает все данные о бронировании, которое требуется выселить.

- **Обновляет статус бронирования на "Выселен"**: В таблице `Reservations` статус меняется на `Выселен`.

- **Добавляет запись в таблицу `Archived_Reservations`**: Вся информация о бронировании копируется в таблицу `Archived_Reservations`, включая `reservation_id`, `customer_id`, `room_id`, даты заезда и выезда, статус, общую сумму и временные метки создания и обновления.

- **Удаляет запись из таблицы `Reservations`**: После архивации, запись удаляется из текущей таблицы бронирований.

- **Обновляет статус номера на "Доступен"**: В таблице `Rooms` статус номера меняется на `Доступен`, чтобы его можно было снова забронировать.

Вот соответствующая часть вашего кода:

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

    //... (код опущен для краткости)

    // Внутри блока try происходит выборка, обновление, архивация и удаление бронирования.

    // Обновление статуса номера на 'Доступен'
    String updateRoomQuery = "UPDATE Rooms SET status = 'Доступен' WHERE room_id = ?";
    //... (код для выполнения updateRoomQuery)
}
```

Таким образом, когда ресепшионист выселяет клиента, его бронирование удаляется из таблицы `Reservations` и перемещается в таблицу `Archived_Reservations`, как вы и хотели.

**2. Сохранение информации об оплате**

В панели `PaymentsPanel` при загрузке платежей вы используете запрос, который соединяет таблицу `Payments` с обоими таблицами `Reservations` и `Archived_Reservations`, чтобы получить информацию о клиенте и номере.

Вот соответствующий фрагмент из вашего класса `PaymentsPanel`:

```java
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 " +
            "LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
            "LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
            "LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
            "LEFT JOIN Rooms r ON (res.room_id = r.room_id OR ar.room_id = r.room_id)";
    //... (код для выполнения запроса и заполнения таблицы)
}
```

Этот запрос гарантирует, что платежи, связанные с архивированными бронированиями, также будут получены и отображены. Используя `LEFT JOIN` с таблицами `Reservations` и `Archived_Reservations`, вы получаете все необходимые данные по платежам, независимо от того, где находится бронирование.

**3. Отображение платежей на панелях администратора и ресепшиониста**

У вас есть аналогичные реализации `PaymentsPanel` как в `DashboardAdmin`, так и в `DashboardReceptionist`. Оба используют похожие запросы для загрузки платежей, что гарантирует отображение всей информации о платежах на обеих панелях.

**Заключение**

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

- После выселения клиента, его бронирование перемещается из таблицы `Reservations` в таблицу `Archived_Reservations`.
- Информация об оплате, связанная как с текущими, так и с архивированными бронированиями, загружается и отображается во вкладке "Платежи" на панелях администратора и ресепшиониста.

**Рекомендации**

Чтобы убедиться, что всё работает корректно, я предлагаю провести следующие тесты:

- **Тестирование процесса выселения**: Запустите приложение, создайте новое бронирование, а затем выселите клиента через панель ресепшиониста. Проверьте, что бронирование больше не отображается во вкладке "Бронирования", а появилось во вкладке "Архивы".

- **Проверка видимости платежей**: Убедитесь, что информация об оплате по этому бронированию всё ещё отображается во вкладке "Платежи" как на панели администратора, так и на панели ресепшиониста.

- **Проверка на ошибки**: Если вы столкнётесь с какими-либо проблемами во время тестирования, например, платеж не отображается или бронирование не перемещается в архив, вам может потребоваться подробно отладить соответствующий участок кода.

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

С уважением.