Update verification photo pages

This commit is contained in:
2025-12-27 11:08:12 -05:00
parent c32cb40333
commit d71403b1cf
3 changed files with 197 additions and 15 deletions
+79 -12
View File
@@ -5348,9 +5348,12 @@ def verification_review():
status_filter = request.args.get('status', 'pending') status_filter = request.args.get('status', 'pending')
date_from = request.args.get('date_from', '') date_from = request.args.get('date_from', '')
date_to = request.args.get('date_to', '') date_to = request.args.get('date_to', '')
project_filter = request.args.get('project', '')
location_filter = request.args.get('location', '')
employee_filter = request.args.get('employee', '')
# Build query # Build query - join with QRCode to access project_id
query = AttendanceData.query.filter( query = AttendanceData.query.join(QRCode).filter(
AttendanceData.verification_required == True AttendanceData.verification_required == True
) )
@@ -5363,11 +5366,54 @@ def verification_review():
if date_to: if date_to:
query = query.filter(AttendanceData.check_in_date <= date_to) query = query.filter(AttendanceData.check_in_date <= date_to)
# Apply project filter
if project_filter:
try:
query = query.filter(QRCode.project_id == int(project_filter))
except (ValueError, TypeError):
pass
# Apply location filter
if location_filter:
query = query.filter(AttendanceData.location_name.ilike(f'%{location_filter}%'))
# Apply employee ID filter
if employee_filter:
query = query.filter(AttendanceData.employee_id.ilike(f'%{employee_filter}%'))
# Get records with QR code information # Get records with QR code information
verifications = query.join(QRCode).order_by( verifications = query.order_by(
AttendanceData.verification_timestamp.desc() AttendanceData.verification_timestamp.desc()
).all() ).all()
# Build a dictionary for employee names lookup
employee_names = {}
for record in verifications:
if record.employee_id and record.employee_id not in employee_names:
try:
employee = Employee.query.filter_by(id=int(record.employee_id)).first()
if employee:
employee_names[record.employee_id] = f"{employee.lastName}, {employee.firstName}"
else:
employee_names[record.employee_id] = None
except (ValueError, TypeError):
employee_names[record.employee_id] = None
# Build a dictionary for project names lookup
project_names = {}
for record in verifications:
if record.qr_code and record.qr_code.project_id:
project_id = record.qr_code.project_id
if project_id not in project_names:
try:
project = Project.query.get(project_id)
if project:
project_names[project_id] = project.name
else:
project_names[project_id] = None
except Exception:
project_names[project_id] = None
# Get counts for status badges # Get counts for status badges
pending_count = AttendanceData.query.filter( pending_count = AttendanceData.query.filter(
AttendanceData.verification_status == 'pending' AttendanceData.verification_status == 'pending'
@@ -5381,6 +5427,20 @@ def verification_review():
AttendanceData.verification_status == 'rejected' AttendanceData.verification_status == 'rejected'
).count() ).count()
# Get all projects for filter dropdown
projects = Project.query.filter_by(active_status=True).order_by(Project.name).all()
# Get unique locations for filter dropdown
locations = db.session.query(AttendanceData.location_name).filter(
AttendanceData.verification_required == True
).distinct().order_by(AttendanceData.location_name).all()
location_list = [loc[0] for loc in locations if loc[0]]
# Log access
logger_handler.logger.info(
f"User {session.get('username')} ({session.get('role')}) accessed verification review page"
)
return render_template('verification_review.html', return render_template('verification_review.html',
verifications=verifications, verifications=verifications,
pending_count=pending_count, pending_count=pending_count,
@@ -5388,7 +5448,14 @@ def verification_review():
rejected_count=rejected_count, rejected_count=rejected_count,
status_filter=status_filter, status_filter=status_filter,
date_from=date_from, date_from=date_from,
date_to=date_to) date_to=date_to,
project_filter=project_filter,
location_filter=location_filter,
employee_filter=employee_filter,
projects=projects,
locations=location_list,
employee_names=employee_names,
project_names=project_names)
except Exception as e: except Exception as e:
logger_handler.logger.error(f"Error in verification review: {e}") logger_handler.logger.error(f"Error in verification review: {e}")
@@ -5564,10 +5631,10 @@ def verification_review_detail(record_id):
except (ValueError, TypeError) as e: except (ValueError, TypeError) as e:
logger_handler.logger.warning(f"Could not lookup employee name for ID {record.employee_id}: {e}") logger_handler.logger.warning(f"Could not lookup employee name for ID {record.employee_id}: {e}")
employee_name = f"Unknown (ID: {record.employee_id})" employee_name = f"Unknown (ID: {record.employee_id})"
# Get event type from QR code (Check In/Check Out) # Get event type from QR code (Check In/Check Out)
location_event = qr_code.location_event if qr_code and qr_code.location_event else 'N/A' location_event = qr_code.location_event if qr_code and qr_code.location_event else 'N/A'
# Log the access for audit trail # Log the access for audit trail
logger_handler.logger.info( logger_handler.logger.info(
f"User {session.get('username')} ({session.get('role')}) " f"User {session.get('username')} ({session.get('role')}) "
@@ -5586,12 +5653,12 @@ def verification_review_detail(record_id):
check_in_time = str(record.check_in_time) if record.check_in_time else 'N/A' check_in_time = str(record.check_in_time) if record.check_in_time else 'N/A'
return render_template('verification_review_detail.html', return render_template('verification_review_detail.html',
record=record, record=record,
qr_code=qr_code, qr_code=qr_code,
check_in_date=check_in_date, check_in_date=check_in_date,
check_in_time=check_in_time, check_in_time=check_in_time,
employee_name=employee_name, employee_name=employee_name,
location_event=location_event) location_event=location_event)
except Exception as e: except Exception as e:
logger_handler.logger.error(f"Error loading verification review detail: {e}") logger_handler.logger.error(f"Error loading verification review detail: {e}")
+117 -2
View File
@@ -60,6 +60,36 @@
</select> </select>
</div> </div>
<div class="filter-group">
<label for="project">Project</label>
<select name="project" id="project" class="filter-select">
<option value="">All Projects</option>
{% for project in projects %}
<option value="{{ project.id }}" {% if project_filter == project.id|string %}selected{% endif %}>
{{ project.name }}
</option>
{% endfor %}
</select>
</div>
<div class="filter-group">
<label for="location">Location</label>
<select name="location" id="location" class="filter-select">
<option value="">All Locations</option>
{% for location in locations %}
<option value="{{ location }}" {% if location_filter == location %}selected{% endif %}>
{{ location }}
</option>
{% endfor %}
</select>
</div>
<div class="filter-group">
<label for="employee">Employee ID</label>
<input type="text" name="employee" id="employee" value="{{ employee_filter }}"
class="filter-input" placeholder="Search by ID...">
</div>
<div class="filter-group"> <div class="filter-group">
<label for="date_from">From Date</label> <label for="date_from">From Date</label>
<input type="date" name="date_from" id="date_from" value="{{ date_from }}" class="filter-input"> <input type="date" name="date_from" id="date_from" value="{{ date_from }}" class="filter-input">
@@ -70,10 +100,13 @@
<input type="date" name="date_to" id="date_to" value="{{ date_to }}" class="filter-input"> <input type="date" name="date_to" id="date_to" value="{{ date_to }}" class="filter-input">
</div> </div>
<div class="filter-group"> <div class="filter-group filter-buttons">
<button type="submit" class="btn-filter"> <button type="submit" class="btn-filter">
<i class="fas fa-filter"></i> Apply Filters <i class="fas fa-filter"></i> Apply Filters
</button> </button>
<a href="{{ url_for('verification_review') }}" class="btn-reset">
<i class="fas fa-undo"></i> Reset
</a>
</div> </div>
</div> </div>
</form> </form>
@@ -89,7 +122,10 @@
<div class="verification-info"> <div class="verification-info">
<h3> <h3>
<i class="fas fa-user"></i> <i class="fas fa-user"></i>
Employee {{ record.employee_id }} {{ record.employee_id }}
{% if employee_names.get(record.employee_id) %}
<span class="employee-name">- {{ employee_names.get(record.employee_id) }}</span>
{% endif %}
</h3> </h3>
<div class="verification-meta"> <div class="verification-meta">
<span class="meta-item"> <span class="meta-item">
@@ -104,7 +140,21 @@
<i class="fas fa-map-marker-alt"></i> <i class="fas fa-map-marker-alt"></i>
{{ record.location_name }} {{ record.location_name }}
</span> </span>
{% if record.qr_code and record.qr_code.location_event %}
<span class="meta-item event-badge {{ 'check-in' if record.qr_code.location_event == 'Check In' else 'check-out' }}">
<i class="fas fa-{{ 'sign-in-alt' if record.qr_code.location_event == 'Check In' else 'sign-out-alt' }}"></i>
{{ record.qr_code.location_event }}
</span>
{% endif %}
</div> </div>
{% if record.qr_code and record.qr_code.project_id and project_names.get(record.qr_code.project_id) %}
<div class="verification-project">
<span class="project-badge">
<i class="fas fa-folder"></i>
{{ project_names.get(record.qr_code.project_id) }}
</span>
</div>
{% endif %}
</div> </div>
<div class="verification-status"> <div class="verification-status">
{% if record.verification_status == 'pending' %} {% if record.verification_status == 'pending' %}
@@ -330,6 +380,71 @@
background: #2563eb; background: #2563eb;
} }
.btn-reset {
width: 100%;
padding: 0.625rem 1.5rem;
background: #6b7280;
color: white;
border: none;
border-radius: 0.375rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
text-decoration: none;
text-align: center;
display: inline-block;
margin-top: 0.5rem;
}
.btn-reset:hover {
background: #4b5563;
color: white;
}
.filter-buttons {
display: flex;
flex-direction: column;
}
.employee-name {
font-weight: 500;
color: #4b5563;
font-size: 0.95em;
}
.verification-project {
margin-top: 0.5rem;
}
.project-badge {
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.25rem 0.75rem;
background: #e0e7ff;
color: #3730a3;
border-radius: 9999px;
font-size: 0.8rem;
font-weight: 500;
}
.event-badge {
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-weight: 600;
font-size: 0.75rem;
}
.event-badge.check-in {
background: #d1fae5;
color: #065f46;
}
.event-badge.check-out {
background: #fef3c7;
color: #92400e;
}
.verifications-grid { .verifications-grid {
display: grid; display: grid;
gap: 2rem; gap: 2rem;
+1 -1
View File
@@ -345,7 +345,7 @@ QR Code Management{% endblock %} {% block extra_head %}
<div class="info-row"> <div class="info-row">
<span class="info-label">QR Address</span> <span class="info-label">QR Address</span>
<span class="info-value" <span class="info-value"
>{{ qr_code.address if qr_code else 'N/A' }}</span >{{ qr_code.location_address if qr_code else 'N/A' }}</span
> >
</div> </div>
<div class="info-row"> <div class="info-row">