160 lines
7.6 KiB
HTML
160 lines
7.6 KiB
HTML
{% extends 'base.html' %}
|
|
{% load i18n %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid py-4">
|
|
<div class="d-sm-flex align-items-center justify-content-between mb-4">
|
|
<div>
|
|
<h1 class="h3 mb-1 text-gray-800">{% trans "Attendance Records" %}</h1>
|
|
<p class="text-muted small mb-0">{% trans "Track and manage employee daily check-ins." %}</p>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<a href="{% url 'hr:sync_all_devices' %}" class="btn btn-outline-info shadow-sm rounded-3">
|
|
<i class="bi bi-arrow-repeat me-1"></i> {% trans "Sync Biometric Data" %}
|
|
</a>
|
|
<a href="{% url 'hr:attendance_add' %}" class="btn btn-primary shadow-sm rounded-3">
|
|
<i class="bi bi-plus-lg me-1"></i> {% trans "Manual Entry" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats Row -->
|
|
<div class="row mb-4">
|
|
<div class="col-xl-3 col-md-6 mb-4">
|
|
<div class="card border-0 shadow-sm rounded-4 h-100 py-2">
|
|
<div class="card-body">
|
|
<div class="row no-gutters align-items-center">
|
|
<div class="col mr-2">
|
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
|
|
{% trans "Today's Present" %}</div>
|
|
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ today_count|default:"0" }}</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<i class="bi bi-people-fill fa-2x text-gray-300"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card border-0 shadow-sm rounded-4 overflow-hidden">
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0" width="100%" cellspacing="0">
|
|
<thead class="bg-light text-secondary small text-uppercase">
|
|
<tr>
|
|
<th class="ps-4">{% trans "Date" %}</th>
|
|
<th>{% trans "Employee" %}</th>
|
|
<th>{% trans "Check In" %}</th>
|
|
<th>{% trans "Check Out" %}</th>
|
|
<th>{% trans "Device" %}</th>
|
|
<th class="text-end pe-4">{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for att in attendances %}
|
|
<tr>
|
|
<td class="ps-4">
|
|
<span class="fw-medium text-dark">{{ att.date|date:"d M Y" }}</span>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar-sm bg-light rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 32px; height: 32px;">
|
|
<i class="bi bi-person text-secondary"></i>
|
|
</div>
|
|
<span class="fw-semibold">{{ att.employee }}</span>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
{% if att.check_in %}
|
|
<span class="badge bg-success-soft text-success">
|
|
<i class="bi bi-clock me-1"></i> {{ att.check_in|time:"H:i" }}
|
|
</span>
|
|
{% else %}
|
|
<span class="text-muted small">--</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if att.check_out %}
|
|
<span class="badge bg-danger-soft text-danger">
|
|
<i class="bi bi-clock me-1"></i> {{ att.check_out|time:"H:i" }}
|
|
</span>
|
|
{% else %}
|
|
<span class="text-muted small">--</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<span class="text-muted small">
|
|
{% if att.device %}
|
|
<i class="bi bi-cpu me-1"></i> {{ att.device.name }}
|
|
{% else %}
|
|
<i class="bi bi-pencil-square me-1"></i> {% trans "Manual" %}
|
|
{% endif %}
|
|
</span>
|
|
</td>
|
|
<td class="text-end pe-4">
|
|
<a href="{% url 'hr:attendance_edit' att.pk %}" class="btn btn-sm btn-light rounded-circle shadow-none border" title="{% trans "Edit" %}">
|
|
<i class="bi bi-pencil text-primary"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="6" class="text-center py-5">
|
|
<div class="py-4">
|
|
<i class="bi bi-calendar-x display-1 text-light mb-3"></i>
|
|
<p class="text-muted">{% trans "No attendance records found." %}</p>
|
|
<a href="{% url 'hr:attendance_add' %}" class="btn btn-sm btn-primary mt-2">
|
|
{% trans "Add First Record" %}
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{% if is_paginated %}
|
|
<div class="px-4 py-3 border-top">
|
|
<nav aria-label="Page navigation">
|
|
<ul class="pagination pagination-sm justify-content-center mb-0">
|
|
{% if page_obj.has_previous %}
|
|
<li class="page-item">
|
|
<a class="page-link rounded-start-3" href="?page={{ page_obj.previous_page_number }}">
|
|
<i class="bi bi-chevron-left"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
|
|
<li class="page-item disabled">
|
|
<span class="page-link text-dark fw-medium">
|
|
{% trans "Page" %} {{ page_obj.number }} {% trans "of" %} {{ page_obj.paginator.num_pages }}
|
|
</span>
|
|
</li>
|
|
|
|
{% if page_obj.has_next %}
|
|
<li class="page-item">
|
|
<a class="page-link rounded-end-3" href="?page={{ page_obj.next_page_number }}">
|
|
<i class="bi bi-chevron-right"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.bg-success-soft { background-color: rgba(25, 135, 84, 0.1); }
|
|
.bg-danger-soft { background-color: rgba(220, 53, 69, 0.1); }
|
|
.avatar-sm { font-size: 0.8rem; }
|
|
.table thead th { border-top: 0; }
|
|
.card { border: none; }
|
|
</style>
|
|
{% endblock %}
|