You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1022 lines
39 KiB

let nodes = [];
let connections = [];
const body = document.querySelector("body");
// Función para obtener el CSRF token
function getCSRFToken() {
const name = 'csrftoken';
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
// Función para extraer el nombre del archivo desde la URL
function extractFileName(url) {
return url.split('/').pop();
}
// Función para crear el menú contextual
function createContextMenu(x, y, options) {
// Eliminar cualquier menú existente
removeContextMenu();
const menu = document.createElement('div');
menu.classList.add('context-menu');
menu.style.top = `${y}px`;
menu.style.left = `${x}px`;
options.forEach(option => {
const item = document.createElement('div');
item.classList.add('context-menu-item');
item.textContent = option.label;
item.addEventListener('click', option.action);
menu.appendChild(item);
});
document.body.appendChild(menu);
menu.classList.add('show');
// Cerrar el menú al hacer clic en cualquier parte
document.addEventListener('click', removeContextMenu);
}
// Función para eliminar el menú contextual
function removeContextMenu() {
const existingMenu = document.querySelector('.context-menu');
if (existingMenu) {
existingMenu.remove();
document.removeEventListener('click', removeContextMenu);
}
}
// Función para crear un nodo
function createNode(e, tabId) {
let container = document.getElementById(`nodeContainer${tabId}`);
let element = document.createElement('div');
element.className = "slice_node";
element.style.position = 'absolute';
element.style.top = '100px';
element.style.left = '100px';
element.id = `node${Date.now()}`; // Asignar un ID único al nodo
// Inicializar propiedades adicionales
element.audioFile = "";
element.textFile = "";
element.extensionNumber = ""; // Para Node1
element.content = ""; // Almacenar internamente el contenido
element.extensionNumber = ""; // Para node4
element.conditions = []; // Para condiciones dinámicas en node3
let node_type = e.target.getAttribute('data-node-type');
let textContent = e.target.textContent;
// Crear elemento para el título
const titleElement = document.createElement('div');
titleElement.className = 'node-title';
titleElement.textContent = textContent; // Solo el nombre, sin ID
element.appendChild(titleElement);
// Crear elemento para el nombre del archivo .txt (Node3)
if (node_type === "node3") {
const txtFileElement = document.createElement('div');
txtFileElement.className = 'txt-file';
txtFileElement.textContent = "No guardado"; // Inicialmente sin archivo
txtFileElement.style.display = 'none'; // Ocultar por defecto
element.appendChild(txtFileElement);
}
// Crear elemento para el ícono de reproducción (Node2 y Node3)
if (node_type === "node2") { // Solo para node2
const playIcon = document.createElement('span');
playIcon.className = 'play-icon';
playIcon.innerHTML = '&#9654;'; // Unicode para el triángulo de reproducción
playIcon.title = "Reproducir Audio";
playIcon.style.cursor = 'pointer';
playIcon.style.marginLeft = '10px';
playIcon.style.color = 'white';
playIcon.style.fontSize = '16px';
playIcon.style.verticalAlign = 'middle';
playIcon.onclick = function() {
if (element.audioFile) {
let audio = new Audio(element.audioFile);
audio.play();
} else {
alert("No hay audio asociado a este nodo.");
}
};
element.appendChild(playIcon);
}
// Asignar puertos en base al tipo de nodo
if (node_type === "node1") {
element.style.background = "#92a5b9";
element.setAttribute('data-node-type', 'node1');
const portOut = document.createElement('div');
portOut.className = 'port port-out';
element.appendChild(portOut);
} else if (node_type === "node2") {
element.style.background = "#00BFA5";
element.setAttribute('data-node-type', 'node2');
const portIn = document.createElement('div');
portIn.className = 'port port-in';
element.appendChild(portIn);
const portOut = document.createElement('div');
portOut.className = 'port port-out';
element.appendChild(portOut);
} else if (node_type === "node3") {
element.style.background = "#DDDDDD";
element.setAttribute('data-node-type', 'node3');
const portIn = document.createElement("div");
portIn.className = "port port-in";
element.appendChild(portIn);
const portOut = document.createElement("div");
portOut.className = "port port-out";
element.appendChild(portOut); // Añadimos un puerto de salida
} else if (node_type === "node4") {
element.style.background = "#1DE9B6"; // Cambiar color a lightseagreen
element.setAttribute('data-node-type', 'node4');
const displayElement = document.createElement('div');
displayElement.className = 'display-extension';
displayElement.textContent = "No recibido"; // Inicialmente sin string
displayElement.style = "display: none";
element.appendChild(displayElement);
const portIn = document.createElement("div");
portIn.className = "port port-in";
element.appendChild(portIn);
}
container.appendChild(element);
// Permitir mover el nodo
element.addEventListener('mousedown', (event) => {
if (!event.target.classList.contains('port') && !event.target.classList.contains('play-icon')) {
mouseDown(event, element, tabId);
}
});
// Agregar eventos a los puertos
const ports = element.querySelectorAll('.port');
ports.forEach(port => {
port.addEventListener('mousedown', (e) => {
startConnection(e, tabId);
});
});
// Agregar evento de doble clic para abrir el modal
element.addEventListener('dblclick', (e) => openModal(e, element));
// Añadir evento de clic derecho para mostrar el menú contextual
element.addEventListener('contextmenu', (e) => {
e.preventDefault();
createContextMenu(e.pageX, e.pageY, [
{
label: 'Eliminar Nodo',
action: () => deleteNode(element, tabId)
}
]);
});
nodes.push(element); // Agregar nodo al array global
}
// Función para manejar el movimiento de los nodos
function mouseDown(e, nodeElement, tabId) {
let startX = e.clientX;
let startY = e.clientY;
let node_target = nodeElement;
let moveHandler = (e) => {
let newX = startX - e.clientX;
let newY = startY - e.clientY;
startX = e.clientX;
startY = e.clientY;
node_target.style.top = (node_target.offsetTop - newY) + 'px';
node_target.style.left = (node_target.offsetLeft - newX) + 'px';
updateConnectionPoints(tabId);
updateConnections(tabId); // Actualizar las conexiones cada vez que se mueva el nodo
};
document.addEventListener('mousemove', moveHandler);
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', moveHandler);
}, { once: true });
}
// Función para abrir el modal de edición del nodo
function openModal(e, node) {
const nodeType2 = e.target.dataset.nodeType;
const parentElement = e.target.parentElement;
const nodeType = parentElement.getAttribute('data-node-type');
const fragment = document.createDocumentFragment();
const modal = document.createElement("div");
modal.classList.add("modal");
modal.id = "modal";
const background = document.createElement("div");
background.classList.add("background");
background.id = "modal-overlay";
if ((nodeType2 || nodeType) == "node3") {
// Crear campo de entrada para el título del nodo
const titleLabel = document.createElement('label');
titleLabel.textContent = 'Título del nodo:';
titleLabel.style.color = 'black';
fragment.appendChild(titleLabel);
const titleInput = document.createElement('input');
titleInput.type = 'text';
titleInput.id = 'nodeTitleInput';
titleInput.value = node.querySelector('.node-title').textContent || '';
titleInput.style = 'width: 250px; height: 30px; margin-bottom: 10px;';
fragment.appendChild(titleInput);
// Crear campo de entrada para el contenido del nodo
const contentLabel = document.createElement('label');
contentLabel.textContent = 'Contenido del nodo:';
contentLabel.style.color = 'black';
fragment.appendChild(contentLabel);
const contentInput = document.createElement('textarea');
contentInput.id = 'nodeContentInput';
contentInput.value = node.content || ""; // Mostrar contenido si existe
contentInput.style = 'width: 250px; height: 70px; margin-bottom: 10px;';
fragment.appendChild(contentInput);
// Sección para condiciones dinámicas
const conditionsContainer = document.createElement('div');
conditionsContainer.id = 'conditionsContainer';
conditionsContainer.style.marginBottom = '10px';
const conditionsLabel = document.createElement('label');
conditionsLabel.textContent = 'Condiciones Dinámicas:';
conditionsLabel.style.color = 'black';
conditionsContainer.appendChild(conditionsLabel);
const addConditionButton = document.createElement('button');
addConditionButton.textContent = 'Agregar Condición';
addConditionButton.type = 'button';
addConditionButton.style.marginLeft = '10px';
addConditionButton.onclick = addCondition;
conditionsContainer.appendChild(addConditionButton);
// Lista de condiciones
const conditionsList = document.createElement('ul');
conditionsList.id = 'conditionsList';
conditionsList.style.listStyleType = 'none';
conditionsList.style.padding = '0';
conditionsContainer.appendChild(conditionsList);
fragment.appendChild(conditionsContainer);
// Función para agregar una nueva condición
function addCondition(condition = { condition_string: '', target_node_id: '' }) {
const conditionItem = document.createElement('li');
conditionItem.style.marginBottom = '5px';
const conditionStringInput = document.createElement('input');
conditionStringInput.type = 'text';
conditionStringInput.placeholder = 'String de Condición';
conditionStringInput.value = condition.condition_string;
conditionStringInput.style.width = '150px';
conditionStringInput.style.marginRight = '10px';
conditionStringInput.style = 'border-radius: 5px; border: 1px solid gray;';
conditionItem.appendChild(conditionStringInput);
const targetNodeSelect = document.createElement('select');
targetNodeSelect.style.width = '150px';
targetNodeSelect.style.borderRadius = '5px';
targetNodeSelect.style.border = '1px solid gray';
targetNodeSelect.innerHTML = '<option value="">Seleccionar node4</option>';
// Obtener todos los node4 disponibles
const tabId = getTabIdFromNode(node);
const nodeContainer = document.getElementById(`nodeContainer${tabId}`);
const node4Elements = nodeContainer.querySelectorAll('[data-node-type="node4"]');
node4Elements.forEach(n4 => {
const option = document.createElement('option');
option.value = n4.id;
option.textContent = n4.querySelector('.node-title').textContent;
if (condition.target_node_id === n4.id) {
option.selected = true;
}
targetNodeSelect.appendChild(option);
});
conditionItem.appendChild(targetNodeSelect);
const removeConditionButton = document.createElement('button');
removeConditionButton.textContent = 'Eliminar';
removeConditionButton.type = 'button';
removeConditionButton.style.marginLeft = '10px';
removeConditionButton.style.borderRadius = '5px';
removeConditionButton.style.border = '1px solid gray';
removeConditionButton.onclick = () => {
conditionItem.remove();
};
conditionItem.appendChild(removeConditionButton);
conditionsList.appendChild(conditionItem);
}
// Cargar condiciones existentes si las hay
if (node.conditions && node.conditions.length > 0) {
node.conditions.forEach(cond => addCondition(cond));
}
const saveButton = document.createElement("button");
saveButton.classList.add("saveButton");
saveButton.innerText = "Guardar";
saveButton.onclick = async function() {
// Actualizar el título y contenido del nodo
const newTitle = titleInput.value.trim();
const newContent = contentInput.value.trim();
if (!newTitle || !newContent) {
alert("El título y el contenido no pueden estar vacíos.");
return;
}
const oldTitle = node.querySelector('.node-title').textContent;
node.querySelector('.node-title').textContent = newTitle;
// Obtener las condiciones
const conditions = [];
const conditionItems = conditionsList.querySelectorAll('li');
conditionItems.forEach(item => {
const conditionString = item.querySelector('input').value.trim();
const targetNodeId = item.querySelector('select').value;
if (conditionString && targetNodeId) {
conditions.push({
condition_string: conditionString,
target_node_id: targetNodeId
});
}
});
// Obtener el tabId del nodo
const tabId = getTabIdFromNode(node);
// Obtener nodeType desde el atributo data-node-type
const nodeType = node.getAttribute('data-node-type');
const tabName = getTabName();
// **Obtener el nombre del archivo de audio del node2 conectado**
let audioFileName = 'saludo_claude'; // Valor por defecto
const connections = window.tabConnections[tabId] || [];
// Buscar conexiones que terminan en este node3
connections.forEach(connection => {
if (connection.endPoint.to.id === node.id) {
const fromNode = document.getElementById(connection.startPoint.from.id);
if (fromNode && fromNode.getAttribute('data-node-type') === 'node2') {
// Obtener el nombre del archivo de audio sin la ruta ni la extensión
if (fromNode.audioFile) {
const audioPathParts = fromNode.audioFile.split('/');
const audioFileWithExtension = audioPathParts[audioPathParts.length - 1];
audioFileName = audioFileWithExtension.split('.').slice(0, -1).join('.');
}
}
}
});
// Enviar los datos a save_tts para generar los archivos
try {
const response = await fetch('/save_tts/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
tabId: tabId,
tabName: tabName,
nodeId: node.id, // Incluir nodeId
nodeType: nodeType, // Incluir nodeType
title: newTitle,
content: newContent,
audioFile: audioFileName // Enviar el nombre del archivo de audio
})
});
const result = await response.json();
if (result.status === 'success') {
// Si el título cambió, eliminar archivos antiguos
if (newTitle !== oldTitle) {
await fetch('/delete_files/', { // Vista ya creada en views.py
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
tabId: tabId,
nodeId: node.id, // Incluir nodeId
nodeType: nodeType, // Incluir nodeType
oldTitle: oldTitle
})
});
}
// Actualizar el nodo con las URLs de los archivos
node.textFile = result.textFile;
node.content = newContent;
node.conditions = conditions; // Actualizar condiciones
// Actualizar el nombre del archivo .txt en el nodo
const txtFileElement = node.querySelector('.txt-file');
txtFileElement.style.display = 'none'; // Mantener oculto
alert("Archivo de texto guardado exitosamente.");
} else if (result.status === 'skipped') {
alert(result.message);
} else {
alert(`Error: ${result.message}`);
}
} catch (error) {
console.error('Error al generar archivos:', error);
alert('Ocurrió un error al generar los archivos.');
}
// Guardar las condiciones en la pestaña
await saveTabData(tabId);
// Cerrar el modal
modal.classList.remove("show");
background.classList.remove("show");
setTimeout(() => {
document.body.removeChild(background);
}, 300);
};
modal.appendChild(fragment);
modal.appendChild(saveButton);
setTimeout(() => {
modal.classList.add("show");
background.classList.add("show");
}, 10);
} else if ((nodeType2 || nodeType) == "node2") {
const div_node2 = document.createElement('div');
div_node2.classList.add('div_node2');
div_node2.id = 'div_node2';
// Crear campo de entrada para el título del nodo
const titleLabel = document.createElement('label');
titleLabel.textContent = 'Título del nodo:';
titleLabel.style.color = 'black';
div_node2.appendChild(titleLabel);
const iname_node2 = document.createElement('input');
iname_node2.type = 'text';
iname_node2.id = 'iname_node2';
iname_node2.classList.add('iname_node2');
iname_node2.placeholder = 'Título del nodo';
iname_node2.value = node.querySelector('.node-title').textContent || '';
iname_node2.style = 'width: 250px; height: 30px; margin-bottom: 10px;';
div_node2.appendChild(iname_node2);
// Crear campo de entrada para el archivo de audio
const audioLabel = document.createElement('label');
audioLabel.textContent = 'Cargar Audio:';
audioLabel.style.color = 'black';
div_node2.appendChild(audioLabel);
const input_node2 = document.createElement('input');
input_node2.type = 'file';
input_node2.accept = 'audio/*';
input_node2.id = 'input_node2';
input_node2.classList.add('input_node2');
div_node2.appendChild(input_node2);
// Mostrar el audio actual si existe
const audioPlay = document.createElement('audio');
audioPlay.id = 'audioPlayer';
audioPlay.classList.add('audioPlayer');
audioPlay.controls = true;
if (node.audioFile) {
audioPlay.src = node.audioFile;
}
div_node2.appendChild(audioPlay);
fragment.appendChild(div_node2);
modal.appendChild(fragment);
// Evento para cargar y previsualizar el audio
input_node2.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
const urlAudio = URL.createObjectURL(file);
audioPlay.src = urlAudio;
audioPlay.play().catch(error => {
console.error('Error al reproducir el audio: ', error);
});
}
});
const saveButton = document.createElement("button");
saveButton.classList.add("saveButton");
saveButton.innerText = "Guardar";
saveButton.onclick = async function() {
const newTitle = iname_node2.value.trim();
if (!newTitle) {
alert("El título no puede estar vacío.");
return;
}
const oldTitle = node.querySelector('.node-title').textContent;
node.querySelector('.node-title').textContent = newTitle;
const tabId = getTabIdFromNode(node);
let newAudioFile = node.audioFile;
// Obtener nodeType desde el atributo data-node-type
const nodeType = node.getAttribute('data-node-type');
// Si se ha seleccionado un nuevo archivo de audio
if (input_node2.files.length > 0) {
const file = input_node2.files[0];
const formData = new FormData();
formData.append('tabId', tabId);
formData.append('nodeId', node.id); // Incluir nodeId
formData.append('nodeType', nodeType); // Incluir nodeType
formData.append('title', newTitle);
formData.append('audioFile', file);
try {
const response = await fetch('/upload_audio/', { // Vista ya creada en views.py
method: 'POST',
headers: {
'X-CSRFToken': getCSRFToken(),
},
body: formData
});
const result = await response.json();
if (result.status === 'success') {
// Si el título o el audio cambió, eliminar archivos antiguos
if (newTitle !== oldTitle || node.audioFile !== result.audioFile) {
await fetch('/delete_audio/', { // Vista ya creada en views.py
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
tabId: tabId,
nodeId: node.id, // Incluir nodeId
nodeType: nodeType, // Incluir nodeType
oldAudioFile: node.audioFile
})
});
}
newAudioFile = result.audioFile;
node.audioFile = newAudioFile;
audioPlay.src = newAudioFile;
alert("Audio guardado exitosamente.");
} else {
alert(`Error: ${result.message}`);
}
} catch (error) {
console.error('Error al subir el audio:', error);
alert('Ocurrió un error al subir el audio.');
}
}
// Si el título cambió y no se subió un nuevo audio
if (newTitle !== oldTitle && input_node2.files.length === 0) {
// Renombrar el archivo de audio existente
try {
const response = await fetch('/rename_audio/', { // Vista ya creada en views.py
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
tabId: tabId,
nodeId: node.id, // Incluir nodeId
nodeType: nodeType, // Incluir nodeType
oldTitle: oldTitle,
newTitle: newTitle,
currentAudioFile: node.audioFile
})
});
const result = await response.json();
if (result.status === 'success') {
node.audioFile = result.newAudioFile;
audioPlay.src = result.newAudioFile;
alert("Audio renombrado exitosamente.");
} else {
alert(`Error: ${result.message}`);
}
} catch (error) {
console.error('Error al renombrar el audio:', error);
alert('Ocurrió un error al renombrar el audio.');
}
}
// Guardar los datos de la pestaña para incluir el título y la extensión
await saveTabData(tabId);
// Cerrar el modal
modal.classList.remove("show");
background.classList.remove("show");
setTimeout(() => {
document.body.removeChild(background);
}, 300);
};
modal.appendChild(saveButton);
setTimeout(() => {
modal.classList.add("show");
background.classList.add("show");
}, 10);
} else if ((nodeType2 || nodeType) == "node1") {
const div_a = document.createElement('div');
div_a.classList.add('div-a');
// Campo para el título del nodo
const titleLabel = document.createElement('label');
titleLabel.textContent = 'Título del nodo:';
titleLabel.style.color = 'black';
div_a.appendChild(titleLabel);
const title_node1 = document.createElement('input');
title_node1.type = 'text';
title_node1.id = 'title_node1';
title_node1.classList.add('title_node1');
title_node1.placeholder = 'Título del nodo';
title_node1.value = node.querySelector('.node-title').textContent || '';
title_node1.style = 'width: 250px; height: 30px; margin-bottom: 10px;';
div_a.appendChild(title_node1);
// Campo para el número de extensión
const extensionLabel = document.createElement('label');
extensionLabel.classList.add('label_node1');
extensionLabel.textContent = "Número de Extensión:";
extensionLabel.style.color = 'black';
div_a.appendChild(extensionLabel);
const input = document.createElement('input');
input.type = 'text';
input.classList = 'input_node1';
input.placeholder = 'Ingrese el número de extensión';
input.value = node.extensionNumber || '';
input.style = 'width: 250px; height: 30px; margin-bottom: 10px;';
div_a.appendChild(input);
const save = document.createElement('button');
save.textContent = "Guardar";
save.classList.add('save_node1');
div_a.appendChild(save);
modal.appendChild(div_a);
save.onclick = async function() {
const newTitle = title_node1.value.trim();
const newExtension = input.value.trim();
if (!newTitle || !newExtension) {
alert("El título y el número de extensión no pueden estar vacíos.");
return;
}
const oldTitle = node.querySelector('.node-title').textContent;
node.querySelector('.node-title').textContent = newTitle;
node.extensionNumber = newExtension; // Guardar internamente
// Obtener el tabId del nodo
const tabId = getTabIdFromNode(node);
// Obtener nodeType desde el atributo data-node-type
const nodeType = node.getAttribute('data-node-type');
const tabName = getTabName();
// Enviar los datos al servidor para actualizar Node1
try {
const response = await fetch('/update_node1/', { // Vista ya creada en views.py
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
tabId: tabId,
tabName: tabName,
nodeId: node.id, // Incluir nodeId
nodeType: nodeType, // Incluir nodeType
newTitle: newTitle,
newExtension: newExtension,
oldTitle: oldTitle
})
});
const result = await response.json();
if (result.status === 'success') {
alert("Nodo actualizado exitosamente.");
} else {
alert(`Error: ${result.message}`);
}
} catch (error) {
console.error('Error al actualizar el nodo:', error);
alert('Ocurrió un error al actualizar el nodo.');
}
// Guardar los datos de la pestaña para incluir el número de extensión
await saveTabData(tabId);
// Cerrar el modal
modal.classList.remove("show");
background.classList.remove("show");
setTimeout(() => {
document.body.removeChild(background);
}, 300);
};
setTimeout(() => {
modal.classList.add("show");
background.classList.add("show");
}, 10);
} else if ((nodeType2 || nodeType) == "node4") {
// Modal para node4
const displayDiv = document.createElement('div');
displayDiv.classList.add('div_node4');
displayDiv.id = 'div_node4';
// Crear campo para el Nombre del Área (Título del Nodo)
const areaLabel = document.createElement('label');
areaLabel.textContent = 'Nombre del área:';
areaLabel.style.color = 'black';
displayDiv.appendChild(areaLabel);
const areaInput = document.createElement('input');
areaInput.type = 'text';
areaInput.id = 'areaInput';
areaInput.classList.add('areaInput');
areaInput.value = node.querySelector('.node-title').textContent || '';
areaInput.style = 'width: 250px; height: 30px; margin-bottom: 10px;';
displayDiv.appendChild(areaInput);
// Crear campo para el Número de Extensión
const extensionLabel = document.createElement('label');
extensionLabel.textContent = 'Número de Extensión:';
extensionLabel.style.color = 'black';
displayDiv.appendChild(extensionLabel);
const extensionInput = document.createElement('input');
extensionInput.type = 'text';
extensionInput.id = 'extensionInput';
extensionInput.classList.add('extensionInput');
extensionInput.value = node.extensionNumber || '';
extensionInput.style = 'width: 250px; height: 30px; margin-bottom: 10px;';
displayDiv.appendChild(extensionInput);
const saveButton = document.createElement("button");
saveButton.classList.add("saveButton");
saveButton.innerText = "Guardar";
saveButton.onclick = async function() {
const newAreaName = areaInput.value.trim();
const newExtensionNumber = extensionInput.value.trim();
if (!newAreaName || !newExtensionNumber) {
alert("El nombre del área y el número de extensión no pueden estar vacíos.");
return;
}
// Actualizar el título del nodo (Nombre del Área)
const oldTitle = node.querySelector('.node-title').textContent;
node.querySelector('.node-title').textContent = newAreaName;
// Actualizar el número de extensión
node.extensionNumber = newExtensionNumber;
const displayElement = node.querySelector('.display-extension');
if (displayElement) {
displayElement.textContent = newExtensionNumber;
}
// Obtener el tabId del nodo
const tabId = getTabIdFromNode(node);
const tabName = getTabName()
// Obtener nodeType desde el atributo data-node-type
const nodeType = node.getAttribute('data-node-type');
// Guardar los datos de la pestaña para incluir el título y la extensión
await saveTabData(tabId);
// Enviar los datos al servidor para actualizar node4
try {
const response = await fetch('/update_node4/', { // Vista ya creada en views.py
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
tabId: tabId,
tabName: tabName,
nodeId: node.id, // Incluir nodeId
nodeType: nodeType, // Incluir nodeType
extensionNumber: newExtensionNumber
})
});
const result = await response.json();
if (result.status === 'success') {
alert("node4 actualizado exitosamente.");
} else {
alert(`Error: ${result.message}`);
}
} catch (error) {
console.error('Error al actualizar node4:', error);
alert('Ocurrió un error al actualizar node4.');
}
// Cerrar el modal
modal.classList.remove("show");
background.classList.remove("show");
setTimeout(() => {
document.body.removeChild(background);
}, 300);
};
displayDiv.appendChild(saveButton);
modal.appendChild(displayDiv);
setTimeout(() => {
modal.classList.add("show");
background.classList.add("show");
}, 10);
};
background.appendChild(modal);
document.body.appendChild(background);
}
// Función para obtener el tabId desde el nodo
function getTabIdFromNode(node) {
// Asumiendo que el contenedor de nodos está dentro de un elemento con id "tabNodes<tabId>"
const parent = node.closest('.tabNodes');
if (parent) {
const id = parent.id.replace('tabNodes', '');
return id;
}
return null;
}
function getTabName() {
const activeTab = document.querySelector('#tabsList .tab.active');
return activeTab ? activeTab.getAttribute('data-tab-name') : null;
}
// Función para cerrar el modal al presionar la tecla Esc
function closeModal(e) {
if (e.key === "Escape" || e.target.id === "modal-overlay") {
let background = document.getElementById("modal-overlay");
try {
document.body.removeChild(background);
} catch {
console.log("No hay modal abierto");
}
}
}
document.addEventListener('keydown', closeModal);
document.addEventListener('click', closeModal);
// Función para eliminar un nodo
async function deleteNode(node, tabId) {
const confirmDelete = confirm("¿Estás seguro de que deseas eliminar este nodo y sus conexiones?");
if (!confirmDelete) return;
const nodeId = node.id;
const nodeType = node.getAttribute('data-node-type');
console.log("Datos enviados para eliminar nodo:", { tabId, nodeId, nodeType });
try {
const response = await fetch('/delete_node/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
tabId: tabId,
nodeId: nodeId,
nodeType: nodeType
})
});
const result = await response.json();
if (result.status === 'success') {
// Eliminar el nodo del DOM
node.remove();
// Eliminar las conexiones asociadas
window.tabConnections[tabId] = window.tabConnections[tabId].filter(conn => {
return conn.startPoint.from.id !== nodeId && conn.endPoint.to.id !== nodeId;
});
// Actualizar el canvas
updateConnections(tabId);
alert("Nodo y sus conexiones eliminados exitosamente.");
} else {
alert(`Error: ${result.message}`);
}
} catch (error) {
console.error('Error al eliminar el nodo:', error);
alert('Ocurrió un error al eliminar el nodo.');
}
}
// Función para eliminar una pestaña
async function deleteTab(tabElement) {
const confirmDelete = confirm("¿Estás seguro de que deseas eliminar esta pestaña y todos sus contenidos?");
if (!confirmDelete) return;
const tabId = tabElement.id.slice(3); // Asumiendo que el ID es 'tab<id>'
const tabName = tabElement.getAttribute('data-tab-name');
try {
const response = await fetch('/delete_tab/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
tabId: tabId,
tabName: tabName
})
});
const result = await response.json();
if (result.status === 'success') {
// Eliminar la pestaña y su contenido del DOM
const contentElement = document.getElementById(`content${tabId}`);
tabElement.remove();
contentElement.remove();
// Eliminar datos de conexiones y nodos almacenados en JS
delete window.tabConnections[tabId];
delete tabNodes[tabId];
alert("Pestaña eliminada exitosamente.");
} else {
alert(`Error: ${result.message}`);
}
} catch (error) {
console.error('Error al eliminar la pestaña:', error);
alert('Ocurrió un error al eliminar la pestaña.');
}
}
// Exportar funciones necesarias para conexiones.js
window.initializeCanvas = initializeCanvas;
window.updateConnectionPoints = updateConnectionPoints;
window.updateConnections = updateConnections;
window.startConnection = startConnection;
window.deleteTab = deleteTab; // Exportar deleteTab para que pueda ser llamado desde el menú contextual