Загрузка данных


import sys
from dataclasses import dataclass
from typing import Optional, List

import pymysql
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import (
    QApplication,
    QDialog,
    QFormLayout,
    QFrame,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QMainWindow,
    QMessageBox,
    QPushButton,
    QTextBrowser,
    QVBoxLayout,
    QWidget,
)

# =========================
# НАСТРОЙКИ БД
# =========================
DB_HOST = "127.0.0.1"
DB_USER = "root"
DB_PASSWORD = "root"
DB_NAME = "bookstore"
DB_PORT = 3306


@dataclass
class BasketItem:
    genre: str
    title: str
    price: float
    quantity: int


class BookShopDB:
    def __init__(self) -> None:
        self.conn = None
        self._connect()

    def _connect(self) -> None:
        self.conn = pymysql.connect(
            host=DB_HOST,
            user=DB_USER,
            password=DB_PASSWORD,
            database=DB_NAME,
            port=DB_PORT,
            charset="utf8mb4",
            cursorclass=pymysql.cursors.DictCursor,
            autocommit=True,
        )

    def auth_user(self, login: str, password: str) -> Optional[dict]:
        sql = """
            SELECT ID_C, surname_c, name_c, login
            FROM customers
            WHERE login = %s AND password = %s
            LIMIT 1
        """
        with self.conn.cursor() as cur:
            cur.execute(sql, (login, password))
            return cur.fetchone()

    def get_basket(self, customer_id: int) -> List[BasketItem]:
        # Если у вас есть хранимая процедура, можно заменить запрос на CALL.
        sql = """
            SELECT g.name_g, b.title, b.price, bs.quantity_bas
            FROM basket bs
            JOIN books b ON b.ID_B = bs.id_b
            LEFT JOIN genes g ON g.ID_G = b.id_g
            WHERE bs.id_c = %s
            ORDER BY bs.data_bas DESC, b.title
        """
        with self.conn.cursor() as cur:
            cur.execute(sql, (customer_id,))
            rows = cur.fetchall()

        result: List[BasketItem] = []
        for row in rows:
            result.append(
                BasketItem(
                    genre=row.get("name_g") or "",
                    title=row.get("title") or "",
                    price=float(row.get("price") or 0),
                    quantity=int(row.get("quantity_bas") or 0),
                )
            )
        return result


class LoginWindow(QDialog):
    def __init__(self, db: BookShopDB) -> None:
        super().__init__()
        self.db = db
        self.user = None
        self.setWindowTitle("Авторизация")
        self.setFixedSize(480, 330)
        self._build_ui()
        self._style_ui()

    def _build_ui(self) -> None:
        root = QVBoxLayout(self)
        root.setContentsMargins(16, 16, 16, 16)
        root.setSpacing(12)

        title = QLabel("BookShop")
        title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        font = QFont()
        font.setPointSize(20)
        font.setUnderline(True)
        title.setFont(font)

        form_frame = QFrame()
        form = QFormLayout(form_frame)
        form.setLabelAlignment(Qt.AlignmentFlag.AlignLeft)
        form.setHorizontalSpacing(14)
        form.setVerticalSpacing(10)

        self.login_edit = QLineEdit()
        self.login_edit.setPlaceholderText("login")

        self.pass_edit = QLineEdit()
        self.pass_edit.setPlaceholderText("password")
        self.pass_edit.setEchoMode(QLineEdit.EchoMode.Password)

        form.addRow("Логин", self.login_edit)
        form.addRow("Пароль", self.pass_edit)

        self.btn_login = QPushButton("Войти")
        self.btn_close = QPushButton("Закрыть")

        self.btn_login.clicked.connect(self.on_login)
        self.btn_close.clicked.connect(self.close)
        self.login_edit.returnPressed.connect(self.on_login)
        self.pass_edit.returnPressed.connect(self.on_login)

        root.addStretch(1)
        root.addWidget(title)
        root.addStretch(1)
        root.addWidget(form_frame)
        root.addWidget(self.btn_login)
        root.addWidget(self.btn_close)

    def _style_ui(self) -> None:
        self.setStyleSheet("""
            QDialog {
                background: #e8c6ff;
            }
            QLabel {
                color: #42153f;
                font-size: 16px;
            }
            QLineEdit {
                background: #f8e6ff;
                border: 1px solid #a881ba;
                padding: 6px;
                font-size: 15px;
                color: #7b1b1b;
            }
            QPushButton {
                background: #ead2ff;
                border: 1px solid #a881ba;
                padding: 8px;
                font-size: 16px;
                color: #42153f;
            }
            QPushButton:hover {
                background: #f5e9ff;
            }
        """)

    def on_login(self) -> None:
        login = self.login_edit.text().strip()
        password = self.pass_edit.text().strip()

        if not login or not password:
            QMessageBox.warning(self, "Ошибка", "Введите логин и пароль.")
            return

        try:
            user = self.db.auth_user(login, password)
        except Exception as exc:
            QMessageBox.critical(self, "Ошибка БД", f"Не удалось выполнить запрос:\n{exc}")
            return

        if not user:
            QMessageBox.warning(self, "Ошибка", "Неверный логин или пароль.")
            return

        self.user = user
        self.accept()


