billing frontend for mariadb. setup as otb_billing for outsidethebox.top accounting. also involved with outsidethedb
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

12 lines
3.6 KiB

<!DOCTYPE html>
<html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Client Dashboard - OutsideTheBox</title> <link rel="stylesheet" href="/static/css/style.css"> <style> .portal-wrap { max-width: 1100px; margin: 2rem auto; padding: 1.25rem; } .portal-top { display:flex; justify-content:space-between; align-items:center; gap:1rem; flex-wrap:wrap; margin-bottom: 1rem; } .portal-actions a { margin-left: 0.75rem; text-decoration: underline; } .summary-grid { display:grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap:1rem; margin: 1rem 0 1.25rem 0; } .summary-card { border: 1px solid rgba(255,255,255,0.16); border-radius: 14px; padding: 1rem; background: rgba(255,255,255,0.03); } .summary-card h3 { margin-top:0; margin-bottom:0.4rem; } table.portal-table { width: 100%; border-collapse: collapse; } table.portal-table th, table.portal-table td { padding: 0.8rem; border-bottom: 1px solid rgba(255,255,255,0.12); text-align: left; } table.portal-table th { background: #e9eef7; color: #10203f; } .invoice-link { color: inherit; text-decoration: underline; font-weight: 600; } .status-badge { display: inline-block; padding: 0.18rem 0.55rem; border-radius: 999px; font-size: 0.86rem; font-weight: 700; } .status-paid { background: rgba(34, 197, 94, 0.18); color: #4ade80; } .status-pending { background: rgba(245, 158, 11, 0.20); color: #fbbf24; } .status-overdue { background: rgba(239, 68, 68, 0.18); color: #f87171; } .status-other { background: rgba(148, 163, 184, 0.20); color: #cbd5e1; } </style> <link rel="icon" type="image/png" href="/static/favicon.png">
</head>
<body>
{% include "includes/site_nav.html" %} <div style="background:#111827;padding:10px 20px;"> </div> <div class="portal-wrap"> <div class="portal-top"> <div> <h1>Client Dashboard</h1> <p>{{ client.company_name or client.contact_name or client.email }}</p> </div> <div class="portal-actions"> <a href="/portal/invoices/download-all">Download All Invoices (ZIP)</a> <a href="mailto:support@outsidethebox.top?subject=Portal%20Support" href="mailto:support@outsidethebox.top">Customer Support</a> <a href="/portal/logout">Logout</a> </div> </div> <div class="summary-grid"> <div class="summary-card"> <h3>Total Invoices</h3> <div>{{ invoice_count }}</div> </div> <div class="summary-card"> <h3>Total Outstanding</h3> <div>{{ total_outstanding }}</div> </div> <div class="summary-card"> <h3>Total Paid</h3> <div>{{ total_paid }}</div> </div> </div> <h2>Invoices</h2> <table class="portal-table"> <thead> <tr> <th>Invoice</th> <th>Status</th> <th>Created</th> <th>Total</th> <th>Paid</th> <th>Outstanding</th> </tr> </thead> <tbody> {% for row in invoices %} <tr> <td> <a class="invoice-link" href="/portal/invoice/{{ row.id }}"> {{ row.invoice_number or ("INV-" ~ row.id) }} </a> </td> <td> {% set s = (row.status or "")|lower %} {% if s == "paid" %} <span class="status-badge status-paid">{{ row.status }}</span> {% elif s == "pending" %} <span class="status-badge status-pending">{{ row.status }}</span> {% elif s == "overdue" %} <span class="status-badge status-overdue">{{ row.status }}</span> {% else %} <span class="status-badge status-other">{{ row.status }}</span> {% endif %} </td> <td>{{ row.created_at }}</td> <td>{{ row.total_amount }}</td> <td>{{ row.amount_paid }}</td> <td>{{ row.outstanding }}</td> </tr> {% else %} <tr> <td colspan="6">No invoices available.</td> </tr> {% endfor %} </tbody> </table>
</div> <script>
(function() { setTimeout(function() { window.location.reload(); }, 20000);
})();
</script> {% include "footer.html" %}
</body>
</html>