[UPDT] BIOMETRIC: Updated employee list views for the biometric devices

This commit is contained in:
Horilla
2025-11-21 12:16:43 +05:30
parent fa9dc0a991
commit 5da14cc51b
10 changed files with 1170 additions and 6 deletions

View File

@@ -591,6 +591,7 @@ if apps.is_installed("attendance"):
DashboardaAttendanceOT,
DashboardAttendanceToValidate,
)
from biometric.cbv.biometric import BiometricCardView
_overtime_attendance_init_orig = DashboardaAttendanceOT.__init__
@@ -604,8 +605,15 @@ if apps.is_installed("attendance"):
_validate_attendance_init_orig(self, **kwargs)
self.header_attrs = {}
_biometric_device_card_init_orig = BiometricCardView.__init__
def _biometric_card_init(self, **kwargs):
_biometric_device_card_init_orig(self, **kwargs)
self.custom_body_template = "cbv/biometric_card_body.html"
DashboardaAttendanceOT.__init__ = _overtime_attendance_init
DashboardAttendanceToValidate.__init__ = _validate_attendance_init
BiometricCardView.__init__ = _biometric_card_init
if apps.is_installed("leave"):

View File

@@ -0,0 +1,328 @@
{% load i18n %}
<script>
function highlightFinger(fingers, handId) {
var hand = $("#" + handId);
var designFingers = hand.find("[data-finger]");
fingers.forEach((fingerId) => {
$.each(designFingers, function (indexInArray, finger) {
dataFingerId = $(finger).attr("data-finger");
var temp1 = toString(dataFingerId);
var temp2 = toString(fingerId);
if (temp2 === temp1) {
$("#" + handId)
.find(`[data-finger=${fingerId}]`)
.attr("fill", "green");
}
});
});
}
</script>
<div class="bg-white p-5 pe-2 pt-3 rounded-md shadow-card">
{% if employees %}
<table class="w-full border-separate">
<thead class="sticky top-0 bg-[white] z-[1]">
<tr class="border-b border-[#ececec]">
<th class="text-sm font-medium text-left p-3 w-[30px]">
<input
type="checkbox"
class="w-4 h-4 bg-[#E9EDF7] rounded-sm text-[#e54f38] all-bio-employee"
title="Select All"
data-device="{{device_id}}"
id="allBioEmployee"
/>
</th>
<th class="text-sm font-medium text-left min-w-[150px] pl-3">
{% trans "Employee" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "User ID" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Badge ID" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Fingerprint" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Work Email" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Job Position" %}
</th>
{% if perms.biometric.delete_biometricemployees %}
<th class="text-sm font-medium text-center min-w-[50px]">
{% trans "Actions" %}
</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for employee in employees %}
<tr class="border-b border-[#f3f3f3]">
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
<input
type="checkbox"
id="{{employee.user_id}}"
class="w-4 h-4 bg-[#E9EDF7] rounded-sm text-[#e54f38] cursor-pointer all-bio-employee-row"
/>
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.employee.get_full_name|title}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.user_id}} {{device_id.machine_ip}}
{{device_id.port}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.badge_id}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
<div id="hand{{employee.user_id}}">
<svg
width="52"
height="42"
viewBox="0 0 52 42"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style="width: 60px"
>
<path
d="M48.0984 41.1622H36.6278V32.6916L31.0249 27.1328V24.1769L35.0396 22.6989V16.8975H51.7161V29.3386L48.0984 32.6916V41.1622Z"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="44.3086"
data-hand-target="hand{{employee.badge_id}}"
data-finger="8"
y="2.5"
width="4.22059"
height="14.1471"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="35.0439"
data-hand-target="hand{{employee.badge_id}}"
data-finger="6"
y="3.86719"
width="4.88235"
height="12.7794"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="39.897"
data-hand-target="hand{{employee.badge_id}}"
data-finger="7"
y="0.25"
width="4.44118"
height="16.3971"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="48.4561"
data-hand-target="hand{{employee.badge_id}}"
data-finger="9"
y="7.35254"
width="3.29412"
height="9.29412"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="28.3124"
data-hand-target="hand{{employee.badge_id}}"
data-finger="5"
y="15.2505"
width="3.90269"
height="9.07403"
transform="rotate(-17.0853 28.3124 15.2505)"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="35.3237"
y="16.1914"
width="16.1029"
height="1.67647"
fill="#bdc3c7"
/>
<path
d="M31.8825 25.8092L31.1987 23.5372L31.0884 23.2283L34.1766 22.1475L34.5957 23.6475L34.9707 24.221L35.059 24.6622L31.8825 25.8092Z"
fill="#bdc3c7"
/>
<path
d="M31.0884 23.2275L31.309 24.3746L31.5075 24.4849L32.3678 24.9261L31.7281 23.2275H31.0884Z"
fill="#bdc3c7"
/>
<path
d="M3.90157 41.1622H15.3722V32.6916L20.9751 27.1328V24.1769L16.9604 22.6989V16.8975H0.28392V29.3386L3.90157 32.6916V41.1622Z"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="-0.25"
y="0.25"
data-hand-target="hand{{employee.badge_id}}"
data-finger="1"
width="4.22059"
height="14.1471"
transform="matrix(-1 0 0 1 7.44141 2.25)"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="-0.25"
y="0.25"
data-hand-target="hand{{employee.badge_id}}"
data-finger="3"
width="4.88235"
height="12.7794"
transform="matrix(-1 0 0 1 16.7061 3.61719)"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="-0.25"
y="0.25"
data-hand-target="hand{{employee.badge_id}}"
data-finger="2"
width="4.44118"
height="16.3971"
transform="matrix(-1 0 0 1 11.853 0)"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="-0.25"
y="0.25"
data-hand-target="hand{{employee.badge_id}}"
data-finger="0"
width="3.29412"
height="9.29412"
transform="matrix(-1 0 0 1 3.29395 7.10254)"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
x="-0.312416"
y="0.165518"
data-hand-target="hand{{employee.badge_id}}"
data-finger="4"
width="3.90269"
height="9.07403"
transform="matrix(-0.955868 -0.293795 -0.293795 0.955868 23.4376 15.0005)"
fill="#bdc3c7"
stroke="#7f8c8d"
stroke-width="0.5"
/>
<rect
width="16.1029"
height="1.67647"
transform="matrix(-1 0 0 1 16.6763 16.1914)"
fill="#bdc3c7"
/>
<path
d="M20.1175 25.8092L20.8013 23.5372L20.9116 23.2283L17.8234 22.1475L17.4043 23.6475L17.0293 24.221L16.941 24.6622L20.1175 25.8092Z"
fill="#bdc3c7"
/>
<path
d="M20.9116 23.2275L20.691 24.3746L20.4925 24.4849L19.6322 24.9261L20.2719 23.2275H20.9116Z"
fill="#bdc3c7"
/>
</svg>
</div>
<script>
highlightFinger({{employee.finger|safe}},"hand{{employee.user_id}}")
</script>
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px] truncate">
{{employee.work_email}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.job_position}}
</td>
{% if perms.biometric.delete_biometricemployees %}
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
href="{% url 'delete-biometric-user' uid=employee.uid device_id=device_id %}"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this user from the biometric device?' %}`)"
title="{% trans 'Delete' %}"
>
<ion-icon name="trash-outline"></ion-icon>
</a>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="oh-wrapper h-full" align="center" >
<div class="xl:col-span-4 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card h-[70%]">
<div class="flex flex-col items-center justify-center h-full">
<img src="/static/horilla_theme/assets/img/no-records.svg" alt="" width="300" class="mb-4">
<p class="text-[#666] mb-5">{% trans "No records available at the moment" %}</p>
</div>
</div>
</div>
{% endif %}
</div>
{% if employees.has_previous or employees.has_next %}
<div class="flex justify-between items-center mt-4 w-full inset-0">
<p class="text-xs text-[#666]">
{% trans "Page" %} {{ employees.number }} {% trans "of" %}
{{ employees.paginator.num_pages }}
</p>
<div class="flex gap-3 text-[#666] items-center">
{% if employees.has_previous %}
<button
class="text-xs hover:text-primary-600 transition duration-300"
hx-target="#section"
hx-get="{% url 'search-employee-in-device' %}?{{pd}}&view=list&page={{ employees.previous_page_number }}&device={{device_id}}"
>
<i class="fa-solid fa-arrow-left"></i>
</button>
{% endif %}
{{ employees.number }} / {{ employees.paginator.num_pages }}
{% if employees.has_next %}
<button
class="text-xs hover:text-primary-600 transition duration-300"
hx-target="#section"
hx-get="{% url 'search-employee-in-device' %}?{{pd}}&view=list&page={{ employees.next_page_number }}&device={{device_id}}"
>
<i class="fa-solid fa-arrow-right"></i>
</button>
{% endif %}
</div>
</div>
{% endif %}
<script>
$(document).ready(function(){
$("thead [type='checkbox']").on("change", function() {
let isChecked = $(this).is(':checked')
$("tbody [type='checkbox']").prop('checked', isChecked)
})
})
</script>

