713 lines
31 KiB
HTML
713 lines
31 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Script Parameter Manager</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script>
|
|
let socket;
|
|
let currentGroup;
|
|
|
|
// Initialize WebSocket connection
|
|
function initWebSocket() {
|
|
socket = new WebSocket(`ws://${location.host}/ws`);
|
|
socket.onmessage = function(event) {
|
|
addLogLine(event.data);
|
|
};
|
|
socket.onclose = function() {
|
|
console.log('WebSocket cerrado, intentando reconexión...');
|
|
setTimeout(initWebSocket, 1000);
|
|
};
|
|
socket.onerror = function(error) {
|
|
console.error('Error en WebSocket:', error);
|
|
};
|
|
}
|
|
|
|
// Load configurations for all levels
|
|
async function loadConfigs() {
|
|
const group = document.getElementById('script-group').value;
|
|
currentGroup = group;
|
|
console.log('Loading configs for group:', group); // Debug line
|
|
|
|
try {
|
|
// Cargar niveles 1 y 2
|
|
for (let level of [1, 2]) {
|
|
console.log(`Loading level ${level} config...`); // Debug line
|
|
const response = await fetch(`/api/config/${level}?group=${group}`);
|
|
const data = await response.json();
|
|
console.log(`Level ${level} data:`, data); // Debug line
|
|
await renderForm(`level${level}-form`, data);
|
|
}
|
|
|
|
// Cargar nivel 3 solo si hay directorio de trabajo
|
|
const workingDirResponse = await fetch(`/api/working-directory/${group}`);
|
|
const workingDirResult = await workingDirResponse.json();
|
|
if (workingDirResult.status === 'success' && workingDirResult.path) {
|
|
console.log('Loading level 3 config...'); // Debug line
|
|
const response = await fetch(`/api/config/3?group=${group}`);
|
|
const data = await response.json();
|
|
console.log('Level 3 data:', data); // Debug line
|
|
await renderForm('level3-form', data);
|
|
}
|
|
|
|
await loadScripts(group);
|
|
} catch (error) {
|
|
console.error('Error loading configs:', error);
|
|
}
|
|
}
|
|
|
|
// Load and display available scripts
|
|
async function loadScripts(group) {
|
|
const response = await fetch(`/api/scripts/${group}`);
|
|
const scripts = await response.json();
|
|
const container = document.getElementById('scripts-list');
|
|
container.innerHTML = scripts.map(script => `
|
|
<div class="mb-4 p-4 border rounded">
|
|
<div class="font-bold">${script.name}</div>
|
|
<div class="text-gray-600 text-sm">${script.description}</div>
|
|
<button onclick="executeScript('${script.name}')"
|
|
class="mt-2 bg-green-500 text-white px-3 py-1 rounded">
|
|
Ejecutar
|
|
</button>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
// Execute a script
|
|
async function executeScript(scriptName) {
|
|
addLogLine(`\nEjecutando script: ${scriptName}...\n`);
|
|
|
|
const response = await fetch('/api/execute_script', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ group: currentGroup, script: scriptName })
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.error) {
|
|
addLogLine(`\nError: ${result.error}\n`);
|
|
}
|
|
}
|
|
|
|
// Form rendering functionality
|
|
async function renderForm(containerId, data) {
|
|
console.log(`Rendering form for ${containerId}`); // Debug line
|
|
const container = document.getElementById(containerId);
|
|
const level = containerId.replace('level', '').split('-')[0];
|
|
|
|
try {
|
|
const schemaResponse = await fetch(`/api/schema/${level}?group=${currentGroup}`);
|
|
const schema = await schemaResponse.json();
|
|
console.log(`Schema for level ${level}:`, schema); // Debug line
|
|
|
|
if (Object.keys(schema).length === 0) {
|
|
container.innerHTML = '<p class="text-gray-500">No hay esquema definido para este nivel.</p>';
|
|
} else {
|
|
const formHtml = generateFormFields(schema, data, '', level);
|
|
console.log(`Generated HTML for ${containerId}:`, formHtml.substring(0, 100) + '...'); // Debug line
|
|
container.innerHTML = formHtml;
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error rendering form ${containerId}:`, error);
|
|
container.innerHTML = '<p class="text-red-500">Error cargando el esquema.</p>';
|
|
}
|
|
}
|
|
|
|
function generateFormFields(schema, data, prefix, level) {
|
|
let html = '';
|
|
for (const [key, def] of Object.entries(schema.properties)) {
|
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
const value = getValue(data, fullKey);
|
|
|
|
html += `<div class="mb-4">
|
|
<label class="block text-gray-700 text-sm font-bold mb-2">${def.title || key}</label>`;
|
|
|
|
if (def.type === 'object') {
|
|
html += `<div class="pl-4 border-l-2 border-gray-200">
|
|
${generateFormFields(def, data, fullKey, level)}
|
|
</div>`;
|
|
} else {
|
|
html += generateInputField(def, fullKey, value, level);
|
|
}
|
|
|
|
if (def.description) {
|
|
html += `<p class="text-gray-500 text-xs mt-1">${def.description}</p>`;
|
|
}
|
|
html += '</div>';
|
|
}
|
|
return html;
|
|
}
|
|
|
|
function generateInputField(def, key, value, level) {
|
|
const baseClasses = "w-full p-2 border rounded bg-green-50"; // Agregado bg-green-50 para fondo verde claro
|
|
|
|
switch (def.type) {
|
|
case 'string':
|
|
if (def.enum) {
|
|
return `<select class="${baseClasses}"
|
|
onchange="updateConfig(${level}, '${key}', this.value)">
|
|
${def.enum.map(opt => `<option value="${opt}" ${value === opt ? 'selected' : ''}>${opt}</option>`).join('')}
|
|
</select>`;
|
|
}
|
|
return `<input type="text" value="${value || ''}"
|
|
class="${baseClasses}"
|
|
onchange="updateConfig(${level}, '${key}', this.value)">`;
|
|
|
|
case 'number':
|
|
return `<input type="number" value="${value || 0}"
|
|
class="${baseClasses}"
|
|
onchange="updateConfig(${level}, '${key}', Number(this.value))">`;
|
|
|
|
case 'boolean':
|
|
return `<div class="flex items-center">
|
|
<input type="checkbox" ${value ? 'checked' : ''}
|
|
class="form-checkbox h-5 w-5 bg-green-50"
|
|
onchange="updateConfig(${level}, '${key}', this.checked)">
|
|
</div>`;
|
|
|
|
default:
|
|
return `<input type="text" value="${value || ''}"
|
|
class="${baseClasses}"
|
|
onchange="updateConfig(${level}, '${key}', this.value)">`;
|
|
}
|
|
}
|
|
|
|
async function modifySchema(level) {
|
|
try {
|
|
console.log('Loading schema for level:', level); // Debug line
|
|
const response = await fetch(`/api/schema/${level}?group=${currentGroup}`);
|
|
const schema = await response.json();
|
|
console.log('Loaded schema:', schema); // Debug line
|
|
|
|
// Show schema editor modal
|
|
const modal = document.getElementById('schema-editor');
|
|
modal.classList.remove('hidden');
|
|
|
|
// Inicializar JSON editor
|
|
const jsonEditor = document.getElementById('json-editor');
|
|
jsonEditor.value = JSON.stringify(schema, null, 2);
|
|
|
|
// Inicializar visual editor
|
|
const visualEditor = document.getElementById('visual-editor');
|
|
visualEditor.innerHTML = '<div id="schema-fields" class="mb-4"></div>' +
|
|
'<button onclick="addSchemaField()" class="mt-4 bg-green-500 text-white px-4 py-2 rounded">Agregar Campo</button>';
|
|
|
|
// Renderizar campos existentes
|
|
renderVisualEditor(schema);
|
|
|
|
// Guardar nivel actual
|
|
document.getElementById('schema-level').value = level;
|
|
|
|
// Activar pestaña visual por defecto
|
|
switchEditorMode('visual');
|
|
} catch (error) {
|
|
console.error('Error loading schema:', error);
|
|
alert('Error cargando el esquema');
|
|
}
|
|
}
|
|
|
|
function switchEditorMode(mode) {
|
|
const visualEditor = document.getElementById('visual-editor');
|
|
const jsonEditor = document.getElementById('json-editor');
|
|
const visualTab = document.getElementById('visual-tab');
|
|
const jsonTab = document.getElementById('json-tab');
|
|
|
|
if (mode === 'visual') {
|
|
visualEditor.classList.remove('hidden');
|
|
jsonEditor.classList.add('hidden');
|
|
visualTab.classList.add('border-blue-500');
|
|
jsonTab.classList.remove('border-blue-500');
|
|
} else {
|
|
visualEditor.classList.add('hidden');
|
|
jsonEditor.classList.remove('hidden');
|
|
visualTab.classList.remove('border-blue-500');
|
|
jsonTab.classList.add('border-blue-500');
|
|
}
|
|
}
|
|
|
|
function renderVisualEditor(schema) {
|
|
const container = document.getElementById('schema-fields');
|
|
container.innerHTML = '';
|
|
|
|
Object.entries(schema.properties || {}).forEach(([key, field]) => {
|
|
container.appendChild(createFieldEditor(key, field));
|
|
});
|
|
}
|
|
|
|
function createFieldEditor(key, field) {
|
|
const div = document.createElement('div');
|
|
div.className = 'mb-6 p-4 border rounded schema-field';
|
|
div.innerHTML = `
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-bold mb-2">Nombre del Campo</label>
|
|
<input type="text" value="${key}"
|
|
class="w-full p-2 border rounded"
|
|
onchange="updateVisualSchema()">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-bold mb-2">Tipo</label>
|
|
<select class="w-full p-2 border rounded"
|
|
onchange="updateFieldType(this)">
|
|
<option value="string" ${field.type === 'string' ? 'selected' : ''}>Texto</option>
|
|
<option value="number" ${field.type === 'number' ? 'selected' : ''}>Número</option>
|
|
<option value="boolean" ${field.type === 'boolean' ? 'selected' : ''}>Booleano</option>
|
|
<option value="enum" ${field.enum ? 'selected' : ''}>Lista de Opciones</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-bold mb-2">Título</label>
|
|
<input type="text" value="${field.title || ''}"
|
|
class="w-full p-2 border rounded"
|
|
onchange="updateVisualSchema()">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-bold mb-2">Descripción</label>
|
|
<input type="text" value="${field.description || ''}"
|
|
class="w-full p-2 border rounded"
|
|
onchange="updateVisualSchema()">
|
|
</div>
|
|
</div>
|
|
${field.enum ? `
|
|
<div class="enum-container mt-4">
|
|
<label class="block text-sm font-bold mb-2">Opciones (una por línea)</label>
|
|
<textarea class="w-full p-2 border rounded" rows="3"
|
|
onchange="updateVisualSchema()">${field.enum.join('\n')}</textarea>
|
|
</div>
|
|
` : ''}
|
|
<button onclick="removeField(this)"
|
|
class="mt-2 bg-red-500 text-white px-3 py-1 rounded">
|
|
Eliminar Campo
|
|
</button>
|
|
`;
|
|
return div;
|
|
}
|
|
|
|
function updateFieldType(select) {
|
|
const fieldContainer = select.closest('.schema-field');
|
|
const enumContainer = fieldContainer.querySelector('.enum-container');
|
|
|
|
if (select.value === 'enum') {
|
|
if (!enumContainer) {
|
|
const div = document.createElement('div');
|
|
div.className = 'enum-container mt-4';
|
|
div.innerHTML = `
|
|
<label class="block text-sm font-bold mb-2">Opciones (una por línea)</label>
|
|
<textarea class="w-full p-2 border rounded" rows="3"
|
|
onchange="updateEnumValues(this)"></textarea>
|
|
`;
|
|
fieldContainer.appendChild(div);
|
|
}
|
|
} else if (enumContainer) {
|
|
enumContainer.remove();
|
|
}
|
|
updateVisualSchema();
|
|
}
|
|
|
|
function removeField(button) {
|
|
const fieldContainer = button.closest('.schema-field');
|
|
fieldContainer.remove();
|
|
updateVisualSchema();
|
|
}
|
|
|
|
function createEnumEditor(enumValues) {
|
|
return `
|
|
<div class="mt-4">
|
|
<label class="block text-sm font-bold mb-2">Opciones (una por línea)</label>
|
|
<textarea class="w-full p-2 border rounded" rows="3"
|
|
onchange="updateEnumValues(this)">${enumValues.join('\n')}</textarea>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function addSchemaField() {
|
|
const container = document.getElementById('schema-fields');
|
|
const newField = createFieldEditor(`campo_${Date.now()}`, {
|
|
type: 'string',
|
|
title: 'Nuevo Campo',
|
|
description: ''
|
|
});
|
|
container.appendChild(newField);
|
|
}
|
|
|
|
// Funciones de actualización del esquema visual
|
|
function updateVisualSchema() {
|
|
const fields = document.getElementById('schema-fields').children;
|
|
const schema = {
|
|
type: 'object',
|
|
properties: {}
|
|
};
|
|
|
|
Array.from(fields).forEach(field => {
|
|
const inputs = field.getElementsByTagName('input');
|
|
const select = field.getElementsByTagName('select')[0];
|
|
const key = inputs[0].value;
|
|
|
|
schema.properties[key] = {
|
|
type: select.value === 'enum' ? 'string' : select.value,
|
|
title: inputs[1].value,
|
|
description: inputs[2].value
|
|
};
|
|
|
|
if (select.value === 'enum') {
|
|
const textarea = field.getElementsByTagName('textarea')[0];
|
|
schema.properties[key].enum = textarea.value.split('\n').filter(v => v.trim());
|
|
}
|
|
});
|
|
|
|
document.getElementById('schema-content').value = JSON.stringify(schema, null, 2);
|
|
return schema;
|
|
}
|
|
|
|
async function saveSchema() {
|
|
try {
|
|
const level = document.getElementById('schema-level').value;
|
|
const schema = updateVisualSchema();
|
|
|
|
await fetch(`/api/schema/${level}?group=${currentGroup}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(schema)
|
|
});
|
|
|
|
const response = await fetch(`/api/config/${level}?group=${currentGroup}`);
|
|
const data = await response.json();
|
|
renderForm(`level${level}-form`, data);
|
|
|
|
document.getElementById('schema-editor').classList.add('hidden');
|
|
} catch (e) {
|
|
alert('Error guardando esquema: ' + e.message);
|
|
}
|
|
}
|
|
|
|
function getValue(data, path) {
|
|
return path.split('.').reduce((obj, key) => obj?.[key], data);
|
|
}
|
|
|
|
// Actualizar configuración cuando cambia un campo
|
|
async function updateConfig(level, key, value) {
|
|
const group = currentGroup;
|
|
// Obtener configuración actual
|
|
const response = await fetch(`/api/config/${level}?group=${group}`);
|
|
const config = await response.json();
|
|
|
|
// Actualizar el valor usando la ruta del campo
|
|
setNestedValue(config, key, value);
|
|
|
|
// Guardar configuración actualizada
|
|
await fetch(`/api/config/${level}?group=${group}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(config)
|
|
});
|
|
}
|
|
|
|
// Función auxiliar para establecer valor en objeto anidado
|
|
function setNestedValue(obj, path, value) {
|
|
const keys = path.split('.');
|
|
let current = obj;
|
|
|
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
if (!(keys[i] in current)) {
|
|
current[keys[i]] = {};
|
|
}
|
|
current = current[keys[i]];
|
|
}
|
|
|
|
current[keys[keys.length - 1]] = value;
|
|
}
|
|
|
|
async function setWorkingDirectory() {
|
|
if (!currentGroup) {
|
|
alert('Por favor, seleccione un grupo de scripts primero');
|
|
return;
|
|
}
|
|
|
|
const path = document.getElementById('working-directory').value;
|
|
await updateWorkingDirectory(path);
|
|
}
|
|
|
|
async function initWorkingDirectory() {
|
|
if (!currentGroup) return;
|
|
|
|
const response = await fetch(`/api/working-directory/${currentGroup}`);
|
|
const result = await response.json();
|
|
if (result.status === 'success' && result.path) {
|
|
await updateWorkingDirectory(result.path);
|
|
}
|
|
}
|
|
|
|
async function browseDirectory() {
|
|
console.log('Current group when browsing:', currentGroup); // Debug line
|
|
if (!currentGroup) {
|
|
alert('Por favor, seleccione un grupo de scripts primero');
|
|
return;
|
|
}
|
|
|
|
const currentPath = document.getElementById('working-directory').value;
|
|
const response = await fetch(`/api/browse-directories?current_path=${encodeURIComponent(currentPath)}`);
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
await updateWorkingDirectory(result.path);
|
|
}
|
|
}
|
|
|
|
// Nueva función auxiliar para actualizar el directorio de trabajo
|
|
async function updateWorkingDirectory(path) {
|
|
console.log('Updating working directory:', { path, group: currentGroup }); // Debug line
|
|
|
|
const response = await fetch('/api/working-directory', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
path: path,
|
|
group: currentGroup
|
|
})
|
|
});
|
|
|
|
const result = await response.json();
|
|
console.log('Update result:', result); // Debug line
|
|
|
|
if (result.status === 'success') {
|
|
// Actualizar input
|
|
document.getElementById('working-directory').value = path;
|
|
|
|
// Recargar configuración de nivel 3
|
|
const configResponse = await fetch(`/api/config/3?group=${currentGroup}`);
|
|
const data = await configResponse.json();
|
|
await renderForm('level3-form', data);
|
|
} else {
|
|
alert('Error: ' + (result.message || 'No se pudo actualizar el directorio de trabajo'));
|
|
}
|
|
}
|
|
|
|
// Función para alternar visibilidad de una sección
|
|
function toggleConfig(sectionId) {
|
|
const content = document.getElementById(sectionId);
|
|
const button = document.querySelector(`[onclick="toggleConfig('${sectionId}')"]`);
|
|
|
|
if (content.classList.contains('hidden')) {
|
|
content.classList.remove('hidden');
|
|
button.innerText = 'Ocultar Configuración';
|
|
|
|
// Recargar la configuración al mostrar
|
|
const level = sectionId.replace('level', '').replace('-content', '');
|
|
const formId = `level${level}-form`;
|
|
console.log(`Reloading config for level ${level}`); // Debug line
|
|
|
|
fetch(`/api/config/${level}?group=${currentGroup}`)
|
|
.then(response => response.json())
|
|
.then(data => renderForm(formId, data))
|
|
.catch(error => console.error('Error reloading config:', error));
|
|
} else {
|
|
content.classList.add('hidden');
|
|
button.innerText = 'Mostrar Configuración';
|
|
}
|
|
}
|
|
|
|
async function clearLogs() {
|
|
const response = await fetch('/api/logs', { method: 'DELETE' });
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
document.getElementById('log-area').innerHTML = '';
|
|
}
|
|
}
|
|
|
|
async function loadStoredLogs() {
|
|
const response = await fetch('/api/logs');
|
|
const result = await response.json();
|
|
const logArea = document.getElementById('log-area');
|
|
logArea.innerHTML = result.logs;
|
|
logArea.scrollTop = logArea.scrollHeight;
|
|
}
|
|
|
|
// Initialize on page load
|
|
async function initializeApp() {
|
|
try {
|
|
initWebSocket();
|
|
await loadStoredLogs(); // Cargar logs almacenados
|
|
|
|
// Primero establecer el grupo actual
|
|
const group = localStorage.getItem('selectedGroup');
|
|
const selectElement = document.getElementById('script-group');
|
|
if (group) {
|
|
selectElement.value = group;
|
|
}
|
|
currentGroup = selectElement.value; // Siempre establecer currentGroup con el valor actual del select
|
|
console.log('Current group initialized as:', currentGroup); // Debug line
|
|
|
|
// Luego cargar el directorio de trabajo
|
|
await initWorkingDirectory();
|
|
|
|
// Finalmente cargar las configuraciones
|
|
await loadConfigs();
|
|
|
|
// Configurar el evento de cambio de grupo
|
|
selectElement.addEventListener('change', async (e) => {
|
|
currentGroup = e.value;
|
|
localStorage.setItem('selectedGroup', e.value);
|
|
console.log('Group changed to:', currentGroup); // Debug line
|
|
await initWorkingDirectory();
|
|
await loadConfigs();
|
|
});
|
|
} catch (error) {
|
|
console.error('Error during initialization:', error);
|
|
}
|
|
}
|
|
|
|
// Modificar la inicialización para usar la nueva función async
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
initializeApp().catch(console.error);
|
|
});
|
|
|
|
// Función auxiliar para obtener timestamp formateado
|
|
function getTimestamp() {
|
|
const now = new Date();
|
|
return now.toLocaleTimeString('es-ES', {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit'
|
|
});
|
|
}
|
|
|
|
// Función para agregar línea al log con timestamp
|
|
function addLogLine(message) {
|
|
const logArea = document.getElementById('log-area');
|
|
const timestamp = getTimestamp();
|
|
const lines = message.split('\n').map(line =>
|
|
line.trim() ? `[${timestamp}] ${line}` : line
|
|
).join('\n');
|
|
logArea.innerHTML += lines;
|
|
logArea.scrollTop = logArea.scrollHeight;
|
|
}
|
|
</script>
|
|
</head>
|
|
<body class="bg-gray-100">
|
|
<div class="container mx-auto px-4 py-8">
|
|
<!-- Level 1 Configuration -->
|
|
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-xl font-bold">Configuración General (Nivel 1)</h2>
|
|
<button class="bg-blue-500 text-white px-4 py-2 rounded"
|
|
onclick="toggleConfig('level1-content')">
|
|
Mostrar Configuración
|
|
</button>
|
|
</div>
|
|
<div id="level1-content" class="hidden">
|
|
<div id="level1-form"></div>
|
|
<button class="bg-blue-500 text-white px-4 py-2 rounded mt-4" onclick="modifySchema(1)">
|
|
Modificar Esquema
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Script Group Selection -->
|
|
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
|
<h2 class="text-xl font-bold mb-4">Grupo de Scripts</h2>
|
|
<select id="script-group" class="w-full p-2 border rounded">
|
|
{% for group in script_groups %}
|
|
<option value="{{ group }}">{{ group }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Level 2 Configuration -->
|
|
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-xl font-bold">Configuración del Grupo (Nivel 2)</h2>
|
|
<button class="bg-blue-500 text-white px-4 py-2 rounded"
|
|
onclick="toggleConfig('level2-content')">
|
|
Mostrar Configuración
|
|
</button>
|
|
</div>
|
|
<div id="level2-content" class="hidden">
|
|
<div id="level2-form"></div>
|
|
<button class="bg-blue-500 text-white px-4 py-2 rounded mt-4" onclick="modifySchema(2)">
|
|
Modificar Esquema
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Working Directory -->
|
|
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
|
<h2 class="text-xl font-bold mb-4">Directorio de Trabajo</h2>
|
|
<div class="flex gap-4">
|
|
<div class="flex-1 flex gap-2">
|
|
<input type="text" id="working-directory" class="flex-1 p-2 border rounded bg-green-50">
|
|
<button class="bg-gray-500 text-white px-4 py-2 rounded" onclick="browseDirectory()">
|
|
Explorar
|
|
</button>
|
|
</div>
|
|
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="setWorkingDirectory()">
|
|
Confirmar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Level 3 Configuration -->
|
|
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-xl font-bold">Configuración del Directorio (Nivel 3)</h2>
|
|
<button class="bg-blue-500 text-white px-4 py-2 rounded"
|
|
onclick="toggleConfig('level3-content')">
|
|
Mostrar Configuración
|
|
</button>
|
|
</div>
|
|
<div id="level3-content" class="hidden">
|
|
<div id="level3-form"></div>
|
|
<button class="bg-blue-500 text-white px-4 py-2 rounded mt-4" onclick="modifySchema(3)">
|
|
Modificar Esquema
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Scripts List -->
|
|
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
|
<h2 class="text-xl font-bold mb-4">Scripts Disponibles</h2>
|
|
<div id="scripts-list"></div>
|
|
</div>
|
|
|
|
<!-- Logs -->
|
|
<div class="bg-white p-6 rounded-lg shadow">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-xl font-bold">Logs</h2>
|
|
<button class="bg-red-500 text-white px-4 py-2 rounded" onclick="clearLogs()">
|
|
Limpiar
|
|
</button>
|
|
</div>
|
|
<div id="log-area" class="bg-gray-100 p-4 rounded h-64 overflow-y-auto font-mono text-sm whitespace-pre-wrap">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Schema Editor Modal -->
|
|
<div id="schema-editor" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center">
|
|
<div class="bg-white p-6 rounded-lg shadow-lg w-3/4 max-h-screen overflow-auto">
|
|
<h3 class="text-xl font-bold mb-4">Editor de Esquema</h3>
|
|
<div class="flex mb-4">
|
|
<button id="visual-tab" class="px-4 py-2 border-b-2" onclick="switchEditorMode('visual')">Visual</button>
|
|
<button id="json-tab" class="px-4 py-2 border-b-2" onclick="switchEditorMode('json')">JSON</button>
|
|
</div>
|
|
<div id="visual-editor" class="hidden">
|
|
<div id="schema-fields"></div>
|
|
<button onclick="addSchemaField()" class="mt-4 bg-green-500 text-white px-4 py-2 rounded">Agregar Campo</button>
|
|
</div>
|
|
<textarea id="json-editor" class="w-full h-96 font-mono p-2 border rounded mb-4"></textarea>
|
|
<input type="hidden" id="schema-level">
|
|
<div class="flex justify-end gap-4">
|
|
<button onclick="document.getElementById('schema-editor').classList.add('hidden')"
|
|
class="bg-gray-500 text-white px-4 py-2 rounded">
|
|
Cancelar
|
|
</button>
|
|
<button onclick="saveSchema()"
|
|
class="bg-blue-500 text-white px-4 py-2 rounded">
|
|
Guardar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|