feat: invoices section on settings page, show paid and active payments
This commit is contained in:
parent
81f1e1585e
commit
c3103f06ce
|
|
@ -204,7 +204,20 @@ export const dashboard = new Elysia()
|
||||||
const loginKey = isSubKey ? null : (cookie?.pingql_key?.value ?? null);
|
const loginKey = isSubKey ? null : (cookie?.pingql_key?.value ?? null);
|
||||||
const [{ count: monitorCount }] = await sql`SELECT COUNT(*)::int as count FROM monitors WHERE account_id = ${accountId}`;
|
const [{ count: monitorCount }] = await sql`SELECT COUNT(*)::int as count FROM monitors WHERE account_id = ${accountId}`;
|
||||||
|
|
||||||
return html("settings", { nav: "settings", account: acc, apiKeys, accountId, loginKey, isSubKey, monitorCount });
|
// Fetch paid + active (non-expired) invoices
|
||||||
|
let invoices: any[] = [];
|
||||||
|
try {
|
||||||
|
invoices = await sql`
|
||||||
|
SELECT id, plan, months, amount_usd, coin, amount_crypto, status, created_at, paid_at, expires_at, txid
|
||||||
|
FROM payments
|
||||||
|
WHERE account_id = ${accountId}
|
||||||
|
AND (status = 'paid' OR (status IN ('pending', 'confirming') AND expires_at >= now()))
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 20
|
||||||
|
`;
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
return html("settings", { nav: "settings", account: acc, apiKeys, accountId, loginKey, isSubKey, monitorCount, invoices });
|
||||||
})
|
})
|
||||||
|
|
||||||
// Checkout — upgrade plan
|
// Checkout — upgrade plan
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,39 @@
|
||||||
<% } %>
|
<% } %>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Invoices -->
|
||||||
|
<% if (it.invoices && it.invoices.length > 0) { %>
|
||||||
|
<section class="bg-gray-900 rounded-xl border border-gray-800 p-6">
|
||||||
|
<h2 class="text-sm font-semibold text-gray-300 mb-4">Invoices</h2>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<% it.invoices.forEach(function(inv) {
|
||||||
|
const statusColors = { paid: 'green', confirming: 'blue', pending: 'yellow' };
|
||||||
|
const statusColor = statusColors[inv.status] || 'gray';
|
||||||
|
const date = new Date(inv.created_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
|
||||||
|
const planLabel = inv.plan === 'lifetime' ? 'Lifetime' : `Pro × ${inv.months}mo`;
|
||||||
|
%>
|
||||||
|
<div class="flex items-center justify-between p-3 bg-gray-800/50 rounded-lg border border-gray-700/50">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<span class="w-2 h-2 rounded-full bg-<%= statusColor %>-500 <%= inv.status === 'pending' || inv.status === 'confirming' ? 'animate-pulse' : '' %>"></span>
|
||||||
|
<div>
|
||||||
|
<span class="text-sm text-gray-200"><%= planLabel %></span>
|
||||||
|
<span class="text-xs text-gray-600 ml-2">$<%= Number(inv.amount_usd).toFixed(2) %> · <%= inv.coin.toUpperCase() %></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<span class="text-xs text-gray-500"><%= date %></span>
|
||||||
|
<% if (inv.status === 'pending' || inv.status === 'confirming') { %>
|
||||||
|
<a href="/dashboard/checkout/<%= inv.id %>" class="text-xs text-blue-400 hover:text-blue-300">View</a>
|
||||||
|
<% } else if (inv.status === 'paid' && inv.txid) { %>
|
||||||
|
<span class="text-xs text-green-500/70">Paid</span>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% }) %>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<!-- Account info -->
|
<!-- Account info -->
|
||||||
<section class="bg-gray-900 rounded-xl border border-gray-800 p-6">
|
<section class="bg-gray-900 rounded-xl border border-gray-800 p-6">
|
||||||
<h2 class="text-sm font-semibold text-gray-300 mb-4">Account</h2>
|
<h2 class="text-sm font-semibold text-gray-300 mb-4">Account</h2>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue