Add CodeMirror SQL editor with autocomplete for tables and columns
This commit is contained in:
@@ -5,6 +5,15 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">␍
|
||||
<title>RIPS Manager - {% block title %}Dashboard{% endblock %}</title>␍
|
||||
<script src="https://cdn.tailwindcss.com"></script>␍
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">␍
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>␍
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/sql/sql.min.js"></script>␍
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/hint/show-hint.min.js"></script>␍
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/hint/sql-hint.min.js"></script>␍
|
||||
<style>␍
|
||||
.CodeMirror { border: 1px solid #d1d5db; border-radius: 0.5rem; font-size: 13px; height: auto; min-height: 200px; }␍
|
||||
.CodeMirror-hints { font-size: 12px; }␍
|
||||
</style>␍
|
||||
<script>␍
|
||||
tailwind.config = {␍
|
||||
theme: {␍
|
||||
|
||||
@@ -56,7 +56,8 @@
|
||||
</div>␍
|
||||
<div>␍
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Consulta SQL</label>␍
|
||||
<textarea name="query_text" id="q_text" rows="10" required class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm font-mono bg-gray-50" placeholder="SELECT ... FROM ... WHERE ..."></textarea>
|
||||
<textarea name="query_text" id="q_text" rows="10" required class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm font-mono bg-gray-50" placeholder="SELECT ... FROM ... WHERE ..." style="display:none"></textarea>␍
|
||||
<div id="sql-editor-wrapper"></div>␍
|
||||
</div>␍
|
||||
<div>␍
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Descripción</label>␍
|
||||
@@ -332,6 +333,7 @@ function usarSql() {
|
||||
const sql = document.getElementById('builder-sql').value;␍
|
||||
if (!sql || sql.startsWith('--')) { showToast('No hay SQL generado', 'warning'); return; }␍
|
||||
document.getElementById('q_text').value = sql;␍
|
||||
if (sqlEditor) sqlEditor.setValue(sql);␍
|
||||
mostrarEditor();␍
|
||||
showToast('SQL copiado al editor');␍
|
||||
}␍
|
||||
@@ -351,6 +353,7 @@ function editQuery(id, name, type, text, desc) {
|
||||
document.getElementById('q_desc').value = desc;␍
|
||||
document.getElementById('btn-cancel').classList.remove('hidden');␍
|
||||
document.querySelector('form').action = '/queries/update/' + id;␍
|
||||
if (sqlEditor) sqlEditor.setValue(text);␍
|
||||
}␍
|
||||
function cancelEdit() {␍
|
||||
document.getElementById('query_id').value = '';␍
|
||||
@@ -359,6 +362,7 @@ function cancelEdit() {
|
||||
document.getElementById('q_desc').value = '';␍
|
||||
document.getElementById('btn-cancel').classList.add('hidden');␍
|
||||
document.querySelector('form').action = '/queries/create';␍
|
||||
if (sqlEditor) sqlEditor.setValue('');␍
|
||||
}␍
|
||||
async function listarTablas(btn) {␍
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-1"></i> Cargando...';␍
|
||||
@@ -374,6 +378,50 @@ async function listarTablas(btn) {
|
||||
} catch(e) { showToast('Error: ' + e.message, 'error'); }␍
|
||||
finally { btn.disabled = false; btn.innerHTML = '<i class="fas fa-table mr-1"></i> Ver tablas disponibles'; }␍
|
||||
}␍
|
||||
let sqlEditor = null;␍
|
||||
␍
|
||||
async function initSqlEditor() {␍
|
||||
sqlEditor = CodeMirror(document.getElementById('sql-editor-wrapper'), {␍
|
||||
value: document.getElementById('q_text').value,␍
|
||||
mode: 'text/x-sql',␍
|
||||
lineNumbers: true,␍
|
||||
indentWithTabs: true,␍
|
||||
smartIndent: true,␍
|
||||
lineWrapping: true,␍
|
||||
extraKeys: {␍
|
||||
'Ctrl-Space': 'autocomplete'␍
|
||||
},␍
|
||||
hintOptions: {␍
|
||||
tables: {},␍
|
||||
words: ['SELECT','FROM','WHERE','AND','OR','JOIN','LEFT','RIGHT','INNER','OUTER','ON','AS','IN','NOT','NULL','IS','LIKE','BETWEEN','INTO','VALUES','SET','UPDATE','DELETE','INSERT','CREATE','ALTER','DROP','TABLE','INDEX','VIEW','ORDER','BY','GROUP','HAVING','DISTINCT','COUNT','SUM','AVG','MIN','MAX','BETWEEN','EXISTS','UNION','ALL','ANY','CASE','WHEN','THEN','ELSE','END','CAST','COALESCE','NULLIF','DESC','ASC']␍
|
||||
}␍
|
||||
});␍
|
||||
␍
|
||||
sqlEditor.on('change', function() {␍
|
||||
document.getElementById('q_text').value = sqlEditor.getValue();␍
|
||||
});␍
|
||||
␍
|
||||
// Cargar esquema para autocomplete␍
|
||||
try {␍
|
||||
const resp = await fetch('/queries/esquema', { method: 'POST' });␍
|
||||
const data = await resp.json();␍
|
||||
if (!data.error && data.tablas) {␍
|
||||
const tables = {};␍
|
||||
data.tablas.forEach(t => {␍
|
||||
tables[t.tabla] = t.columnas.map(c => c.nombre);␍
|
||||
});␍
|
||||
sqlEditor.setOption('hintOptions', { tables });␍
|
||||
}␍
|
||||
} catch(e) {}␍
|
||||
}␍
|
||||
␍
|
||||
document.addEventListener('DOMContentLoaded', function() {␍
|
||||
document.querySelector('form').addEventListener('submit', function() {␍
|
||||
document.getElementById('q_text').value = sqlEditor ? sqlEditor.getValue() : document.getElementById('q_text').value;␍
|
||||
});␍
|
||||
initSqlEditor();␍
|
||||
});␍
|
||||
␍
|
||||
async function ejecutarTest(btn, queryId) {␍
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-1"></i> Probando...';␍
|
||||
try {␍
|
||||
|
||||
Reference in New Issue
Block a user