UI: instance list with names+status, click to select, v1.0.39

This commit is contained in:
2026-07-26 08:05:45 +04:00
parent 9668f03d3e
commit b0ef0c98fe
2 changed files with 41 additions and 26 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ from routes.main import bp as main_bp
from routes.api import bp as api_bp from routes.api import bp as api_bp
from routes.api_test import bp as api_test_bp from routes.api_test import bp as api_test_bp
VERSION = "1.0.38" VERSION = "1.0.39"
app = Flask(__name__, template_folder="templates", static_folder="static") app = Flask(__name__, template_folder="templates", static_folder="static")
app.config["NUBES_API_ENDPOINT"] = os.getenv("NUBES_API_ENDPOINT", "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc") app.config["NUBES_API_ENDPOINT"] = os.getenv("NUBES_API_ENDPOINT", "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc")
+40 -25
View File
@@ -92,22 +92,20 @@
</div> </div>
<script> <script>
let selectedSvc=null, selectedOp=null, svcInstances=[]; let selectedSvc=null, selectedOp=null, svcInstances=[], selectedInst=null;
// Load services — only dummy for now
(async function(){ (async function(){
const el=document.getElementById('svc-list'); const el=document.getElementById('svc-list');
try{ try{
const r=await fetch('/api/services'); const r=await fetch('/api/services');
const data=await r.json(); const data=await r.json();
const dummy=data.find(s=>s.svcId===1); el.innerHTML=data.map(s=>`<div class="svc-item" data-svc-id="${s.svcId}" onclick="selectService(${s.svcId})">${s.svcId}. ${s.svc}</div>`).join('');
if(dummy) el.innerHTML=`<div class="svc-item active" data-svc-id="1" onclick="selectService(1)">1. ${dummy.svc}</div>`; if(data.length>0) selectService(data[0].svcId);
selectService(1);
}catch(e){el.innerHTML='<span style="color:var(--destructive);font-size:11px;">Ошибка загрузки</span>';} }catch(e){el.innerHTML='<span style="color:var(--destructive);font-size:11px;">Ошибка загрузки</span>';}
})(); })();
async function selectService(svcId){ async function selectService(svcId){
selectedSvc=svcId; selectedOp=null; selectedSvc=svcId; selectedOp=null; selectedInst=null;
document.querySelectorAll('.svc-item').forEach(e=>e.classList.toggle('active',e.dataset.svcId==String(svcId))); document.querySelectorAll('.svc-item').forEach(e=>e.classList.toggle('active',e.dataset.svcId==String(svcId)));
document.getElementById('params-card').style.display='none'; document.getElementById('params-card').style.display='none';
document.getElementById('btn-test').style.display='none'; document.getElementById('btn-test').style.display='none';
@@ -115,24 +113,43 @@ async function selectService(svcId){
const r=await fetch('/api/operations/'+svcId); const r=await fetch('/api/operations/'+svcId);
const d=await r.json(); const d=await r.json();
svcInstances=d.instances||[]; svcInstances=d.instances||[];
const hasInstance=svcInstances.length>0;
const activeStatuses=svcInstances.map(i=>i.explainedStatus);
const opsCard=document.getElementById('ops-card'); document.getElementById('ops-card').style.display='block';
opsCard.style.display='block';
document.getElementById('ops-title').textContent=d.svc+' — операции'; document.getElementById('ops-title').textContent=d.svc+' — операции';
const opsList=document.getElementById('ops-list'); let html='';
opsList.innerHTML=d.operations.filter(o=>o.operation!=='reconcile').map(o=>{ // Инстансы с именами и статусами
let disabled=!hasInstance && o.operation!=='create'; if(svcInstances.length>0){
if(hasInstance && o.operation==='create') disabled=true; html+='<div style="font-size:11px;font-weight:600;padding:4px 8px;color:var(--muted);">Инстансы</div>';
if(o.operation==='reconcile') disabled=false; svcInstances.forEach(i=>{
const cls=disabled?'op-item disabled':'op-item'; const sc=i.explainedStatus==='running'?'badge-success':'';
const btn=disabled?'':'<button class="btn btn-primary btn-sm" onclick="selectOperation('+o.svcOperationId+',\''+o.operation+'\','+svcId+')">Тест</button>'; html+=`<div class="op-item" style="cursor:pointer;" onclick="selectInstance('${i.instanceUid}')" data-iuid="${i.instanceUid}">
const hint=disabled?`<span style="font-size:10px;color:var(--muted);">${hasInstance?'уже есть':'нет инстанса'}</span>`:''; <span style="flex:1;font-size:12px;">${i.displayName}</span>
const instUid=svcInstances.length>0?`<span style="font-size:10px;color:var(--muted);">${svcInstances[0].instanceUid.slice(0,8)}...</span>`:''; <span class="badge ${sc}" style="font-size:9px;">${i.explainedStatus||'?'}</span>
return `<div class="${cls}">${btn} ${o.operation} ${hint} ${instUid}</div>`; </div>`;
}).join(''); });
}
// Операции
html+='<div style="font-size:11px;font-weight:600;padding:4px 8px;color:var(--muted);margin-top:4px;">Операции</div>';
d.operations.filter(o=>o.operation!=='reconcile').forEach(o=>{
const isCreate=o.operation==='create';
const disabled=!isCreate&&!selectedInst;
const btn=disabled?'':`<button class="btn btn-primary btn-sm" onclick="selectOperation(${o.svcOperationId},'${o.operation}',${svcId})">Тест</button>`;
const hint=isCreate&&svcInstances.length>0?'<span style="font-size:10px;color:var(--muted);">уже есть</span>':
(!isCreate&&!selectedInst?'<span style="font-size:10px;color:var(--muted);">выберите инстанс</span>':'');
html+=`<div class="op-item${disabled?' disabled':''}" id="op-${o.operation}">${btn} ${o.operation} ${hint}</div>`;
});
document.getElementById('ops-list').innerHTML=html;
}
function selectInstance(iuid){
selectedInst=iuid;
document.querySelectorAll('[data-iuid]').forEach(e=>e.classList.toggle('active',e.dataset.iuid===iuid));
document.querySelectorAll('#ops-list .op-item').forEach(e=>{
if(e.id!=='op-create') e.classList.remove('disabled');
});
document.querySelectorAll('#op-create').forEach(e=>e.classList.add('disabled'));
} }
async function selectOperation(opId,opName,svcId){ async function selectOperation(opId,opName,svcId){
@@ -181,10 +198,8 @@ async function runTest(){
params[el.name.replace('p_','')]=v; params[el.name.replace('p_','')]=v;
}); });
let instUid=''; let instUid=selectedInst||'';
if(selectedOp.opName!=='create'&&svcInstances.length>0){ if(selectedOp.opName==='create') instUid='';
instUid=svcInstances[0].instanceUid;
}
try{ try{
const r=await fetch('/api/test',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ const r=await fetch('/api/test',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({