Attendance report add employee name column

This commit is contained in:
Nguyen Ngo
2025-08-19 17:19:25 -04:00
parent 4248917d17
commit 758fc7f7a9
4 changed files with 93 additions and 64 deletions
+6 -2
View File
@@ -3257,9 +3257,11 @@ def attendance_report():
ad.longitude, ad.longitude,
ad.location_accuracy, ad.location_accuracy,
ad.accuracy as gps_accuracy, ad.accuracy as gps_accuracy,
ad.device_info ad.device_info,
CONCAT(e.firstName, ' ', e.lastName) as employee_name
FROM attendance_data ad FROM attendance_data ad
LEFT JOIN qr_codes qc ON ad.qr_code_id = qc.id LEFT JOIN qr_codes qc ON ad.qr_code_id = qc.id
LEFT JOIN employee e ON ad.employee_id = e.id
WHERE 1=1 WHERE 1=1
""" """
else: else:
@@ -3278,9 +3280,11 @@ def attendance_report():
ad.longitude, ad.longitude,
NULL as location_accuracy, NULL as location_accuracy,
ad.accuracy as gps_accuracy, ad.accuracy as gps_accuracy,
ad.device_info ad.device_info,
CONCAT(e.firstName, ' ', e.lastName) as employee_name
FROM attendance_data ad FROM attendance_data ad
LEFT JOIN qr_codes qc ON ad.qr_code_id = qc.id LEFT JOIN qr_codes qc ON ad.qr_code_id = qc.id
LEFT JOIN employee e ON ad.employee_id = e.id
WHERE 1=1 WHERE 1=1
""" """
+17
View File
@@ -393,6 +393,23 @@
border-radius: var(--radius); border-radius: var(--radius);
} }
.employee-name {
display: flex;
align-items: center;
gap: var(--spacing-2);
color: var(--gray-700);
font-weight: 500;
}
.employee-name i {
color: var(--primary-color);
font-size: var(--font-size-sm);
}
.employee-name span {
font-weight: 500;
}
.location-info { .location-info {
display: flex; display: flex;
align-items: center; align-items: center;
+55 -54
View File
@@ -69,31 +69,27 @@ function loadTableData() {
} }
return { return {
id: row.dataset.recordId, id: row.dataset.recordId,
index: index + 1, index: index + 1,
employeeId: cells[1] ? cells[1].textContent.trim() : "", employeeId: cells[1] ? cells[1].textContent.trim() : "",
location: cells[2] ? cells[2].textContent.trim() : "", employeeName: cells[2] ? cells[2].textContent.trim() : "",
event: cells[3] ? cells[3].textContent.trim() : "", location: cells[3] ? cells[3].textContent.trim() : "",
date: cells[4] ? cells[4].textContent.trim() : "", event: cells[4] ? cells[4].textContent.trim() : "",
time: cells[5] ? cells[5].textContent.trim() : "", date: cells[5] ? cells[5].textContent.trim() : "",
qr_address: cells[6] time: cells[6] ? cells[6].textContent.trim() : "",
? cells[6].getAttribute("title") || cells[6].textContent.trim() qr_address: cells[7]
: "", ? cells[7].getAttribute("title") || cells[7].textContent.trim()
checked_in_address: cells[7] : "",
? cells[7].getAttribute("title") || cells[7].textContent.trim() checked_in_address: cells[8]
: "", ? cells[8].getAttribute("title") || cells[8].textContent.trim()
// FIXED: Extract location accuracy for address display logic : "",
location_accuracy: cells[8] ? extractLocationAccuracy(cells[8]) : null, location_accuracy: cells[9] ? extractLocationAccuracy(cells[9]) : null,
accuracy_level: cells[8] accuracy_level: cells[9]
? extractLocationAccuracyLevel(cells[8]) ? extractLocationAccuracyLevel(cells[9])
: "unknown", : "unknown",
device: cells[9] device: cells[10]
? cells[9].getAttribute("title") || cells[9].textContent.trim() ? cells[10].textContent.trim()
: "", : ""
has_location_data: cells[8]
? !cells[8].textContent.includes("Unknown")
: false,
coordinates: extractCoordinates(cells[8]),
}; };
}); });
@@ -568,31 +564,28 @@ function loadTableData() {
attendanceData = Array.from(rows).map((row, index) => { attendanceData = Array.from(rows).map((row, index) => {
const cells = row.querySelectorAll("td"); const cells = row.querySelectorAll("td");
return { return {
id: row.dataset.recordId, id: row.dataset.recordId,
index: index + 1, index: index + 1,
employeeId: cells[1] ? cells[1].textContent.trim() : "", employeeId: cells[1] ? cells[1].textContent.trim() : "",
location: cells[2] ? cells[2].textContent.trim() : "", employeeName: cells[2] ? cells[2].textContent.trim() : "", // NEW: Employee Name column
event: cells[3] ? cells[3].textContent.trim() : "", location: cells[3] ? cells[3].textContent.trim() : "", // Updated from cells[2]
date: cells[4] ? cells[4].textContent.trim() : "", event: cells[4] ? cells[4].textContent.trim() : "", // Updated from cells[3]
time: cells[5] ? cells[5].textContent.trim() : "", date: cells[5] ? cells[5].textContent.trim() : "", // Updated from cells[4]
qr_address: cells[6] time: cells[6] ? cells[6].textContent.trim() : "", // Updated from cells[5]
? cells[6].getAttribute("title") || cells[6].textContent.trim() qr_address: cells[7] // Updated from cells[6]
: "", ? cells[7].getAttribute("title") || cells[7].textContent.trim()
checked_in_address: cells[7] : "",
? cells[7].getAttribute("title") || cells[7].textContent.trim() checked_in_address: cells[8] // Updated from cells[7]
: "", ? cells[8].getAttribute("title") || cells[8].textContent.trim()
// FIXED: Extract location accuracy for address display logic : "",
location_accuracy: cells[8] ? extractLocationAccuracy(cells[8]) : null, // FIXED: Extract location accuracy for address display logic
accuracy_level: cells[8] location_accuracy: cells[9] ? extractLocationAccuracy(cells[9]) : null, // Updated from cells[8]
? extractLocationAccuracyLevel(cells[8]) accuracy_level: cells[9] // Updated from cells[8]
: "unknown", ? extractLocationAccuracyLevel(cells[9])
device: cells[9] : "unknown",
? cells[9].getAttribute("title") || cells[9].textContent.trim() device: cells[10] // Updated from cells[9]
: "", ? cells[10].textContent.trim()
has_location_data: cells[8] : ""
? !cells[8].textContent.includes("Unknown")
: false,
coordinates: extractCoordinates(cells[8]),
}; };
}); });
@@ -787,6 +780,12 @@ function createTableRow(record, displayIndex) {
<span class="employee-id">${record.employeeId}</span> <span class="employee-id">${record.employeeId}</span>
</div> </div>
</td> </td>
<td>
<div class="employee-name">
<i class="fas fa-user"></i>
<span>${record.employeeName || 'Unknown'}</span>
</div>
</td>
<td> <td>
<div class="location-info"> <div class="location-info">
<i class="fas fa-map-marker-alt"></i> <i class="fas fa-map-marker-alt"></i>
@@ -966,7 +965,9 @@ function getAccuracyLevelColor(level) {
function exportAttendanceWithAccuracy() { function exportAttendanceWithAccuracy() {
// Build CSV header with location accuracy // Build CSV header with location accuracy
const headers = [ const headers = [
"#",
"Employee ID", "Employee ID",
"Employee Name",
"Location", "Location",
"Event", "Event",
"Date", "Date",
@@ -979,17 +980,17 @@ function exportAttendanceWithAccuracy() {
]; ];
// Build CSV rows // Build CSV rows
const rows = filteredData.map((record) => [ const rows = filteredData.map((record, index) => [
index + 1,
record.employeeId, record.employeeId,
record.employeeName || "Unknown",
record.location, record.location,
record.event, record.event,
record.date, record.date,
record.time, record.time,
record.qr_address, record.qr_address,
record.checked_in_address, record.checked_in_address,
record.location_accuracy !== null record.location_accuracy ? record.location_accuracy.toFixed(3) : "Unknown",
? record.location_accuracy.toFixed(3)
: "Unknown",
record.accuracy_level, record.accuracy_level,
record.device, record.device,
]); ]);
+15 -8
View File
@@ -163,17 +163,18 @@
<tr> <tr>
<th onclick="sortTable(0)">#</th> <th onclick="sortTable(0)">#</th>
<th onclick="sortTable(1)">Employee ID <i class="fas fa-sort"></i></th> <th onclick="sortTable(1)">Employee ID <i class="fas fa-sort"></i></th>
<th onclick="sortTable(2)">Location <i class="fas fa-sort"></i></th> <th onclick="sortTable(2)">Employee Name <i class="fas fa-sort"></i></th>
<th onclick="sortTable(3)">Event <i class="fas fa-sort"></i></th> <th onclick="sortTable(3)">Location <i class="fas fa-sort"></i></th>
<th onclick="sortTable(4)">Date <i class="fas fa-sort"></i></th> <th onclick="sortTable(4)">Event <i class="fas fa-sort"></i></th>
<th onclick="sortTable(5)">Time <i class="fas fa-sort"></i></th> <th onclick="sortTable(5)">Date <i class="fas fa-sort"></i></th>
<th onclick="sortTable(6)" class="address-column">QR Address <i class="fas fa-sort"></i></th> <th onclick="sortTable(6)">Time <i class="fas fa-sort"></i></th>
<th onclick="sortTable(7)" class="address-column">Check-in Address <i class="fas fa-sort"></i></th> <th onclick="sortTable(7)" class="address-column">QR Address <i class="fas fa-sort"></i></th>
<th onclick="sortTable(8)"> <th onclick="sortTable(8)" class="address-column">Check-in Address <i class="fas fa-sort"></i></th>
<th onclick="sortTable(9)">
{% if has_location_accuracy_feature %}Location Accuracy{% else %}GPS Accuracy{% endif %} {% if has_location_accuracy_feature %}Location Accuracy{% else %}GPS Accuracy{% endif %}
<i class="fas fa-sort"></i> <i class="fas fa-sort"></i>
</th> </th>
<th onclick="sortTable(9)">Device <i class="fas fa-sort"></i></th> <th onclick="sortTable(10)">Device <i class="fas fa-sort"></i></th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
@@ -186,6 +187,12 @@
<span class="employee-id">{{ record.employee_id }}</span> <span class="employee-id">{{ record.employee_id }}</span>
</div> </div>
</td> </td>
<td>
<div class="employee-name">
<i class="fas fa-user"></i>
<span>{{ record.employee_name if record.employee_name else 'Unknown' }}</span>
</div>
</td>
<td> <td>
<div class="location-info"> <div class="location-info">
<i class="fas fa-map-marker-alt"></i> <i class="fas fa-map-marker-alt"></i>