class MainWindow(QMainWindow):
    def __init__(self, db: BookShopDB, user: dict) -> None:
        super().__init__()
        self.db = db
        self.user = user
        self.setWindowTitle("BookShop")
        self.setMinimumSize(760, 540)
        self._build_ui()
        self._style_ui()
        self._load_data()

    def _build_ui(self) -> None:
        central = QWidget()
        self.setCentralWidget(central)

        root = QVBoxLayout(central)
        root.setContentsMargins(18, 18, 18, 18)
        root.setSpacing(12)

        self.title_label = QLabel("BookShop")
        self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        font = QFont()
        font.setPointSize(20)
        font.setUnderline(True)
        self.title_label.setFont(font)

        self.greeting_label = QLabel()
        self.greeting_label.setWordWrap(True)
        self.greeting_label.setFont(QFont("", 13))

        basket_caption = QLabel("Корзина")
        basket_caption.setFont(QFont("", 14))

        self.basket_view = QTextBrowser()
        self.basket_view.setFrameShape(QFrame.Shape.NoFrame)

        btn_row = QHBoxLayout()
        btn_row.addStretch(1)
        self.btn_close = QPushButton("Закрыть")
        self.btn_close.setFixedWidth(130)
        self.btn_close.clicked.connect(self.close)
        btn_row.addWidget(self.btn_close)

        root.addWidget(self.title_label)
        root.addWidget(self.greeting_label)
        root.addWidget(basket_caption)
        root.addWidget(self.basket_view, 1)
        root.addLayout(btn_row)

    def _style_ui(self) -> None:
        self.setStyleSheet("""
            QMainWindow, QWidget {
                background: #e8c6ff;
            }
            QLabel {
                color: #42153f;
            }
            QTextBrowser {
                background: #f8e6ff;
                border: 1px solid #a881ba;
                color: #42153f;
                font-size: 15px;
            }
            QPushButton {
                background: #ead2ff;
                border: 1px solid #a881ba;
                padding: 8px 14px;
                font-size: 15px;
                color: #42153f;
            }
            QPushButton:hover {
                background: #f5e9ff;
            }
        """)

    def _load_data(self) -> None:
        name = self.user.get("name_c", "")
        surname = self.user.get("surname_c", "")
        self.greeting_label.setText(f"Здравствуйте, {surname} {name}!")

        try:
            items = self.db.get_basket(int(self.user["ID_C"]))
        except Exception as exc:
            self.basket_view.setText(f"Не удалось загрузить корзину:\n{exc}")
            return

        if not items:
            self.basket_view.setText("Корзина пуста.")
            return

        total = 0.0
        lines = []
        for i, item in enumerate(items, start=1):
            subtotal = item.price * item.quantity
            total += subtotal
            lines.append(
                f"{i}. {item.genre} | {item.title} | "
                f"Цена: {item.price:.2f} | Кол-во: {item.quantity} | Сумма: {subtotal:.2f}"
            )

        lines.append("")
        lines.append(f"Итого: {total:.2f}")
        self.basket_view.setText("\n".join(lines))


def main() -> None:
    app = QApplication(sys.argv)

    try:
        db = BookShopDB()
    except Exception as exc:
        QMessageBox.critical(None, "Ошибка", f"Не удалось подключиться к БД:\n{exc}")
        sys.exit(1)

    login_window = LoginWindow(db)
    if login_window.exec() == QDialog.DialogCode.Accepted and login_window.user:
        main_window = MainWindow(db, login_window.user)
        main_window.show()
        sys.exit(app.exec())

    sys.exit(0)


if __name__ == "__main__":
    main()