Specs stay at repo root (cross-VM). Move deploy and code into logical projects with README per domain, updated manifest.yaml, and symlinks at legacy paths for VM122 backward compatibility.
64 lines
2.7 KiB
JavaScript
64 lines
2.7 KiB
JavaScript
/**
|
|
* Billing UI — Spec 023 (conta cliente modal + overview badge)
|
|
*/
|
|
const DeskBilling = (() => {
|
|
const API = '/api';
|
|
|
|
function esc(s) {
|
|
return String(s ?? '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
async function openAccountModal(domain) {
|
|
closeModal();
|
|
const acc = await api(`/v1/billing/accounts/by-domain/${encodeURIComponent(domain)}`);
|
|
const backdrop = document.createElement('div');
|
|
backdrop.className = 'billing-modal-backdrop';
|
|
backdrop.innerHTML = `
|
|
<div class="billing-modal" role="dialog">
|
|
<h3 style="margin-top:0">Conta do cliente — ${esc(domain)}</h3>
|
|
<dl class="kv">
|
|
<dt>Estado</dt><dd>${esc(acc.billing_state)}</dd>
|
|
<dt>Razão social</dt><dd>${esc(acc.legal_name || acc.trade_name || '—')}</dd>
|
|
<dt>Email cobrança</dt><dd>${esc(acc.email_billing || '—')}</dd>
|
|
<dt>CNPJ/CPF</dt><dd>${esc(acc.tax_id || '—')}</dd>
|
|
<dt>Recorrência</dt><dd>${acc.recurrence_active ? '✅ ativa' : '—'}</dd>
|
|
</dl>
|
|
<p class="ticket-meta">
|
|
<a href="${esc(acc.links?.fossbilling || '#')}" target="_blank" rel="noreferrer">FOSSBilling 💳</a>
|
|
· <a href="${esc(acc.links?.odoo || '#')}" target="_blank" rel="noreferrer">Odoo</a>
|
|
</p>
|
|
<div style="display:flex;gap:0.5rem;margin-top:1rem;flex-wrap:wrap">
|
|
${canManageVm112Domains?.() ? `<button type="button" class="btn btn-primary btn-sm" data-billing-ativate="${acc.id}">Activar recorrência</button>` : ''}
|
|
<button type="button" class="btn btn-sm" data-billing-close>Fechar</button>
|
|
</div>
|
|
</div>`;
|
|
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-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 (state.view === 'overview-home') await renderOverviewHome();
|
|
});
|
|
}
|
|
|
|
return { openAccountModal, closeModal };
|
|
})();
|
|
|
|
window.DeskBilling = DeskBilling;
|