/**
* Billing UI — Spec 023 + Spec 043 (conta cliente + activação preview)
*/
const DeskBilling = (() => {
const API = '/api';
const PLAN_OPTIONS = [
{ code: 'ligbox-mail-starter', label: 'Mail Starter (10)' },
{ code: 'ligbox-mail-business', label: 'Mail Business (25)' },
{ code: 'ligbox-mail-enterprise', label: 'Mail Enterprise (50)' },
{ code: 'ligbox-mail-custom', label: 'Mail Custom' },
];
function esc(s) {
return String(s ?? '').replace(/&/g, '&').replace(//g, '>');
}
function canActivate() {
return typeof canManageBilling === 'function' && canManageBilling();
}
async function api(path, options = {}) {
const res = await fetch(`${API}${path}`, {
...options,
headers: { ...authHeaders(), 'Content-Type': 'application/json', ...(options.headers || {}) },
});
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).detail || res.statusText);
return res.json();
}
function closeModal() {
document.querySelector('.billing-modal-backdrop')?.remove();
}
function renderMappedBlock(title, obj) {
const rows = Object.entries(obj || {})
.filter(([, v]) => v != null && v !== '' && typeof v !== 'object')
.map(([k, v]) => `
${esc(k)}${esc(v)}`)
.join('');
return `
${esc(title)}
${rows || '- —
- sem dados
'}
`;
}
async function openActivationPreview(accountId, domain) {
const planSel = document.querySelector('[data-billing-plan]');
const plan = planSel?.value || '';
const qs = plan ? `?plan_code=${encodeURIComponent(plan)}` : '';
const preview = await api(`/v1/billing/accounts/${accountId}/activation-preview${qs}`);
const m = preview.mapped || {};
const foss = m.foss_client || {};
const op = m.openpanel || {};
const odoo = m.odoo_partner || {};
const meta = op.metadata || {};
const backdrop = document.createElement('div');
backdrop.className = 'billing-modal-backdrop';
backdrop.innerHTML = `
Activar conta — ${esc(domain)}
Estado: ${esc(preview.billing_state)} · Plano: ${esc(preview.plan_code)}
${renderMappedBlock('FOSSBilling — cliente', foss)}
${renderMappedBlock('OpenPanel — hub', { username: op.username, email: op.email, plan_name: op.plan_name, domain: op.domain, seats: meta.max_seats })}
${renderMappedBlock('Odoo — empresa', { name: odoo.name, vat: odoo.vat, email: odoo.email, ref: odoo.ref })}
${esc(preview.notes || '')}
`;
document.body.appendChild(backdrop);
backdrop.addEventListener('click', (e) => { if (e.target === backdrop) closeModal(); });
backdrop.querySelector('[data-billing-close]')?.addEventListener('click', closeModal);
}
async function openAccountModal(domain) {
closeModal();
const acc = await api(`/v1/billing/accounts/by-domain/${encodeURIComponent(domain)}`);
const planOptions = PLAN_OPTIONS.map(
(p) => ``
).join('');
const backdrop = document.createElement('div');
backdrop.className = 'billing-modal-backdrop';
backdrop.innerHTML = `
Conta do cliente — ${esc(domain)}
- Estado
- ${esc(acc.billing_state)}
- Razão social
- ${esc(acc.legal_name || acc.trade_name || '—')}
- Email cobrança
- ${esc(acc.email_billing || '—')}
- CNPJ/CPF
- ${esc(acc.tax_id || '—')}
- Recorrência
- ${acc.recurrence_active ? '✅ ativa' : '—'}
${canActivate() ? `
` : ''}
FOSSBilling 💳
· Odoo
${canActivate() && !acc.recurrence_active ? `` : ''}
${canManageVm112Domains?.() ? `` : ''}
`;
document.body.appendChild(backdrop);
backdrop.addEventListener('click', (e) => { if (e.target === backdrop) closeModal(); });
backdrop.querySelector('[data-billing-close]')?.addEventListener('click', closeModal);
backdrop.querySelector('[data-billing-activate]')?.addEventListener('click', async (e) => {
const btn = e.currentTarget;
const id = btn.getAttribute('data-billing-activate');
const dom = btn.getAttribute('data-domain') || domain;
try {
await openActivationPreview(id, dom);
} catch (err) {
alert(err.message || 'Erro ao carregar preview');
}
});
backdrop.querySelector('[data-billing-ativate]')?.addEventListener('click', async () => {
await api(`/v1/billing/accounts/${acc.id}`, {
method: 'PATCH',
body: JSON.stringify({ recurrence_active: true, billing_state: 'billing_active' }),
});
closeModal();
if (typeof state !== 'undefined' && state.view === 'overview-home' && typeof renderOverviewHome === 'function') {
await renderOverviewHome();
}
});
}
return { openAccountModal, openActivationPreview, closeModal };
})();
window.DeskBilling = DeskBilling;