Updated: enable import for payroll users

This commit is contained in:
2025-10-10 09:50:20 -04:00
parent 7d0f5c363c
commit 6f13859132
2 changed files with 268 additions and 96 deletions
+131 -42
View File
@@ -20,7 +20,7 @@
</div> </div>
<div class="header-actions"> <div class="header-actions">
{% if session.role == 'admin' %} {% if session.role in ['admin', 'payroll'] %}
<a href="{{ url_for('import_time_attendance') }}" class="btn btn-primary"> <a href="{{ url_for('import_time_attendance') }}" class="btn btn-primary">
<i class="fas fa-upload"></i> <i class="fas fa-upload"></i>
Import Excel Data Import Excel Data
@@ -78,7 +78,6 @@
</div> </div>
</div> </div>
<!-- Recent Records Table -->
{% if recent_records %} {% if recent_records %}
<div class="attendance-table-section"> <div class="attendance-table-section">
<div class="table-header"> <div class="table-header">
@@ -98,84 +97,92 @@
<thead> <thead>
<tr> <tr>
<th>#</th> <th>#</th>
<th>Employee ID</th> <th>ID</th>
<th>Employee Name</th> <th>Name</th>
<th>Location</th> <th>Platform</th>
<th>Action</th>
<th>Date</th> <th>Date</th>
<th>Time</th> <th>Time</th>
<th>Platform</th> <th>Location Name</th>
<th>Address</th> <th>Action Description</th>
<th>Event Description</th>
<th>Recorded Address</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for record in recent_records %} {% for record in recent_records %}
<tr> <tr>
<!-- # -->
<td>{{ loop.index }}</td> <td>{{ loop.index }}</td>
<!-- 1. ID (Employee ID) -->
<td> <td>
<div class="employee-info"> <div class="employee-info">
<span class="employee-id">{{ record.employee_id }}</span> <span class="employee-id">{{ record.employee_id }}</span>
</div> </div>
</td> </td>
<!-- 2. Name -->
<td> <td>
<div class="employee-name"> <div class="employee-name-simple">
<i class="fas fa-user"></i> {{ record.employee_name or 'Unknown' }}
<span>{{ record.employee_name or 'Unknown' }}</span>
</div> </div>
</td> </td>
<!-- 3. Platform -->
<td class="platform-cell">
{{ record.platform or 'Unknown' }}
</td>
<!-- 4. Date -->
<td class="date-cell">
{{ record.attendance_date.strftime('%Y-%m-%d') }}
</td>
<!-- 5. Time -->
<td class="time-cell">
{{ record.attendance_time.strftime('%H:%M:%S') }}
</td>
<!-- 6. Location Name -->
<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>
{{ record.location_name }} {{ record.location_name }}
</div> </div>
</td> </td>
<!-- 7. Action Description -->
<td> <td>
<div class="action-info"> <span class="action-badge {{ record.action_description.lower().replace(' ', '-') }}">
{% if record.action_description.lower() == 'check in' %} {% if record.action_description.lower() == 'check in' %}
<i class="fas fa-sign-in-alt" style="color: #059669;"></i> <i class="fas fa-sign-in-alt"></i>
{% elif record.action_description.lower() == 'check out' %} {% elif record.action_description.lower() == 'check out' %}
<i class="fas fa-sign-out-alt" style="color: #d97706;"></i> <i class="fas fa-sign-out-alt"></i>
{% else %} {% else %}
<i class="fas fa-clock" style="color: #6b7280;"></i> <i class="fas fa-clock"></i>
{% endif %} {% endif %}
{{ record.action_description }} {{ record.action_description }}
</div> </span>
</td> </td>
<td>
<div class="date-info"> <!-- 8. Event Description -->
<i class="fas fa-calendar"></i> <td class="event-cell">
{{ record.attendance_date.strftime('%Y-%m-%d') }} {{ record.event_description if record.event_description else '-' }}
</div>
</td> </td>
<td>
<div class="time-info"> <!-- 9. Recorded Address -->
<i class="fas fa-clock"></i> <td class="address-cell">
{{ record.attendance_time.strftime('%H:%M:%S') }}
</div>
</td>
<td>
<div class="platform-info">
{% if record.platform %}
<i class="fas fa-mobile-alt"></i>
{{ record.platform[:25] }}{% if record.platform|length > 25 %}...{% endif %}
{% else %}
<span class="text-muted">-</span>
{% endif %}
</div>
</td>
<td>
<div class="address-info">
{% if record.recorded_address %} {% if record.recorded_address %}
<i class="fas fa-map-pin"></i>
<span title="{{ record.recorded_address }}"> <span title="{{ record.recorded_address }}">
{{ record.recorded_address[:35] }}{% if record.recorded_address|length > 35 %}...{% endif %} {{ record.recorded_address[:35] }}{% if record.recorded_address|length > 35 %}...{% endif %}
</span> </span>
{% else %} {% else %}
<span class="text-muted">-</span> <span class="text-muted">No address</span>
{% endif %} {% endif %}
</div>
</td> </td>
<!-- Actions -->
<td> <td>
<div class="record-actions"> <div class="record-actions">
<button class="action-btn btn-view" onclick="viewRecord({{ record.id }})" title="View Details"> <button class="action-btn btn-view" onclick="viewRecord({{ record.id }})" title="View Details">
@@ -422,6 +429,88 @@
display: none; display: none;
} }
} }
.employee-name-simple {
font-weight: 500;
color: #0f172a;
}
.date-cell,
.time-cell {
font-family: 'Courier New', monospace;
color: #374151;
font-size: 0.875rem;
}
.event-cell {
color: #64748b;
font-size: 0.875rem;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.address-cell {
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.875rem;
}
.text-muted {
color: #94a3b8;
font-style: italic;
font-size: 0.875rem;
}
.platform-cell {
color: #64748b;
font-size: 0.875rem;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Responsive adjustments for dashboard table */
@media (max-width: 1400px) {
.attendance-table th:nth-child(4),
.attendance-table td:nth-child(4),
.attendance-table th:nth-child(9),
.attendance-table td:nth-child(9) {
display: none;
}
}
@media (max-width: 1024px) {
.attendance-table th:nth-child(4),
.attendance-table td:nth-child(4),
.attendance-table th:nth-child(7),
.attendance-table td:nth-child(7),
.attendance-table th:nth-child(9),
.attendance-table td:nth-child(9),
.attendance-table th:nth-child(10),
.attendance-table td:nth-child(10) {
display: none;
}
}
@media (max-width: 768px) {
.attendance-table th:nth-child(4),
.attendance-table td:nth-child(4),
.attendance-table th:nth-child(7),
.attendance-table td:nth-child(7),
.attendance-table th:nth-child(8),
.attendance-table td:nth-child(8),
.attendance-table th:nth-child(9),
.attendance-table td:nth-child(9),
.attendance-table th:nth-child(10),
.attendance-table td:nth-child(10) {
display: none;
}
}
</style> </style>
<script> <script>
+128 -45
View File
@@ -128,37 +128,59 @@
<table class="records-table"> <table class="records-table">
<thead> <thead>
<tr> <tr>
<th>Employee ID</th> <th>ID</th>
<th>Name</th> <th>Name</th>
<th>Date & Time</th>
<th>Action</th>
<th>Location</th>
<th>Platform</th> <th>Platform</th>
<th>QR Address</th> <th>Date</th>
<th>Check-in Address</th> <th>Time</th>
<th>Location Name</th>
<th>Action Description</th>
<th>Event Description</th>
<th>Recorded Address</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for record in records.items %} {% for record in records.items %}
<tr> <tr>
<!-- 1. ID (Employee ID) -->
<td> <td>
<div class="employee-id-cell"> <div class="employee-id-cell">
<span class="employee-id">{{ record.employee_id }}</span> <span class="employee-id">{{ record.employee_id }}</span>
</div> </div>
</td> </td>
<!-- 2. Name -->
<td> <td>
<div class="employee-name-cell"> <div class="employee-name-cell">
<i class="fas fa-user"></i>
<span>{{ record.employee_name }}</span> <span>{{ record.employee_name }}</span>
</div> </div>
</td> </td>
<td class="datetime-cell">
<div class="datetime-info"> <!-- 3. Platform -->
<div class="date">{{ record.attendance_date.strftime('%Y-%m-%d') }}</div> <td class="platform-cell">
<div class="time">{{ record.attendance_time.strftime('%H:%M:%S') }}</div> {{ record.platform or 'Unknown' }}
</td>
<!-- 4. Date -->
<td class="date-cell">
{{ record.attendance_date.strftime('%Y-%m-%d') }}
</td>
<!-- 5. Time -->
<td class="time-cell">
{{ record.attendance_time.strftime('%H:%M:%S') }}
</td>
<!-- 6. Location Name -->
<td class="location-cell">
<div class="location-info">
<i class="fas fa-map-marker-alt"></i>
{{ record.location_name }}
</div> </div>
</td> </td>
<!-- 7. Action Description -->
<td> <td>
<span class="action-badge {{ record.action_description.lower().replace(' ', '-') }}"> <span class="action-badge {{ record.action_description.lower().replace(' ', '-') }}">
{% if record.action_description.lower() == 'check in' %} {% if record.action_description.lower() == 'check in' %}
@@ -171,48 +193,24 @@
{{ record.action_description }} {{ record.action_description }}
</span> </span>
</td> </td>
<td class="location-cell">
<div class="location-info"> <!-- 8. Event Description -->
<i class="fas fa-map-marker-alt"></i> <td class="event-cell">
{{ record.location_name }} {{ record.event_description if record.event_description else '-' }}
</div>
</td> </td>
<td class="platform-cell">
{{ record.platform or 'Unknown' }} <!-- 9. Recorded Address -->
</td> <td class="address-cell">
<td class="address-cell qr-address-cell">
{% if record.qr_address %}
<span title="{{ record.qr_address }}">
<i class="fas fa-qrcode" style="color: #6366f1;"></i>
{{ record.qr_address[:40] }}{% if record.qr_address|length > 40 %}...{% endif %}
</span>
{% else %}
<span class="text-muted">No QR address</span>
{% endif %}
</td>
<td class="address-cell checkin-address-cell">
{% if record.location_accuracy is not none and record.location_accuracy <= 0.3 %}
{# Within 0.3 mile threshold - show QR address #}
{% if record.qr_address %}
<span title="QR Address (High Accuracy ≤ 0.3 mi): {{ record.qr_address }}">
<i class="fas fa-check-circle" style="color: #059669;"></i>
{{ record.qr_address[:40] }}{% if record.qr_address|length > 40 %}...{% endif %}
</span>
{% else %}
<span class="text-muted">No address</span>
{% endif %}
{% else %}
{# Beyond 0.3 mile threshold or no accuracy - show actual check-in address #}
{% if record.recorded_address %} {% if record.recorded_address %}
<span title="Check-in Address: {{ record.recorded_address }}"> <span title="{{ record.recorded_address }}">
<i class="fas fa-location-arrow" style="color: #f59e0b;"></i>
{{ record.recorded_address[:40] }}{% if record.recorded_address|length > 40 %}...{% endif %} {{ record.recorded_address[:40] }}{% if record.recorded_address|length > 40 %}...{% endif %}
</span> </span>
{% else %} {% else %}
<span class="text-muted">No address</span> <span class="text-muted">No address</span>
{% endif %} {% endif %}
{% endif %}
</td> </td>
<!-- Actions -->
<td class="actions-cell"> <td class="actions-cell">
<div class="action-buttons"> <div class="action-buttons">
<a href="{{ url_for('time_attendance_record_detail', record_id=record.id) }}" <a href="{{ url_for('time_attendance_record_detail', record_id=record.id) }}"
@@ -348,4 +346,89 @@ document.addEventListener('DOMContentLoaded', function() {
} }
}); });
</script> </script>
<style>
/* Additional styles for Excel-matched column layout */
.employee-id-cell {
font-family: 'Courier New', monospace;
font-weight: 600;
color: #0f172a;
}
.employee-name-cell {
font-weight: 500;
color: #0f172a;
}
.date-cell,
.time-cell {
font-family: 'Courier New', monospace;
color: #374151;
font-size: 0.875rem;
}
.event-cell {
color: #64748b;
font-size: 0.875rem;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.text-muted {
color: #94a3b8;
font-style: italic;
font-size: 0.875rem;
}
.address-cell {
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.875rem;
}
/* Responsive table adjustments */
@media (max-width: 1400px) {
.records-table th,
.records-table td {
padding: 0.75rem 0.5rem;
font-size: 0.8125rem;
}
.address-cell {
max-width: 150px;
}
.event-cell {
max-width: 120px;
}
}
@media (max-width: 1024px) {
/* Hide less critical columns on smaller screens */
.records-table th:nth-child(3),
.records-table td:nth-child(3),
.records-table th:nth-child(8),
.records-table td:nth-child(8) {
display: none;
}
}
@media (max-width: 768px) {
/* Show only most important columns on mobile */
.records-table th:nth-child(3),
.records-table td:nth-child(3),
.records-table th:nth-child(6),
.records-table td:nth-child(6),
.records-table th:nth-child(8),
.records-table td:nth-child(8),
.records-table th:nth-child(9),
.records-table td:nth-child(9) {
display: none;
}
}
</style>
{% endblock %} {% endblock %}