Pages Medium
Invoice Page
An invoice history page with list of invoices, status filters, detail modal, and downloadable invoice view. No external libraries.
Open in Lab
MCP
vanilla-js css
Targets: JS HTML
Code
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: #f3f4f6;
color: #1f2937;
line-height: 1.5;
min-height: 100vh;
}
.invoice-container {
max-width: 1060px;
margin: 0 auto;
padding: 32px 20px;
}
.page-title {
font-size: 28px;
font-weight: 700;
color: #111827;
margin-bottom: 24px;
}
/* โโ Summary Cards โโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.summary-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 28px;
}
.summary-card {
background: #fff;
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 6px;
border-left: 4px solid transparent;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
.summary-green {
border-left-color: #22c55e;
}
.summary-yellow {
border-left-color: #eab308;
}
.summary-red {
border-left-color: #ef4444;
}
.summary-blue {
border-left-color: #3b82f6;
}
.summary-label {
font-size: 13px;
color: #6b7280;
font-weight: 500;
}
.summary-value {
font-size: 22px;
font-weight: 700;
color: #111827;
}
/* โโ Toolbar โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.search-wrap {
position: relative;
}
.search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
}
.search-input {
padding: 8px 12px 8px 36px;
font-size: 13px;
border: 1px solid #d1d5db;
border-radius: 8px;
width: 240px;
background: #fff;
color: #1f2937;
}
.search-input:focus {
outline: none;
border-color: #6366f1;
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
}
.filter-tabs {
display: flex;
gap: 4px;
background: #e5e7eb;
padding: 3px;
border-radius: 10px;
}
.tab {
padding: 6px 14px;
font-size: 13px;
font-weight: 500;
border: none;
background: transparent;
color: #6b7280;
border-radius: 8px;
cursor: pointer;
transition: all 0.15s;
}
.tab:hover {
color: #374151;
}
.tab.active {
background: #fff;
color: #111827;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}
/* โโ Table โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.table-wrap {
background: #fff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
.invoice-table {
width: 100%;
border-collapse: collapse;
}
.invoice-table th {
text-align: left;
font-size: 12px;
font-weight: 600;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.4px;
padding: 14px 16px;
border-bottom: 1px solid #e5e7eb;
background: #f9fafb;
}
.invoice-table td {
padding: 14px 16px;
font-size: 14px;
border-bottom: 1px solid #f3f4f6;
}
.invoice-table tbody tr:hover {
background: #f9fafb;
}
.invoice-table tbody tr:nth-child(even) {
background: #fafbfc;
}
.invoice-table tbody tr:nth-child(even):hover {
background: #f3f4f6;
}
.invoice-table tbody tr.hidden {
display: none;
}
.cell-id {
font-weight: 600;
color: #6366f1;
}
.cell-amount {
font-weight: 600;
font-variant-numeric: tabular-nums;
}
.cell-actions {
display: flex;
gap: 6px;
}
/* โโ Status Badges โโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.status-badge {
display: inline-block;
font-size: 11px;
font-weight: 600;
padding: 3px 10px;
border-radius: 10px;
text-transform: uppercase;
letter-spacing: 0.3px;
}
.badge-paid {
background: #dcfce7;
color: #16a34a;
}
.badge-pending {
background: #fef3c7;
color: #d97706;
}
.badge-overdue {
background: #fee2e2;
color: #dc2626;
}
.badge-cancelled {
background: #f3f4f6;
color: #6b7280;
}
/* โโ Action Buttons โโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.btn-view {
padding: 5px 12px;
font-size: 12px;
font-weight: 500;
color: #6366f1;
background: #eef2ff;
border: none;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s;
}
.btn-view:hover {
background: #6366f1;
color: #fff;
}
.btn-download {
padding: 5px 8px;
border: none;
background: #f3f4f6;
color: #6b7280;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
}
.btn-download:hover {
background: #e5e7eb;
color: #374151;
}
/* โโ Pagination โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.pagination {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
padding: 0 4px;
}
.pagination-info {
font-size: 13px;
color: #6b7280;
}
.pagination-buttons {
display: flex;
gap: 4px;
}
.page-btn {
padding: 6px 12px;
font-size: 13px;
border: 1px solid #d1d5db;
background: #fff;
color: #374151;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s;
}
.page-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.page-btn.active {
background: #6366f1;
color: #fff;
border-color: #6366f1;
}
/* โโ Modal โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s, visibility 0.2s;
padding: 20px;
}
.modal-overlay.open {
opacity: 1;
visibility: visible;
}
.modal {
background: #fff;
border-radius: 16px;
max-width: 700px;
width: 100%;
max-height: 90vh;
overflow-y: auto;
position: relative;
transform: translateY(20px);
transition: transform 0.2s;
}
.modal-overlay.open .modal {
transform: translateY(0);
}
.modal-close {
position: absolute;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
border: none;
background: #f3f4f6;
color: #6b7280;
border-radius: 8px;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
transition: all 0.15s;
}
.modal-close:hover {
background: #e5e7eb;
color: #111827;
}
/* โโ Invoice Detail โโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.invoice-detail {
padding: 40px;
}
.inv-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 32px;
}
.inv-brand {
display: flex;
gap: 14px;
align-items: flex-start;
}
.inv-logo {
width: 48px;
height: 48px;
background: #6366f1;
color: #fff;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 16px;
flex-shrink: 0;
}
.inv-company {
font-size: 18px;
font-weight: 700;
color: #111827;
}
.inv-addr {
font-size: 13px;
color: #6b7280;
margin-top: 2px;
}
.inv-meta {
text-align: right;
}
.inv-label {
font-size: 22px;
font-weight: 700;
color: #6366f1;
letter-spacing: 1px;
}
.inv-number {
font-size: 14px;
font-weight: 600;
color: #374151;
margin-top: 4px;
}
.inv-dates {
font-size: 13px;
color: #6b7280;
margin-top: 4px;
}
.inv-parties {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
margin-bottom: 28px;
padding-bottom: 24px;
border-bottom: 1px solid #e5e7eb;
}
.inv-section-label {
font-size: 11px;
font-weight: 600;
color: #9ca3af;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 6px;
}
.inv-party-text {
font-size: 14px;
color: #374151;
}
/* โโ Invoice Items Table โโโโโโโโโโโโโโโโโโโโโโ */
.inv-items-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.inv-items-table th {
text-align: left;
font-size: 11px;
font-weight: 600;
color: #9ca3af;
text-transform: uppercase;
letter-spacing: 0.4px;
padding: 10px 0;
border-bottom: 1px solid #e5e7eb;
}
.inv-items-table th:last-child,
.inv-items-table td:last-child {
text-align: right;
}
.inv-items-table td {
padding: 12px 0;
font-size: 14px;
color: #374151;
border-bottom: 1px solid #f3f4f6;
}
/* โโ Totals โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.inv-totals {
margin-left: auto;
width: 240px;
margin-bottom: 24px;
}
.inv-totals-row {
display: flex;
justify-content: space-between;
padding: 6px 0;
font-size: 14px;
color: #6b7280;
}
.inv-total-final {
font-size: 16px;
font-weight: 700;
color: #111827;
border-top: 2px solid #111827;
padding-top: 10px;
margin-top: 4px;
}
/* โโ Payment โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.inv-payment {
display: flex;
align-items: center;
gap: 10px;
padding: 14px 16px;
background: #f0fdf4;
border-radius: 10px;
margin-bottom: 24px;
}
.inv-payment-status {
font-size: 12px;
font-weight: 600;
color: #16a34a;
background: #dcfce7;
padding: 3px 10px;
border-radius: 8px;
text-transform: uppercase;
}
.inv-payment-method {
font-size: 13px;
color: #6b7280;
}
/* โโ Invoice Actions โโโโโโโโโโโโโโโโโโโโโโโโโโ */
.inv-actions {
display: flex;
gap: 10px;
}
.btn-print {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 10px 20px;
font-size: 13px;
font-weight: 600;
color: #fff;
background: #3b82f6;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background 0.15s;
}
.btn-print:hover {
background: #2563eb;
}
.btn-download-inv {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 10px 20px;
font-size: 13px;
font-weight: 600;
color: #374151;
background: #fff;
border: 1px solid #d1d5db;
border-radius: 8px;
cursor: pointer;
transition: all 0.15s;
}
.btn-download-inv:hover {
background: #f9fafb;
border-color: #9ca3af;
}
/* โโ Toast โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.toast {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%) translateY(80px);
background: #1f2937;
color: #fff;
padding: 12px 24px;
border-radius: 10px;
font-size: 14px;
font-weight: 500;
opacity: 0;
transition: transform 0.3s, opacity 0.3s;
pointer-events: none;
z-index: 2000;
white-space: nowrap;
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* โโ Print โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
@media print {
body {
background: #fff;
}
.invoice-container,
.modal-overlay {
display: none !important;
}
.modal {
position: static;
max-height: none;
box-shadow: none;
border-radius: 0;
}
.modal-close,
.inv-actions {
display: none !important;
}
.invoice-detail {
padding: 0;
}
}
/* โโ Responsive โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
@media (max-width: 768px) {
.summary-cards {
grid-template-columns: repeat(2, 1fr);
}
.toolbar {
flex-direction: column;
align-items: flex-start;
}
.search-input {
width: 100%;
}
.filter-tabs {
width: 100%;
overflow-x: auto;
}
.invoice-table {
font-size: 13px;
}
.invoice-table th:nth-child(3),
.invoice-table td:nth-child(3),
.invoice-table th:nth-child(4),
.invoice-table td:nth-child(4) {
display: none;
}
.invoice-detail {
padding: 24px;
}
.inv-header {
flex-direction: column;
gap: 16px;
}
.inv-meta {
text-align: left;
}
.inv-parties {
grid-template-columns: 1fr;
}
.pagination {
flex-direction: column;
gap: 10px;
}
}
@media (max-width: 480px) {
.summary-cards {
grid-template-columns: 1fr;
}
}(function () {
var invoiceData = [
{
id: "INV-001",
client: "Acme Corp",
clientAddr: "Acme Corp\naccounts@acme.com\n456 Business Ave, NY 10001",
issued: "Mar 1, 2026",
due: "Mar 15, 2026",
status: "Paid",
paymentInfo: "via Bank Transfer on Mar 10, 2026",
items: [
{ desc: "Web Development", qty: 40, rate: 50 },
{ desc: "UI/UX Design", qty: 10, rate: 45 },
{ desc: "Server Setup", qty: 1, rate: 50 },
],
},
{
id: "INV-002",
client: "Globex Inc",
clientAddr: "Globex Inc\nbilling@globex.io\n789 Market St, SF 94103",
issued: "Mar 3, 2026",
due: "Mar 17, 2026",
status: "Paid",
paymentInfo: "via Credit Card on Mar 12, 2026",
items: [
{ desc: "API Integration", qty: 20, rate: 65 },
{ desc: "Testing & QA", qty: 15, rate: 35 },
{ desc: "Documentation", qty: 5, rate: 30 },
],
},
{
id: "INV-003",
client: "Wayne Enterprises",
clientAddr: "Wayne Enterprises\nfinance@wayne.com\n1 Gotham Plaza, Gotham 12345",
issued: "Mar 5, 2026",
due: "Mar 19, 2026",
status: "Pending",
paymentInfo: "Awaiting payment",
items: [
{ desc: "Consulting", qty: 8, rate: 100 },
{ desc: "Code Review", qty: 4, rate: 30 },
],
},
{
id: "INV-004",
client: "Stark Industries",
clientAddr: "Stark Industries\nap@stark.ind\n200 Park Avenue, NY 10166",
issued: "Feb 15, 2026",
due: "Mar 1, 2026",
status: "Overdue",
paymentInfo: "Payment overdue since Mar 1, 2026",
items: [
{ desc: "Mobile App Development", qty: 16, rate: 70 },
{ desc: "Backend API", qty: 4, rate: 30 },
],
},
{
id: "INV-005",
client: "Umbrella Corp",
clientAddr: "Umbrella Corp\npayables@umbrella.co\n10 Raccoon Drive, Midwest 55123",
issued: "Mar 8, 2026",
due: "Mar 22, 2026",
status: "Paid",
paymentInfo: "via PayPal on Mar 15, 2026",
items: [
{ desc: "Landing Page Design", qty: 1, rate: 350 },
{ desc: "SEO Optimization", qty: 5, rate: 25 },
],
},
{
id: "INV-006",
client: "Initech LLC",
clientAddr: "Initech LLC\nbilling@initech.com\n4120 Freidrich Lane, Austin TX 78744",
issued: "Mar 10, 2026",
due: "Mar 24, 2026",
status: "Pending",
paymentInfo: "Awaiting payment",
items: [
{ desc: "Full-Stack Development", qty: 32, rate: 65 },
{ desc: "Database Design", qty: 8, rate: 35 },
{ desc: "Deployment & DevOps", qty: 4, rate: 45 },
],
},
{
id: "INV-007",
client: "Soylent Corp",
clientAddr: "Soylent Corp\nfinance@soylent.co\n77 Green St, Portland OR 97201",
issued: "Mar 12, 2026",
due: "Mar 26, 2026",
status: "Cancelled",
paymentInfo: "Invoice cancelled on Mar 14, 2026",
items: [{ desc: "Logo Design", qty: 1, rate: 150 }],
},
{
id: "INV-008",
client: "Massive Dynamic",
clientAddr: "Massive Dynamic\nar@massivedynamic.com\n1 Science Way, Boston MA 02210",
issued: "Mar 15, 2026",
due: "Mar 29, 2026",
status: "Paid",
paymentInfo: "via Wire Transfer on Mar 18, 2026",
items: [
{ desc: "Enterprise Platform Build", qty: 120, rate: 95 },
{ desc: "Security Audit", qty: 20, rate: 85 },
{ desc: "Training Sessions", qty: 8, rate: 55.63 },
],
},
];
var overlay = document.getElementById("modalOverlay");
var closeBtn = document.getElementById("modalClose");
var filterTabs = document.getElementById("filterTabs");
var searchInput = document.getElementById("searchInput");
var invoiceBody = document.getElementById("invoiceBody");
var toast = document.getElementById("toast");
var printBtn = document.getElementById("printBtn");
var downloadBtn = document.getElementById("downloadBtn");
var toastTimer = null;
function showToast(msg) {
toast.textContent = msg;
toast.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toast.classList.remove("show");
}, 2500);
}
function formatCurrency(n) {
return "$" + n.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
// Filter by status
filterTabs.addEventListener("click", function (e) {
var btn = e.target.closest(".tab");
if (!btn) return;
filterTabs.querySelectorAll(".tab").forEach(function (t) {
t.classList.remove("active");
});
btn.classList.add("active");
var status = btn.dataset.status;
var rows = invoiceBody.querySelectorAll("tr");
rows.forEach(function (row) {
if (status === "all" || row.dataset.status === status) {
row.classList.remove("hidden");
} else {
row.classList.add("hidden");
}
});
applySearch();
});
// Search
function applySearch() {
var query = searchInput.value.trim().toLowerCase();
var rows = invoiceBody.querySelectorAll("tr:not(.hidden)");
rows.forEach(function (row) {
if (!query) {
row.style.display = "";
return;
}
var invoiceId = (row.dataset.invoice || "").toLowerCase();
var client = (row.dataset.client || "").toLowerCase();
if (invoiceId.indexOf(query) > -1 || client.indexOf(query) > -1) {
row.style.display = "";
} else {
row.style.display = "none";
}
});
}
searchInput.addEventListener("input", applySearch);
// Open modal
invoiceBody.addEventListener("click", function (e) {
var viewBtn = e.target.closest(".btn-view");
if (viewBtn) {
var idx = parseInt(viewBtn.dataset.invoice, 10);
openModal(idx);
return;
}
var dlBtn = e.target.closest(".btn-download");
if (dlBtn) {
showToast("Invoice downloaded");
return;
}
});
function openModal(idx) {
var inv = invoiceData[idx];
if (!inv) return;
document.getElementById("invNumber").textContent = inv.id;
document.getElementById("invDates").innerHTML =
"Issued: " + inv.issued + "<br/>Due: " + inv.due;
document.getElementById("invBillTo").innerHTML = inv.clientAddr.replace(/\n/g, "<br/>");
var tbody = document.getElementById("invItemsBody");
tbody.innerHTML = "";
var subtotal = 0;
inv.items.forEach(function (item) {
var amount = item.qty * item.rate;
subtotal += amount;
var tr = document.createElement("tr");
tr.innerHTML =
"<td>" +
item.desc +
"</td>" +
"<td>" +
item.qty +
"</td>" +
"<td>" +
formatCurrency(item.rate) +
"</td>" +
"<td>" +
formatCurrency(amount) +
"</td>";
tbody.appendChild(tr);
});
document.getElementById("invSubtotal").textContent = formatCurrency(subtotal);
document.getElementById("invTax").textContent = "$0.00";
document.getElementById("invTotal").textContent = formatCurrency(subtotal);
var paymentStatus = document.getElementById("invPaymentStatus");
var paymentMethod = document.getElementById("invPaymentMethod");
var paymentWrap = paymentStatus.parentElement;
paymentStatus.textContent = inv.status;
paymentMethod.textContent = inv.paymentInfo;
paymentStatus.className = "inv-payment-status";
paymentWrap.style.background = "";
if (inv.status === "Paid") {
paymentStatus.style.color = "#16a34a";
paymentStatus.style.background = "#dcfce7";
paymentWrap.style.background = "#f0fdf4";
} else if (inv.status === "Pending") {
paymentStatus.style.color = "#d97706";
paymentStatus.style.background = "#fef3c7";
paymentWrap.style.background = "#fffbeb";
} else if (inv.status === "Overdue") {
paymentStatus.style.color = "#dc2626";
paymentStatus.style.background = "#fee2e2";
paymentWrap.style.background = "#fef2f2";
} else {
paymentStatus.style.color = "#6b7280";
paymentStatus.style.background = "#f3f4f6";
paymentWrap.style.background = "#f9fafb";
}
overlay.classList.add("open");
document.body.style.overflow = "hidden";
}
function closeModal() {
overlay.classList.remove("open");
document.body.style.overflow = "";
}
closeBtn.addEventListener("click", closeModal);
overlay.addEventListener("click", function (e) {
if (e.target === overlay) closeModal();
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape") closeModal();
});
// Print
printBtn.addEventListener("click", function () {
window.print();
});
// Download
downloadBtn.addEventListener("click", function () {
showToast("Invoice downloaded");
});
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Invoices</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="invoice-container">
<h1 class="page-title">Invoices</h1>
<!-- Summary Cards -->
<div class="summary-cards">
<div class="summary-card summary-green">
<span class="summary-label">Total Revenue</span>
<span class="summary-value" id="summaryRevenue">$24,580.00</span>
</div>
<div class="summary-card summary-yellow">
<span class="summary-label">Pending</span>
<span class="summary-value" id="summaryPending">$3,420.00</span>
</div>
<div class="summary-card summary-red">
<span class="summary-label">Overdue</span>
<span class="summary-value" id="summaryOverdue">$1,240.00</span>
</div>
<div class="summary-card summary-blue">
<span class="summary-label">This Month</span>
<span class="summary-value" id="summaryMonth">$4,820.00</span>
</div>
</div>
<!-- Toolbar -->
<div class="toolbar">
<div class="search-wrap">
<svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
<input type="text" id="searchInput" class="search-input" placeholder="Search invoices..." />
</div>
<div class="filter-tabs" id="filterTabs">
<button class="tab active" data-status="all">All</button>
<button class="tab" data-status="paid">Paid</button>
<button class="tab" data-status="pending">Pending</button>
<button class="tab" data-status="overdue">Overdue</button>
<button class="tab" data-status="cancelled">Cancelled</button>
</div>
</div>
<!-- Invoice Table -->
<div class="table-wrap">
<table class="invoice-table">
<thead>
<tr>
<th>Invoice #</th>
<th>Client</th>
<th>Date</th>
<th>Due Date</th>
<th>Amount</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="invoiceBody">
<tr data-status="paid" data-invoice="INV-001" data-client="Acme Corp" data-amount="2500.00">
<td class="cell-id">INV-001</td>
<td>Acme Corp</td>
<td>Mar 1, 2026</td>
<td>Mar 15, 2026</td>
<td class="cell-amount">$2,500.00</td>
<td><span class="status-badge badge-paid">Paid</span></td>
<td class="cell-actions">
<button class="btn-view" data-invoice="0">View</button>
<button class="btn-download" title="Download">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
</td>
</tr>
<tr data-status="paid" data-invoice="INV-002" data-client="Globex Inc" data-amount="1850.00">
<td class="cell-id">INV-002</td>
<td>Globex Inc</td>
<td>Mar 3, 2026</td>
<td>Mar 17, 2026</td>
<td class="cell-amount">$1,850.00</td>
<td><span class="status-badge badge-paid">Paid</span></td>
<td class="cell-actions">
<button class="btn-view" data-invoice="1">View</button>
<button class="btn-download" title="Download">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
</td>
</tr>
<tr data-status="pending" data-invoice="INV-003" data-client="Wayne Enterprises" data-amount="920.00">
<td class="cell-id">INV-003</td>
<td>Wayne Enterprises</td>
<td>Mar 5, 2026</td>
<td>Mar 19, 2026</td>
<td class="cell-amount">$920.00</td>
<td><span class="status-badge badge-pending">Pending</span></td>
<td class="cell-actions">
<button class="btn-view" data-invoice="2">View</button>
<button class="btn-download" title="Download">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
</td>
</tr>
<tr data-status="overdue" data-invoice="INV-004" data-client="Stark Industries" data-amount="1240.00">
<td class="cell-id">INV-004</td>
<td>Stark Industries</td>
<td>Feb 15, 2026</td>
<td>Mar 1, 2026</td>
<td class="cell-amount">$1,240.00</td>
<td><span class="status-badge badge-overdue">Overdue</span></td>
<td class="cell-actions">
<button class="btn-view" data-invoice="3">View</button>
<button class="btn-download" title="Download">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
</td>
</tr>
<tr data-status="paid" data-invoice="INV-005" data-client="Umbrella Corp" data-amount="475.00">
<td class="cell-id">INV-005</td>
<td>Umbrella Corp</td>
<td>Mar 8, 2026</td>
<td>Mar 22, 2026</td>
<td class="cell-amount">$475.00</td>
<td><span class="status-badge badge-paid">Paid</span></td>
<td class="cell-actions">
<button class="btn-view" data-invoice="4">View</button>
<button class="btn-download" title="Download">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
</td>
</tr>
<tr data-status="pending" data-invoice="INV-006" data-client="Initech LLC" data-amount="2500.00">
<td class="cell-id">INV-006</td>
<td>Initech LLC</td>
<td>Mar 10, 2026</td>
<td>Mar 24, 2026</td>
<td class="cell-amount">$2,500.00</td>
<td><span class="status-badge badge-pending">Pending</span></td>
<td class="cell-actions">
<button class="btn-view" data-invoice="5">View</button>
<button class="btn-download" title="Download">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
</td>
</tr>
<tr data-status="cancelled" data-invoice="INV-007" data-client="Soylent Corp" data-amount="150.00">
<td class="cell-id">INV-007</td>
<td>Soylent Corp</td>
<td>Mar 12, 2026</td>
<td>Mar 26, 2026</td>
<td class="cell-amount">$150.00</td>
<td><span class="status-badge badge-cancelled">Cancelled</span></td>
<td class="cell-actions">
<button class="btn-view" data-invoice="6">View</button>
<button class="btn-download" title="Download">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
</td>
</tr>
<tr data-status="paid" data-invoice="INV-008" data-client="Massive Dynamic" data-amount="14945.00">
<td class="cell-id">INV-008</td>
<td>Massive Dynamic</td>
<td>Mar 15, 2026</td>
<td>Mar 29, 2026</td>
<td class="cell-amount">$14,945.00</td>
<td><span class="status-badge badge-paid">Paid</span></td>
<td class="cell-actions">
<button class="btn-view" data-invoice="7">View</button>
<button class="btn-download" title="Download">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Pagination -->
<div class="pagination">
<span class="pagination-info">Showing 1-8 of 8 invoices</span>
<div class="pagination-buttons">
<button class="page-btn" disabled>« Previous</button>
<button class="page-btn page-num active">1</button>
<button class="page-btn" disabled>Next »</button>
</div>
</div>
</div>
<!-- Invoice Detail Modal -->
<div class="modal-overlay" id="modalOverlay">
<div class="modal" id="invoiceModal">
<button class="modal-close" id="modalClose">×</button>
<div class="invoice-detail" id="invoiceDetail">
<div class="inv-header">
<div class="inv-brand">
<div class="inv-logo">ST</div>
<div>
<h2 class="inv-company">StealThis Inc.</h2>
<p class="inv-addr">123 Dev Street, Suite 404<br/>San Francisco, CA 94102</p>
</div>
</div>
<div class="inv-meta">
<h3 class="inv-label">INVOICE</h3>
<p class="inv-number" id="invNumber">INV-001</p>
<p class="inv-dates" id="invDates">Issued: Mar 1, 2026<br/>Due: Mar 15, 2026</p>
</div>
</div>
<div class="inv-parties">
<div>
<h4 class="inv-section-label">From</h4>
<p class="inv-party-text">StealThis Inc.<br/>billing@stealthis.dev<br/>+1 (555) 123-4567</p>
</div>
<div>
<h4 class="inv-section-label">Bill To</h4>
<p class="inv-party-text" id="invBillTo">Acme Corp<br/>accounts@acme.com<br/>456 Business Ave, NY 10001</p>
</div>
</div>
<table class="inv-items-table">
<thead>
<tr>
<th>Description</th>
<th>Qty</th>
<th>Rate</th>
<th>Amount</th>
</tr>
</thead>
<tbody id="invItemsBody">
<tr><td>Web Development</td><td>40</td><td>$50.00</td><td>$2,000.00</td></tr>
<tr><td>UI/UX Design</td><td>10</td><td>$45.00</td><td>$450.00</td></tr>
<tr><td>Server Setup</td><td>1</td><td>$50.00</td><td>$50.00</td></tr>
</tbody>
</table>
<div class="inv-totals">
<div class="inv-totals-row"><span>Subtotal</span><span id="invSubtotal">$2,500.00</span></div>
<div class="inv-totals-row"><span>Tax (0%)</span><span id="invTax">$0.00</span></div>
<div class="inv-totals-row inv-total-final"><span>Total</span><span id="invTotal">$2,500.00</span></div>
</div>
<div class="inv-payment">
<span class="inv-payment-status" id="invPaymentStatus">Paid</span>
<span class="inv-payment-method" id="invPaymentMethod">via Bank Transfer on Mar 10, 2026</span>
</div>
<div class="inv-actions">
<button class="btn-print" id="printBtn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/>
</svg>
Print
</button>
<button class="btn-download-inv" id="downloadBtn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
</svg>
Download
</button>
</div>
</div>
</div>
</div>
<div class="toast" id="toast"></div>
<script src="script.js"></script>
</body>
</html>Invoice Page
An invoice management page with a history table, status filtering, invoice detail view in a modal, and print/download functionality.
Features
- Invoice table โ list with invoice number, date, amount, status, actions
- Status filters โ All, Paid, Pending, Overdue, Cancelled tabs
- Invoice detail modal โ full invoice view with line items, totals, payment info
- Download/Print โ trigger browser print for invoice detail
- Summary cards โ total paid, pending, overdue amounts at top
- Search โ filter invoices by number or client name
When to use it
- SaaS billing history page
- Freelancer invoice management
- Accounting / finance dashboard