View File

@@ -0,0 +1,229 @@
{% load i18n %}
<div class="bg-white p-5 pe-2 pt-3 rounded-md shadow-card">
{% if employees.users %}
<table class="w-full border-separate">
<thead class="sticky top-0 bg-[white] z-[1]">
<tr class="border-b border-[#ececec]">
<th class="text-sm font-medium text-left p-3 w-[30px]">
<input
type="checkbox"
class="w-4 h-4 bg-[#E9EDF7] rounded-sm text-[#e54f38] all-bio-employee"
title="Select All"
data-device="{{device_id}}"
id="allBioEmployee"
/>
</th>
<th class="text-sm font-medium text-left min-w-[150px] pl-3">
{% trans "Employee" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "User ID" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Reference ID" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Active" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% if employees.users %}
{% if employees.users.0.finger_count %}
{% trans "Finger Count" %}
{% else %}
{% trans "Face Count" %}
{% endif %}
{% else %}
{% trans "Biometric Template" %}
{% endif %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Card Count" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Valid Active" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Valid End Date" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Work Email" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Job Position" %}
</th>
<th class="text-sm font-medium text-center min-w-[100px]">
{% trans "Actions" %}
</th>
</tr>
</thead>
<tbody>
{% for employee in employees.users %}
<tr class="border-b border-[#f3f3f3]">
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
<input
type="checkbox"
id="{{employee.user_id}}"
class="w-4 h-4 bg-[#E9EDF7] rounded-sm text-[#e54f38] cursor-pointer all-bio-employee-row"
/>
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.employee_id.get_full_name|title}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.user_id}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.ref_user_id}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{% if employee.user_active == "1" %}
{% trans "Yes" %}
{% else %}
{% trans "No" %}
{% endif %}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px] truncate">
{% if employee.finger_count or employee.face_count %}
{% if employee.finger_count %}
{{employee.finger_count}}
{% else %}
{{employee.face_count}}
{% endif %}
{% else %}
{% trans "Not Enrolled" %}
{% endif %}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px] truncate">
{{employee.card_count}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px] truncate">
{% if employee.validity_enable == "1" %}
{% trans "Yes" %}
{% else %}
{% trans "No" %}
{% endif %}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px] truncate">
{{employee.validity_date_dd}}/{{employee.validity_date_mm}}/{{employee.validity_date_yyyy}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px] truncate">
{{employee.employee_id.get_mail}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.employee_id.get_job_position}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
<div class="oh-btn-group">
{% if employee.face_count %}
<a
class="oh-btn oh-btn--light-bkg w-100"
href="{% url 'enable-cosec-face-recognition' user_id=employee.user_id device_id=device_id %}"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this user from the biometric device?' %}`)"
title="{% trans 'Enable Face' %}"
>
<ion-icon name="person-circle-outline"></ion-icon>
</a>
{% endif %}
{% if perms.biometric.change_biometricemployees %}
<a
onclick="event.stopPropagation();"
class="oh-btn oh-btn--light-bkg w-100"
data-toggle="oh-modal-toggle"
data-target="#objectUpdateModal"
hx-target="#objectUpdateModalTarget"
hx-get="{% url 'edit-cosec-user' user_id=employee.user_id device_id=device_id %}"
title="{% trans 'Edit' %}"
>
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
aria-label="create outline"
></ion-icon>
</a>
{% endif %}
{% if perms.biometric.delete_biometricemployees %}
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
href="{% url 'delete-cosec-user' user_id=employee.user_id device_id=device_id %}"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this user from the biometric device?' %}`)"
title="{% trans 'Delete' %}"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</a>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="oh-wrapper h-full" align="center">
<div
class="xl:col-span-4 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card h-[70%]"
>
<div class="flex flex-col items-center justify-center h-full">
<img
src="/static/horilla_theme/assets/img/no-records.svg"
alt=""
width="300"
class="mb-4"
/>
<p class="text-[#666] mb-5">
{% trans "No records available at the moment" %}
</p>
</div>
</div>
</div>
{% endif %}
</div>
{% if employees.paginator.has_previous or employees.paginator.has_next %}
<div class="flex justify-between items-center mt-4 w-full inset-0">
<p class="text-xs text-[#666]">
{% trans "Page" %} {{ employees.paginator.number }} {% trans "of" %}
{{ employees.paginator.num_pages }}
</p>
<div class="flex gap-3 text-[#666] items-center">
{% if employees.paginator.has_previous %}
<button
class="text-xs hover:text-primary-600 transition duration-300"
hx-target="#section"
hx-get="{% url 'search-employee-in-device' %}?{{pd}}&view=list&page={{ employees.paginator.previous_page_number }}&device={{device_id}}"
>
<i class="fa-solid fa-arrow-left"></i>
</button>
{% endif %}
{{ employees.paginator.number }} / {{ employees.paginator.num_pages }}
{% if employees.paginator.has_next %}
<button
class="text-xs hover:text-primary-600 transition duration-300"
hx-target="#section"
hx-get="{% url 'search-employee-in-device' %}?{{pd}}&view=list&page={{ employees.paginator.next_page_number }}&device={{device_id}}"
>
<i class="fa-solid fa-arrow-right"></i>
</button>
{% endif %}
</div>
</div>
{% endif %}
<script>
$(document).ready(function(){
$("thead [type='checkbox']").on("change", function() {
let isChecked = $(this).is(':checked')
$("tbody [type='checkbox']").prop('checked', isChecked)
})
})
</script>

