Initial commit: CloudRestoreAS v1.0.0 - Aplicación completa de restauración automática SQL Server
This commit is contained in:
257
app/ui/nodes_tab.py
Normal file
257
app/ui/nodes_tab.py
Normal file
@@ -0,0 +1,257 @@
|
||||
"""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}")
|
||||
Reference in New Issue
Block a user