Files
maps-saas/apps/dashboard/js/billing.js
T

130 lines
4.5 KiB
JavaScript

/**
* Billing & Subscription Logic
*/
const billing = {
state: {
subscription: null,
invoices: []
},
init: async () => {
console.log('💳 Initializing Billing...');
await billing.fetchSubscription();
await billing.fetchInvoices();
billing.renderUI();
},
fetchSubscription: async () => {
try {
const headers = auth.getAuthHeader();
const res = await fetch('/api/billing/subscription', { headers });
if (res.ok) {
billing.state.subscription = await res.json();
}
} catch (error) {
console.error('Failed to fetch subscription', error);
}
},
fetchInvoices: async () => {
try {
const headers = auth.getAuthHeader();
const res = await fetch('/api/billing/invoices', { headers });
if (res.ok) {
billing.state.invoices = await res.json();
}
} catch (error) {
console.error('Failed to fetch invoices', error);
}
},
renderUI: () => {
const sub = billing.state.subscription;
if (!sub) return;
// 1. Update Current Plan Badges
document.querySelectorAll('.current-plan-name').forEach(el => el.textContent = sub.plan);
// 2. Render Plan Cards logic
const plans = ['FREE', 'STARTER', 'PRO', 'ENTERPRISE'];
plans.forEach(p => {
const card = document.getElementById(`plan-card-${p.toLowerCase()}`);
if (card) {
const btn = card.querySelector('.plan-btn');
if (p === sub.plan) {
card.classList.add('border-blue-500/50');
if (btn) {
btn.textContent = 'Current Plan';
btn.disabled = true;
btn.classList.add('opacity-50');
}
} else {
card.classList.remove('border-blue-500/50');
if (btn) {
btn.textContent = p === 'ENTERPRISE' ? 'Contact Sales' : 'Upgrade Now';
btn.disabled = false;
btn.classList.remove('opacity-50');
}
}
}
});
// 3. Render Invoice History
const tbody = document.getElementById('invoice-table-body');
if (tbody) {
if (billing.state.invoices.length === 0) {
tbody.innerHTML = `<tr><td colspan="4" class="py-10 text-center text-slate-500 text-xs">No transactions yet.</td></tr>`;
} else {
tbody.innerHTML = billing.state.invoices.map(inv => `
<tr class="border-b border-white/[0.02] hover:bg-white/[0.01]">
<td class="py-4 text-xs font-medium">${new Date(inv.createdAt).toLocaleDateString()}</td>
<td class="py-4 text-xs font-bold">$${inv.amount}</td>
<td class="py-4 text-xs">
<span class="px-2 py-0.5 rounded bg-slate-800 text-[10px] uppercase font-black">${inv.provider}</span>
</td>
<td class="py-4 text-xs">
<span class="status-badge ${inv.status.toLowerCase()}">${inv.status}</span>
</td>
</tr>
`).join('');
}
}
},
startCheckout: async (plan, provider) => {
try {
const btn = event.target;
const originalText = btn.textContent;
btn.textContent = 'Processing...';
btn.disabled = true;
const headers = {
'Content-Type': 'application/json',
...auth.getAuthHeader()
};
const res = await fetch('/api/billing/checkout', {
method: 'POST',
headers,
body: JSON.stringify({ plan, provider })
});
if (res.ok) {
const data = await res.json();
if (data.checkoutUrl) {
window.location.href = data.checkoutUrl;
}
} else {
alert('Checkout failed. Please try again.');
}
btn.textContent = originalText;
btn.disabled = false;
} catch (error) {
console.error('Checkout error', error);
alert('An error occurred during checkout.');
}
}
};