View File

@@ -0,0 +1,76 @@
{% load static i18n %}
<div class="flex flex-wrap justify-between items-center mb-3">
<h3 class="text-lg font-semibold">{% trans "Employees" %}</h3>
<div class="flex flex-wrap gap-1">
<div class="relative">
<input
type="text"
class="text-color-600 ps-8 p-1.5 pb-2 placeholder:text-xs w-full border border-dark-50 rounded-md focus-visible:outline-0 placeholder:text-dark-100 text-sm [transition:.3s] focus:border-primary-600"
name="search"
aria-label="Search Input"
placeholder="Search"
hx-get="{% url 'search-employee-in-device' %}?device={{device_id}}"
name="search"
hx-trigger="keyup changed delay:.2s"
hx-target="#section"
hx-vals='{"view":"{{request.GET.view}}"}'
autocomplete="false"
autofocus="true"
/>
<i
class="fas fa-search absolute left-3 top-[1px] bottom-0 m-auto flex items-center opacity-50 text-xs"
></i>
</div>
<div class="relative dropdown-wrapper">
<button
class="px-5 py-2 h-full bg-[white] rounded-md text-xs flex items-center gap-2 border border-primary-500 hover:border-primary-600 transition duration-300"
>
<img
src="{% static 'horilla_theme/assets/img/icons/setting-line.svg' %}"
alt="{% trans 'Actions' %}"
width="15"
class="mt-[-1px]"
/>
{% trans "Actions" %}
</button>
<div
class="dropdown-content absolute z-10 bg-white rounded-lg [box-shadow:0px_0px_20px_0px_rgb(0_0_0_/_8%)] min-w-[150px] py-2 right-0"
>
<ul class="text-sm">
{% if perms.biometric.delete_biometricemployees %}
<li>
<a
href="#"
class="px-4 py-1.5 hover:text-primary-600 transition duration-300 text-xs gap-4 items-center flex w-full"
id="deleteBioUsers"
data-action="delete"
>
{% trans "Delete" %}
</a>
</li>
{% endif %}
</ul>
</div>
</div>
{% if perms.biometric.add_biometricemployees %}
<a
class="px-4 py-2 bg-primary-600 text-white rounded-md text-xs flex items-center gap-2 hover:bg-primary-800 transition duration-300 cursor-pointer"
data-toggle="oh-modal-toggle"
data-target="#objectCreateModal"
hx-target="#objectCreateModalTarget"
hx-get="{% url 'add-biometric-user' device_id %}"
>
<img
src="{% static 'horilla_theme/assets/img/icons/more.svg' %}"
alt="{% trans 'Create' %}"
width="13"
class="filter brightness-0 invert"
/>
{% trans "Add" %}
</a>
{% endif %}
</div>
</div>

