diff --git a/src/controller/stockController.cpp b/src/controller/stockController.cpp index 3ecedb7..fde2287 100644 --- a/src/controller/stockController.cpp +++ b/src/controller/stockController.cpp @@ -10,6 +10,7 @@ #include "../errors/stockFull.hpp" #include "../errors/stockEmpty.hpp" #include "../errors/invalidItemType.hpp" +#include "../errors/stockExisting.hpp" StockController::StockController(AbstractView* view, Model* model) : view(view), model(model) { @@ -108,12 +109,27 @@ void StockController::process() case Command::AddItemToStock: // Handle adding items to stock: stockName, itemTypeName, quantity if (commandArgs.size() >= 3) { + if (model->getItemTypes().empty()) { + view->displayError("❌ Aucun type d'article disponible"); + break; + } + if (model->getStocks().empty()) { + view->displayError("❌ Aucun stock disponible"); + break; + } std::string stockName = commandArgs[0]; std::string itemTypeName = commandArgs[1]; int quantity = std::stoi(commandArgs[2]); bool success = model->addItemToStock(stockName, itemTypeName, quantity); - view->displayAddItemResult(stockName, itemTypeName, quantity, success); + + if (!success) { + view->displayError("❌ Échec de l'ajout de l'article"); + } + else{ + view->displayAddItemResult(stockName, itemTypeName, quantity, success); + } + } break; case Command::Refresh: @@ -204,16 +220,20 @@ void StockController::process() } catch (const StockFull& e) { // Gérer les exceptions et afficher un message d'erreur - view->displayError("The Stock is full"); + view->displayError("❌ Stock plein: " + std::string(e.what())); } catch (const StockEmpty& e) { // Gérer les exceptions et afficher un message d'erreur - view->displayError("The Stock is empty"); + view->displayError("❌ Stock vide: " + std::string(e.what())); } catch (const InvalidItemType& e) { // Gérer les exceptions et afficher un message d'erreur view->displayError("Invalid Item Type: " + std::string(e.what())); } + catch (const StockExisting& e) { + // Gérer les exceptions et afficher un message d'erreur + view->displayError("❌ Stock existant: " + std::string(e.what())); + } catch (const std::invalid_argument& e) { // Gérer les erreurs de conversion de chaînes en nombres view->displayError("Invalid argument: " + std::string(e.what())); diff --git a/src/errors/stockExisting.cpp b/src/errors/stockExisting.cpp new file mode 100644 index 0000000..216a3ca --- /dev/null +++ b/src/errors/stockExisting.cpp @@ -0,0 +1,2 @@ +#include "stockExisting.hpp" + diff --git a/src/errors/stockExisting.hpp b/src/errors/stockExisting.hpp new file mode 100644 index 0000000..bd7fa66 --- /dev/null +++ b/src/errors/stockExisting.hpp @@ -0,0 +1,13 @@ +#ifndef STOCK_EXISTING_HPP +#define STOCK_EXISTING_HPP + +#include +#include + +class StockExisting : public std::runtime_error { +public: + explicit StockExisting(const std::string& stockName) + : std::runtime_error("Le stock '" + stockName + "' existe déjà.") {} +}; + +#endif // STOCK_EXISTING_HPP \ No newline at end of file diff --git a/src/model/model.cpp b/src/model/model.cpp index 843144c..fcb500b 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -1,5 +1,6 @@ #include "model.hpp" #include "../errors/stockFull.hpp" +#include "../errors/stockExisting.hpp" #include Model::Model() @@ -19,6 +20,11 @@ void Model::addData(const std::string &data) void Model::createStock(const std::string &name, int capacity, const std::string &comment) { + if (findStockByName(name)) + { + throw StockExisting(name); + } + Stock newStock(name, capacity, comment, nextStockId++); stocks.push_back(newStock); } @@ -37,6 +43,10 @@ void Model::createItemType(const std::string &name, const std::string &comment, void Model::createItemType(const std::string &name, int id, const std::string &comment, int size) { + if (findItemTypeByName(name)) + { + throw std::invalid_argument("Item type name '" + name + "' already exists"); + } // Vérifier si l'ID existe déjà for (const auto& existingType : itemTypes) { if (existingType.getId() == id) { @@ -98,7 +108,7 @@ bool Model::addItemToStock(const std::string &stockName, const std::string &item if (itemType->getSize() * quantity + stock->getCurrentSize() > stock->getCapacity()) { - throw StockFull("Cannot add items, stock is full"); + return false; // Stock is full } for (int i = 0; i < quantity; ++i) diff --git a/src/model/stock.cpp b/src/model/stock.cpp index ab178b7..8faeabe 100644 --- a/src/model/stock.cpp +++ b/src/model/stock.cpp @@ -79,6 +79,26 @@ ItemType Stock::getItem(int id) const throw std::runtime_error("Item not found"); } +std::map> Stock::getItemsWithQuantities() const +{ + std::map> itemsWithQuantities; + + for (const auto& item : items) { + std::string itemName = item.getName(); + + auto it = itemsWithQuantities.find(itemName); + if (it != itemsWithQuantities.end()) { + // Incrementer la quantité + it->second.second++; + } else { + // Ajouter un nouveau type d'article avec quantité 1 + itemsWithQuantities.insert({itemName, std::make_pair(item, 1)}); + } + } + + return itemsWithQuantities; +} + // Getters implementation std::string Stock::getName() const { diff --git a/src/model/stock.hpp b/src/model/stock.hpp index 115b925..2265e35 100644 --- a/src/model/stock.hpp +++ b/src/model/stock.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "itemType.hpp" class Stock @@ -15,6 +16,7 @@ public: void removeItem(int id); bool removeItem(const ItemType &item); // Nouvelle méthode ItemType getItem(int id) const; + std::map> getItemsWithQuantities() const; // Getters std::string getName() const; diff --git a/src/view/qtView.cpp b/src/view/qtView.cpp index d20a2db..1bb6e06 100644 --- a/src/view/qtView.cpp +++ b/src/view/qtView.cpp @@ -11,6 +11,7 @@ QtView::QtView(QWidget* parent) : QWidget(parent), m_controller(nullptr), m_virtualKeyboard(nullptr), m_stockSelectionLayout(nullptr), m_stockDisplayWidget(nullptr), m_stockDisplayLayout(nullptr), m_itemTypeDisplayWidget(nullptr), m_itemTypeDisplayLayout(nullptr) { + setupUI(); } QtView::QtView(StockController* controller, QWidget* parent) @@ -26,15 +27,11 @@ QtView::~QtView() void QtView::start() { - // Initialize the virtual keyboard - m_virtualKeyboard = new VirtualKeyboard(this); - m_virtualKeyboard->hide(); - - // Set up the UI components - setupUI(); - - // Connect signals and slots for the refresh button - connect(m_refreshButton, &QPushButton::clicked, this, &QtView::onRefresh); + // Initialize the virtual keyboard if not already done + if (!m_virtualKeyboard) { + m_virtualKeyboard = new VirtualKeyboard(this); + m_virtualKeyboard->hide(); + } // full-screen mode setWindowState(windowState() | Qt::WindowFullScreen); @@ -76,14 +73,9 @@ void QtView::setupUI() m_outputDisplay->setMinimumHeight(80); m_outputDisplay->setMaximumHeight(120); - // Bouton refresh - m_refreshButton = new QPushButton("🔄 Actualiser", this); - m_refreshButton->setMinimumHeight(50); - // Ajout des éléments au layout principal m_mainLayout->addWidget(m_menuDisplayWidget, 1); // Prend la majorité de l'espace m_mainLayout->addWidget(m_outputDisplay, 0); // Taille fixe - m_mainLayout->addWidget(m_refreshButton, 0); // Taille fixe // Configuration du clavier virtuel setupVirtualKeyboard(); @@ -93,34 +85,43 @@ void QtView::setupUI() // Application des styles tactiles applyTouchFriendlyStyling(); - - // Connexions des signaux - connect(m_refreshButton, &QPushButton::clicked, this, &QtView::onRefresh); - - // Afficher le menu Stock par défaut - showMainMenu(0); - - setLayout(m_mainLayout); -} - -void QtView::onRefresh() -{ - if (!m_controller) { - showMessage("Aucun contrôleur disponible", true); - return; + + // Afficher le menu Stock par défaut seulement si le contrôleur est défini + if (m_controller) { + showMainMenu(0); + } else { + // Créer un écran d'accueil temporaire + QWidget* welcomeScreen = new QWidget(); + QVBoxLayout* welcomeLayout = new QVBoxLayout(welcomeScreen); + + QLabel* welcomeLabel = new QLabel("🏭 Gestionnaire de Stock"); + welcomeLabel->setStyleSheet("font-size: 32px; font-weight: bold; color: #2c3e50; padding: 50px;"); + welcomeLabel->setAlignment(Qt::AlignCenter); + + QLabel* infoLabel = new QLabel("Interface tactile prête\nSélectionnez un menu à gauche"); + infoLabel->setStyleSheet("font-size: 18px; color: #7f8c8d; text-align: center;"); + infoLabel->setAlignment(Qt::AlignCenter); + + welcomeLayout->addStretch(); + welcomeLayout->addWidget(welcomeLabel); + welcomeLayout->addWidget(infoLabel); + welcomeLayout->addStretch(); + + m_stackedWidget->addWidget(welcomeScreen); + m_stackedWidget->setCurrentWidget(welcomeScreen); } - // Envoyer la commande Refresh au contrôleur - m_controller->sendCommand(Refresh); - - // Afficher un message de rafraîchissement - m_outputDisplay->append("🔄 Rafraîchissement en cours..."); - showMessage("Données actualisées"); + setLayout(m_mainLayout); } void QtView::setController(StockController* controller) { m_controller = controller; + + // Une fois le contrôleur défini, afficher le menu principal par défaut + if (m_controller && m_stackedWidget) { + showMainMenu(0); + } } void QtView::displayStocks(const std::vector& stocks) @@ -468,8 +469,8 @@ void QtView::setupStockActionScreens() void QtView::setupItemTypeActionScreens() { // Écran de création de type d'article - QStringList createTypeLabels = {"Nom du type:", "ID:", "Commentaire:", "Taille:"}; - QStringList createTypePlaceholders = {"Type d'article", "1", "Description", "1"}; + QStringList createTypeLabels = {"Nom du type:", "Commentaire:", "Taille:"}; + QStringList createTypePlaceholders = {"Type d'article", "Description", "1"}; m_createItemTypeScreen = createFormScreen("Créer un Type d'Article", createTypeLabels, createTypePlaceholders); m_stackedWidget->addWidget(m_createItemTypeScreen); @@ -478,29 +479,26 @@ void QtView::setupItemTypeActionScreens() m_stackedWidget->addWidget(m_showItemTypesScreen); // Écran de suppression de type - QStringList deleteTypeLabels = {"Type à supprimer:"}; - m_deleteItemTypeScreen = createFormScreen("Supprimer un Type", deleteTypeLabels); + m_deleteItemTypeScreen = createItemTypeSelectionScreen(); m_stackedWidget->addWidget(m_deleteItemTypeScreen); } void QtView::setupItemActionScreens() { - // Écran d'ajout d'article + // Écran d'ajout d'article avec interface tactile QStringList addItemLabels = {"Stock:", "Type d'article:", "Quantité:"}; QStringList addItemFieldTypes = {"stock", "itemtype", "quantity"}; - m_addItemScreen = createSelectionFormScreen("Ajouter un Article", addItemLabels, addItemFieldTypes); + m_addItemScreen = createTactileQuantityScreen("Ajouter un Article", addItemLabels, addItemFieldTypes); m_stackedWidget->addWidget(m_addItemScreen); - // Écran de déplacement d'article + // Écran de déplacement d'article avec interface tactile QStringList moveItemLabels = {"Stock source:", "Stock destination:", "Type d'article:", "Quantité:"}; QStringList moveItemFieldTypes = {"stock", "stock", "itemtype", "quantity"}; - m_moveItemScreen = createSelectionFormScreen("Déplacer un Article", moveItemLabels, moveItemFieldTypes); + m_moveItemScreen = createTactileQuantityScreen("Déplacer un Article", moveItemLabels, moveItemFieldTypes); m_stackedWidget->addWidget(m_moveItemScreen); // Écran de retrait d'article - QStringList removeItemLabels = {"Stock:", "Type d'article:", "Quantité:"}; - QStringList removeItemFieldTypes = {"stock", "itemtype", "quantity"}; - m_removeItemScreen = createSelectionFormScreen("Retirer un Article", removeItemLabels, removeItemFieldTypes); + m_removeItemScreen = createItemRemovalScreen(); m_stackedWidget->addWidget(m_removeItemScreen); } @@ -831,6 +829,255 @@ QWidget* QtView::createSelectionFormScreen(const QString& title, const QStringLi return screen; } +QWidget* QtView::createTactileQuantityScreen(const QString& title, const QStringList& labels, const QStringList& fieldTypes) +{ + QWidget* screen = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(screen); + layout->setSpacing(20); + layout->setContentsMargins(30, 30, 30, 30); + + // Titre + QLabel* titleLabel = new QLabel(title); + titleLabel->setStyleSheet("font-size: 28px; font-weight: bold; color: #2c3e50; margin-bottom: 30px;"); + titleLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(titleLabel); + + // Formulaire + QWidget* formWidget = new QWidget(); + QVBoxLayout* formLayout = new QVBoxLayout(formWidget); + formLayout->setSpacing(15); + + for (int i = 0; i < labels.size(); ++i) { + // Label + QLabel* label = new QLabel(labels[i]); + label->setStyleSheet("font-size: 16px; font-weight: bold; color: #34495e;"); + formLayout->addWidget(label); + + QString fieldType = (i < fieldTypes.size()) ? fieldTypes[i] : "text"; + + if (fieldType == "stock") { + // ComboBox pour sélectionner un stock + QComboBox* combo = new QComboBox(); + combo->setMinimumHeight(50); + combo->setStyleSheet( + "QComboBox {" + " border: 2px solid #bdc3c7;" + " border-radius: 8px;" + " padding: 10px;" + " font-size: 16px;" + " background-color: white;" + "}" + "QComboBox:focus {" + " border-color: #3498db;" + "}" + "QComboBox::drop-down {" + " subcontrol-origin: padding;" + " subcontrol-position: top right;" + " width: 20px;" + " border-left-width: 1px;" + " border-left-color: #bdc3c7;" + " border-left-style: solid;" + "}" + "QComboBox::down-arrow {" + " image: none;" + " border: 3px solid #34495e;" + " border-radius: 3px;" + " margin: 5px;" + "}" + ); + + // Remplir avec les stocks disponibles + QStringList stocks = getAvailableStocks(); + combo->addItems(stocks); + + combo->setObjectName(QString("field_%1").arg(i)); + formLayout->addWidget(combo); + + } else if (fieldType == "itemtype") { + // ComboBox pour sélectionner un type d'article + QComboBox* combo = new QComboBox(); + combo->setMinimumHeight(50); + combo->setStyleSheet( + "QComboBox {" + " border: 2px solid #bdc3c7;" + " border-radius: 8px;" + " padding: 10px;" + " font-size: 16px;" + " background-color: white;" + "}" + "QComboBox:focus {" + " border-color: #9b59b6;" + "}" + "QComboBox::drop-down {" + " subcontrol-origin: padding;" + " subcontrol-position: top right;" + " width: 20px;" + " border-left-width: 1px;" + " border-left-color: #bdc3c7;" + " border-left-style: solid;" + "}" + "QComboBox::down-arrow {" + " image: none;" + " border: 3px solid #34495e;" + " border-radius: 3px;" + " margin: 5px;" + "}" + ); + + // Remplir avec les types d'articles disponibles + QStringList itemTypes = getAvailableItemTypes(); + combo->addItems(itemTypes); + + combo->setObjectName(QString("field_%1").arg(i)); + formLayout->addWidget(combo); + + } else if (fieldType == "quantity") { + // Interface tactile pour la quantité avec boutons + et - + QWidget* quantityWidget = new QWidget(); + QHBoxLayout* quantityLayout = new QHBoxLayout(quantityWidget); + quantityLayout->setSpacing(15); + + // Bouton - + QPushButton* minusButton = new QPushButton("−"); + minusButton->setMinimumSize(80, 80); + minusButton->setStyleSheet( + "QPushButton {" + " background-color: #e74c3c;" + " color: white;" + " border: none;" + " border-radius: 15px;" + " font-size: 36px;" + " font-weight: bold;" + "}" + "QPushButton:hover {" + " background-color: #c0392b;" + "}" + "QPushButton:pressed {" + " background-color: #a93226;" + " transform: scale(0.95);" + "}" + ); + + // Affichage de la quantité + QLabel* quantityDisplay = new QLabel("1"); + quantityDisplay->setAlignment(Qt::AlignCenter); + quantityDisplay->setMinimumSize(120, 80); + quantityDisplay->setStyleSheet( + "QLabel {" + " background-color: white;" + " border: 3px solid #bdc3c7;" + " border-radius: 15px;" + " font-size: 32px;" + " font-weight: bold;" + " color: #2c3e50;" + "}" + ); + quantityDisplay->setObjectName(QString("field_%1").arg(i)); + + // Bouton + + QPushButton* plusButton = new QPushButton("+"); + plusButton->setMinimumSize(80, 80); + plusButton->setStyleSheet( + "QPushButton {" + " background-color: #27ae60;" + " color: white;" + " border: none;" + " border-radius: 15px;" + " font-size: 36px;" + " font-weight: bold;" + "}" + "QPushButton:hover {" + " background-color: #229954;" + "}" + "QPushButton:pressed {" + " background-color: #1e8449;" + " transform: scale(0.95);" + "}" + ); + + // Connecter les boutons + connect(minusButton, &QPushButton::clicked, [quantityDisplay]() { + int currentValue = quantityDisplay->text().toInt(); + if (currentValue > 1) { + quantityDisplay->setText(QString::number(currentValue - 1)); + } + }); + + connect(plusButton, &QPushButton::clicked, [quantityDisplay]() { + int currentValue = quantityDisplay->text().toInt(); + if (currentValue < 999) { + quantityDisplay->setText(QString::number(currentValue + 1)); + } + }); + + quantityLayout->addStretch(); + quantityLayout->addWidget(minusButton); + quantityLayout->addWidget(quantityDisplay); + quantityLayout->addWidget(plusButton); + quantityLayout->addStretch(); + + formLayout->addWidget(quantityWidget); + + } else { + // Champ de saisie normal pour autres types + QLineEdit* field = new QLineEdit(); + field->setMinimumHeight(50); + field->setStyleSheet( + "QLineEdit {" + " border: 2px solid #bdc3c7;" + " border-radius: 8px;" + " padding: 10px;" + " font-size: 16px;" + " background-color: white;" + "}" + "QLineEdit:focus {" + " border-color: #e67e22;" + "}" + ); + + field->setObjectName(QString("field_%1").arg(i)); + field->installEventFilter(this); + formLayout->addWidget(field); + } + } + + layout->addWidget(formWidget); + + // Boutons d'action + QWidget* buttonWidget = new QWidget(); + QHBoxLayout* buttonLayout = new QHBoxLayout(buttonWidget); + buttonLayout->setSpacing(20); + + QPushButton* executeButton = new QPushButton("✅ Exécuter"); + QPushButton* cancelButton = new QPushButton("❌ Annuler"); + + // Style des boutons + executeButton->setMinimumHeight(60); + executeButton->setStyleSheet( + "QPushButton { background-color: #27ae60; color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: bold; padding: 15px; }" + "QPushButton:pressed { background-color: #229954; }" + "QPushButton:hover { background-color: #2ecc71; }"); + + cancelButton->setMinimumHeight(60); + cancelButton->setStyleSheet( + "QPushButton { background-color: #e74c3c; color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: bold; padding: 15px; }" + "QPushButton:pressed { background-color: #c0392b; }" + "QPushButton:hover { background-color: #c0392b; }"); + + // Connecter les boutons + connect(executeButton, &QPushButton::clicked, [this, screen, title]() { + executeFormAction(screen, title); + }); + connect(cancelButton, &QPushButton::clicked, this, &QtView::goBack); + + buttonLayout->addWidget(executeButton); + buttonLayout->addWidget(cancelButton); + layout->addWidget(buttonWidget); + + layout->addStretch(); + return screen; +} + QWidget* QtView::createStockSelectionScreen() { QWidget* screen = new QWidget(); @@ -891,6 +1138,132 @@ QWidget* QtView::createStockSelectionScreen() return screen; } +QWidget* QtView::createItemTypeSelectionScreen() +{ + QWidget* screen = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(screen); + layout->setSpacing(20); + layout->setContentsMargins(30, 30, 30, 30); + + // Titre + QLabel* titleLabel = new QLabel("Sélectionner un Type d'Article à Supprimer"); + titleLabel->setStyleSheet("font-size: 28px; font-weight: bold; color: #2c3e50; margin-bottom: 30px;"); + titleLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(titleLabel); + + // Scroll area pour la liste des types d'articles + QScrollArea* scrollArea = new QScrollArea(); + scrollArea->setWidgetResizable(true); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setStyleSheet( + "QScrollArea {" + " border: 2px solid #bdc3c7;" + " border-radius: 8px;" + " background-color: white;" + "}" + ); + + // Widget conteneur pour les boutons de types d'articles + QWidget* itemTypeListWidget = new QWidget(); + m_itemTypeSelectionLayout = new QVBoxLayout(itemTypeListWidget); + m_itemTypeSelectionLayout->setSpacing(10); + m_itemTypeSelectionLayout->setContentsMargins(10, 10, 10, 10); + + scrollArea->setWidget(itemTypeListWidget); + layout->addWidget(scrollArea); + + // Bouton retour + QPushButton* backButton = new QPushButton("← Retour"); + backButton->setStyleSheet( + "QPushButton {" + " background-color: #95a5a6;" + " color: white;" + " border: none;" + " border-radius: 8px;" + " font-size: 16px;" + " font-weight: bold;" + " padding: 12px 20px;" + " min-height: 40px;" + "}" + "QPushButton:hover { background-color: #7f8c8d; }" + "QPushButton:pressed { transform: translateY(1px); }" + ); + connect(backButton, &QPushButton::clicked, this, &QtView::goBack); + layout->addWidget(backButton); + + // Charger la liste des types d'articles + refreshItemTypeSelectionScreen(); + + return screen; +} + +QWidget* QtView::createItemRemovalScreen() +{ + QWidget* screen = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(screen); + layout->setSpacing(20); + layout->setContentsMargins(30, 30, 30, 30); + + // Titre + QLabel* titleLabel = new QLabel("Sélectionner un Stock"); + titleLabel->setStyleSheet("font-size: 28px; font-weight: bold; color: #2c3e50; margin-bottom: 30px;"); + titleLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(titleLabel); + + // Sous-titre + QLabel* subtitleLabel = new QLabel("Choisissez le stock duquel retirer des articles"); + subtitleLabel->setStyleSheet("font-size: 16px; color: #7f8c8d; margin-bottom: 20px;"); + subtitleLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(subtitleLabel); + + // Scroll area pour la liste des stocks + QScrollArea* scrollArea = new QScrollArea(); + scrollArea->setWidgetResizable(true); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setStyleSheet( + "QScrollArea {" + " border: 2px solid #bdc3c7;" + " border-radius: 8px;" + " background-color: white;" + "}" + ); + + // Widget conteneur pour les boutons de stocks + QWidget* stockListWidget = new QWidget(); + m_removeItemLayout = new QVBoxLayout(stockListWidget); + m_removeItemLayout->setSpacing(10); + m_removeItemLayout->setContentsMargins(10, 10, 10, 10); + + scrollArea->setWidget(stockListWidget); + layout->addWidget(scrollArea); + + // Bouton retour + QPushButton* backButton = new QPushButton("← Retour"); + backButton->setStyleSheet( + "QPushButton {" + " background-color: #95a5a6;" + " color: white;" + " border: none;" + " border-radius: 8px;" + " font-size: 16px;" + " font-weight: bold;" + " padding: 12px 20px;" + " min-height: 40px;" + "}" + "QPushButton:hover { background-color: #7f8c8d; }" + "QPushButton:pressed { transform: translateY(1px); }" + ); + connect(backButton, &QPushButton::clicked, this, &QtView::goBack); + layout->addWidget(backButton); + + // Charger la liste des stocks + refreshItemRemovalScreen(); + + return screen; +} + void QtView::refreshStockSelectionScreen() { if (!m_stockSelectionLayout) return; @@ -981,6 +1354,213 @@ void QtView::refreshStockSelectionScreen() m_stockSelectionLayout->addStretch(); } +void QtView::refreshItemTypeSelectionScreen() +{ + if (!m_itemTypeSelectionLayout) return; + if (!m_controller) return; + + // Supprimer tous les boutons existants + QLayoutItem *child; + while ((child = m_itemTypeSelectionLayout->takeAt(0)) != nullptr) { + delete child->widget(); + delete child; + } + + // Vérifier que le modèle existe + auto model = m_controller->getModel(); + if (!model) { + QLabel* errorLabel = new QLabel("Erreur: Modèle non disponible"); + errorLabel->setStyleSheet( + "QLabel {" + " font-size: 18px;" + " color: #e74c3c;" + " padding: 20px;" + " text-align: center;" + "}" + ); + errorLabel->setAlignment(Qt::AlignCenter); + m_itemTypeSelectionLayout->addWidget(errorLabel); + m_itemTypeSelectionLayout->addStretch(); + return; + } + + // Obtenir la liste des types d'articles depuis le modèle + auto itemTypes = model->getItemTypes(); + + if (itemTypes.empty()) { + QLabel* noItemTypeLabel = new QLabel("Aucun type d'article disponible"); + noItemTypeLabel->setStyleSheet( + "QLabel {" + " font-size: 18px;" + " color: #7f8c8d;" + " padding: 20px;" + " text-align: center;" + "}" + ); + noItemTypeLabel->setAlignment(Qt::AlignCenter); + m_itemTypeSelectionLayout->addWidget(noItemTypeLabel); + } else { + // Créer un bouton pour chaque type d'article + for (const auto& itemType : itemTypes) { + QString typeName = QString::fromStdString(itemType.getName()); + QString comment = QString::fromStdString(itemType.getComment()); + + // Créer des informations détaillées sur le type + QString typeInfo; + if (!comment.isEmpty()) { + typeInfo = QString("%1\nTaille: %2 | Commentaire: %3") + .arg(typeName) + .arg(itemType.getSize()) + .arg(comment); + } else { + typeInfo = QString("%1\nTaille: %2") + .arg(typeName) + .arg(itemType.getSize()); + } + + QPushButton* typeButton = new QPushButton(typeInfo); + typeButton->setMinimumHeight(80); + typeButton->setStyleSheet( + "QPushButton {" + " background-color: #e74c3c;" + " color: white;" + " border: none;" + " border-radius: 8px;" + " font-size: 16px;" + " font-weight: bold;" + " padding: 15px;" + " text-align: left;" + "}" + "QPushButton:hover {" + " background-color: #c0392b;" + "}" + "QPushButton:pressed {" + " transform: translateY(1px);" + "}" + ); + + // Connecter le bouton à la confirmation de suppression + connect(typeButton, &QPushButton::clicked, [this, typeName]() { + confirmItemTypeDeletion(typeName); + }); + + m_itemTypeSelectionLayout->addWidget(typeButton); + } + } + + // Ajouter un stretch à la fin + m_itemTypeSelectionLayout->addStretch(); +} + +void QtView::refreshItemRemovalScreen() +{ + if (!m_removeItemLayout) return; + if (!m_controller) return; + + // Supprimer tous les boutons existants + QLayoutItem *child; + while ((child = m_removeItemLayout->takeAt(0)) != nullptr) { + delete child->widget(); + delete child; + } + + // Vérifier que le modèle existe + auto model = m_controller->getModel(); + if (!model) { + QLabel* errorLabel = new QLabel("Erreur: Modèle non disponible"); + errorLabel->setStyleSheet( + "QLabel {" + " font-size: 18px;" + " color: #e74c3c;" + " padding: 20px;" + " text-align: center;" + "}" + ); + errorLabel->setAlignment(Qt::AlignCenter); + m_removeItemLayout->addWidget(errorLabel); + m_removeItemLayout->addStretch(); + return; + } + + // Obtenir la liste des stocks depuis le modèle + auto stocks = model->getStocks(); + + if (stocks.empty()) { + QLabel* noStockLabel = new QLabel("Aucun stock disponible"); + noStockLabel->setStyleSheet( + "QLabel {" + " font-size: 18px;" + " color: #7f8c8d;" + " padding: 20px;" + " text-align: center;" + "}" + ); + noStockLabel->setAlignment(Qt::AlignCenter); + m_removeItemLayout->addWidget(noStockLabel); + } else { + // Créer un bouton pour chaque stock + for (const auto& stock : stocks) { + QString stockName = QString::fromStdString(stock.getName()); + + // Obtenir les articles dans ce stock pour afficher des informations + auto itemsInStock = stock.getItemsWithQuantities(); + int totalItems = 0; + for (const auto& [itemName, itemData] : itemsInStock) { + totalItems += itemData.second; + } + + // Créer des informations détaillées sur le stock + QString stockInfo = QString("%1\nCapacité: %2 | Articles: %3 types (%4 total)") + .arg(stockName) + .arg(stock.getCapacity()) + .arg(itemsInStock.size()) + .arg(totalItems); + + QPushButton* stockButton = new QPushButton(stockInfo); + stockButton->setMinimumHeight(80); + + // Couleur différente selon si le stock contient des articles + QString buttonColor = itemsInStock.empty() ? "#95a5a6" : "#3498db"; + QString hoverColor = itemsInStock.empty() ? "#7f8c8d" : "#2980b9"; + + stockButton->setStyleSheet(QString( + "QPushButton {" + " background-color: %1;" + " color: white;" + " border: none;" + " border-radius: 8px;" + " font-size: 16px;" + " font-weight: bold;" + " padding: 15px;" + " text-align: left;" + "}" + "QPushButton:hover {" + " background-color: %2;" + "}" + "QPushButton:pressed {" + " transform: translateY(1px);" + "}" + ).arg(buttonColor, hoverColor)); + + // Désactiver le bouton si le stock est vide + if (itemsInStock.empty()) { + stockButton->setEnabled(false); + stockButton->setText(stockInfo + "\n(Aucun article disponible)"); + } else { + // Connecter le bouton à l'écran de sélection d'articles pour ce stock + connect(stockButton, &QPushButton::clicked, [this, stockName]() { + showStockItemsForRemoval(stockName); + }); + } + + m_removeItemLayout->addWidget(stockButton); + } + } + + // Ajouter un stretch à la fin + m_removeItemLayout->addStretch(); +} + QWidget* QtView::createStockDisplayScreen() { QWidget* screen = new QWidget(); @@ -1321,6 +1901,102 @@ void QtView::updateItemTypeDisplayScreen(const std::vector& itemTypes) m_itemTypeDisplayLayout->addStretch(); } +void QtView::refreshComboBoxes(QWidget* screen) +{ + if (!screen || !m_controller) return; + + // Obtenir les listes actuelles + QStringList stocks = getAvailableStocks(); + QStringList itemTypes = getAvailableItemTypes(); + + // Parcourir tous les ComboBox de l'écran et les mettre à jour + QList comboBoxes = screen->findChildren(); + + for (QComboBox* combo : comboBoxes) { + if (!combo) continue; + + QString objectName = combo->objectName(); + QString currentText = combo->currentText(); // Sauvegarder la sélection actuelle + + // Effacer le contenu actuel + combo->clear(); + + // Déterminer le type de ComboBox basé sur son nom d'objet et remplir avec les bonnes données + if (objectName.contains("field_")) { + // Récupérer l'index du champ + QString indexStr = objectName.mid(6); // Enlever "field_" + bool ok; + int fieldIndex = indexStr.toInt(&ok); + + if (ok) { + // Déterminer le type basé sur l'écran et l'index + if (screen == m_addItemScreen) { + // Écran d'ajout: Stock (0), Type d'article (1), Quantité (2) + if (fieldIndex == 0) { + combo->addItems(stocks); + } else if (fieldIndex == 1) { + combo->addItems(itemTypes); + } + } else if (screen == m_moveItemScreen) { + // Écran de déplacement: Stock source (0), Stock destination (1), Type d'article (2), Quantité (3) + if (fieldIndex == 0 || fieldIndex == 1) { + combo->addItems(stocks); + } else if (fieldIndex == 2) { + combo->addItems(itemTypes); + } + } else if (screen == m_removeItemScreen) { + // Écran de retrait: Stock (0), Type d'article (1), Quantité (2) + if (fieldIndex == 0) { + combo->addItems(stocks); + } else if (fieldIndex == 1) { + combo->addItems(itemTypes); + } + } + + // Restaurer la sélection précédente si elle existe encore + int currentIndex = combo->findText(currentText); + if (currentIndex >= 0) { + combo->setCurrentIndex(currentIndex); + } + } + } + } +} + +void QtView::resetFormFields(QWidget* screen) +{ + if (!screen) return; + + // Réinitialiser tous les QLineEdit + QList lineEdits = screen->findChildren(); + for (QLineEdit* lineEdit : lineEdits) { + lineEdit->clear(); + } + + // Réinitialiser tous les QComboBox à la première option + QList comboBoxes = screen->findChildren(); + for (QComboBox* combo : comboBoxes) { + if (combo->count() > 0) { + combo->setCurrentIndex(0); + } + } + + // Réinitialiser tous les QSpinBox à leur valeur minimale + QList spinBoxes = screen->findChildren(); + for (QSpinBox* spinBox : spinBoxes) { + spinBox->setValue(spinBox->minimum()); + } + + // Réinitialiser tous les QTextEdit + QList textEdits = screen->findChildren(); + for (QTextEdit* textEdit : textEdits) { + // Ne pas effacer le QTextEdit principal (m_outputDisplay) + if (textEdit != m_outputDisplay) { + textEdit->clear(); + } + } +} + void QtView::confirmStockDeletion(const QString& stockName) { if (!m_controller) { @@ -1454,6 +2130,483 @@ void QtView::addSuccessMessageToStockScreen(const QString& message) }); } +void QtView::confirmItemTypeDeletion(const QString& itemTypeName) +{ + if (!m_controller) { + showMessage("Erreur: Contrôleur non disponible"); + return; + } + + // Afficher une notification de confirmation + QString confirmationMessage = QString( + "
" + "

