/* ---------- 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);