View File

@@ -0,0 +1,76 @@
{% load static i18n %}
<div class="flex flex-wrap justify-between items-center mb-3">
<h3 class="text-lg font-semibold">{% trans "Employees" %}</h3>
<div class="flex flex-wrap gap-1">
<div class="relative">
<input
type="text"
class="text-color-600 ps-8 p-1.5 pb-2 placeholder:text-xs w-full border border-dark-50 rounded-md focus-visible:outline-0 placeholder:text-dark-100 text-sm [transition:.3s] focus:border-primary-600"
name="search"
aria-label="Search Input"
placeholder="Search"
hx-get="{% url 'search-employee-in-device' %}?device={{device_id}}"
name="search"
hx-trigger="keyup changed delay:.2s"
hx-target="#section"
hx-vals='{"view":"{{request.GET.view}}"}'
autocomplete="false"
autofocus="true"
/>
<i
class="fas fa-search absolute left-3 top-[1px] bottom-0 m-auto flex items-center opacity-50 text-xs"
></i>
</div>
<div class="relative dropdown-wrapper">
<button
class="px-5 py-2 h-full bg-[white] rounded-md text-xs flex items-center gap-2 border border-primary-500 hover:border-primary-600 transition duration-300"
>
<img
src="{% static 'horilla_theme/assets/img/icons/setting-line.svg' %}"
alt="{% trans 'Actions' %}"
width="15"
class="mt-[-1px]"
/>
{% trans "Actions" %}
</button>
<div
class="dropdown-content absolute z-10 bg-white rounded-lg [box-shadow:0px_0px_20px_0px_rgb(0_0_0_/_8%)] min-w-[150px] py-2 right-0"
>
<ul class="text-sm">
{% if perms.biometric.delete_biometricemployees %}
<li>
<a
href="#"
class="px-4 py-1.5 hover:text-primary-600 transition duration-300 text-xs gap-4 items-center flex w-full"
id="deleteCosecUsers"
data-action="delete"
>
{% trans "Delete" %}
</a>
</li>
{% endif %}
</ul>
</div>
</div>
{% if perms.biometric.add_biometricemployees %}
<a
class="px-4 py-2 bg-primary-600 text-white rounded-md text-xs flex items-center gap-2 hover:bg-primary-800 transition duration-300 cursor-pointer"
data-toggle="oh-modal-toggle"
data-target="#objectCreateModal"
hx-target="#objectCreateModalTarget"
hx-get="{% url 'add-biometric-user' device_id %}"
>
<img
src="{% static 'horilla_theme/assets/img/icons/more.svg' %}"
alt="{% trans 'Create' %}"
width="13"
class="filter brightness-0 invert"
/>
{% trans "Add" %}
</a>
{% endif %}
</div>
</div>

