feat: invoice PDF redesign — professional table-based layout
- Header with red accent border, larger invoice number - Address blocks in connected table grid with equal heights - Customer and bank info highlighted with gray background - Bank info uses same row layout as dates (aligned labels/values) - Labels nowrap, values right-aligned - Item font size 8pt, table header border gray - Removed duplicate separator lines Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -515,76 +515,31 @@ export default async function invoicesPdfRoutes(
|
||||
|
||||
.accent { color: #de3a3a; }
|
||||
|
||||
/* Hlavicka */
|
||||
/* ── Hlavicka ── */
|
||||
.invoice-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 1mm;
|
||||
align-items: center;
|
||||
margin-bottom: 3mm;
|
||||
padding-bottom: 3mm;
|
||||
border-bottom: 2pt solid #de3a3a;
|
||||
}
|
||||
.invoice-header .left {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
gap: 3mm;
|
||||
}
|
||||
.logo-header {
|
||||
text-align: left;
|
||||
}
|
||||
.logo-header { text-align: left; }
|
||||
.company-title {
|
||||
font-size: 12pt;
|
||||
font-weight: 700;
|
||||
margin-top: 2mm;
|
||||
}
|
||||
.invoice-title {
|
||||
font-size: 10pt;
|
||||
font-size: 13pt;
|
||||
font-weight: 700;
|
||||
color: #de3a3a;
|
||||
text-align: right;
|
||||
margin-top: 2mm;
|
||||
}
|
||||
|
||||
/* Adresy - dva sloupce, stejna vyska */
|
||||
.addresses-row {
|
||||
display: flex;
|
||||
gap: 8mm;
|
||||
align-items: stretch;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.addresses-row .address-block {
|
||||
flex: 1;
|
||||
padding-bottom: 2mm;
|
||||
border-bottom: 0.5pt solid #e0e0e0;
|
||||
}
|
||||
|
||||
/* Detaily pod adresami */
|
||||
.details-row {
|
||||
display: flex;
|
||||
gap: 8mm;
|
||||
margin-bottom: 3mm;
|
||||
}
|
||||
.details-row .col { flex: 1; }
|
||||
|
||||
/* Adresy - styl z nabidek */
|
||||
.address-block {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.address-label {
|
||||
font-size: 8pt;
|
||||
font-weight: 600;
|
||||
color: #646464;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.address-name {
|
||||
font-size: 9pt;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.address-line {
|
||||
font-size: 8.5pt;
|
||||
color: #1a1a1a;
|
||||
line-height: 1.5;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.logo {
|
||||
@@ -593,46 +548,68 @@ export default async function invoicesPdfRoutes(
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* Separator */
|
||||
.header-separator {
|
||||
border: none;
|
||||
border-top: 0.5pt solid #e0e0e0;
|
||||
margin: 2mm 0 3mm 0;
|
||||
/* ── Adresy ── */
|
||||
.header-grid {
|
||||
border: 0.5pt solid #d0d0d0;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin-bottom: 3mm;
|
||||
}
|
||||
|
||||
/* Banka */
|
||||
.bank-box {
|
||||
font-size: 8pt;
|
||||
line-height: 1.4;
|
||||
padding-top: 2mm;
|
||||
.header-grid td {
|
||||
padding: 3mm 4mm;
|
||||
border: 0.5pt solid #d0d0d0;
|
||||
vertical-align: top;
|
||||
width: 50%;
|
||||
}
|
||||
.bank-box .lbl {
|
||||
font-weight: 600;
|
||||
.header-grid td.addr-customer {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.header-grid td.details-bank {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.address-label {
|
||||
font-size: 7pt;
|
||||
font-weight: 700;
|
||||
color: #de3a3a;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin-bottom: 1mm;
|
||||
}
|
||||
.address-name {
|
||||
font-size: 10pt;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
display: inline-block;
|
||||
min-width: 16mm;
|
||||
line-height: 1.3;
|
||||
margin-bottom: 1mm;
|
||||
}
|
||||
.address-line {
|
||||
font-size: 8pt;
|
||||
color: #444;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Datumy */
|
||||
.dates-box {
|
||||
font-size: 8pt;
|
||||
line-height: 1.4;
|
||||
padding-top: 2mm;
|
||||
}
|
||||
.dates-row {
|
||||
/* ── Detaily (banka + datumy) — inside header-grid ── */
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5mm;
|
||||
align-items: baseline;
|
||||
font-size: 8pt;
|
||||
padding: 1mm 0;
|
||||
border-bottom: 0.5pt solid #f0f0f0;
|
||||
}
|
||||
.dates-row .lbl {
|
||||
flex: 1;
|
||||
color: #1a1a1a;
|
||||
.info-row:last-child { border-bottom: none; }
|
||||
.info-row .lbl {
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
margin-right: 3mm;
|
||||
}
|
||||
.dates-row .val {
|
||||
.info-row .val {
|
||||
font-weight: 600;
|
||||
min-width: 22mm;
|
||||
text-align: center;
|
||||
padding: 0.5mm 2mm;
|
||||
color: #1a1a1a;
|
||||
text-align: right;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* VS/KS blok */
|
||||
@@ -642,25 +619,16 @@ export default async function invoicesPdfRoutes(
|
||||
padding-top: 2mm;
|
||||
}
|
||||
|
||||
/* Konecny prijemce */
|
||||
.recipient-box {
|
||||
font-size: 8pt;
|
||||
margin-top: 2mm;
|
||||
padding-top: 2mm;
|
||||
border-top: 0.5pt solid #e0e0e0;
|
||||
}
|
||||
.recipient-box .lbl {
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
color: #646464;
|
||||
}
|
||||
|
||||
/* Polozky tabulka - styl z nabidek */
|
||||
/* ── Polozky ── */
|
||||
.billing-label {
|
||||
font-weight: 600;
|
||||
color: #de3a3a;
|
||||
font-size: 8.5pt;
|
||||
padding: 3px 5px;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
font-size: 9pt;
|
||||
padding: 2mm 0 1mm 0;
|
||||
border-bottom: 1.5pt solid #de3a3a;
|
||||
margin-bottom: 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
table.items {
|
||||
@@ -678,7 +646,7 @@ export default async function invoicesPdfRoutes(
|
||||
text-align: left;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1pt solid #1a1a1a;
|
||||
border-bottom: 0.5pt solid #d0d0d0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.items thead th.center { text-align: center; }
|
||||
@@ -698,7 +666,7 @@ export default async function invoicesPdfRoutes(
|
||||
font-size: 8pt;
|
||||
}
|
||||
table.items tbody td.desc {
|
||||
font-size: 9.5pt;
|
||||
font-size: 8pt;
|
||||
font-weight: 600;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
@@ -898,46 +866,40 @@ ${indentCSS}
|
||||
<div class="invoice-title">${escapeHtml(t.heading)} ${invoiceNumber}</div>
|
||||
</div>
|
||||
|
||||
<hr class="header-separator" />
|
||||
|
||||
<!-- Dodavatel / Odberatel - stejna vyska -->
|
||||
<div class="addresses-row">
|
||||
<div class="address-block">
|
||||
<div class="address-label">${escapeHtml(t.supplier)}</div>
|
||||
<div class="address-name">${escapeHtml(supp.name)}</div>
|
||||
${suppLinesHtml}
|
||||
</div>
|
||||
<div class="address-block">
|
||||
<div class="address-label">${escapeHtml(t.customer)}</div>
|
||||
<div class="address-name">${escapeHtml(cust.name)}</div>
|
||||
${custLinesHtml}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Banka + VS / Datumy -->
|
||||
<div class="details-row">
|
||||
<div class="col">
|
||||
<div class="bank-box">
|
||||
<span class="lbl">${escapeHtml(t.bank)}</span> ${escapeHtml(invoice.bank_name)}<br>
|
||||
<span class="lbl">${escapeHtml(t.swift)}</span> ${escapeHtml(invoice.bank_swift)}<br>
|
||||
<span class="lbl">${escapeHtml(t.iban)}</span> ${escapeHtml(invoice.bank_iban)}<br>
|
||||
<span class="lbl">${escapeHtml(t.account_no)}</span> ${escapeHtml(invoice.bank_account)}
|
||||
</div>
|
||||
<div class="vs-block">
|
||||
${escapeHtml(t.var_symbol)} <strong>${invoiceNumber}</strong>
|
||||
${escapeHtml(t.const_symbol)} <strong>${escapeHtml(invoice.constant_symbol)}</strong><br>
|
||||
${orderNumber ? `${escapeHtml(t.order_no)} ${orderNumber}` : ""}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="dates-box">
|
||||
<div class="dates-row"><span class="lbl">${escapeHtml(t.issue_date)}</span> <span class="val">${escapeHtml(formatDate(invoice.issue_date))}</span></div>
|
||||
<div class="dates-row"><span class="lbl">${escapeHtml(t.due_date)}</span> <span class="val">${escapeHtml(formatDate(invoice.due_date))}</span></div>
|
||||
<div class="dates-row"><span class="lbl">${escapeHtml(t.tax_date)}</span> <span class="val">${escapeHtml(formatDate(invoice.tax_date))}</span></div>
|
||||
<div class="dates-row"><span class="lbl">${escapeHtml(t.payment_method)}</span> <span class="val">${escapeHtml(invoice.payment_method)}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Dodavatel / Odberatel + Banka / Datumy -->
|
||||
<table class="header-grid" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="address-label">${escapeHtml(t.supplier)}</div>
|
||||
<div class="address-name">${escapeHtml(supp.name)}</div>
|
||||
${suppLinesHtml}
|
||||
</td>
|
||||
<td class="addr-customer">
|
||||
<div class="address-label">${escapeHtml(t.customer)}</div>
|
||||
<div class="address-name">${escapeHtml(cust.name)}</div>
|
||||
${custLinesHtml}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="details-bank">
|
||||
<div class="info-row"><span class="lbl">${escapeHtml(t.bank)}</span> <span class="val">${escapeHtml(invoice.bank_name)}</span></div>
|
||||
<div class="info-row"><span class="lbl">${escapeHtml(t.swift)}</span> <span class="val">${escapeHtml(invoice.bank_swift)}</span></div>
|
||||
<div class="info-row"><span class="lbl">${escapeHtml(t.iban)}</span> <span class="val">${escapeHtml(invoice.bank_iban)}</span></div>
|
||||
<div class="info-row"><span class="lbl">${escapeHtml(t.account_no)}</span> <span class="val">${escapeHtml(invoice.bank_account)}</span></div>
|
||||
<div class="vs-block">
|
||||
${escapeHtml(t.var_symbol)} <strong>${invoiceNumber}</strong>
|
||||
${escapeHtml(t.const_symbol)} <strong>${escapeHtml(invoice.constant_symbol)}</strong><br>
|
||||
${orderNumber ? `${escapeHtml(t.order_no)} ${orderNumber}` : ""}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="info-row"><span class="lbl">${escapeHtml(t.issue_date)}</span> <span class="val">${escapeHtml(formatDate(invoice.issue_date))}</span></div>
|
||||
<div class="info-row"><span class="lbl">${escapeHtml(t.due_date)}</span> <span class="val">${escapeHtml(formatDate(invoice.due_date))}</span></div>
|
||||
<div class="info-row"><span class="lbl">${escapeHtml(t.tax_date)}</span> <span class="val">${escapeHtml(formatDate(invoice.tax_date))}</span></div>
|
||||
<div class="info-row"><span class="lbl">${escapeHtml(t.payment_method)}</span> <span class="val">${escapeHtml(invoice.payment_method)}</span></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Polozky -->
|
||||
<div class="billing-label">${escapeHtml(invoice.billing_text || t.billing)}</div>
|
||||
|
||||
Reference in New Issue
Block a user