⚠️ Confirmation de suppression

" + "

Êtes-vous sûr de vouloir supprimer le type d'article :

" + "

%1

" + "

Cette action est irréversible.

" + "
" + ).arg(itemTypeName); + + // Créer un écran de confirmation temporaire + QWidget* confirmationScreen = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(confirmationScreen); + layout->setSpacing(30); + layout->setContentsMargins(50, 50, 50, 50); + + // Message de confirmation + QLabel* messageLabel = new QLabel(confirmationMessage); + messageLabel->setWordWrap(true); + messageLabel->setAlignment(Qt::AlignCenter); + messageLabel->setStyleSheet( + "QLabel {" + " background-color: white;" + " border: 2px solid #e74c3c;" + " border-radius: 10px;" + " padding: 20px;" + "}" + ); + layout->addWidget(messageLabel); + + // Boutons d'action + QWidget* buttonWidget = new QWidget(); + QHBoxLayout* buttonLayout = new QHBoxLayout(buttonWidget); + buttonLayout->setSpacing(20); + + QPushButton* confirmButton = new QPushButton("✓ Confirmer la suppression"); + QPushButton* cancelButton = new QPushButton("✕ Annuler"); + + QString buttonStyle = + "QPushButton {" + " border: none;" + " border-radius: 8px;" + " font-size: 18px;" + " font-weight: bold;" + " padding: 15px 25px;" + " min-height: 50px;" + "}" + "QPushButton:pressed {" + " transform: translateY(1px);" + "}"; + + confirmButton->setStyleSheet(buttonStyle + + "QPushButton { background-color: #e74c3c; color: white; }" + "QPushButton:hover { background-color: #c0392b; }"); + + cancelButton->setStyleSheet(buttonStyle + + "QPushButton { background-color: #95a5a6; color: white; }" + "QPushButton:hover { background-color: #7f8c8d; }"); + + // Connecter les boutons + connect(confirmButton, &QPushButton::clicked, [this, itemTypeName, confirmationScreen]() { + // Effectuer la suppression + std::vector args = {itemTypeName.toStdString()}; + m_controller->sendCommand(Command::DeleteItemType, args); + + // Retourner à l'écran de sélection et actualiser + refreshItemTypeSelectionScreen(); + m_stackedWidget->removeWidget(confirmationScreen); + delete confirmationScreen; + m_stackedWidget->setCurrentWidget(m_deleteItemTypeScreen); + + // Ajouter un message de succès temporaire dans l'écran + addSuccessMessageToItemTypeScreen(QString("Type d'article '%1' supprimé avec succès").arg(itemTypeName)); + }); + + connect(cancelButton, &QPushButton::clicked, [this, confirmationScreen]() { + // Retourner à l'écran de sélection sans suppression + m_stackedWidget->removeWidget(confirmationScreen); + delete confirmationScreen; + m_stackedWidget->setCurrentWidget(m_deleteItemTypeScreen); + }); + + buttonLayout->addWidget(confirmButton); + buttonLayout->addWidget(cancelButton); + layout->addWidget(buttonWidget); + + layout->addStretch(); + + // Ajouter l'écran de confirmation et l'afficher + m_stackedWidget->addWidget(confirmationScreen); + m_stackedWidget->setCurrentWidget(confirmationScreen); +} + +void QtView::addSuccessMessageToItemTypeScreen(const QString& message) +{ + if (!m_itemTypeSelectionLayout) return; + + // Créer un label de succès temporaire + QLabel* successLabel = new QLabel(message); + successLabel->setStyleSheet( + "QLabel {" + " background-color: #27ae60;" + " color: white;" + " border: none;" + " border-radius: 8px;" + " font-size: 16px;" + " font-weight: bold;" + " padding: 15px;" + " margin: 10px 0;" + "}" + ); + successLabel->setAlignment(Qt::AlignCenter); + successLabel->setObjectName("successMessage"); + + // Insérer le message au début de la liste + m_itemTypeSelectionLayout->insertWidget(0, successLabel); + + // Programmer la suppression automatique du message après 3 secondes + QTimer::singleShot(3000, [this, successLabel]() { + if (successLabel) { + m_itemTypeSelectionLayout->removeWidget(successLabel); + delete successLabel; + } + }); +} + +QWidget* QtView::createItemQuantityRemovalScreen(const QString& stockName, const QString& itemTypeName, int currentQuantity) +{ + QWidget* screen = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(screen); + layout->setSpacing(30); + layout->setContentsMargins(50, 50, 50, 50); + + // Titre + QLabel* titleLabel = new QLabel("Quantité à Retirer"); + titleLabel->setStyleSheet("font-size: 28px; font-weight: bold; color: #2c3e50;"); + titleLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(titleLabel); + + // Informations sur l'article + QString infoText = QString( + "
" + "

Stock: %1

" + "

Type d'article: %2

" + "

Quantité disponible: %3

" + "
" + ).arg(stockName).arg(itemTypeName).arg(currentQuantity); + + QLabel* infoLabel = new QLabel(infoText); + infoLabel->setWordWrap(true); + infoLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(infoLabel); + + // Interface tactile pour la quantité avec boutons + et - + QWidget* quantityWidget = new QWidget(); + QVBoxLayout* quantityMainLayout = new QVBoxLayout(quantityWidget); + + QLabel* quantityTitleLabel = new QLabel("Quantité à retirer:"); + quantityTitleLabel->setStyleSheet("font-size: 18px; font-weight: bold; color: #2c3e50; margin-bottom: 15px;"); + quantityTitleLabel->setAlignment(Qt::AlignCenter); + quantityMainLayout->addWidget(quantityTitleLabel); + + QWidget* tactileWidget = new QWidget(); + QHBoxLayout* tactileLayout = new QHBoxLayout(tactileWidget); + tactileLayout->setSpacing(20); + + // Bouton - + QPushButton* minusButton = new QPushButton("−"); + minusButton->setMinimumSize(80, 80); + minusButton->setStyleSheet( + "QPushButton {" + " background-color: #e74c3c;" + " color: white;" + " border: none;" + " border-radius: 15px;" + " font-size: 36px;" + " font-weight: bold;" + "}" + "QPushButton:hover {" + " background-color: #c0392b;" + "}" + "QPushButton:pressed {" + " background-color: #a93226;" + " transform: scale(0.95);" + "}" + ); + + // Affichage de la quantité + QLabel* quantityDisplay = new QLabel("1"); + quantityDisplay->setAlignment(Qt::AlignCenter); + quantityDisplay->setMinimumSize(120, 80); + quantityDisplay->setStyleSheet( + "QLabel {" + " background-color: white;" + " border: 3px solid #bdc3c7;" + " border-radius: 15px;" + " font-size: 32px;" + " font-weight: bold;" + " color: #2c3e50;" + "}" + ); + + // Bouton + + QPushButton* plusButton = new QPushButton("+"); + plusButton->setMinimumSize(80, 80); + plusButton->setStyleSheet( + "QPushButton {" + " background-color: #27ae60;" + " color: white;" + " border: none;" + " border-radius: 15px;" + " font-size: 36px;" + " font-weight: bold;" + "}" + "QPushButton:hover {" + " background-color: #229954;" + "}" + "QPushButton:pressed {" + " background-color: #1e8449;" + " transform: scale(0.95);" + "}" + ); + + // Connecter les boutons avec limites + connect(minusButton, &QPushButton::clicked, [quantityDisplay]() { + int currentValue = quantityDisplay->text().toInt(); + if (currentValue > 1) { + quantityDisplay->setText(QString::number(currentValue - 1)); + } + }); + + connect(plusButton, &QPushButton::clicked, [quantityDisplay, currentQuantity]() { + int currentValue = quantityDisplay->text().toInt(); + if (currentValue < currentQuantity) { + quantityDisplay->setText(QString::number(currentValue + 1)); + } + }); + + tactileLayout->addStretch(); + tactileLayout->addWidget(minusButton); + tactileLayout->addWidget(quantityDisplay); + tactileLayout->addWidget(plusButton); + tactileLayout->addStretch(); + + quantityMainLayout->addWidget(tactileWidget); + layout->addWidget(quantityWidget); + + // Boutons d'action + QWidget* buttonWidget = new QWidget(); + QHBoxLayout* buttonLayout = new QHBoxLayout(buttonWidget); + buttonLayout->setSpacing(20); + + QPushButton* confirmButton = new QPushButton("✓ Confirmer le retrait"); + QPushButton* cancelButton = new QPushButton("✕ Annuler"); + + QString buttonStyle = + "QPushButton {" + " border: none;" + " border-radius: 8px;" + " font-size: 18px;" + " font-weight: bold;" + " padding: 15px 25px;" + " min-height: 50px;" + "}" + "QPushButton:pressed {" + " transform: translateY(1px);" + "}"; + + confirmButton->setStyleSheet(buttonStyle + + "QPushButton { background-color: #e74c3c; color: white; }" + "QPushButton:hover { background-color: #c0392b; }"); + + cancelButton->setStyleSheet(buttonStyle + + "QPushButton { background-color: #95a5a6; color: white; }" + "QPushButton:hover { background-color: #7f8c8d; }"); + + // Connecter les boutons + connect(confirmButton, &QPushButton::clicked, [this, stockName, itemTypeName, quantityDisplay, screen]() { + int quantityToRemove = quantityDisplay->text().toInt(); + + // Effectuer le retrait + std::vector args = {stockName.toStdString(), itemTypeName.toStdString(), std::to_string(quantityToRemove)}; + m_controller->sendCommand(RemoveItemFromStock, args); + + // Retourner à l'écran de sélection d'articles et actualiser + refreshItemRemovalScreen(); + m_stackedWidget->removeWidget(screen); + delete screen; + m_stackedWidget->setCurrentWidget(m_removeItemScreen); + + // Afficher un message de succès + displayResult(QString("✅ %1 unité(s) de '%2' retirée(s) du stock '%3'") + .arg(quantityToRemove).arg(itemTypeName).arg(stockName)); + }); + + connect(cancelButton, &QPushButton::clicked, [this, screen]() { + // Retourner à l'écran de sélection d'articles sans retrait + m_stackedWidget->removeWidget(screen); + delete screen; + m_stackedWidget->setCurrentWidget(m_removeItemScreen); + }); + + buttonLayout->addWidget(confirmButton); + buttonLayout->addWidget(cancelButton); + layout->addWidget(buttonWidget); + + layout->addStretch(); + + return screen; +} + +void QtView::showQuantityRemovalScreen(const QString& stockName, const QString& itemTypeName, int currentQuantity) +{ + // Créer l'écran de sélection de quantité + QWidget* quantityScreen = createItemQuantityRemovalScreen(stockName, itemTypeName, currentQuantity); + + // Ajouter l'écran et l'afficher + m_stackedWidget->addWidget(quantityScreen); + m_stackedWidget->setCurrentWidget(quantityScreen); +} + +void QtView::showStockItemsForRemoval(const QString& stockName) +{ + // Créer un écran temporaire pour afficher les articles du stock sélectionné + QWidget* itemsScreen = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(itemsScreen); + layout->setSpacing(20); + layout->setContentsMargins(30, 30, 30, 30); + + // Titre + QLabel* titleLabel = new QLabel(QString("Articles dans le stock: %1").arg(stockName)); + titleLabel->setStyleSheet("font-size: 28px; font-weight: bold; color: #2c3e50; margin-bottom: 20px;"); + titleLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(titleLabel); + + // Sous-titre + QLabel* subtitleLabel = new QLabel("Sélectionnez l'article à retirer"); + subtitleLabel->setStyleSheet("font-size: 16px; color: #7f8c8d; margin-bottom: 20px;"); + subtitleLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(subtitleLabel); + + // Scroll area pour la liste des articles + QScrollArea* scrollArea = new QScrollArea(); + scrollArea->setWidgetResizable(true); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setStyleSheet( + "QScrollArea {" + " border: 2px solid #bdc3c7;" + " border-radius: 8px;" + " background-color: white;" + "}" + ); + + // Widget conteneur pour les boutons d'articles + QWidget* itemListWidget = new QWidget(); + QVBoxLayout* itemLayout = new QVBoxLayout(itemListWidget); + itemLayout->setSpacing(10); + itemLayout->setContentsMargins(10, 10, 10, 10); + + // Obtenir le stock spécifique + if (m_controller && m_controller->getModel()) { + auto stocks = m_controller->getModel()->getStocks(); + for (const auto& stock : stocks) { + if (QString::fromStdString(stock.getName()) == stockName) { + auto itemsInStock = stock.getItemsWithQuantities(); + + if (itemsInStock.empty()) { + QLabel* emptyLabel = new QLabel("Aucun article dans ce stock"); + emptyLabel->setStyleSheet( + "QLabel {" + " font-size: 18px;" + " color: #7f8c8d;" + " padding: 20px;" + " text-align: center;" + "}" + ); + emptyLabel->setAlignment(Qt::AlignCenter); + itemLayout->addWidget(emptyLabel); + } else { + // Créer un bouton pour chaque type d'article dans le stock + for (const auto& [itemName, itemData] : itemsInStock) { + const ItemType& itemType = itemData.first; + int quantity = itemData.second; + QString itemTypeName = QString::fromStdString(itemType.getName()); + + // Informations détaillées sur l'article + QString itemInfo = QString("%1\nQuantité disponible: %2\nTaille: %3") + .arg(itemTypeName) + .arg(quantity) + .arg(itemType.getSize()); + + QPushButton* itemButton = new QPushButton(itemInfo); + itemButton->setMinimumHeight(80); + itemButton->setStyleSheet( + "QPushButton {" + " background-color: #e74c3c;" + " color: white;" + " border: none;" + " border-radius: 8px;" + " font-size: 16px;" + " font-weight: bold;" + " padding: 15px;" + " text-align: left;" + "}" + "QPushButton:hover {" + " background-color: #c0392b;" + "}" + "QPushButton:pressed {" + " transform: translateY(1px);" + "}" + ); + + // Connecter le bouton à l'écran de sélection de quantité + connect(itemButton, &QPushButton::clicked, [this, stockName, itemTypeName, quantity, itemsScreen]() { + // Créer l'écran de sélection de quantité + QWidget* quantityScreen = createItemQuantityRemovalScreen(stockName, itemTypeName, quantity); + + // Ajouter l'écran et l'afficher + m_stackedWidget->addWidget(quantityScreen); + m_stackedWidget->setCurrentWidget(quantityScreen); + + // Supprimer l'écran temporaire des articles + m_stackedWidget->removeWidget(itemsScreen); + delete itemsScreen; + }); + + itemLayout->addWidget(itemButton); + } + } + break; + } + } + } + + scrollArea->setWidget(itemListWidget); + layout->addWidget(scrollArea); + + // Bouton retour + QPushButton* backButton = new QPushButton("← Retour à la sélection de stock"); + backButton->setStyleSheet( + "QPushButton {" + " background-color: #95a5a6;" + " color: white;" + " border: none;" + " border-radius: 8px;" + " font-size: 16px;" + " font-weight: bold;" + " padding: 12px 20px;" + " min-height: 40px;" + "}" + "QPushButton:hover { background-color: #7f8c8d; }" + "QPushButton:pressed { transform: translateY(1px); }" + ); + connect(backButton, &QPushButton::clicked, [this, itemsScreen]() { + // Retourner à l'écran de sélection de stock + m_stackedWidget->removeWidget(itemsScreen); + delete itemsScreen; + m_stackedWidget->setCurrentWidget(m_removeItemScreen); + }); + layout->addWidget(backButton); + + // Ajouter un stretch à la fin + itemLayout->addStretch(); + + // Ajouter l'écran et l'afficher + m_stackedWidget->addWidget(itemsScreen); + m_stackedWidget->setCurrentWidget(itemsScreen); +} + // ============================================================================= // MÉTHODES DE NAVIGATION // ============================================================================= @@ -1484,6 +2637,27 @@ void QtView::showActionScreen(QWidget* screen) refreshStockSelectionScreen(); } + // Si c'est l'écran de suppression de type d'article, actualiser la liste + if (screen == m_deleteItemTypeScreen) { + refreshItemTypeSelectionScreen(); + } + + // Si c'est l'écran de retrait d'article, actualiser la liste des articles + if (screen == m_removeItemScreen) { + refreshItemRemovalScreen(); + } + + // Rafraîchir les ComboBox pour les écrans de gestion d'articles (ajout et déplacement seulement) + if (screen == m_addItemScreen || screen == m_moveItemScreen) { + refreshComboBoxes(screen); + resetFormFields(screen); + } + + // Réinitialiser les formulaires pour les autres écrans + if (screen == m_createStockScreen || screen == m_createItemTypeScreen || screen == m_deleteItemTypeScreen || screen == m_showStockContentScreen) { + resetFormFields(screen); + } + m_stackedWidget->setCurrentIndex(index); // Afficher le bouton retour @@ -1531,6 +2705,14 @@ void QtView::executeFormAction(QWidget* screen, const QString& title) continue; } + // Ensuite chercher un QLabel (pour l'affichage tactile de quantité) + QLabel* label = screen->findChild(fieldName); + if (label) { + values.append(label->text()); + fieldIndex++; + continue; + } + // Sinon chercher un QLineEdit QLineEdit* lineEdit = screen->findChild(fieldName); if (lineEdit) { @@ -1548,14 +2730,10 @@ void QtView::executeFormAction(QWidget* screen, const QString& title) executeCreateStock(values); } else if (title.contains("Créer un Type d'Article")) { executeCreateItemType(values); - } else if (title.contains("Supprimer un Type")) { - executeDeleteItemType(values); } else if (title.contains("Ajouter un Article")) { executeAddItem(values); } else if (title.contains("Déplacer un Article")) { executeMoveItem(values); - } else if (title.contains("Retirer un Article")) { - executeRemoveItem(values); } else if (title.contains("Contenu des Stocks")) { executeShowStockContent(values); } @@ -1582,7 +2760,6 @@ void QtView::executeCreateStock(const QStringList& values) } m_controller->sendCommand(CreateStock, args); - showMessage(QString("Stock '%1' créé avec succès (capacité: %2)").arg(stockName).arg(capacity), false); goBack(); } else { displayResult("❌ ERREUR: La capacité doit être un nombre entier positif."); @@ -1598,7 +2775,6 @@ void QtView::executeDeleteStock(const QStringList& values) QString stockName = values[0].trimmed(); m_controller->sendCommand(DeleteStock, stockName.toStdString()); - showMessage(QString("Stock '%1' supprimé avec succès").arg(stockName), false); goBack(); } else { displayResult("❌ ERREUR: Veuillez spécifier le nom du stock à supprimer."); @@ -1607,37 +2783,33 @@ void QtView::executeDeleteStock(const QStringList& values) void QtView::executeCreateItemType(const QStringList& values) { - if (values.size() >= 4 && !values[0].trimmed().isEmpty() && !values[1].trimmed().isEmpty()) { + if (values.size() >= 3 && !values[0].trimmed().isEmpty()) { QString typeName = values[0].trimmed(); - bool idOk, sizeOk; - int id = values[1].trimmed().toInt(&idOk); - QString comment = values[2].trimmed(); - int size = values[3].trimmed().toInt(&sizeOk); + QString comment = values[1].trimmed(); + bool sizeOk; + int size = values[2].trimmed().toInt(&sizeOk); - if (idOk && sizeOk && id >= 0 && size > 0) { - std::vector args = {typeName.toStdString(), std::to_string(id), comment.toStdString(), std::to_string(size)}; + if (sizeOk && size > 0) { + // Générer automatiquement l'ID en trouvant le prochain ID disponible + int nextId = 1; + if (m_controller && m_controller->getModel()) { + auto itemTypes = m_controller->getModel()->getItemTypes(); + for (const auto& itemType : itemTypes) { + if (itemType.getId() >= nextId) { + nextId = itemType.getId() + 1; + } + } + } + + std::vector args = {typeName.toStdString(), std::to_string(nextId), comment.toStdString(), std::to_string(size)}; m_controller->sendCommand(CreateItemType, args); - showMessage(QString("Type d'article '%1' créé avec succès (ID: %2)").arg(typeName).arg(id), false); goBack(); } else { - displayResult("❌ ERREUR: L'ID et la taille doivent être des nombres entiers valides."); + displayResult("❌ ERREUR: La taille doit être un nombre entier positif."); } } else { - displayResult("❌ ERREUR: Veuillez remplir tous les champs obligatoires."); - } -} - -void QtView::executeDeleteItemType(const QStringList& values) -{ - if (values.size() >= 1 && !values[0].trimmed().isEmpty()) { - QString typeName = values[0].trimmed(); - - m_controller->sendCommand(DeleteItemType, typeName.toStdString()); - showMessage(QString("Type d'article '%1' supprimé avec succès").arg(typeName), false); - goBack(); - } else { - displayResult("❌ ERREUR: Veuillez spécifier le nom du type à supprimer."); + displayResult("❌ ERREUR: Veuillez remplir le nom du type et la taille."); } } @@ -1653,7 +2825,6 @@ void QtView::executeAddItem(const QStringList& values) std::vector args = {stockName.toStdString(), itemTypeName.toStdString(), std::to_string(quantity)}; m_controller->sendCommand(AddItemToStock, args); - showMessage(QString("%1 article(s) de type '%2' ajouté(s) au stock '%3'").arg(quantity).arg(itemTypeName).arg(stockName), false); goBack(); } else { displayResult("❌ ERREUR: La quantité doit être un nombre entier positif."); @@ -1676,29 +2847,6 @@ void QtView::executeMoveItem(const QStringList& values) std::vector args = {sourceStock.toStdString(), destStock.toStdString(), itemTypeName.toStdString(), std::to_string(quantity)}; m_controller->sendCommand(MoveItemBetweenStocks, args); - showMessage(QString("%1 article(s) de type '%2' déplacé(s) de '%3' vers '%4'").arg(quantity).arg(itemTypeName).arg(sourceStock).arg(destStock), false); - goBack(); - } else { - displayResult("❌ ERREUR: La quantité doit être un nombre entier positif."); - } - } else { - displayResult("❌ ERREUR: Veuillez remplir tous les champs."); - } -} - -void QtView::executeRemoveItem(const QStringList& values) -{ - if (values.size() >= 3 && !values[0].trimmed().isEmpty() && !values[1].trimmed().isEmpty()) { - QString stockName = values[0].trimmed(); - QString itemTypeName = values[1].trimmed(); - bool ok; - int quantity = values[2].trimmed().toInt(&ok); - - if (ok && quantity > 0) { - std::vector args = {stockName.toStdString(), itemTypeName.toStdString(), std::to_string(quantity)}; - - m_controller->sendCommand(RemoveItemFromStock, args); - showMessage(QString("%1 article(s) de type '%2' retiré(s) du stock '%3'").arg(quantity).arg(itemTypeName).arg(stockName), false); goBack(); } else { displayResult("❌ ERREUR: La quantité doit être un nombre entier positif."); @@ -1748,8 +2896,23 @@ void QtView::onShowStatistics() // MÉTHODES UTILITAIRES POUR LES DIALOGUES // ============================================================================= +void QtView::clearMessages() +{ + m_outputDisplay->clear(); +} + +void QtView::displayNotify(const QString& message) +{ + m_outputDisplay->append(message); +} + void QtView::showMessage(const QString& message, bool isError) { + if (message.isEmpty()) return; + + // clear previous messages + clearMessages(); + // Afficher seulement dans la zone de sortie avec du style QString prefix = isError ? "❌ ERREUR" : "✅ INFO"; QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss"); @@ -1758,9 +2921,9 @@ void QtView::showMessage(const QString& message, bool isError) .arg(timestamp) .arg(prefix) .arg(message); - - m_outputDisplay->append(styledMessage); - + + displayNotify(styledMessage); + // Faire défiler vers le bas pour voir le nouveau message QScrollBar* scrollBar = m_outputDisplay->verticalScrollBar(); scrollBar->setValue(scrollBar->maximum()); diff --git a/src/view/qtView.hpp b/src/view/qtView.hpp index 96a04c5..a94a264 100644 --- a/src/view/qtView.hpp +++ b/src/view/qtView.hpp @@ -69,7 +69,7 @@ private slots: void onDisplayMenuSelected(); // Application control - void onRefresh(); + // Removed refresh functionality // Virtual keyboard slots void onTextFieldFocused(); @@ -100,24 +100,34 @@ private: QWidget* createTitleAndButtonScreen(const QString& title, const QStringList& buttonTexts, const std::vector>& callbacks); QWidget* createFormScreen(const QString& title, const QStringList& labels, const QStringList& placeholders = QStringList()); QWidget* createSelectionFormScreen(const QString& title, const QStringList& labels, const QStringList& fieldTypes); + QWidget* createTactileQuantityScreen(const QString& title, const QStringList& labels, const QStringList& fieldTypes); QWidget* createStockSelectionScreen(); + QWidget* createItemTypeSelectionScreen(); + QWidget* createItemRemovalScreen(); + QWidget* createItemQuantityRemovalScreen(const QString& stockName, const QString& itemTypeName, int currentQuantity); QWidget* createStockDisplayScreen(); QWidget* createItemTypeDisplayScreen(); void refreshStockSelectionScreen(); + void refreshItemTypeSelectionScreen(); + void refreshItemRemovalScreen(); void updateStockDisplayScreen(const std::vector& stocks); void updateItemTypeDisplayScreen(const std::vector& itemTypes); + void refreshComboBoxes(QWidget* screen); + void resetFormFields(QWidget* screen); void confirmStockDeletion(const QString& stockName); + void confirmItemTypeDeletion(const QString& itemTypeName); + void showQuantityRemovalScreen(const QString& stockName, const QString& itemTypeName, int currentQuantity); + void showStockItemsForRemoval(const QString& stockName); void addSuccessMessageToStockScreen(const QString& message); + void addSuccessMessageToItemTypeScreen(const QString& message); // Form execution methods void executeFormAction(QWidget* screen, const QString& title); void executeCreateStock(const QStringList& values); void executeDeleteStock(const QStringList& values); void executeCreateItemType(const QStringList& values); - void executeDeleteItemType(const QStringList& values); void executeAddItem(const QStringList& values); void executeMoveItem(const QStringList& values); - void executeRemoveItem(const QStringList& values); void executeShowStockContent(const QStringList& values); // Direct action methods (without forms) @@ -127,6 +137,8 @@ private: void onShowStatistics(); // Méthodes pour les notifications (plus de popups) + void clearMessages(); + void displayNotify(const QString& message); void showMessage(const QString& message, bool isError = false); void displayResult(const QString& message); // Méthode pour afficher les résultats @@ -174,12 +186,15 @@ private: QWidget* m_createItemTypeScreen; QWidget* m_showItemTypesScreen; QWidget* m_deleteItemTypeScreen; + QVBoxLayout* m_itemTypeSelectionLayout; // Layout pour les boutons de types d'articles QWidget* m_itemTypeDisplayWidget; // Widget pour l'affichage des types d'articles QVBoxLayout* m_itemTypeDisplayLayout; // Layout pour l'affichage des types d'articles QWidget* m_addItemScreen; QWidget* m_moveItemScreen; QWidget* m_removeItemScreen; + QWidget* m_removeItemQuantityScreen; + QVBoxLayout* m_removeItemLayout; // Layout pour la liste des articles à retirer QWidget* m_showStockContentScreen; QWidget* m_showCapacitiesScreen; @@ -187,7 +202,6 @@ private: // Bottom area QTextEdit* m_outputDisplay; - QPushButton* m_refreshButton; // Virtual keyboard VirtualKeyboard* m_virtualKeyboard;