218 lines
7.4 KiB
HTML
218 lines
7.4 KiB
HTML
{% extends "layout.html" %}
|
|
{% block content %}
|
|
<div class="bundle">
|
|
{% for b in bundles %}
|
|
<section class="timesheet">
|
|
<div class="header">
|
|
<div class="brand">Timesheet</div>
|
|
<div class="row">
|
|
<div class="emp"><strong>Employee:</strong> {{ b.employee.name }}</div>
|
|
<div class="period"><strong>Period:</strong> {{ period_name }}</div>
|
|
</div>
|
|
<!-- Totals at the top (kept) -->
|
|
<div class="totals-top">
|
|
<span><strong>Regular:</strong> {{ b.grouped.totals.regular|fmt2 }}</span>
|
|
<span><strong>Overtime:</strong> {{ b.grouped.totals.overtime|fmt2 }}</span>
|
|
<span><strong>PTO:</strong> {{ b.grouped.totals.pto|fmt2 }}</span>
|
|
<span><strong>Holiday:</strong> {{ b.grouped.totals.holiday|fmt2 }}</span>
|
|
<span><strong>Other:</strong> {{ b.grouped.totals.bereavement|fmt2 }}</span>
|
|
<span><strong>Paid Total:</strong> {{ b.grouped.totals.paid_total|fmt2 }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="grid">
|
|
<thead>
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Clock In</th>
|
|
<th>Clock Out</th>
|
|
<th class="num">Break</th>
|
|
<th class="num">Total</th>
|
|
<!-- PTO before PTO Type (kept) -->
|
|
<th class="num">PTO</th>
|
|
<th>PTO Type</th>
|
|
<th class="num">Holiday</th>
|
|
<th class="num">Other</th>
|
|
<th class="num">Paid Total</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for r in b.grouped.rows %}
|
|
<tr>
|
|
<td class="mono">{{ r.work_date }}</td>
|
|
<!-- Holiday takes precedence; then PTO type; else times -->
|
|
<td class="mono">
|
|
{% if r.holiday_hours and r.holiday_hours > 0 %}
|
|
Holiday
|
|
{% elif r.pto_type %}
|
|
{{ r.pto_type }}
|
|
{% else %}
|
|
{{ r.clock_in|fmt_excel_dt }}
|
|
{% endif %}
|
|
</td>
|
|
<td class="mono">
|
|
{% if r.holiday_hours and r.holiday_hours > 0 %}
|
|
Holiday
|
|
{% elif r.pto_type %}
|
|
{{ r.pto_type }}
|
|
{% else %}
|
|
{{ r.clock_out|fmt_excel_dt }}
|
|
{% endif %}
|
|
</td>
|
|
<td class="num mono">{{ r.break_hours|fmt2 }}</td>
|
|
<td class="num mono">{{ r.total_hours|fmt2 }}</td>
|
|
<td class="num mono">{{ r.pto_hours|fmt2 }}</td>
|
|
<td>{{ r.pto_type or "" }}</td>
|
|
<td class="num mono">{{ r.holiday_hours|fmt2 }}</td>
|
|
<td class="num mono">{{ r.bereavement_hours|fmt2 }}</td>
|
|
<td class="num mono">{{ r.hours_paid|fmt2 }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- Footer underneath, per timesheet -->
|
|
<div class="footer-block">
|
|
<div class="comments">
|
|
<div class="comments-title">Comments</div>
|
|
<div class="comments-box">
|
|
{% if b.grouped.comments %}{{ b.grouped.comments }}{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="signatures">
|
|
<div class="sig-block">
|
|
<div class="signature-line"></div>
|
|
<div class="sig-label">Employee Signature</div>
|
|
</div>
|
|
<div class="sig-block">
|
|
<div class="signature-line"></div>
|
|
<div class="sig-label">Reviewer Signature</div>
|
|
</div>
|
|
<div class="sig-block">
|
|
<div class="signature-line"></div>
|
|
<div class="sig-label">Date</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<style>
|
|
header, nav, .navbar, .topbar, .site-header, .app-nav, .tk-header, .tk-nav, .tk-brand { 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; --margin: 0.30in; }
|
|
@page { size: Letter landscape; margin: var(--margin); }
|
|
|
|
.bundle {
|
|
width: calc(11in - (2 * var(--margin)));
|
|
margin: 0;
|
|
color: var(--ink);
|
|
font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
|
|
}
|
|
|
|
.timesheet { break-after: auto; page-break-after: auto; margin-bottom: 0.16in; }
|
|
.timesheet + .timesheet { break-before: page; page-break-before: always; }
|
|
.timesheet:last-child { break-after: avoid; page-break-after: avoid; }
|
|
|
|
.header { margin-bottom: 0.06in; }
|
|
.brand { font-size: 19.5pt; font-weight: 800; line-height: 1.1; margin-bottom: 0.03in; }
|
|
.row { display: grid; grid-template-columns: 1fr auto; align-items: end; column-gap: 0.16in; }
|
|
.emp, .period { font-size: 10.4pt; }
|
|
|
|
.totals-top {
|
|
display:flex; flex-wrap:wrap; gap:0.12in;
|
|
font-size: 11pt; margin-top: 0.04in; margin-bottom: 0.08in;
|
|
border: none; padding: 0;
|
|
}
|
|
|
|
/* Smaller body */
|
|
table.grid { width:100%; border-collapse: separate; border-spacing: 0; }
|
|
thead { display: table-header-group; }
|
|
table.grid th, table.grid td {
|
|
border: 1pt solid var(--grid);
|
|
padding: 0.042in 0.058in;
|
|
font-size: 9.8pt;
|
|
background: #fff;
|
|
}
|
|
table.grid thead th {
|
|
background: var(--head);
|
|
text-transform: uppercase;
|
|
font-size: 9.2pt;
|
|
letter-spacing: .02em;
|
|
color: #3a4856;
|
|
}
|
|
.num { text-align: right; }
|
|
.mono { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; }
|
|
table.grid tr { page-break-inside: avoid; }
|
|
.grid { margin-bottom: 0; padding-bottom: 0; }
|
|
|
|
|
|
/* Footer underneath each section */
|
|
.footer-block { margin-top: 0.14in; page-break-inside: avoid; }
|
|
.comments-title { font-size: 10.2pt; font-weight: 600; margin-bottom: 0.06in; }
|
|
.comments-box {
|
|
border: none;
|
|
min-height: 0.60in;
|
|
padding: 0.06in;
|
|
font-size: 9.8pt;
|
|
background: transparent;
|
|
}
|
|
.signatures {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr 0.8fr;
|
|
column-gap: 0.16in;
|
|
align-items: end;
|
|
margin-top: 0.12in;
|
|
}
|
|
.signature-line { border-bottom: 1.3pt solid #333; height: 0.20in; }
|
|
.sig-label { font-size: 9.4pt; color:#555; text-align: center; margin-top: 0.04in; }
|
|
|
|
/* Tighten if needed */
|
|
.compact table.grid th, .compact table.grid td { padding: 0.038in 0.054in; font-size: 9.5pt; }
|
|
.compact .comments-box { min-height: 0.50in; }
|
|
.micro table.grid th, .micro table.grid td { padding: 0.034in 0.050in; font-size: 9.2pt; }
|
|
.micro .comments-box { min-height: 0.45in; }
|
|
</style>
|
|
|
|
<script>
|
|
(function(){
|
|
// Fit each section to a page by shrinking body/footers if necessary
|
|
const DPI = 96;
|
|
const pageHeightInches = 8.5;
|
|
const marginInches = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--margin')) || 0.30;
|
|
const availablePx = (pageHeightInches - (2 * marginInches)) * DPI;
|
|
|
|
function fitSection(sec) {
|
|
sec.classList.remove('compact','micro');
|
|
let h = sec.scrollHeight;
|
|
if (h > availablePx) {
|
|
sec.classList.add('compact');
|
|
h = sec.scrollHeight;
|
|
}
|
|
if (h > availablePx) {
|
|
sec.classList.add('micro');
|
|
}
|
|
}
|
|
|
|
function fitAll() {
|
|
document.querySelectorAll('.timesheet').forEach(fitSection);
|
|
}
|
|
|
|
window.addEventListener('load', function(){
|
|
fitAll();
|
|
setTimeout(function(){ window.focus(); window.print(); }, 60);
|
|
});
|
|
if (typeof window.onbeforeprint !== 'undefined') window.addEventListener('beforeprint', fitAll);
|
|
if (window.matchMedia) {
|
|
const mq = window.matchMedia('print');
|
|
if (mq && mq.addEventListener) mq.addEventListener('change', e => { if (e.matches) fitAll(); });
|
|
}
|
|
})();
|
|
</script>
|
|
{% endblock %} |