106 lines
5.6 KiB
HTML
106 lines
5.6 KiB
HTML
{% extends "layout.html" %}
|
|
{% block content %}
|
|
|
|
<style>
|
|
.att-toolbar { display:flex; gap:12px; align-items:center; flex-wrap:wrap; }
|
|
.att-grid { display:flex; flex-direction:column; gap:14px; }
|
|
.att-row { display:grid; grid-template-columns: 260px 1fr; gap:12px; align-items:flex-start; }
|
|
.att-name { font-weight:600; }
|
|
/* Bigger cells for easier on-screen viewing */
|
|
.cells { display:grid; grid-auto-flow: column; grid-auto-columns: 24px; gap:4px; overflow-x:auto; padding-bottom:6px; }
|
|
.cell { width:24px; height:24px; border-radius:4px; border:1px solid #e5e7eb; }
|
|
.c-worked { background:#22c55e33; border-color:#16a34a66; }
|
|
.c-off { background:#06b6d433; border-color:#0ea5a566; } /* Off PTO and skipped workdays */
|
|
.c-sick { background:#f43f5e33; border-color:#e11d4866; } /* Sick PTO (distinct color) */
|
|
.c-pto { background:#3b82f633; border-color:#2563eb66; } /* PTO only when type is 'PTO' */
|
|
.c-holiday { background:#f59e0b33; border-color:#d9770666; }
|
|
.c-other { background:#a855f733; border-color:#7e22ce66; } /* Was bereavement */
|
|
.c-weekend { background:#e5e7eb; border-color:#d1d5db; }
|
|
.c-nodata { background:#fafafa; border-color:#eeeeee; } /* No submissions in range */
|
|
|
|
.legend { display:flex; gap:12px; flex-wrap:wrap; margin-top:6px; }
|
|
.legend .item { display:flex; gap:6px; align-items:center; }
|
|
.legend .swatch { width:16px; height:16px; border-radius:3px; border:1px solid #e5e7eb; }
|
|
</style>
|
|
|
|
<div class="page-wide">
|
|
<div class="panel">
|
|
<div class="panel-title">Attendance Tracker</div>
|
|
|
|
<form class="panel att-toolbar" method="get" action="/attendance">
|
|
<label class="label">Start</label>
|
|
<input class="input" type="date" name="start" value="{{ start.isoformat() }}">
|
|
<label class="label">End</label>
|
|
<input class="input" type="date" name="end" value="{{ end.isoformat() }}">
|
|
|
|
<label class="label">Employee</label>
|
|
<select class="select" name="employee_id" id="employee_id" style="min-width:220px;">
|
|
<option value="all" {% if not selected_employee_id %}selected{% endif %}>All employees</option>
|
|
{% for e in employees %}
|
|
<option value="{{ e.id }}" {% if selected_employee_id and e.id == selected_employee_id %}selected{% endif %}>{{ e.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<label class="checkbox" style="display:flex; gap:6px; align-items:center;">
|
|
<input type="checkbox" name="include_weekends" value="1" {% if include_weekends %}checked{% endif %}>
|
|
Include weekends
|
|
</label>
|
|
|
|
<button class="btn primary" type="submit">Run</button>
|
|
|
|
<a class="btn" target="_blank"
|
|
href="/attendance/export.csv?start={{ start.isoformat() }}&end={{ end.isoformat() }}&employee_id={{ selected_employee_id if selected_employee_id else 'all' }}&include_weekends={{ 1 if include_weekends else 0 }}">
|
|
Export CSV
|
|
</a>
|
|
</form>
|
|
|
|
<div class="legend">
|
|
<div class="item"><span class="swatch" style="background:#22c55e33;border-color:#16a34a66;"></span> Worked</div>
|
|
<div class="item"><span class="swatch" style="background:#06b6d433;border-color:#0ea5a566;"></span> Off</div>
|
|
<div class="item"><span class="swatch" style="background:#f43f5e33;border-color:#e11d4866;"></span> Sick</div>
|
|
<div class="item"><span class="swatch" style="background:#3b82f633;border-color:#2563eb66;"></span> PTO</div>
|
|
<div class="item"><span class="swatch" style="background:#f59e0b33;border-color:#d9770666;"></span> Holiday</div>
|
|
<div class="item"><span class="swatch" style="background:#a855f733;border-color:#7e22ce66;"></span> Other</div>
|
|
<div class="item"><span class="swatch" style="background:#e5e7eb;border-color:#d1d5db;"></span> Weekend</div>
|
|
<div class="item"><span class="swatch" style="background:#fafafa;border-color:#eeeeee;"></span> No data (no submissions in range)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<div class="panel-title">Period Overview</div>
|
|
<div class="att-grid">
|
|
{% for row in visual %}
|
|
<div class="att-row">
|
|
<div class="att-name">{{ row.employee.name }}</div>
|
|
<div class="cells" title="Scroll horizontally for full range">
|
|
{% for c in row.cells %}
|
|
<div class="cell c-{{ c.status }}" title="{{ c.date.isoformat() }} — {{ c.status }}"></div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<div class="att-row" style="grid-template-columns:260px 1fr;">
|
|
<div></div>
|
|
<div style="display:flex; gap:16px; flex-wrap:wrap; margin-bottom:10px;">
|
|
<div><strong>Worked days:</strong> {{ row.totals.worked_days }} ({{ row.hours.worked|round(2) }} hrs)</div>
|
|
<div><strong>Off days:</strong> {{ row.totals.off_days }} ({{ row.hours.off|round(2) }} hrs)</div>
|
|
<div><strong>Sick days:</strong> {{ row.totals.sick_days }} ({{ row.hours.sick|round(2) }} hrs)</div>
|
|
<div><strong>PTO days:</strong> {{ row.totals.pto_days }} ({{ row.hours.pto|round(2) }} hrs)</div>
|
|
<div><strong>Holiday days:</strong> {{ row.totals.holiday_days }} ({{ row.hours.holiday|round(2) }} hrs)</div>
|
|
<div><strong>Other days:</strong> {{ row.totals.other_days }} ({{ row.hours.other|round(2) }} hrs)</div>
|
|
</div>
|
|
</div>
|
|
<hr style="border:none; border-top:1px solid #eee; margin:10px 0;">
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Auto-submit when employee dropdown changes
|
|
(function () {
|
|
var sel = document.getElementById('employee_id');
|
|
if (!sel) return;
|
|
sel.addEventListener('change', function () { sel.form.submit(); });
|
|
})();
|
|
</script>
|
|
{% endblock %} |