fix(desk): parar piscar da página SOC Infra 2 no poll 15s
Refresh suave sem ecrã "Carregando SOC", guard de render concorrente, flash de linha só em evento novo e event delegation nos controlos. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
af58565999
commit
48b0dc2fe3
3 changed files with 70 additions and 22 deletions
|
|
@ -277,6 +277,7 @@ function setView(name) {
|
|||
}
|
||||
|
||||
let pollTimer = null;
|
||||
let socRenderInFlight = false;
|
||||
function reschedulePoll() {
|
||||
if (pollTimer) clearInterval(pollTimer);
|
||||
const ms = state.view === 'infra2' ? 15000 : 30000;
|
||||
|
|
@ -3529,7 +3530,7 @@ async function runWebhookIntegrationTest(refreshView) {
|
|||
try {
|
||||
const r = await api('/v1/integrations/onboard/test', { method: 'POST' });
|
||||
showSocWebhookTestResult(r);
|
||||
if (refreshView === 'infra2') await renderInfra2();
|
||||
if (refreshView === 'infra2') await renderInfra2({ force: true });
|
||||
else if (refreshView === 'infra') await renderInfra();
|
||||
} catch (ex) {
|
||||
showSocWebhookTestError(ex);
|
||||
|
|
@ -3541,14 +3542,64 @@ async function runWebhookIntegrationTest(refreshView) {
|
|||
}
|
||||
}
|
||||
|
||||
async function renderInfra2() {
|
||||
function bindSocEventsOnce() {
|
||||
if (state._socEventsBound) return;
|
||||
const host = document.getElementById('infra2-content');
|
||||
if (!host) return;
|
||||
host.addEventListener('change', (e) => {
|
||||
if (e.target.id !== 'soc-window-select') return;
|
||||
state.socWindow = e.target.value;
|
||||
renderInfra2({ force: true });
|
||||
});
|
||||
host.addEventListener('click', (e) => {
|
||||
if (e.target.closest('#soc-btn-refresh')) {
|
||||
e.preventDefault();
|
||||
renderInfra2({ force: true });
|
||||
return;
|
||||
}
|
||||
if (e.target.closest('#soc-btn-test')) {
|
||||
e.preventDefault();
|
||||
runWebhookIntegrationTest('infra2');
|
||||
return;
|
||||
}
|
||||
const card = e.target.closest('[data-soc-session]');
|
||||
if (card) {
|
||||
state.selectedSessionId = card.dataset.socSession;
|
||||
const tid = card.dataset.socTicket;
|
||||
state.selectedTicketId = tid ? Number(tid) : null;
|
||||
setView('tickets');
|
||||
}
|
||||
});
|
||||
state._socEventsBound = true;
|
||||
}
|
||||
|
||||
async function renderInfra2(options = {}) {
|
||||
const { poll = false, force = false } = options;
|
||||
const el = document.getElementById('infra2-content');
|
||||
if (!el) return;
|
||||
if (window.DeskModules?.loaded && !DeskModules.isEnabled('infra2-soc')) {
|
||||
el.innerHTML = '<p class="loading">Módulo Infra 2 SOC desativado. Active em <strong>Módulos</strong>.</p>';
|
||||
return;
|
||||
}
|
||||
el.innerHTML = '<p class="loading">Carregando SOC…</p>';
|
||||
|
||||
const hasShell = !!document.getElementById('soc-console-root');
|
||||
const soft = poll && hasShell && !force;
|
||||
|
||||
if (socRenderInFlight) {
|
||||
if (poll) return;
|
||||
while (socRenderInFlight) {
|
||||
await new Promise((r) => setTimeout(r, 50));
|
||||
}
|
||||
}
|
||||
|
||||
socRenderInFlight = true;
|
||||
const root = document.getElementById('soc-console-root');
|
||||
if (soft && root) {
|
||||
root.classList.add('soc-console--refreshing');
|
||||
} else if (!hasShell || force) {
|
||||
el.innerHTML = '<p class="loading">Carregando SOC…</p>';
|
||||
}
|
||||
|
||||
const windowHours = socWindowHours();
|
||||
try {
|
||||
const [health, vm112, wazuh, funnel, eventsRes, secRes, summary] = await Promise.all([
|
||||
|
|
@ -3610,8 +3661,9 @@ async function renderInfra2() {
|
|||
}
|
||||
|
||||
const newestId = feedEvents[0]?.id;
|
||||
const flashNew = state.socLastEventId && newestId && newestId > state.socLastEventId;
|
||||
state.socLastEventId = newestId || state.socLastEventId;
|
||||
const newestKey = newestId != null ? String(newestId) : null;
|
||||
const flashNew = newestKey != null && newestKey !== state.socLastEventId;
|
||||
if (newestKey != null) state.socLastEventId = newestKey;
|
||||
|
||||
const onboardTicketsOpen = (summary.recent_tickets || []).filter(
|
||||
(t) => (t.source === 'vm112-onboard' || String(t.subject || '').includes('[onboarding]')) && t.status !== 'closed'
|
||||
|
|
@ -3834,22 +3886,12 @@ async function renderInfra2() {
|
|||
</div>
|
||||
</div>`;
|
||||
|
||||
document.getElementById('soc-window-select')?.addEventListener('change', (e) => {
|
||||
state.socWindow = e.target.value;
|
||||
renderInfra2();
|
||||
});
|
||||
document.getElementById('soc-btn-refresh')?.addEventListener('click', () => renderInfra2());
|
||||
document.getElementById('soc-btn-test')?.addEventListener('click', () => runWebhookIntegrationTest('infra2'));
|
||||
el.querySelectorAll('[data-soc-session]').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
state.selectedSessionId = btn.dataset.socSession;
|
||||
const tid = btn.dataset.socTicket;
|
||||
state.selectedTicketId = tid ? Number(tid) : null;
|
||||
setView('tickets');
|
||||
});
|
||||
});
|
||||
bindSocEventsOnce();
|
||||
} catch (e) {
|
||||
el.innerHTML = `<p class="loading">Erro SOC: ${esc(e.message)}</p>`;
|
||||
} finally {
|
||||
socRenderInFlight = false;
|
||||
document.getElementById('soc-console-root')?.classList.remove('soc-console--refreshing');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4254,7 +4296,7 @@ async function refresh(options = {}) {
|
|||
if (state.view === 'events') await renderEvents();
|
||||
if (state.view === 'tenants') await renderTenants();
|
||||
if (state.view === 'infra') await renderInfra();
|
||||
if (state.view === 'infra2') await renderInfra2();
|
||||
if (state.view === 'infra2') await renderInfra2({ poll });
|
||||
if (state.view === 'agentic-ops' && window.renderAgenticOps) await window.renderAgenticOps();
|
||||
if (state.view === 'messages') await renderMessages();
|
||||
if (state.view === 'admin') await renderAdmin();
|
||||
|
|
|
|||
|
|
@ -2535,6 +2535,12 @@ button.health-card {
|
|||
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.soc-console--refreshing {
|
||||
opacity: 0.94;
|
||||
transition: opacity 0.25s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.soc-header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>Ligbox Ops — Support Desk</title>
|
||||
<link rel="stylesheet" href="/assets/styles.css?v=20260619tickets2"/>
|
||||
<link rel="stylesheet" href="/assets/styles.css?v=20260625socfix"/>
|
||||
<link rel="stylesheet" href="/assets/tickets-workspace.css?v=20260619tickets2"/>
|
||||
<link rel="stylesheet" href="/assets/agentic-ops.css?v=20260620v10"/>
|
||||
<link rel="stylesheet" href="/assets/access-matrix.css?v=20260620am7"/>
|
||||
|
|
@ -476,6 +476,6 @@
|
|||
<script src="/assets/servicos.js?v=20260620agentic"></script>
|
||||
<script src="/assets/agentic-ops.js?v=20260620v10"></script>
|
||||
<script src="/assets/access-matrix.js?v=20260620am7"></script>
|
||||
<script src="/assets/app.js?v=20260620v4"></script>
|
||||
<script src="/assets/app.js?v=20260625socfix"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in a new issue