View File

@@ -0,0 +1,147 @@
{% load i18n %}
<div class="bg-white p-5 pe-2 pt-3 rounded-md shadow-card">
{% if employees %}
<table class="w-full border-separate">
<thead class="sticky top-0 bg-[white] z-[1]">
<tr class="border-b border-[#ececec]">
<th class="text-sm font-medium text-left p-3 w-[30px]">
<input
type="checkbox"
class="w-4 h-4 bg-[#E9EDF7] rounded-sm text-[#e54f38] all-bio-employee"
title="Select All"
data-device="{{device_id}}"
id="allBioEmployee"
/>
</th>
<th class="text-sm font-medium text-left min-w-[150px] pl-3">
{% trans "Employee" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "User ID" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Badge ID" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Work Email" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Job Position" %}
</th>
<th class="text-sm font-medium text-center min-w-[100px]">
{% trans "Actions" %}
</th>
</tr>
</thead>
<tbody>
{% for employee in employees %}
<tr class="border-b border-[#f3f3f3] fade-me-out" id="dahuaUser{{employee.id}}">
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
<input
type="checkbox"
id="{{employee.user_id}}"
class="w-4 h-4 bg-[#E9EDF7] rounded-sm text-[#e54f38] cursor-pointer all-bio-employee-row"
/>
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.employee_id.get_full_name|title}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.user_id}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.employee_id.badge_id}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px] truncate">
{{employee.employee_id.get_mail}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.employee_id.get_job_position}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
<div class="oh-btn-group">
{% if perms.biometric.delete_biometricemployees %}
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
hx-confirm="{% trans 'Are you sure you want to delete this user?' %}"
hx-post="{% url 'delete-dahua-user' employee.id %}"
hx-target="#dahuaUser{{employee.id}}"
hx-swap="outerHTML swap:.5s"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
>
</ion-icon>
</a>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="oh-wrapper h-full" align="center">
<div
class="xl:col-span-4 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card h-[70%]"
>
<div class="flex flex-col items-center justify-center h-full">
<img
src="/static/horilla_theme/assets/img/no-records.svg"
alt=""
width="300"
class="mb-4"
/>
<p class="text-[#666] mb-5">
{% trans "No records available at the moment" %}
</p>
</div>
</div>
</div>
{% endif %}
</div>
{% if employees.has_previous or employees.has_next %}
<div class="flex justify-between items-center mt-4 w-full inset-0">
<p class="text-xs text-[#666]">
{% trans "Page" %} {{ employees.number }} {% trans "of" %}
{{ employees.paginator.num_pages }}
</p>
<div class="flex gap-3 text-[#666] items-center">
{% if employees.has_previous %}
<button
class="text-xs hover:text-primary-600 transition duration-300"
hx-target="#section"
hx-get="{% url 'search-employee-in-device' %}?{{pd}}&view=list&page={{ employees.previous_page_number }}&device={{device_id}}"
>
<i class="fa-solid fa-arrow-left"></i>
</button>
{% endif %}
{{ employees.number }} / {{ employees.paginator.num_pages }}
{% if employees.has_next %}
<button
class="text-xs hover:text-primary-600 transition duration-300"
hx-target="#section"
hx-get="{% url 'search-employee-in-device' %}?{{pd}}&view=list&page={{ employees.next_page_number }}&device={{device_id}}"
>
<i class="fa-solid fa-arrow-right"></i>
</button>
{% endif %}
</div>
</div>
{% endif %}
<script>
$(document).ready(function(){
$("thead [type='checkbox']").on("change", function() {
let isChecked = $(this).is(':checked')
$("tbody [type='checkbox']").prop('checked', isChecked)
})
})
</script>

