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
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 %}
|
|
|