/* ---------- CONFIGURAÇÕES BÁSICAS ---------- */ const APPS = [ { name: 'Kanban', src: 'https://kanban.appwoot.com/?api_password=ASCOIHJOP@OPJCAafadcsaojp', icon: 'i-lucide-grid' }, { name: 'Disparador', src: 'https://disparador.appwoot.com/?url_password=qualquersenhasegura', icon: '' } ]; const MAIN_SEL = 'main.flex.flex-1.h-full.min-h-0.px-0.overflow-hidden'; const SIDEBAR_SEL = 'aside.bg-n-solid-2'; const UL_SEL = 'nav ul.flex.flex-col.list-none'; /* ------------------------------------------- */ /* Tenta injetar o grupo assim que possível */ document.addEventListener('DOMContentLoaded', () => { const injectIfReady = () => { // Busca a sidebar pelo novo seletor const sidebar = document.querySelector(SIDEBAR_SEL); if (!sidebar) { // Fallback para seletor mais genérico const fallbackSidebar = document.querySelector('aside.bg-n-solid-2'); if (!fallbackSidebar) return false; sidebar = fallbackSidebar; } const ul = sidebar.querySelector(UL_SEL); if (!ul) { // Fallback para seletor mais genérico da ul const fallbackUl = sidebar.querySelector('nav ul'); if (!fallbackUl) return false; } const targetUl = ul || sidebar.querySelector('nav ul'); if (targetUl && !targetUl.querySelector('[data-src*="kanban"]')) { // Kanban acima de Conversas const conversationBtn = sidebar.querySelector('div[role="button"][name="Conversation"], div[title="Conversas"], div[name="Conversation"]'); const conversationLi = conversationBtn?.closest('li'); if (conversationLi && !conversationLi.parentElement.querySelector('[data-src*="kanban"]')) { const li = document.createElement('li'); li.className = 'grid gap-1 text-sm cursor-pointer select-none'; li.innerHTML = `
Kanban
`; const a = li.querySelector('a'); a.onclick = e => { e.preventDefault(); // Remove estado ativo de todos os itens da sidebar document.querySelectorAll('nav ul li, nav ul li a').forEach(el => { el.classList.remove('router-link-active', 'router-link-exact-active', 'bg-n-alpha-2', 'active', 'n-blue-text', 'appwoot-kanban-active'); }); li.classList.add('appwoot-kanban-active'); a.classList.add('router-link-active', 'router-link-exact-active', 'bg-n-alpha-2', 'active', 'n-blue-text', 'appwoot-kanban-active'); openAppPanel(APPS[0]); }; conversationLi.parentElement.insertBefore(li, conversationLi); // Fecha painel e remove ativo ao clicar em qualquer outro item da sidebar document.addEventListener('click', function handler(ev) { const sidebar = document.querySelector(SIDEBAR_SEL); if (!sidebar) return; const isKanban = ev.target.closest('a[data-src*="kanban"]'); const isSidebar = ev.target.closest(SIDEBAR_SEL); if (isSidebar && !isKanban) { document.querySelectorAll('.appwoot-kanban-active').forEach(el => { el.classList.remove('router-link-active', 'router-link-exact-active', 'bg-n-alpha-2', 'active', 'n-blue-text', 'appwoot-kanban-active'); }); closeAppPanel(); } }, true); } // Disparador dentro de Campanhas const campaignBtn = sidebar.querySelector('div[role="button"][name="Campaigns"], div[title="Campanhas"], div[name="Campaigns"]'); const campaignLi = campaignBtn?.closest('li'); const campaignUl = campaignLi?.querySelector('ul.sidebar-group-children, ul'); if (campaignUl && !campaignUl.querySelector('[data-src*="disparador"]')) { const li = document.createElement('li'); li.className = 'py-0.5 ltr:pl-3 rtl:pr-3 rtl:mr-3 ltr:ml-3 relative text-n-slate-11 child-item before:bg-n-slate-4 after:bg-transparent after:border-n-slate-4 before:left-0'; li.innerHTML = `
${APPS[1].name}
`; const a = li.querySelector('a'); a.onclick = e => { e.preventDefault(); // Remove estado ativo de todos os itens da sidebar document.querySelectorAll('nav ul li, nav ul li a').forEach(el => { el.classList.remove('router-link-active', 'router-link-exact-active', 'bg-n-alpha-2', 'active', 'n-blue-text', 'appwoot-kanban-active'); }); li.classList.add('appwoot-kanban-active'); a.classList.add('router-link-active', 'router-link-exact-active', 'bg-n-alpha-2', 'active', 'n-blue-text', 'appwoot-kanban-active'); openAppPanel(APPS[1]); }; campaignUl.appendChild(li); } return true; } return false; }; /* 1) tentativa imediata */ if (injectIfReady()) return; /* 2) MutationObserver (quando a sidebar aparecer) */ const mo = new MutationObserver(() => { if (injectIfReady()) mo.disconnect(); }); mo.observe(document.body, { childList: true, subtree: true }); /* 3) fallback: interval de 300 ms por 10 s */ const iv = setInterval(() => { if (injectIfReady()) clearInterval(iv); }, 300); setTimeout(() => clearInterval(iv), 10_000); // Garante largura da sidebar em 150px mesmo se a classe mudar depois do carregamento const forceSidebarWidth = () => { const aside = document.querySelector('aside.bg-n-solid-2'); if (aside) { aside.classList.remove('w-[200px]'); aside.classList.remove('w-[12.5rem]'); aside.classList.remove('w-[180px]'); aside.classList.add('w-[150px]'); } }; setTimeout(forceSidebarWidth, 1000); const moSidebar = new MutationObserver(forceSidebarWidth); moSidebar.observe(document.body, { childList: true, subtree: true }); }); /* ---------- CRIA GRUPO NA SIDEBAR ---------- */ /* ---------- ABRE / FECHA PAINEL ---------- */ let hiddenNodes = []; async function openAppPanel({ src, name }) { const main = document.querySelector(MAIN_SEL); if (!main) return; const existing = document.getElementById('appwoot-panel'); if (existing) { existing.querySelector('iframe').src = src; sendAppContext(existing.querySelector('iframe')); return; } hiddenNodes = [...main.children]; hiddenNodes.forEach(n => n.style.display = 'none'); const panel = document.createElement('section'); panel.id = 'appwoot-panel'; panel.className = 'w-full h-full relative'; panel.style.zIndex = 1; /* modais internos têm z-index bem maior */ panel.innerHTML = ` `; main.appendChild(panel); document.getElementById('close-appwoot').onclick = closeAppPanel; // Envia contexto para o iframe assim que carregar const iframe = panel.querySelector('iframe'); iframe.addEventListener('load', () => sendAppContext(iframe)); } // Obtém informações do usuário e envia via postMessage para o iframe async function sendAppContext(iframe) { // Função baseada em color.js let accountId = 1; let userRole = 'agent'; let isAdmin = false; let currentUser = { id: 1, name: "Usuário", email: "" }; try { // Tenta obter account_id da URL const urlMatch = location.pathname.match(/\/app\/accounts\/(\d+)/); if (urlMatch) { accountId = parseInt(urlMatch[1], 10); } else if (window.chatwootConfig?.account_id) { accountId = window.chatwootConfig.account_id; } // Tenta obter dados do usuário do Vuex store if (window.$store?.state?.auth?.currentUser) { const user = window.$store.state.auth.currentUser; currentUser = { id: user.id || 1, name: user.display_name || user.name || "Usuário", email: user.email || "" }; userRole = user.role || window.$store?.state?.auth?.currentRole || 'agent'; } isAdmin = userRole === 'administrator' || userRole === 'admin'; // Tenta obter via cookie (igual color.js) const sessionCookie = document.cookie.split(';').find(c => c.trim().startsWith('cw_d_session_info=')); if (sessionCookie) { try { const parts = sessionCookie.split('='); if (parts.length >= 2) { const encodedValue = parts.slice(1).join('='); const decodedValue = decodeURIComponent(encodedValue); const auth = JSON.parse(decodedValue); // Busca perfil const profileRes = await fetch('/api/v1/profile', { headers: { 'access-token': auth['access-token'], 'token-type': auth['token-type'], client: auth.client, expiry: auth.expiry, uid: auth.uid, accept: 'application/json' } }); if (profileRes.ok) { const profile = await profileRes.json(); userRole = profile.role; isAdmin = profile.role === 'administrator' || profile.role === 'admin'; // Atualiza dados do usuário com informações do perfil currentUser = { id: profile.id || currentUser.id, name: profile.display_name || profile.name || currentUser.name, email: profile.email || currentUser.email }; } } } catch (e) { console.warn('Erro ao processar cookie de sessão:', e); } } } catch (e) { console.warn('Erro ao obter contexto do usuário:', e); } // Monta estrutura compatível com o listener do novo_inbox_v4 const data = { data: { currentAgent: { id: currentUser.id, name: currentUser.name, email: currentUser.email, role: userRole }, conversation: { account_id: accountId, meta: { assignee: { id: currentUser.id, account_id: accountId, role: userRole } } } } }; // Envia para o iframe embedado try { iframe.contentWindow.postMessage(JSON.stringify(data), "*"); console.log('Contexto enviado para iframe:', data); } catch (e) { console.error('Erro ao enviar contexto para iframe:', e); } } function closeAppPanel() { const p = document.getElementById('appwoot-panel'); if (p) p.remove(); hiddenNodes.forEach(n => n.style.display = ''); } /* Fecha ao trocar de rota (SPA) */ ['pushState','replaceState'].forEach(m => { const orig = history[m]; history[m] = function () { orig.apply(this, arguments); closeAppPanel(); }; }); addEventListener('popstate', closeAppPanel);