Initial Codes

This commit is contained in:
NguyenND
2025-07-29 17:02:08 -04:00
committed by GitHub
parent 946b18f142
commit 7459d0fd15
34 changed files with 23480 additions and 0 deletions
+327
View File
@@ -0,0 +1,327 @@
{% extends "base.html" %}
{% block title %}Attendance Report - QR Code Management{% endblock %}
{% block content %}
<div class="attendance-page">
<div class="attendance-header">
<div class="header-content">
<h1>
<i class="fas fa-chart-line"></i>
Attendance Report
</h1>
<p>Monitor and analyze staff attendance across all locations</p>
</div>
<div class="header-actions">
<button onclick="exportAttendance()" class="btn btn-success">
<i class="fas fa-download"></i>
Export Data
</button>
<button onclick="refreshReport()" class="btn btn-secondary">
<i class="fas fa-sync-alt"></i>
Refresh
</button>
</div>
</div>
<!-- Statistics Cards -->
<div class="stats-section">
<div class="stats-grid">
<div class="stat-card primary">
<div class="stat-icon">
<i class="fas fa-user-check"></i>
</div>
<div class="stat-content">
<h3>{{ stats.total_checkins or 0 }}</h3>
<p>Total Check-ins</p>
<span class="stat-trend">All time</span>
</div>
</div>
<div class="stat-card success">
<div class="stat-icon">
<i class="fas fa-users"></i>
</div>
<div class="stat-content">
<h3>{{ stats.unique_employees or 0 }}</h3>
<p>Unique Employees</p>
<span class="stat-trend">Have checked in</span>
</div>
</div>
<div class="stat-card info">
<div class="stat-icon">
<i class="fas fa-map-marker-alt"></i>
</div>
<div class="stat-content">
<h3>{{ stats.active_locations or 0 }}</h3>
<p>Active Locations</p>
<span class="stat-trend">With check-ins</span>
</div>
</div>
<div class="stat-card warning">
<div class="stat-icon">
<i class="fas fa-calendar-day"></i>
</div>
<div class="stat-content">
<h3>{{ stats.today_checkins or 0 }}</h3>
<p>Today's Check-ins</p>
<span class="stat-trend">{{ "now"|strftime('%B %d') }}</span>
</div>
</div>
</div>
</div>
<!-- Filters Section -->
<div class="filters-section">
<div class="filters-card">
<div class="filters-header">
<h3>
<i class="fas fa-filter"></i>
Filter Records
</h3>
</div>
<form method="GET" class="filters-form" id="filtersForm">
<div class="filter-row">
<div class="filter-group">
<label for="date">
<i class="fas fa-calendar"></i>
Date
</label>
<input type="date"
id="date"
name="date"
value="{{ date_filter }}"
max="{{ 'now'|strftime('%Y-%m-%d') }}">
</div>
<div class="filter-group">
<label for="location">
<i class="fas fa-building"></i>
Location
</label>
<select id="location" name="location">
<option value="">All Locations</option>
{% for location in locations %}
<option value="{{ location }}"
{{ 'selected' if location == location_filter else '' }}>
{{ location }}
</option>
{% endfor %}
</select>
</div>
<div class="filter-group">
<label for="employee">
<i class="fas fa-id-badge"></i>
Employee ID
</label>
<input type="text"
id="employee"
name="employee"
value="{{ employee_filter }}"
placeholder="Enter Employee ID">
</div>
<div class="filter-actions">
<button type="submit" class="btn btn-primary">
<i class="fas fa-search"></i>
Apply Filters
</button>
<button type="button" onclick="clearFilters()" class="btn btn-outline">
<i class="fas fa-times"></i>
Clear
</button>
</div>
</div>
</form>
</div>
</div>
<!-- Attendance Table -->
<div class="attendance-table-section">
<div class="table-header">
<h3>
<i class="fas fa-list"></i>
Attendance Records
</h3>
<div class="table-controls">
<div class="entries-per-page">
<label>Show:</label>
<select id="entriesPerPage" onchange="changeEntriesPerPage()">
<option value="25">25</option>
<option value="50" selected>50</option>
<option value="100">100</option>
<option value="all">All</option>
</select>
<span>entries</span>
</div>
</div>
</div>
<div class="table-container">
{% if attendance_records %}
<table class="attendance-table" id="attendanceTable">
<thead>
<tr>
<th onclick="sortTable(0)">#</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(3)">Event <i class="fas fa-sort"></i></th>
<th onclick="sortTable(4)">Date <i class="fas fa-sort"></i></th>
<th onclick="sortTable(5)">Time <i class="fas fa-sort"></i></th>
<th onclick="sortTable(6)">Device <i class="fas fa-sort"></i></th>
<th onclick="sortTable(7)">Status <i class="fas fa-sort"></i></th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for record in attendance_records %}
<tr data-record-id="{{ record.id }}">
<td>{{ loop.index }}</td>
<td>
<div class="employee-info">
<span class="employee-id">{{ record.employee_id }}</span>
</div>
</td>
<td>
<div class="location-info">
<i class="fas fa-map-marker-alt"></i>
{{ record.location_name }}
</div>
</td>
<td>
<div class="event-info">
{{ record.location_event }}
</div>
</td>
<td>
<div class="date-info">
{{ record.check_in_date.strftime('%Y-%m-%d') }}
</div>
</td>
<td>
<div class="time-info">
{{ record.check_in_time.strftime('%H:%M') }}
</div>
</td>
<td>
<div class="device-info">
<i class="fas fa-mobile-alt"></i>
<span title="{{ record.device_info }}">
{{ record.device_info[:20] }}{% if record.device_info|length > 20 %}...{% endif %}
</span>
</div>
</td>
<td>
<span class="status-badge {{ record.status }}">
<i class="fas {{ 'fa-check-circle' if record.status == 'present' else 'fa-times-circle' }}"></i>
{{ record.status.title() }}
</span>
</td>
<td>
<div class="record-actions">
<button onclick="viewRecordDetails({{ record.id }})"
class="action-btn btn-view"
title="View Details">
<i class="fas fa-eye"></i>
</button>
<button onclick="editRecord({{ record.id }})"
class="action-btn btn-edit"
title="Edit Record">
<i class="fas fa-edit"></i>
</button>
<button onclick="deleteRecord({{ record.id }})"
class="action-btn btn-delete"
title="Delete Record">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="empty-state">
<div class="empty-icon">
<i class="fas fa-clipboard-list"></i>
</div>
<h3>No Attendance Records Found</h3>
<p>{% if date_filter or location_filter or employee_filter %}
No records match your current filters. Try adjusting the filter criteria.
{% else %}
No staff have checked in yet. QR codes need to be scanned to generate attendance data.
{% endif %}</p>
<button onclick="clearFilters()" class="btn btn-primary">
<i class="fas fa-refresh"></i>
Clear Filters
</button>
</div>
{% endif %}
</div>
<!-- Pagination -->
<div class="pagination-container" id="paginationContainer">
<!-- Pagination will be dynamically generated -->
</div>
</div>
<!-- Charts Section -->
<div class="charts-section">
<div class="charts-grid">
<div class="chart-card">
<div class="chart-header">
<h3>
<i class="fas fa-chart-bar"></i>
Daily Check-ins (Last 7 Days)
</h3>
</div>
<div class="chart-container">
<canvas id="dailyChart"></canvas>
</div>
</div>
<div class="chart-card">
<div class="chart-header">
<h3>
<i class="fas fa-chart-pie"></i>
Check-ins by Location
</h3>
</div>
<div class="chart-container">
<canvas id="locationChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Record Details Modal -->
<div id="recordModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3 id="modalTitle">Attendance Record Details</h3>
<button class="modal-close" onclick="closeRecordModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body" id="modalBody">
<!-- Dynamic content will be loaded here -->
</div>
<div class="modal-footer">
<button onclick="closeRecordModal()" class="btn btn-secondary">
Close
</button>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<script src="{{ url_for('static', filename='js/attendance_report.js') }}"></script>
{% endblock %}