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.
 
 
 
 

149 lines
5.4 KiB

{% extends "portal_base.html" %}
{% block title %}Client Dashboard - OutsideTheBox{% endblock %}
{% block portal_content %}
<form id="portal-invoice-download-form" method="post" action="/portal/invoices/download-all">
<div class="portal-page-header">
<div>
<h1 class="portal-page-title">Client Dashboard</h1>
<p class="portal-client-name">{{ client.company_name or client.contact_name or client.email }}</p>
<p class="portal-page-subtitle">Invoices, balances, and account activity in one place.</p>
</div>
<div class="portal-toolbar" style="display:flex;flex-direction:column;align-items:flex-end;gap:10px;">
<div style="display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap;">
<a class="portal-btn primary" href="/portal/services">Services Here</a>
<button type="submit" class="portal-btn primary" style="border:0;cursor:pointer;">Download All Invoices</button>
<a class="portal-btn" href="mailto:support@outsidethebox.top?subject=Customer%20Support">Customer Support</a>
<a class="portal-btn" href="/portal/logout">Logout</a>
</div>
<div style="text-align:right;font-size:14px;opacity:0.95;">
<div>Logged in as: <strong>{{ client.contact_name or client.company_name or client.email }}</strong></div>
{% if client_credit_balance and client_credit_balance != "0.00" %}
<div style="margin-top:6px;">
<span style="display:inline-block;padding:4px 10px;border-radius:999px;background:#0f2f21;color:#86efac;font-weight:700;">
🏦 Credit: ${{ client_credit_balance }}
</span>
</div>
{% endif %}
</div>
</div>
</div>
<div class="summary-grid">
<div class="summary-card">
<h3>Total Invoices</h3>
<div class="summary-value">{{ invoice_count }}</div>
<div class="summary-sub">Invoices currently visible in your portal</div>
</div>
<div class="summary-card">
<h3>Total Outstanding</h3>
<div class="summary-value">{{ total_outstanding }}</div>
<div class="summary-sub">Current unpaid balance</div>
</div>
<div class="summary-card">
<h3>Total Paid</h3>
<div class="summary-value">{{ total_paid }}</div>
<div class="summary-sub">Payments already applied</div>
</div>
</div>
<h2 class="section-title">Invoices</h2>
<div class="table-card">
<table class="portal-table">
<thead>
<tr>
<th style="width:44px;"><input type="checkbox" id="select-all-invoices" aria-label="Select all invoices"></th>
<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>
<input type="checkbox" class="invoice-select" name="invoice_ids" value="{{ row.id }}" aria-label="Select invoice {{ row.invoice_number or row.id }}">
</td>
<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>
{% if row.payment_method_label %}
<div class="payment-method{% if row.payment_method_label == "Square" %} payment-square{% elif row.payment_method_label == "e-Transfer" %} payment-etransfer{% elif row.payment_method_label == "ETHO" %} payment-etho{% elif row.payment_method_label == "ETI" or row.payment_method_label == "EGAZ" %} payment-etica{% elif row.payment_method_label == "ALT" %} payment-alt{% elif row.payment_method_label == "CAD" %} payment-cad{% endif %}">
{{ row.payment_method_label }}
</div>
{% endif %}
{% 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="7">No invoices available.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</form>
{% endblock %}
{% block scripts %}
<script>
(function() {
const selectAll = document.getElementById("select-all-invoices");
const invoiceChecks = Array.from(document.querySelectorAll(".invoice-select"));
function anyChecked() {
return invoiceChecks.some(function(cb) { return cb.checked; });
}
if (selectAll) {
selectAll.addEventListener("change", function() {
invoiceChecks.forEach(function(cb) {
cb.checked = selectAll.checked;
});
});
}
invoiceChecks.forEach(function(cb) {
cb.addEventListener("change", function() {
if (!selectAll) {
return;
}
const checkedCount = invoiceChecks.filter(function(x) { return x.checked; }).length;
selectAll.checked = checkedCount === invoiceChecks.length && invoiceChecks.length > 0;
selectAll.indeterminate = checkedCount > 0 && checkedCount < invoiceChecks.length;
});
});
setTimeout(function() {
if (!anyChecked()) {
window.location.reload();
}
}, 20000);
})();
</script>
{% endblock %}