"""Tab de gestión de nodos.""" from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, QPushButton, QDialog, QFormLayout, QLineEdit, QTextEdit, QCheckBox, QDialogButtonBox, QMessageBox ) from PySide6.QtCore import Qt from ..db.node_repository import NodeRepository class NodeDialog(QDialog): """Diálogo para crear/editar nodos.""" def __init__(self, parent=None, node=None): """Inicializa el diálogo.""" super().__init__(parent) self.node = node self.setWindowTitle("Editar Nodo" if node else "Nuevo Nodo") self.setMinimumWidth(400) self._setup_ui() if node: self._load_node_data() def _setup_ui(self): """Configura la interfaz.""" layout = QFormLayout(self) self.node_name_input = QLineEdit() self.node_name_input.setPlaceholderText("Ej: BACKUP_SISTEMA.ZIP") layout.addRow("Nombre del Nodo:", self.node_name_input) self.db_name_input = QLineEdit() self.db_name_input.setPlaceholderText("Ej: Sistema_DB") layout.addRow("Base de Datos:", self.db_name_input) self.active_checkbox = QCheckBox() self.active_checkbox.setChecked(True) layout.addRow("Activo:", self.active_checkbox) self.notes_input = QTextEdit() self.notes_input.setPlaceholderText("Notas opcionales...") self.notes_input.setMaximumHeight(100) layout.addRow("Notas:", self.notes_input) # Botones buttons = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel ) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) layout.addRow(buttons) def _load_node_data(self): """Carga los datos del nodo a editar.""" self.node_name_input.setText(self.node.node_name) self.node_name_input.setEnabled(False) # No permitir cambiar el nombre self.db_name_input.setText(self.node.db_name) self.active_checkbox.setChecked(self.node.active) self.notes_input.setPlainText(self.node.notes or "") def get_data(self): """Obtiene los datos del formulario.""" return { "node_name": self.node_name_input.text().strip(), "db_name": self.db_name_input.text().strip(), "active": self.active_checkbox.isChecked(), "notes": self.notes_input.toPlainText().strip() } class NodesTab(QWidget): """Tab de gestión de nodos.""" def __init__(self, engine): """Inicializa el tab.""" super().__init__() self.engine = engine self._setup_ui() self.refresh_nodes() def _setup_ui(self): """Configura la interfaz.""" layout = QVBoxLayout(self) # Botones de acción actions_layout = QHBoxLayout() self.add_btn = QPushButton("➕ Nuevo Nodo") self.add_btn.clicked.connect(self._add_node) actions_layout.addWidget(self.add_btn) self.edit_btn = QPushButton("✏️ Editar") self.edit_btn.clicked.connect(self._edit_node) self.edit_btn.setEnabled(False) actions_layout.addWidget(self.edit_btn) self.delete_btn = QPushButton("🗑️ Eliminar") self.delete_btn.clicked.connect(self._delete_node) self.delete_btn.setEnabled(False) actions_layout.addWidget(self.delete_btn) actions_layout.addStretch() refresh_btn = QPushButton("🔄 Actualizar") refresh_btn.clicked.connect(self.refresh_nodes) actions_layout.addWidget(refresh_btn) layout.addLayout(actions_layout) # Tabla de nodos self.nodes_table = QTableWidget() self.nodes_table.setColumnCount(4) self.nodes_table.setHorizontalHeaderLabels([ "Nombre del Nodo", "Base de Datos", "Activo", "Notas" ]) self.nodes_table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows) self.nodes_table.setSelectionMode(QTableWidget.SelectionMode.SingleSelection) self.nodes_table.itemSelectionChanged.connect(self._on_selection_changed) self.nodes_table.itemDoubleClicked.connect(self._edit_node) layout.addWidget(self.nodes_table) # Info info_label = QLabel( "💡 El nombre del nodo debe coincidir con el nombre del archivo ZIP (con extensión). " "Se compara en MAYÚSCULAS y case-insensitive." ) info_label.setWordWrap(True) info_label.setStyleSheet("color: #666; font-size: 10pt;") layout.addWidget(info_label) def refresh_nodes(self): """Refresca la lista de nodos.""" nodes = NodeRepository.get_all() self.nodes_table.setRowCount(len(nodes)) for i, node in enumerate(nodes): self.nodes_table.setItem(i, 0, QTableWidgetItem(node.node_name)) self.nodes_table.setItem(i, 1, QTableWidgetItem(node.db_name)) active_item = QTableWidgetItem("✓" if node.active else "✗") active_item.setForeground( Qt.GlobalColor.darkGreen if node.active else Qt.GlobalColor.red ) self.nodes_table.setItem(i, 2, active_item) self.nodes_table.setItem(i, 3, QTableWidgetItem(node.notes or "")) # Guardar node_name en la fila self.nodes_table.item(i, 0).setData(Qt.ItemDataRole.UserRole, node.node_name) self.nodes_table.resizeColumnsToContents() def _on_selection_changed(self): """Maneja el cambio de selección.""" has_selection = len(self.nodes_table.selectedItems()) > 0 self.edit_btn.setEnabled(has_selection) self.delete_btn.setEnabled(has_selection) def _add_node(self): """Abre el diálogo para agregar un nodo.""" dialog = NodeDialog(self) if dialog.exec() == QDialog.DialogCode.Accepted: data = dialog.get_data() if not data["node_name"] or not data["db_name"]: QMessageBox.warning( self, "Error", "El nombre del nodo y la base de datos son obligatorios." ) return try: NodeRepository.create( node_name=data["node_name"], db_name=data["db_name"], notes=data["notes"] ) if not data["active"]: NodeRepository.update(data["node_name"], active=False) self.refresh_nodes() QMessageBox.information(self, "Éxito", "Nodo creado exitosamente.") except Exception as e: QMessageBox.critical(self, "Error", f"Error creando nodo: {e}") def _edit_node(self): """Abre el diálogo para editar un nodo.""" selected_items = self.nodes_table.selectedItems() if not selected_items: return row = selected_items[0].row() node_name = self.nodes_table.item(row, 0).data(Qt.ItemDataRole.UserRole) node = NodeRepository.get(node_name) if not node: return dialog = NodeDialog(self, node) if dialog.exec() == QDialog.DialogCode.Accepted: data = dialog.get_data() if not data["db_name"]: QMessageBox.warning(self, "Error", "La base de datos es obligatoria.") return try: NodeRepository.update( node_name=node_name, db_name=data["db_name"], active=data["active"], notes=data["notes"] ) self.refresh_nodes() QMessageBox.information(self, "Éxito", "Nodo actualizado exitosamente.") except Exception as e: QMessageBox.critical(self, "Error", f"Error actualizando nodo: {e}") def _delete_node(self): """Elimina el nodo seleccionado.""" selected_items = self.nodes_table.selectedItems() if not selected_items: return row = selected_items[0].row() node_name = self.nodes_table.item(row, 0).data(Qt.ItemDataRole.UserRole) reply = QMessageBox.question( self, "Confirmar Eliminación", f"¿Está seguro de eliminar el nodo '{node_name}'?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply == QMessageBox.StandardButton.Yes: try: NodeRepository.delete(node_name) self.refresh_nodes() QMessageBox.information(self, "Éxito", "Nodo eliminado exitosamente.") except Exception as e: QMessageBox.critical(self, "Error", f"Error eliminando nodo: {e}")