LocalScriptsWeb/frontend/static/js/scripts.js

790 lines
34 KiB
JavaScript

// frontend/static/js/scripts.js
// Script groups state
let scriptGroups = [];
// Load script groups when page loads
document.addEventListener('DOMContentLoaded', async () => {
await loadScriptGroups();
});
// Load script groups when page loads
document.addEventListener('DOMContentLoaded', async () => {
await loadScriptGroups();
});
async function loadScriptGroups() {
try {
// Obtener los grupos desde el servidor
const groups = await apiRequest('/script-groups');
console.log('Loaded script groups:', groups);
// Obtener el selector y el último grupo seleccionado
const select = document.getElementById('groupSelect');
const lastGroupId = localStorage.getItem('lastGroupId');
console.log('Last group ID:', lastGroupId);
// Remover event listener anterior si existe
select.removeEventListener('change', handleGroupChange);
// Construir las opciones
select.innerHTML = `
<option value="">Select a group...</option>
${groups.map(group => `
<option value="${group.id}" ${group.id === lastGroupId ? 'selected' : ''}>
${group.name}
</option>
`).join('')}
`;
// Agregar event listener para cambios
select.addEventListener('change', handleGroupChange);
console.log('Added change event listener to groupSelect');
// Si hay un grupo guardado, cargarlo
if (lastGroupId) {
console.log('Loading last group scripts:', lastGroupId);
await loadGroupScripts(lastGroupId);
}
} catch (error) {
console.error('Failed to load script groups:', error);
showError('Failed to load script groups');
}
}
// Función para manejar el cambio de grupo
async function handleGroupChange(event) {
const groupId = event.target.value;
console.log('Group selection changed:', groupId);
if (groupId) {
localStorage.setItem('lastGroupId', groupId);
console.log('Saved lastGroupId:', groupId);
} else {
localStorage.removeItem('lastGroupId');
console.log('Removed lastGroupId');
}
await loadGroupScripts(groupId);
}
// Actualizar función de cambio de perfil para mantener la persistencia
async function changeProfile() {
const select = document.getElementById('profileSelect');
if (select.value) {
await selectProfile(select.value);
localStorage.setItem('lastProfileId', select.value);
// Al cambiar de perfil, intentamos mantener el último grupo seleccionado
const lastGroupId = localStorage.getItem('lastGroupId');
if (lastGroupId) {
const groupSelect = document.getElementById('groupSelect');
if (groupSelect) {
groupSelect.value = lastGroupId;
await loadGroupScripts(lastGroupId);
}
}
}
}
async function loadGroupScripts(groupId) {
const scriptList = document.getElementById('scriptList');
if (!groupId) {
scriptList.style.display = 'none';
localStorage.removeItem('lastGroupId'); // Limpiar selección
return;
}
// Guardar grupo seleccionado
localStorage.setItem('lastGroupId', groupId);
console.log('Group saved:', groupId);
if (!currentProfile?.work_dir) {
scriptList.innerHTML = `
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-4">
<div class="flex">
<div class="ml-3">
<p class="text-sm text-yellow-700">
Please select a work directory first
</p>
</div>
</div>
</div>
`;
scriptList.style.display = 'block';
return;
}
try {
console.log('Loading data for group:', groupId);
// Actualizar el selector para reflejar la selección actual
const groupSelect = document.getElementById('groupSelect');
if (groupSelect && groupSelect.value !== groupId) {
groupSelect.value = groupId;
}
// Cargar y loguear scripts
let groupScripts, configSchema;
try {
groupScripts = await apiRequest(`/script-groups/${groupId}/scripts`);
console.log('Scripts loaded:', groupScripts);
} catch (e) {
console.error('Error loading scripts:', e);
throw e;
}
try {
configSchema = await apiRequest(`/script-groups/${groupId}/config-schema`);
console.log('Config schema loaded:', configSchema);
} catch (e) {
console.error('Error loading config schema:', e);
throw e;
}
// Intentar cargar configuración actual
let currentConfig = {};
try {
currentConfig = await apiRequest(
`/workdir-config/${encodeURIComponent(currentProfile.work_dir)}/group/${groupId}`
);
console.log('Current config loaded:', currentConfig);
} catch (e) {
console.warn('No existing configuration found, using defaults');
}
// Verificar que tenemos los datos necesarios
if (!groupScripts || !configSchema) {
throw new Error('Failed to load required data');
}
console.log('Rendering UI with:', {
groupScripts,
configSchema,
currentConfig
});
scriptList.innerHTML = `
<!-- Sección de Configuración -->
<div class="mb-6 bg-white shadow sm:rounded-lg">
<div class="border-b border-gray-200 p-4 flex justify-between items-center">
<div>
<h3 class="text-lg font-medium text-gray-900">
${configSchema.group_name || 'Configuration'}
</h3>
<p class="mt-1 text-sm text-gray-500">${configSchema.description || ''}</p>
</div>
<button onclick="editConfigSchema('${groupId}')"
class="rounded-md bg-gray-100 px-3 py-2 text-sm font-semibold text-gray-900 hover:bg-gray-200 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit Schema
</button>
</div>
<div class="p-4">
<form id="groupConfigForm" class="grid grid-cols-2 gap-4">
${Object.entries(configSchema.config_schema || {}).map(([key, field]) => `
<div class="space-y-2 col-span-2">
<label class="block text-sm font-medium text-gray-700">
${field.description}
</label>
${generateFormField(key, field, currentConfig[key])}
</div>
`).join('')}
<div class="col-span-2 flex justify-end pt-4">
<button type="submit"
class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">
Save Configuration
</button>
</div>
</form>
</div>
</div>
<!-- Lista de Scripts -->
<div class="space-y-4">
${groupScripts.map(script => `
<div class="bg-white px-4 py-3 rounded-md border border-gray-200 hover:border-gray-300 shadow sm:rounded-lg">
<div class="flex justify-between items-start">
<div>
<h4 class="text-sm font-medium text-gray-900">${script.name || script.id}</h4>
<p class="mt-1 text-sm text-gray-500">${script.description || 'No description available'}</p>
</div>
<button onclick="runScript('${groupId}', '${script.id}')"
class="ml-4 rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">
Run
</button>
</div>
</div>
`).join('')}
</div>`;
scriptList.style.display = 'block';
// Agregar evento para guardar configuración
const form = document.getElementById('groupConfigForm');
form.addEventListener('submit', async (e) => {
e.preventDefault();
await saveGroupConfig(groupId, form);
});
} catch (error) {
console.error('Error in loadGroupScripts:', error);
showError('Failed to load scripts and configuration');
}
}
// Update script groups display
function updateScriptGroupsDisplay() {
const container = document.getElementById('scriptGroups');
if (!scriptGroups.length) {
container.innerHTML = '<p class="no-scripts">No script groups available</p>';
return;
}
container.innerHTML = scriptGroups.map(group => `
<div class="script-group" data-group-id="${group.id}">
<div class="script-group-header">
<h3>${group.name}</h3>
<button onclick="configureGroup('${group.id}')" class="config-btn">
Configure
</button>
</div>
<div class="script-list">
${group.scripts.map(script => `
<div class="script-item">
<div class="script-info">
<h4>${script.name}</h4>
<p>${script.description || 'No description available'}</p>
</div>
<div class="script-actions">
<button onclick="runScript('${group.id}', '${script.id}')" class="run-btn">
Run
</button>
</div>
</div>
`).join('')}
</div>
</div>
`).join('');
}
// Run a script
async function runScript(groupId, scriptId) {
if (!currentProfile?.work_dir) {
showError('Please select a work directory first');
return;
}
try {
const result = await apiRequest(`/scripts/${groupId}/${scriptId}/run`, {
method: 'POST',
body: JSON.stringify({
work_dir: currentProfile.work_dir,
profile: currentProfile
})
});
if (result.status === 'error') {
showError(result.error);
} else {
showSuccess(`Script ${scriptId} executed successfully`);
if (result.output) {
const output = document.getElementById('outputArea');
output.innerHTML += `\n[${new Date().toLocaleTimeString()}] ${result.output}`;
output.scrollTop = output.scrollHeight;
}
}
} catch (error) {
showError(`Failed to run script: ${error.message}`);
}
}
// Configure script group
async function configureGroup(groupId) {
if (!currentProfile?.work_dir) {
showError('Please select a work directory first');
return;
}
try {
const config = await getGroupConfig(groupId);
showGroupConfigEditor(groupId, config);
} catch (error) {
showError('Failed to load group configuration');
}
}
// Show group configuration editor
function showGroupConfigEditor(groupId, config) {
const group = scriptGroups.find(g => g.id === groupId);
if (!group) return;
const modal = document.createElement('div');
modal.className = 'modal active';
modal.innerHTML = `
<div class="modal-content">
<h2>${group.name} Configuration</h2>
<form id="groupConfigForm" onsubmit="saveGroupConfig(event, '${groupId}')">
<div class="form-group">
<label for="configData">Configuration (JSON)</label>
<textarea id="configData" name="config" rows="10"
class="config-editor">${JSON.stringify(config || {}, null, 2)}</textarea>
</div>
<div class="button-group">
<button type="button" onclick="closeModal(this)">Cancel</button>
<button type="submit">Save</button>
</div>
</form>
</div>
`;
document.body.appendChild(modal);
}
function generateFormField(key, field, value) {
const currentValue = value !== undefined ? value : field.default;
switch (field.type) {
case 'string':
return `
<input type="text"
name="${key}"
value="${currentValue || ''}"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
`;
case 'number':
return `
<input type="number"
name="${key}"
value="${currentValue || 0}"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
`;
case 'boolean':
return `
<div class="flex items-center">
<input type="checkbox"
name="${key}"
${currentValue ? 'checked' : ''}
class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500">
<span class="ml-2 text-sm text-gray-500">Enable</span>
</div>
`;
case 'select':
return `
<select name="${key}"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
${field.options.map(opt => `
<option value="${opt}" ${currentValue === opt ? 'selected' : ''}>
${opt}
</option>
`).join('')}
</select>
`;
default:
return `<input type="text" name="${key}" value="${currentValue || ''}" class="w-full">`;
}
}
async function loadGroupScripts(groupId) {
const scriptList = document.getElementById('scriptList');
if (!groupId) {
scriptList.style.display = 'none';
localStorage.removeItem('lastGroupId');
return;
}
if (!currentProfile?.work_dir) {
scriptList.innerHTML = `
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-4">
<div class="flex">
<div class="ml-3">
<p class="text-sm text-yellow-700">
Please select a work directory first
</p>
</div>
</div>
</div>
`;
scriptList.style.display = 'block';
return;
}
try {
console.log('Loading data for group:', groupId);
// Cargar y loguear scripts
let groupScripts, configSchema;
try {
groupScripts = await apiRequest(`/script-groups/${groupId}/scripts`);
console.log('Scripts loaded:', groupScripts);
} catch (e) {
console.error('Error loading scripts:', e);
throw e;
}
try {
configSchema = await apiRequest(`/script-groups/${groupId}/config-schema`);
console.log('Config schema loaded:', configSchema);
} catch (e) {
console.error('Error loading config schema:', e);
throw e;
}
// Intentar cargar configuración actual
let currentConfig = {};
try {
console.log('Loading current config for work_dir:', currentProfile.work_dir);
currentConfig = await apiRequest(
`/workdir-config/${encodeURIComponent(currentProfile.work_dir)}/group/${groupId}`
);
console.log('Current config loaded:', currentConfig);
} catch (e) {
console.warn('No existing configuration found, using defaults');
}
// Verificar que tenemos los datos necesarios
if (!groupScripts || !configSchema) {
throw new Error('Failed to load required data');
}
console.log('Rendering UI with:', {
groupScripts,
configSchema,
currentConfig
});
scriptList.innerHTML = `
<!-- Sección de Configuración -->
<div class="mb-6 bg-white shadow sm:rounded-lg">
<div class="border-b border-gray-200 p-4 flex justify-between items-center">
<div>
<h3 class="text-lg font-medium text-gray-900">
${configSchema.group_name || 'Configuration'}
</h3>
<p class="mt-1 text-sm text-gray-500">${configSchema.description || ''}</p>
</div>
<button onclick="editConfigSchema('${groupId}')"
class="rounded-md bg-gray-100 px-3 py-2 text-sm font-semibold text-gray-900 hover:bg-gray-200 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit Schema
</button>
</div>
<div class="p-4">
<form id="groupConfigForm" class="grid grid-cols-2 gap-4">
${Object.entries(configSchema.config_schema || {}).map(([key, field]) => `
<div class="space-y-2 col-span-2">
<label class="block text-sm font-medium text-gray-700">
${field.description}
</label>
${generateFormField(key, field, currentConfig[key])}
</div>
`).join('')}
<div class="col-span-2 flex justify-end pt-4">
<button type="submit"
class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">
Save Configuration
</button>
</div>
</form>
</div>
</div>
<!-- Lista de Scripts -->
<div class="space-y-4">
${groupScripts.map(script => `
<div class="bg-white px-4 py-3 rounded-md border border-gray-200 hover:border-gray-300 shadow sm:rounded-lg">
<div class="flex justify-between items-start">
<div>
<h4 class="text-sm font-medium text-gray-900">${script.name || script.id}</h4>
<p class="mt-1 text-sm text-gray-500">${script.description || 'No description available'}</p>
</div>
<button onclick="runScript('${groupId}', '${script.id}')"
class="ml-4 rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">
Run
</button>
</div>
</div>
`).join('')}
</div>`;
scriptList.style.display = 'block';
// Agregar evento para guardar configuración
const form = document.getElementById('groupConfigForm');
form.addEventListener('submit', async (e) => {
e.preventDefault();
await saveGroupConfig(groupId, form);
});
} catch (error) {
console.error('Error in loadGroupScripts:', error);
showError('Failed to load scripts and configuration');
}
}
async function editConfigSchema(groupId) {
try {
const schema = await apiRequest(`/script-groups/${groupId}/config-schema`);
const configSection = document.createElement('div');
configSection.id = 'schemaEditor';
configSection.className = 'mb-6 bg-white shadow sm:rounded-lg';
configSection.innerHTML = `
<div class="border-b border-gray-200 p-4 flex justify-between items-center bg-gray-50">
<h3 class="text-lg font-medium text-gray-900">Edit Schema Configuration</h3>
<button onclick="this.closest('#schemaEditor').remove()"
class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
Close Editor
</button>
</div>
<div class="p-4">
<div class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700">Group Name</label>
<input type="text" name="group_name" value="${schema.group_name}"
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div class="text-right">
<button onclick="addParameter(this)"
class="mt-6 rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-500">
Add Parameter
</button>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Description</label>
<input type="text" name="description" value="${schema.description}"
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div id="parameters" class="space-y-2">
<div class="flex justify-between items-center">
<h4 class="font-medium text-gray-900">Parameters</h4>
</div>
${Object.entries(schema.config_schema).map(([key, param]) => `
<div class="parameter-item bg-gray-50 p-4 rounded-md relative">
<button onclick="removeParameter(this)"
class="absolute top-2 right-2 text-red-600 hover:text-red-700">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700">Parameter Name</label>
<input type="text" name="param_name" value="${key}"
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Type</label>
<select name="param_type"
onchange="handleTypeChange(this)"
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option value="string" ${param.type === 'string' ? 'selected' : ''}>String</option>
<option value="number" ${param.type === 'number' ? 'selected' : ''}>Number</option>
<option value="boolean" ${param.type === 'boolean' ? 'selected' : ''}>Boolean</option>
<option value="select" ${param.type === 'select' ? 'selected' : ''}>Select</option>
</select>
</div>
<div class="col-span-2">
<label class="block text-sm font-medium text-gray-700">Description</label>
<input type="text" name="param_description" value="${param.description}"
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Default Value</label>
<input type="text" name="param_default" value="${param.default}"
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
${param.type === 'select' ? `
<div>
<label class="block text-sm font-medium text-gray-700">Options (comma-separated)</label>
<input type="text" name="param_options" value="${param.options.join(', ')}"
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
` : ''}
</div>
</div>
`).join('')}
</div>
<div class="flex justify-end space-x-3 pt-4 border-t border-gray-200">
<button onclick="this.closest('#schemaEditor').remove()"
class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
Cancel
</button>
<button onclick="saveConfigSchema('${groupId}', this.closest('#schemaEditor'))"
class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">
Save Changes
</button>
</div>
</div>
</div>
`;
// Insertamos el editor justo después del botón "Edit Schema"
const scriptList = document.getElementById('scriptList');
const existingEditor = document.getElementById('schemaEditor');
if (existingEditor) {
existingEditor.remove();
}
scriptList.insertBefore(configSection, scriptList.firstChild);
} catch (error) {
showError('Failed to load configuration schema');
}
}
function createModal(title, content, onSave = null) {
const modal = document.createElement('div');
modal.className = 'fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center z-50 p-4';
modal.innerHTML = `
<div class="bg-white rounded-lg shadow-xl max-w-4xl w-full max-h-[90vh] flex flex-col">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-medium text-gray-900">${title}</h3>
</div>
<div class="px-6 py-4 overflow-y-auto flex-1">
${content}
</div>
<div class="px-6 py-4 bg-gray-50 rounded-b-lg flex justify-end gap-3 border-t border-gray-200">
<button onclick="closeModal(this)"
class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
Cancel
</button>
${onSave ? `
<button onclick="saveModal(this)"
class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">
Save
</button>
` : ''}
</div>
</div>
`;
document.body.appendChild(modal);
return modal;
}
function addParameter(button) {
const parametersDiv = button.closest('.space-y-4').querySelector('#parameters');
const newParam = document.createElement('div');
newParam.className = 'parameter-item bg-gray-50 p-4 rounded-md relative';
newParam.innerHTML = `
<button onclick="removeParameter(this)"
class="absolute top-2 right-2 text-red-600 hover:text-red-700">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700">Parameter Name</label>
<input type="text" name="param_name" value=""
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Type</label>
<select name="param_type"
onchange="handleTypeChange(this)"
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option value="string">String</option>
<option value="number">Number</option>
<option value="boolean">Boolean</option>
<option value="select">Select</option>
</select>
</div>
<div class="col-span-2">
<label class="block text-sm font-medium text-gray-700">Description</label>
<input type="text" name="param_description" value=""
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div class="col-span-2">
<label class="block text-sm font-medium text-gray-700">Default Value</label>
<input type="text" name="param_default" value=""
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
</div>
</div>
`;
parametersDiv.appendChild(newParam);
}
function removeParameter(button) {
button.closest('.parameter-item').remove();
}
function handleTypeChange(select) {
const paramItem = select.closest('.parameter-item');
const optionsDiv = paramItem.querySelector('[name="param_options"]')?.closest('div');
if (select.value === 'select') {
if (!optionsDiv) {
const div = document.createElement('div');
div.className = 'col-span-2';
div.innerHTML = `
<label class="block text-sm font-medium text-gray-700">Options (comma-separated)</label>
<input type="text" name="param_options" value=""
class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
`;
paramItem.querySelector('.grid').appendChild(div);
}
} else {
optionsDiv?.remove();
}
}
async function saveConfigSchema(groupId, modal) {
const form = modal.querySelector('div');
const schema = {
group_name: form.querySelector('[name="group_name"]').value,
description: form.querySelector('[name="description"]').value,
config_schema: {}
};
// Recopilar parámetros
form.querySelectorAll('.parameter-item').forEach(item => {
const name = item.querySelector('[name="param_name"]').value;
const type = item.querySelector('[name="param_type"]').value;
const description = item.querySelector('[name="param_description"]').value;
const defaultValue = item.querySelector('[name="param_default"]').value;
const param = {
type,
description,
default: type === 'boolean' ? defaultValue === 'true' : defaultValue
};
if (type === 'select') {
const options = item.querySelector('[name="param_options"]').value
.split(',')
.map(opt => opt.trim())
.filter(Boolean);
param.options = options;
}
schema.config_schema[name] = param;
});
try {
await apiRequest(`/script-groups/${groupId}/config-schema`, {
method: 'PUT',
body: JSON.stringify(schema)
});
closeModal(modal.querySelector('button'));
showSuccess('Configuration schema updated successfully');
// Recargar la página para mostrar los cambios
loadGroupScripts(groupId);
} catch (error) {
showError('Failed to update configuration schema');
}
}