UI: instance list with names+status, click to select, v1.0.39
This commit is contained in:
+1
-1
@@ -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
@@ -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({
|
||||||
|
|||||||
Reference in New Issue
Block a user