Updated: enable import for payroll users
This commit is contained in:
@@ -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,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 %}
|
||||||
Reference in New Issue
Block a user