View File

@@ -0,0 +1,85 @@
{% load static i18n %}
<div class="flex flex-wrap justify-between items-center mb-3">
<h3 class="text-lg font-semibold">{% trans "Employees" %}</h3>
<div class="flex flex-wrap gap-1">
<div class="relative">
<input
type="text"
class="text-color-600 ps-8 p-1.5 pb-2 placeholder:text-xs w-full border border-dark-50 rounded-md focus-visible:outline-0 placeholder:text-dark-100 text-sm [transition:.3s] focus:border-primary-600"
name="search"
aria-label="Search Input"
placeholder="Search"
hx-get="{% url 'search-employee-in-device' %}?device={{device_id}}"
name="search"
hx-trigger="keyup changed delay:.2s"
hx-target="#dahuUsersList"
hx-vals='{"view":"{{request.GET.view}}"}'
autocomplete="false"
autofocus="true"
/>
<i
class="fas fa-search absolute left-3 top-[1px] bottom-0 m-auto flex items-center opacity-50 text-xs"
></i>
</div>
<div class="relative dropdown-wrapper">
<button
class="px-5 py-2 h-full bg-[white] rounded-md text-xs flex items-center gap-2 border border-primary-500 hover:border-primary-600 transition duration-300"
>
<img
src="{% static 'horilla_theme/assets/img/icons/setting-line.svg' %}"
alt="{% trans 'Actions' %}"
width="15"
class="mt-[-1px]"
/>
{% trans "Actions" %}
</button>
<div
class="dropdown-content absolute z-10 bg-white rounded-lg [box-shadow:0px_0px_20px_0px_rgb(0_0_0_/_8%)] min-w-[150px] py-2 right-0"
>
<ul class="text-sm">
<li class="oh-dropdown__item">
<a href="#" class="px-4 hover:text-primary-600 transition duration-300 text-xs gap-4 items-center flex w-full"
data-toggle="oh-modal-toggle"
data-target="#objectCreateModal"
hx-get="{% url 'map-biometric-users' device_id %}"
hx-target="#objectCreateModalTarget">
{% trans "Map Employee" %}
</a>
</li>
{% if perms.biometric.delete_biometricemployees %}
<li>
<a
href="#"
class="px-4 hover:text-primary-600 transition duration-300 text-xs gap-4 items-center flex w-full"
id="deleteDahuaUsers(this)"
data-action="delete"
>
{% trans "Delete" %}
</a>
</li>
{% endif %}
</ul>
</div>
</div>
{% if perms.biometric.add_biometricemployees %}
<a
class="px-4 py-2 bg-primary-600 text-white rounded-md text-xs flex items-center gap-2 hover:bg-primary-800 transition duration-300 cursor-pointer"
data-toggle="oh-modal-toggle"
data-target="#objectCreateModal"
hx-target="#objectCreateModalTarget"
hx-get="{% url 'add-dahua-biometric-user' device_id %}"
>
<img
src="{% static 'horilla_theme/assets/img/icons/more.svg' %}"
alt="{% trans 'Create' %}"
width="13"
class="filter brightness-0 invert"
/>
{% trans "Add" %}
</a>
{% endif %}
</div>
</div>

