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.

1475 lines
57 KiB

// flow_doc.js
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 y Node4
element.content = ""; // Almacenar internamente el contenido
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);
if (node_type === "node7") {
element.style.background = "#A0D2EB"; // Color para node7
element.setAttribute('data-node-type', 'node7');
// Agregar puertos de entrada y salida
const portIn1 = document.createElement('div');
portIn1.className = 'port port-in';
element.appendChild(portIn1);
const portIn2 = document.createElement('div');
portIn2.className = 'port port-in';
element.appendChild(portIn2);
const portOut1 = document.createElement('div');
portOut1.className = 'port port-out';
element.appendChild(portOut1);
const portOut2 = document.createElement('div');
portOut2.className = 'port port-out';
element.appendChild(portOut2);
}
// Asignar propiedades y puertos según el tipo de nodo
if (node_type === "node5") {
element.style.background = "#CDDC39"; // Color para node5
element.setAttribute('data-node-type', 'node5');
// Agregar puertos de entrada y salida
const portIn1 = document.createElement('div');
portIn1.className = 'port port-in';
element.appendChild(portIn1);
const portIn2 = document.createElement('div');
portIn2.className = 'port port-in';
element.appendChild(portIn2);
const portOut1 = document.createElement('div');
portOut1.className = 'port port-out';
element.appendChild(portOut1);
const portOut2 = document.createElement('div');
portOut2.className = 'port port-out';
element.appendChild(portOut2);
}
if (node_type === "node6") {
element.style.background = "#BFD4BB"; // Color para node6
element.setAttribute('data-node-type', 'node6');
// Agregar puertos de entrada y salida
const portIn1 = document.createElement('div');
portIn1.className = 'port port-in';
element.appendChild(portIn1);
const portIn2 = document.createElement('div');
portIn2.className = 'port port-in';
element.appendChild(portIn2);
const portOut1 = document.createElement('div');
portOut1.className = 'port port-out';
element.appendChild(portOut1);
const portOut2 = document.createElement('div');
portOut2.className = 'port port-out';
element.appendChild(portOut2);
}
// 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" || node_type === "node6") { // 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
saveTabData(tabId);
}
// 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 o node6 conectado**
let audioFileName = 'saludo_claude'; // Por defecto
const connections = window.tabConnections[tabId] || [];
let audioFound = false;
connections.forEach(connection => {
if (connection.endPoint.to.id === node.id) {
const fromNode = document.getElementById(connection.startPoint.from.id);
console.log(fromNode)
if (fromNode) {
const fromNodeType = fromNode.getAttribute('data-node-type');
if (fromNodeType === 'node2' && fromNode.audioFile) {
audioFileName = extractFileName(fromNode.audioFile).split('.').slice(0, -1).join('.');
audioFound = true;
} else if (fromNodeType === 'node6' && fromNode.audioFile && !audioFound) {
// Si no se encontró audio en node2, usar node6
audioFileName = extractFileName(fromNode.audioFile).split('.').slice(0, -1).join('.');
audioFound = true;
console.log(audioFileName)
}
}
}
});
audioFileName = `${audioFileName}`;
console.log(audioFileName)
// 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');
const tabName = getTabName();
// 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.audioFile;
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);
} else if ((nodeType2 || nodeType) == "node5") {
const div_all = document.createElement('div');
div_all.classList.add('div_node5');
div_all.id = 'div_node5';
const label_select_node5 = document.createElement('p');
label_select_node5.innerText = 'Base de datos a la que deseas conectarte';
label_select_node5.classList.add('label_select_node5');
label_select_node5.id = 'label_select_node5';
div_all.append(label_select_node5);
const select_db = document.createElement('select');
select_db.classList.add('select_db');
select_db.id = 'select_db';
const options_db = [
{'value' : '', 'text' : 'Elige una opción'},
{'value' : 'mysql', 'text' : 'MySQL'},
{'value' : 'mysql', 'text' : 'MariaDB'},
{'value' : 'mssql', 'text' : 'SQL Server'},
{'value' : 'oracle', 'text' : 'OracleDB'},
{'value' : 'postgresql', 'text' : 'PostgreSQL'},
];
options_db.forEach(option => {
const optionElementDb = document.createElement('option');
optionElementDb.classList.add('optionElementDb');
optionElementDb.id = 'optionElementDb';
optionElementDb.value = option.value;
optionElementDb.textContent = option.text;
select_db.append(optionElementDb);
})
div_all.append(select_db);
const text_node5 = document.createElement('p');
text_node5.innerText = 'Nombre de la base de datos';
text_node5.classList.add('text_node5');
text_node5.id = 'text_node5';
const input_node5 = document.createElement('input');
input_node5.type = 'text';
input_node5.classList.add('input_node5');
input_node5.id = 'input_node5';
const text_node5_2 = document.createElement('p');
text_node5_2.innerText = 'Usuario de la base de datos';
text_node5_2.classList.add('text_node5_2');
text_node5_2.id = 'text_node5_2';
const input_user_node5 = document.createElement('input');
input_user_node5.type = 'text';
input_user_node5.classList.add('input_user_node5');
input_user_node5.id = 'input_user_node5';
const label_password_node5 = document.createElement('p');
label_password_node5.classList.add('label_password_node5');
label_password_node5.id = 'label_password_node5';
label_password_node5.innerText = 'Contraseña de la base de datos';
const password_node5 = document.createElement('input');
password_node5.type = 'password';
password_node5.classList.add('password_node5');
password_node5.id = 'password_node5';
const label_host_node5 = document.createElement('p');
label_host_node5.classList.add('label_host_node5');
label_host_node5.id = 'label_host_node5';
label_host_node5.innerText = 'Host de la base de datos';
const host_node5 = document.createElement('input');
host_node5.type = 'text';
host_node5.classList.add('host_node5');
host_node5.id = 'host_node5';
const label_port_node5 = document.createElement('p');
label_port_node5.classList.add('label_port_node5');
label_port_node5.id = 'label_port_node5';
label_port_node5.innerText = 'Puerto de la base de datos';
const port_node5 = document.createElement('input');
port_node5.type = 'text';
port_node5.classList.add('port_node5');
port_node5.id = 'port_node5';
const btnGuardar = document.createElement('button');
btnGuardar.classList.add('save_node5');
btnGuardar.id = 'save_node5';
btnGuardar.textContent = 'Guardar';
btnGuardar.onclick = async function () {
try {
const response = await fetch('/connect_bd/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
bd: document.getElementById('select_db').value,
name_bd: document.getElementById('input_node5').value,
user_bd: document.getElementById('input_user_node5').value,
password_bd: document.getElementById('password_node5').value,
host_bd: document.getElementById('host_node5').value,
port_bd: document.getElementById('port_node5').value
})
});
if (!response.ok) {
throw new Error(`Error HTTP: ${response.status}`);
}
const result = await response.json();
if (result.status === 'success') {
alert("Conexión a la base de datos exitosa.");
} else {
alert(`Error: ${result.message}\nDetalles: ${JSON.stringify(result.details || {}, null, 2)}`);
}
} catch (error) {
console.error('Error completo:', error);
alert('Error al intentar conectar con la base de datos: ' + error.message);
}
};
const elements_mysql = [
text_node5,
input_node5,
text_node5_2,
input_user_node5,
label_password_node5,
password_node5,
label_host_node5,
host_node5,
label_port_node5,
port_node5,
btnGuardar,
];
const elements_oracle = [
label_port_node5,
port_node5
];
select_db.addEventListener('change', (event) => {
const selectValue = event.target.value;
if (selectValue == 'mysql' || selectValue == 'mssql' || selectValue == 'postgresql'){
elements_mysql.forEach(element => {
div_all.append(element);
});
} else if (selectValue == 'oracle') {
elements_oracle.forEach(element => {
if (element) {
element.remove();
}
})
} else {
elements_mysql.forEach(element => {
if(element){
element.remove();
}
});
}
});
modal.append(div_all);
} else if ((nodeType2 || nodeType) == "node6") {
const div_node6 = document.createElement('div');
div_node6.classList.add('div_node6');
div_node6.id = 'div_node6';
// 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_node6.appendChild(titleLabel);
const title_node6 = document.createElement('input');
title_node6.type = 'text';
title_node6.classList.add('title_node6');
title_node6.id = 'title_node6';
title_node6.placeholder = 'Título del nodo';
title_node6.value = node.querySelector('.node-title').textContent || '';
div_node6.appendChild(title_node6);
// Crear campo de entrada para el texto a convertir
const textLabel = document.createElement('label');
textLabel.textContent = 'Texto a convertir:';
textLabel.style.color = 'black';
div_node6.appendChild(textLabel);
const text_node6 = document.createElement('textarea'); // Cambiado a textarea para permitir textos largos
text_node6.type = 'text';
text_node6.classList.add('text_node6');
text_node6.id = 'text_node6';
text_node6.placeholder = 'Ingrese el texto para generar el audio';
text_node6.value = node.content || '';
div_node6.appendChild(text_node6);
// Mostrar el audio actual si existe
const audioPlayNode6 = document.createElement('audio');
audioPlayNode6.id = 'audioPlayerNode6';
audioPlayNode6.classList.add('audioPlayerNode6');
audioPlayNode6.controls = true;
if (node.audioFile) {
audioPlayNode6.src = node.audioFile;
}
div_node6.appendChild(audioPlayNode6);
fragment.appendChild(div_node6);
modal.appendChild(fragment);
// Evento para generar y previsualizar el audio
text_node6.addEventListener('input', (event) => {
const text = event.target.value.trim();
if (text) {
// Opcional: Mostrar una vista previa o indicar que el audio está listo
}
});
const saveButtonNode6 = document.createElement("button");
saveButtonNode6.classList.add("saveButtonNode6");
saveButtonNode6.innerText = "Generar y Guardar Audio";
saveButtonNode6.onclick = async function() {
const newTitle = title_node6.value.trim();
const text = text_node6.value.trim();
if (!newTitle || !text) {
alert("El título y el texto no pueden estar vacíos.");
return;
}
const oldTitle = node.querySelector('.node-title').textContent;
node.querySelector('.node-title').textContent = newTitle;
node.content = text; // Guardar el contenido
const tabId = getTabIdFromNode(node);
const nodeId = node.id;
const nodeType = node.getAttribute('data-node-type');
const tabName = getTabName();
// Formatear el nombre del archivo como {node_id}_{node_title}.WAV
const audioFileName = `${nodeId}_${newTitle}.WAV`;
try {
const response = await fetch('/tts_module/', { // Vista ya creada en views.py
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
tabId: tabId,
title: newTitle,
text: text,
nodeId: nodeId, // Incluir nodeId
nodeType: nodeType, // Incluir nodeType
})
});
const result = await response.json();
if (result.status === 'success') {
// Actualizar el audioFile del nodo6
node.audioFile = result.audioFile; // Asegurarse de que el nombre siga la estructura
audioPlayNode6.src = result.audioFile;
alert("Audio generado y guardado exitosamente.");
// Guardar los datos de la pestaña
await saveTabData(tabId);
} else {
alert(`Error: ${result.message}`);
}
} catch (error) {
console.error('Error al generar el audio:', error);
alert('Ocurrió un error al generar el audio.');
}
};
div_node6.appendChild(saveButtonNode6);
const playButtonNode6 = document.createElement('button');
playButtonNode6.classList.add("playButtonNode6");
playButtonNode6.innerText = "Reproducir Audio";
playButtonNode6.onclick = function() {
if (node.audioFile) {
let audio = new Audio(node.audioFile);
audio.play();
} else {
alert("No hay audio asociado a este nodo.");
}
};
div_node6.appendChild(playButtonNode6);
modal.appendChild(saveButtonNode6);
modal.appendChild(playButtonNode6);
setTimeout(() => {
modal.classList.add("show");
background.classList.add("show");
}, 10);
} else if ((nodeType2 || nodeType) == "node7") {
const div_node7 = document.createElement('div');
div_node7.classList.add('div_node7');
div_node7.id = 'div_node7';
const label_node7 = document.createElement('p');
label_node7.classList.add('label_node7');
label_node7.id = 'label_node7';
label_node7.innerText = 'Nombre del nodo';
div_node7.append(label_node7);
const name_node7 = document.createElement('input');
name_node7.type = 'text';
name_node7.classList.add('name_node7');
name_node7.id = 'name_node7';
div_node7.append(name_node7);
const label_query = document.createElement('p');
label_query.classList.add('label_query');
label_query.id = 'label_query';
label_query.innerText = 'Consulta a la Base de datos';
div_node7.append(label_query);
const input_query = document.createElement('input');
input_query.classList.add('input_query');
input_query.id = 'input_query';
div_node7.append(input_query);
const save_node7 = document.createElement('button');
save_node7.classList.add('save_node7');
save_node7.id = 'save_node7';
save_node7.innerText = 'Consultar';
div_node7.append(save_node7);
save_node7.onclick = async function() {
const name_value = document.getElementById('name_node7').value;
const query_value = document.getElementById('input_query').value;
console.log(name_value, query_value);
try {
const response = await fetch('/query/', { // Vista ya creada en views.py
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
name : name_value,
query : query_value,
})
});
const result = await response.json();
if (result.status === 'success') {
console.log("Datos recibidos:", result.data);
console.log("Columnas:", result.columns);
alert("Query realizado exitosamente");
} else {
alert(`Error: ${result.message}`);
}
} catch (error) {
console.error('Error al generar la respuesta del query:', error);
alert('Ocurrió un error al retornar la respuesta.');
}
};
modal.append(div_node7);
}
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