timekeeper/app/templates/print_overview.html
2026-01-15 15:46:35 -05:00

169 lines
5.5 KiB
HTML

{% extends "layout.html" %}
{% block content %}
<div class="sheet">
<div class="header">
<div class="brand">Time Period Overview</div>
<div class="row">
<div class="period"><strong>Period:</strong> {{ period_name }}</div>
</div>
</div>
<table class="grid">
<thead>
<tr>
<th>Employee</th>
<th>Regular</th>
<th>Overtime</th>
<th>PTO</th>
<th>Holiday</th>
<th>Other</th>
<th>Paid Total</th>
</tr>
</thead>
<tbody>
{% for b in bundles %}
<tr>
<td>{{ b.employee.name }}</td>
<td class="num mono">{{ b.grouped.totals.regular|fmt2 }}</td>
<td class="num mono">{{ b.grouped.totals.overtime|fmt2 }}</td>
<td class="num mono">{{ b.grouped.totals.pto|fmt2 }}</td>
<td class="num mono">{{ b.grouped.totals.holiday|fmt2 }}</td>
<td class="num mono">{{ b.grouped.totals.bereavement|fmt2 }}</td>
<td class="num mono">{{ b.grouped.totals.paid_total|fmt2 }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="7" class="no-pad">
<div class="totals-footer">
<span><strong>Regular:</strong> {{ totals.regular|fmt2 }}</span>
<span><strong>Overtime:</strong> {{ totals.overtime|fmt2 }}</span>
<span><strong>PTO:</strong> {{ totals.pto|fmt2 }}</span>
<span><strong>Holiday:</strong> {{ totals.holiday|fmt2 }}</span>
<span><strong>Other:</strong> {{ totals.bereavement|fmt2 }}</span>
<span><strong>Paid Total:</strong> {{ totals.paid_total|fmt2 }}</span>
</div>
</td>
</tr>
</tfoot>
</table>
</div>
<style>
header, nav, .navbar, .topbar, .site-header, .app-nav { display:none !important; }
@media print {
body { margin: 0 !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
.tk-container { display:block !important; margin:0 !important; padding:0 !important; }
}
:root { --ink:#222; --grid:#cdd3da; --head:#f4f6f9; }
@page { size: Letter landscape; margin: 0.30in; }
.sheet {
width: 10.4in;
margin: 0;
color: var(--ink);
font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
}
.header { margin-bottom: 0.08in; }
.brand { font-size: 20pt; font-weight: 800; line-height: 1.1; margin-bottom: 0.03in; }
.row { display: grid; grid-template-columns: auto 1fr; align-items: end; column-gap: 0.16in; }
.period { font-size: 10.6pt; }
table.grid { width:100%; border-collapse:collapse; }
thead { display: table-header-group; }
tfoot { display: table-footer-group; }
.no-pad { padding: 0; }
/* Main body scaled down slightly to fit more employees */
table.grid th, table.grid td {
border: 1pt solid var(--grid);
padding: 0.052in 0.072in;
font-size: 10.4pt;
background: #fff;
}
table.grid thead th {
background: var(--head);
text-transform: uppercase;
font-size: 9.6pt;
letter-spacing: .02em;
color: #3a4856;
}
.num { text-align: right; }
.mono { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; }
/* Footer totals: slightly larger and bold, fixed to footer area on each page */
.totals-footer {
display: flex; flex-wrap: wrap; gap: 0.12in;
border-top: 1.5pt solid #333; margin-top: 0.06in; padding-top: 0.08in;
font-size: 11.6pt; /* a little bigger than body */
}
/* Keep rows intact; natural pagination */
table.grid tr { page-break-inside: avoid; }
/* Prevent extra whitespace creating another page */
.sheet, .grid { margin-bottom: 0; padding-bottom: 0; }
/* Nudge trims vertical spacing slightly when needed */
.nudge table.grid th, .nudge table.grid td { padding: 0.049in 0.069in; font-size: 10.2pt; }
.nudge .brand { font-size: 19pt; }
.nudge .totals-footer { gap: 0.11in; font-size: 11.2pt; padding-top: 0.07in; }
/* Clip: apply when remaining overflow is tiny (prevents page 2) */
.clip { overflow: hidden; max-block-size: calc(8.5in - 0.60in); }
</style>
<script>
(function(){
// Ensure it fits on one page by tightening spacing when needed.
const DPI = 96;
const pageHeightInches = 8.5;
const marginInches = 0.30; // sync with @page
const availablePx = (pageHeightInches - (marginInches * 2)) * DPI;
function fitToOnePage() {
const sheet = document.querySelector('.sheet');
if (!sheet) return;
sheet.classList.remove('nudge', 'clip');
const h = sheet.scrollHeight;
const nearThreshold = 28; // px
const tinyOverflow = 14; // px
if (h > availablePx) {
sheet.classList.add('nudge');
const h2 = sheet.scrollHeight;
if (h2 > availablePx && (h2 - availablePx) <= tinyOverflow) {
sheet.classList.add('clip');
}
}
}
// After print/cancel, return to Overview page (not Review)
const backUrl = "/overview?timesheet_id={{ timesheet_id }}";
function finish() {
if (window.parent && window.parent !== window) {
try { window.parent.postMessage({ type: 'close-print' }, '*'); } catch(e){}
} else {
window.location.href = backUrl;
}
}
window.addEventListener('load', function(){
fitToOnePage();
setTimeout(function(){ window.focus(); window.print(); }, 80);
});
if (typeof window.onbeforeprint !== 'undefined') {
window.addEventListener('beforeprint', fitToOnePage);
}
if (window.matchMedia) {
window.matchMedia('print').addEventListener('change', e => { if (e.matches) fitToOnePage(); });
}
window.addEventListener('afterprint', finish);
})();
</script>
{% endblock %}