From 44dad0063ca94a80715932b9dc1d565f4188a3a8 Mon Sep 17 00:00:00 2001 From: Ligbox Spec Hub Date: Fri, 19 Jun 2026 19:30:39 +0000 Subject: [PATCH] fix: wire TicketsWorkspace.renderPage in app.js (VM122 live) --- projects/ops-desk/frontend/assets/app.js | 159 +++++++++++++++++++++-- 1 file changed, 148 insertions(+), 11 deletions(-) diff --git a/projects/ops-desk/frontend/assets/app.js b/projects/ops-desk/frontend/assets/app.js index 71a6fe4..660cdf0 100644 --- a/projects/ops-desk/frontend/assets/app.js +++ b/projects/ops-desk/frontend/assets/app.js @@ -84,9 +84,26 @@ const views = { }; function roleLabel(role) { - return { super_admin: 'Super Admin', ops_lead: 'Chefe Ops', technician: 'Suporte', noc: 'NOC' }[role] || role; + return ROLE_LABELS[role] || role; } +const ROLE_LABELS = { + super_admin: 'Super Admin', + ops_lead: 'Chefe Ops', + technician: 'Suporte', + noc: 'NOC', + sales_admin: 'Sales Admin', + sales_support: 'Sales Support', + finance: 'Financeiro', + marketing: 'Marketing', + seo: 'SEO', + developer: 'Developer', + devops: 'DevOps', + security_analyst: 'Segurança / SOC', + content_editor: 'Conteúdo / CMS', + agentic_operator: 'Operador Agentes IA', +}; + function statusLabel(status) { return { pending: 'pendente', @@ -1914,7 +1931,7 @@ async function renderTickets(options = {}) { state.tickets = tickets; listEl.innerHTML = state.tickets.length ? state.tickets.map(ticketRowHtml).join('') - : '

Nenhum ticket nesto filtro

'; + : '

Nenhum ticket neste filtro

'; listEl.querySelectorAll('.ticket-row').forEach((btn) => { btn.addEventListener('click', () => { state.selectedTicketId = Number(btn.dataset.id); @@ -2568,6 +2585,16 @@ function roleBadgeHtml(role) { ops_lead: 'role-lead', technician: 'role-tech', noc: 'role-noc', + sales_admin: 'role-sales-admin', + sales_support: 'role-sales-support', + finance: 'role-finance', + marketing: 'role-marketing', + seo: 'role-seo', + developer: 'role-developer', + devops: 'role-devops', + security_analyst: 'role-security', + content_editor: 'role-content', + agentic_operator: 'role-agentic', }[role] || 'role-default'; return `${esc(roleLabel(role))}`; } @@ -2582,14 +2609,34 @@ function mfaBadgeHtml(user) { } const ROLE_OPTIONS = [ - { value: 'super_admin', label: 'Super Admin' }, - { value: 'ops_lead', label: 'Chefe Ops' }, - { value: 'technician', label: 'Suporte' }, - { value: 'noc', label: 'NOC' }, + { value: 'super_admin', label: 'Super Admin', group: 'Ops' }, + { value: 'ops_lead', label: 'Chefe Ops', group: 'Ops' }, + { value: 'technician', label: 'Suporte', group: 'Ops' }, + { value: 'noc', label: 'NOC', group: 'Ops' }, + { value: 'sales_admin', label: 'Sales Admin', group: 'Comercial' }, + { value: 'sales_support', label: 'Sales Support', group: 'Comercial' }, + { value: 'finance', label: 'Financeiro', group: 'Negócio' }, + { value: 'marketing', label: 'Marketing', group: 'Negócio' }, + { value: 'seo', label: 'SEO', group: 'Negócio' }, + { value: 'developer', label: 'Developer', group: 'Plataforma' }, + { value: 'devops', label: 'DevOps', group: 'Plataforma' }, + { value: 'security_analyst', label: 'Segurança / SOC', group: 'Plataforma' }, + { value: 'content_editor', label: 'Conteúdo / CMS', group: 'Plataforma' }, + { value: 'agentic_operator', label: 'Operador Agentes IA', group: 'Plataforma' }, ]; const ASSIGNABLE_ROLE_OPTIONS = ROLE_OPTIONS.filter((r) => r.value !== 'super_admin'); +function registrationRoleSelectHtml(selected = 'technician') { + const groups = [...new Set(ASSIGNABLE_ROLE_OPTIONS.map((r) => r.group))]; + return groups.map((group) => { + const opts = ASSIGNABLE_ROLE_OPTIONS.filter((r) => r.group === group) + .map((r) => ``) + .join(''); + return `${opts}`; + }).join(''); +} + function roleSelectHtml(username, current, assignableOnly = true) { const options = assignableOnly && current !== 'super_admin' ? ASSIGNABLE_ROLE_OPTIONS @@ -2961,7 +3008,7 @@ async function renderModules() { } } -const REG_ROLE_LABELS = { ops_lead: 'Chefe Ops (admin)', technician: 'Técnico', noc: 'NOC' }; +const REG_ROLE_LABELS = ROLE_LABELS; async function renderMessages() { const el = document.getElementById('messages-content'); @@ -2986,9 +3033,7 @@ async function renderMessages() {
@@ -3001,7 +3046,7 @@ async function renderMessages() { ${esc(r.email)} ${esc(statusLabel(r.status))} - ${esc(r.role || '—')} + ${esc(r.role ? roleLabel(r.role) : '—')} ${fmtDate(r.updated_at || r.created_at)} `).join(''); el.innerHTML = ` @@ -3356,6 +3401,90 @@ function showSocWebhookTestError(err) { modal.setAttribute('aria-hidden', 'false'); } +function showOpenPanelTestResult(result) { + const modal = document.getElementById('soc-test-modal'); + const title = document.getElementById('soc-test-modal-title'); + const sub = document.getElementById('soc-test-modal-sub'); + const body = document.getElementById('soc-test-modal-body'); + if (!modal || !body) return; + + const ok = result.ok === true; + title.textContent = ok ? 'OpenPanel API — confirmado' : 'OpenPanel API — falha'; + sub.textContent = `Spec 028 · ${result.steps_passed || 0}/${result.steps_total || 0} passos · ${result.duration_sec || '—'}s`; + + const steps = (result.steps || []).map((s) => ` +
  • + ${esc(s.name)} — ${esc(s.detail || (s.ok ? 'OK' : 'FAIL'))} +
  • `).join(''); + + body.innerHTML = ` +
    +
    + + ${esc(result.message || (ok ? 'Multidomínio OK' : 'Falha'))} +
    +
      ${steps || '
    • '}
    +

    + Suite openpanel-multidomain-api-confirm — provisiona 2 contas temporárias + (2 domínios na plataforma), valida listagem e remove. Pode executar quantas vezes quiser. + Script CLI: scripts/test-openpanel-multidomain-api.sh +

    +
    + +
    +
    `; + body.querySelector('[data-close-soc-test-modal]')?.addEventListener('click', closeSocTestModal); + modal.classList.remove('hidden'); + modal.setAttribute('aria-hidden', 'false'); +} + +function showOpenPanelTestError(err) { + const modal = document.getElementById('soc-test-modal'); + const title = document.getElementById('soc-test-modal-title'); + const sub = document.getElementById('soc-test-modal-sub'); + const body = document.getElementById('soc-test-modal-body'); + if (!modal || !body) return; + + const msg = err?.message || String(err); + const is403 = /403|permiss/i.test(msg); + title.textContent = 'OpenPanel API — erro'; + sub.textContent = 'Teste não concluído'; + body.innerHTML = ` +
    +
    + + ${esc(msg)} +
    + ${is403 ? '

    Perfis: super_admin, devops, developer.

    ' : ''} +
    + +
    +
    `; + body.querySelector('[data-close-soc-test-modal]')?.addEventListener('click', closeSocTestModal); + modal.classList.remove('hidden'); + modal.setAttribute('aria-hidden', 'false'); +} + +async function runOpenPanelApiTest() { + const btn = document.getElementById('btn-test-openpanel-api'); + const prevLabel = btn?.textContent; + if (btn) { + btn.disabled = true; + btn.textContent = 'Testando…'; + } + try { + const r = await api('/v1/vm123/openpanel/test-confirm', { method: 'POST' }); + showOpenPanelTestResult(r); + } catch (ex) { + showOpenPanelTestError(ex); + } finally { + if (btn) { + btn.disabled = false; + btn.textContent = prevLabel || 'Testar multidomínio'; + } + } +} + async function runWebhookIntegrationTest(refreshView) { const btn = document.getElementById('soc-btn-test') || document.getElementById('btn-test-webhook'); const prevLabel = btn?.textContent; @@ -3726,6 +3855,13 @@ async function renderInfra() {

    Alerta se gap > ${health.webhook_gap_alert_minutes || 15} min sem eventos VM112.

    +
    +

    OpenPanel API — Re-engenharia Ligbox

    +

    Spec 028 · VM123 bridge :18087 · multidomínio · conta temporária com cleanup automático.

    +
    + +
    +

    VM112 — Portal Onboard

    @@ -3746,6 +3882,7 @@ async function renderInfra() {
    `; document.getElementById('btn-refresh-health')?.addEventListener('click', () => renderInfra()); document.getElementById('btn-test-webhook')?.addEventListener('click', () => runWebhookIntegrationTest('infra')); + document.getElementById('btn-test-openpanel-api')?.addEventListener('click', () => runOpenPanelApiTest()); } catch (e) { el.innerHTML = `

    Erro: ${esc(e.message)}

    `; }