Files
CloudRecoveryAS/app/ui/nodes_tab.py

258 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Tab de gestión de nodos."""
from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QTableWidget,
QTableWidgetItem, QPushButton, QDialog, QFormLayout,
QLineEdit, QTextEdit, QCheckBox, QDialogButtonBox,
QMessageBox, QLabel
)
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}")