View File

@@ -0,0 +1,147 @@
{% load i18n %}
<div class="bg-white p-5 pe-2 pt-3 rounded-md shadow-card">
{% if employees %}
<table class="w-full border-separate">
<thead class="sticky top-0 bg-[white] z-[1]">
<tr class="border-b border-[#ececec]">
<th class="text-sm font-medium text-left p-3 w-[30px]">
<input
type="checkbox"
class="w-4 h-4 bg-[#E9EDF7] rounded-sm text-[#e54f38] all-bio-employee"
title="Select All"
data-device="{{device_id}}"
id="allBioEmployee"
/>
</th>
<th class="text-sm font-medium text-left min-w-[150px] pl-3">
{% trans "Employee" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Emp Code" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Badge ID" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Work Email" %}
</th>
<th class="text-sm font-medium text-left min-w-[100px] pl-3">
{% trans "Job Position" %}
</th>
<th class="text-sm font-medium text-center min-w-[100px]">
{% trans "Actions" %}
</th>
</tr>
</thead>
<tbody>
{% for employee in employees %}
<tr class="border-b border-[#f3f3f3] fade-me-out" id="eTimeOfficeUser{{employee.id}}">
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
<input
type="checkbox"
id="{{employee.user_id}}"
class="w-4 h-4 bg-[#E9EDF7] rounded-sm text-[#e54f38] cursor-pointer all-bio-employee-row"
/>
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.employee_id.get_full_name|title}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.user_id}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.employee_id.badge_id}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px] truncate">
{{employee.employee_id.get_mail}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
{{employee.employee_id.get_job_position}}
</td>
<td class="text-sm text-left p-3 text-[#666] h-[45px]">
<div class="oh-btn-group">
{% if perms.biometric.delete_biometricemployees %}
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
hx-confirm="{% trans 'Are you sure you want to delete this user?' %}"
hx-post="{% url 'delete-etimeoffice-user' employee.id %}"
hx-target="#eTimeOfficeUser{{employee.id}}"
hx-swap="outerHTML swap:.5s"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
>
</ion-icon>
</a>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="oh-wrapper h-full" align="center">
<div
class="xl:col-span-4 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card h-[70%]"
>
<div class="flex flex-col items-center justify-center h-full">
<img
src="/static/horilla_theme/assets/img/no-records.svg"
alt=""
width="300"
class="mb-4"
/>
<p class="text-[#666] mb-5">
{% trans "No records available at the moment" %}
</p>
</div>
</div>
</div>
{% endif %}
</div>
{% if employees.has_previous or employees.has_next %}
<div class="flex justify-between items-center mt-4 w-full inset-0">
<p class="text-xs text-[#666]">
{% trans "Page" %} {{ employees.number }} {% trans "of" %}
{{ employees.paginator.num_pages }}
</p>
<div class="flex gap-3 text-[#666] items-center">
{% if employees.has_previous %}
<button
class="text-xs hover:text-primary-600 transition duration-300"
hx-target="#section"
hx-get="{% url 'search-employee-in-device' %}?{{pd}}&view=list&page={{ employees.previous_page_number }}&device={{device_id}}"
>
<i class="fa-solid fa-arrow-left"></i>
</button>
{% endif %}
{{ employees.number }} / {{ employees.paginator.num_pages }}
{% if employees.has_next %}
<button
class="text-xs hover:text-primary-600 transition duration-300"
hx-target="#section"
hx-get="{% url 'search-employee-in-device' %}?{{pd}}&view=list&page={{ employees.next_page_number }}&device={{device_id}}"
>
<i class="fa-solid fa-arrow-right"></i>
</button>
{% endif %}
</div>
</div>
{% endif %}
<script>
$(document).ready(function(){
$("thead [type='checkbox']").on("change", function() {
let isChecked = $(this).is(':checked')
$("tbody [type='checkbox']").prop('checked', isChecked)
})
})
</script>

View File

@@ -0,0 +1,68 @@
{% load static i18n %}
<div class="flex flex-wrap justify-between items-center mb-3">
<h3 class="text-lg font-semibold">{% trans "Employees" %}</h3>
<div class="flex flex-wrap gap-1">
<div class="relative">
<input
type="text"
class="text-color-600 ps-8 p-1.5 pb-2 placeholder:text-xs w-full border border-dark-50 rounded-md focus-visible:outline-0 placeholder:text-dark-100 text-sm [transition:.3s] focus:border-primary-600"
name="search"
aria-label="Search Input"
placeholder="Search"
hx-get="{% url 'search-employee-in-device' %}?device={{device_id}}"
name="search"
hx-trigger="keyup changed delay:.2s"
hx-target="#eTimeOfficeUsersList"
hx-vals='{"view":"{{request.GET.view}}"}'
autocomplete="false"
autofocus="true"
/>
<i
class="fas fa-search absolute left-3 top-[1px] bottom-0 m-auto flex items-center opacity-50 text-xs"
></i>
</div>
<div class="relative dropdown-wrapper">
<button
class="px-5 py-2 h-full bg-[white] rounded-md text-xs flex items-center gap-2 border border-primary-500 hover:border-primary-600 transition duration-300"
>
<img
src="{% static 'horilla_theme/assets/img/icons/setting-line.svg' %}"
alt="{% trans 'Actions' %}"
width="15"
class="mt-[-1px]"
/>
{% trans "Actions" %}
</button>
<div
class="dropdown-content absolute z-10 bg-white rounded-lg [box-shadow:0px_0px_20px_0px_rgb(0_0_0_/_8%)] min-w-[150px] py-2 right-0"
>
<ul class="text-sm">
<li class="oh-dropdown__item">
<a href="#" class="px-4 hover:text-primary-600 transition duration-300 text-xs gap-4 items-center flex w-full"
data-toggle="oh-modal-toggle"
data-target="#objectCreateModal"
hx-get="{% url 'map-biometric-users' device_id %}"
hx-target="#objectCreateModalTarget">
{% trans "Map Employee" %}
</a>
</li>
{% if perms.biometric.delete_biometricemployees %}
<li>
<a
href="#"
class="px-4 hover:text-primary-600 transition duration-300 text-xs gap-4 items-center flex w-full"
id="deleteETimeOfficeUsers(this)"
data-action="delete"
>
{% trans "Delete" %}
</a>
</li>
{% endif %}
</ul>
</div>
</div>
</div>
</div>

View File

@@ -5,10 +5,10 @@
}
#enlargeImageContainer {
display:none;
position: absolute;
width: 300px;
height: 400px;
display:none;
position: absolute;
width: 300px;
height: 400px;
z-index: 1000;
}
.diff-cell {
@@ -99,8 +99,8 @@
var elementOffset = $(element).offset();
var elementWidth = $(element).outerWidth();
var topPosition = elementOffset.top - 30;
var leftPosition = elementWidth + 10;
var topPosition = elementOffset.top - 30;
var leftPosition = elementWidth + 10;
// Position the enlarge image container dynamically
enlargeImageContainer.css({