281 lines
13 KiB
HTML
281 lines
13 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block content %}
|
|
<div class="min-h-full">
|
|
<!-- Navbar -->
|
|
<nav class="bg-white shadow-sm">
|
|
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|
<div class="flex h-16 justify-between">
|
|
<div class="flex">
|
|
<div class="flex flex-shrink-0 items-center">
|
|
<h1 class="text-xl font-semibold text-gray-900">Local Scripts Web</h1>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-4">
|
|
<select id="profileSelect"
|
|
class="rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-indigo-600"
|
|
onchange="changeProfile()">
|
|
<option value="">Select Profile</option>
|
|
</select>
|
|
<button onclick="editProfile()"
|
|
class="rounded-md bg-white px-2.5 py-1.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
|
|
Edit Profile
|
|
</button>
|
|
<button onclick="newProfile()"
|
|
class="rounded-md bg-indigo-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">
|
|
New Profile
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Main content -->
|
|
<main>
|
|
<div class="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
|
|
<div class="space-y-6">
|
|
<!-- Profile Config Section -->
|
|
<div class="bg-white shadow sm:rounded-lg">
|
|
<div class="px-4 py-5 sm:p-6">
|
|
<h3 class="text-base font-semibold leading-6 text-gray-900">Profile Configuration</h3>
|
|
<div id="profileConfig" class="mt-4">
|
|
<!-- Profile config will be loaded here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Script Groups Section -->
|
|
<div class="bg-white shadow sm:rounded-lg">
|
|
<div class="px-4 py-5 sm:p-6">
|
|
<div class="flex justify-between items-center">
|
|
<h3 class="text-base font-semibold leading-6 text-gray-900">Script Groups</h3>
|
|
<button onclick="editGroupSchema()"
|
|
class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">
|
|
Edit Schema
|
|
</button>
|
|
</div>
|
|
<div class="mt-4 space-y-4">
|
|
<select id="groupSelect"
|
|
class="w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600">
|
|
<option value="">Select Script Group</option>
|
|
</select>
|
|
|
|
<!-- Group Configuration -->
|
|
<div id="groupConfig" class="mt-4">
|
|
<!-- Group config will be loaded here -->
|
|
</div>
|
|
|
|
<!-- Work Directory Configuration -->
|
|
<div id="workDirConfig" class="mt-4">
|
|
<!-- Work dir config will be loaded here -->
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Scripts Section -->
|
|
<div class="bg-white shadow sm:rounded-lg">
|
|
<div class="px-4 py-5 sm:p-6">
|
|
<h3 class="text-base font-semibold leading-6 text-gray-900">Scripts</h3>
|
|
<div id="scriptList" class="mt-4 space-y-4">
|
|
<!-- Scripts will be loaded here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Output Section -->
|
|
<div class="bg-white shadow sm:rounded-lg">
|
|
<div class="px-4 py-5 sm:p-6">
|
|
<div class="flex justify-between items-center">
|
|
<h3 class="text-base font-semibold leading-6 text-gray-900">Output</h3>
|
|
<button onclick="clearOutput()"
|
|
class="rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500">
|
|
Clear
|
|
</button>
|
|
</div>
|
|
<div id="outputArea"
|
|
class="mt-4 h-64 overflow-y-auto p-4 font-mono text-sm bg-gray-50 rounded-md border border-gray-200">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<!-- Load utilities first -->
|
|
<script src="{{ url_for('static', filename='js/utils.js') }}"></script>
|
|
|
|
<!-- Utility Functions -->
|
|
<script>
|
|
function generateFormField(key, field, value) {
|
|
switch (field.type) {
|
|
case 'string':
|
|
return `
|
|
<input type="text" name="${key}"
|
|
value="${value || field.default || ''}"
|
|
class="${STYLES.editableInput}">
|
|
`;
|
|
case 'number':
|
|
return `
|
|
<input type="number" name="${key}"
|
|
value="${value || field.default || 0}"
|
|
class="${STYLES.editableInput}">
|
|
`;
|
|
case 'boolean':
|
|
return `
|
|
<select name="${key}" class="${STYLES.editableInput}">
|
|
<option value="true" ${value ? 'selected' : ''}>Yes</option>
|
|
<option value="false" ${!value ? 'selected' : ''}>No</option>
|
|
</select>
|
|
`;
|
|
case 'select':
|
|
return `
|
|
<select name="${key}" class="${STYLES.editableInput}">
|
|
${field.options.map(opt => `
|
|
<option value="${opt}" ${value === opt ? 'selected' : ''}>
|
|
${opt}
|
|
</option>
|
|
`).join('')}
|
|
</select>
|
|
`;
|
|
case 'directory':
|
|
return `
|
|
<div class="flex gap-2">
|
|
<input type="text" name="${key}"
|
|
value="${value || field.default || ''}"
|
|
readonly
|
|
class="${STYLES.readonlyInput}">
|
|
<button type="button"
|
|
onclick="selectDirectory('${key}')"
|
|
class="${STYLES.buttonSecondary}">
|
|
Browse
|
|
</button>
|
|
</div>
|
|
`;
|
|
default:
|
|
return `
|
|
<input type="text" name="${key}"
|
|
value="${value || field.default || ''}"
|
|
class="${STYLES.editableInput}">
|
|
`;
|
|
}
|
|
}
|
|
|
|
function generateSchemaField(key = '', field = {}) {
|
|
const fieldId = `field_${Math.random().toString(36).substr(2, 9)}`;
|
|
return `
|
|
<div class="schema-field bg-gray-50 p-4 rounded-md relative" id="${fieldId}">
|
|
<button type="button"
|
|
onclick="removeSchemaField('${fieldId}')"
|
|
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">Field Name</label>
|
|
<input type="text" name="field_name"
|
|
value="${key}"
|
|
class="${STYLES.editableInput}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">Type</label>
|
|
<select name="field_type"
|
|
onchange="handleFieldTypeChange(this)"
|
|
class="${STYLES.editableInput}">
|
|
<option value="string" ${field.type === 'string' ? 'selected' : ''}>String</option>
|
|
<option value="number" ${field.type === 'number' ? 'selected' : ''}>Number</option>
|
|
<option value="boolean" ${field.type === 'boolean' ? 'selected' : ''}>Boolean</option>
|
|
<option value="select" ${field.type === 'select' ? 'selected' : ''}>Select</option>
|
|
<option value="directory" ${field.type === 'directory' ? 'selected' : ''}>Directory</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-span-2">
|
|
<label class="block text-sm font-medium text-gray-700">Description</label>
|
|
<input type="text" name="field_description"
|
|
value="${field.description || ''}"
|
|
class="${STYLES.editableInput}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">Default Value</label>
|
|
<input type="text" name="field_default"
|
|
value="${field.default || ''}"
|
|
class="${STYLES.editableInput}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">Required</label>
|
|
<select name="field_required" class="${STYLES.editableInput}">
|
|
<option value="true" ${field.required ? 'selected' : ''}>Yes</option>
|
|
<option value="false" ${!field.required ? 'selected' : ''}>No</option>
|
|
</select>
|
|
</div>
|
|
${field.type === 'select' ? `
|
|
<div class="col-span-2">
|
|
<label class="block text-sm font-medium text-gray-700">Options (comma-separated)</label>
|
|
<input type="text" name="field_options"
|
|
value="${(field.options || []).join(', ')}"
|
|
class="${STYLES.editableInput}">
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function handleFieldTypeChange(select) {
|
|
const fieldDiv = select.closest('.schema-field');
|
|
const optionsDiv = fieldDiv.querySelector('[name="field_options"]')?.closest('.col-span-2');
|
|
|
|
if (select.value === 'select' && !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="field_options" class="${STYLES.editableInput}">
|
|
`;
|
|
fieldDiv.querySelector('.grid').appendChild(div);
|
|
} else if (select.value !== 'select' && optionsDiv) {
|
|
optionsDiv.remove();
|
|
}
|
|
}
|
|
|
|
function addSchemaField() {
|
|
const container = document.getElementById('schemaFields');
|
|
const field = document.createElement('div');
|
|
field.innerHTML = generateSchemaField();
|
|
container.appendChild(field.firstElementChild);
|
|
}
|
|
|
|
function removeSchemaField(fieldId) {
|
|
document.getElementById(fieldId).remove();
|
|
}
|
|
|
|
async function selectDirectory(inputName) {
|
|
try {
|
|
const response = await apiRequest('/select-directory');
|
|
if (response.path) {
|
|
const input = document.querySelector(`input[name="${inputName}"]`);
|
|
if (input) {
|
|
input.value = response.path;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
showError('Error selecting directory');
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<!-- Initialize app -->
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
console.log('DOM loaded, initializing...');
|
|
await initializeApp();
|
|
});
|
|
</script>
|
|
{% endblock %} |