From b0b0aa49945ec26f898a1946aedc587767256bca Mon Sep 17 00:00:00 2001 From: NguyenND Date: Wed, 24 Dec 2025 15:27:45 -0500 Subject: [PATCH] Updated: add verification review detail --- app.py | 49 +++ static/js/attendance_report.js | 247 +----------- templates/attendance_report.html | 50 +-- templates/verification_review_detail.html | 440 ++++++++++++++++++++++ 4 files changed, 513 insertions(+), 273 deletions(-) create mode 100644 templates/verification_review_detail.html diff --git a/app.py b/app.py index 6844eef..ef2aa7d 100644 --- a/app.py +++ b/app.py @@ -5531,6 +5531,55 @@ def get_verification_details(record_id): 'message': 'Error loading verification details' }), 500 +@app.route('/verification-review/') +@login_required +def verification_review_detail(record_id): + """Review a single verification photo on a dedicated page""" + try: + # Check permissions + if session.get('role') not in ['admin', 'payroll', 'accounting']: + flash('Access denied. Only administrators, payroll, and accounting staff can review verification photos.', 'error') + return redirect(url_for('attendance_report')) + + # Get the attendance record + record = AttendanceData.query.get_or_404(record_id) + + # Check if this record has verification + if not record.verification_required: + flash('This record does not require verification.', 'warning') + return redirect(url_for('attendance_report')) + + # Get the QR code information for additional context + qr_code = QRCode.query.get(record.qr_code_id) if record.qr_code_id else None + + # Log the access for audit trail + logger_handler.logger.info( + f"User {session.get('username')} ({session.get('role')}) " + f"accessed verification review for record {record_id}" + ) + + # Format date and time for display + try: + check_in_date = record.check_in_date.strftime('%m/%d/%Y') if record.check_in_date else 'N/A' + except: + check_in_date = str(record.check_in_date) if record.check_in_date else 'N/A' + + try: + check_in_time = record.check_in_time.strftime('%I:%M %p') if record.check_in_time else 'N/A' + except: + check_in_time = str(record.check_in_time) if record.check_in_time else 'N/A' + + return render_template('verification_review_detail.html', + record=record, + qr_code=qr_code, + check_in_date=check_in_date, + check_in_time=check_in_time) + + except Exception as e: + logger_handler.logger.error(f"Error loading verification review detail: {e}") + flash('Error loading verification details.', 'error') + return redirect(url_for('attendance_report')) + @app.route('/api/attendance/stats') @admin_required def attendance_stats_api(): diff --git a/static/js/attendance_report.js b/static/js/attendance_report.js index 81f1a76..e68fd32 100644 --- a/static/js/attendance_report.js +++ b/static/js/attendance_report.js @@ -639,15 +639,15 @@ function createTableRow(record, displayIndex) { let locationAccuracyBadge; if (record.verification_required && record.verification_status === 'pending') { - // Show Review Needed badge for pending verification - locationAccuracyBadge = ` Review Needed (${record.location_accuracy ? record.location_accuracy.toFixed(3) : 'N/A'} mi) - `; + `; } else if (record.verification_status === 'approved') { // Show Verified badge for approved verification locationAccuracyBadge = ` ${ record.verification_required && record.verification_status === 'pending' - ? `` + ` : '' } ${ @@ -1176,237 +1176,4 @@ document.addEventListener("DOMContentLoaded", function () { } console.log("Enhanced export functionality initialized"); -}); - -// ============================================ -// VERIFICATION PHOTO REVIEW FUNCTIONS -// ============================================ - -/** - * Open verification photo modal for review - * @param {string} recordId - The attendance record ID - */ -function openVerificationPhotoModal(recordId) { - console.log(`Opening verification photo modal for record: ${recordId}`); - - const modal = document.getElementById("verificationPhotoModal"); - const modalBody = document.getElementById("verificationPhotoModalBody"); - - if (!modal || !modalBody) { - console.error("Verification photo modal elements not found"); - return; - } - - // Show loading state - modalBody.innerHTML = ` -
- -

Loading verification photo...

-
- `; - - modal.style.display = "block"; - - // Fetch record details with verification photo - fetch(`/api/attendance/${recordId}/verification-details`) - .then((response) => { - if (!response.ok) { - throw new Error("Failed to fetch verification details"); - } - return response.json(); - }) - .then((data) => { - if (!data.success) { - throw new Error(data.message || "Failed to load verification data"); - } - - renderVerificationPhotoModal(data.record); - }) - .catch((error) => { - console.error("Error loading verification photo:", error); - modalBody.innerHTML = ` -
- -

Error loading verification photo. Please try again.

- -
- `; - }); -} - -/** - * Render verification photo modal content - * @param {Object} record - The attendance record with verification data - */ -function renderVerificationPhotoModal(record) { - const modalBody = document.getElementById("verificationPhotoModalBody"); - - const content = ` -
- -
-

- - Employee: ${record.employee_id} -

-

- - ${record.check_in_date} at ${record.check_in_time} -

-
- - -
- ${ - record.verification_photo - ? `Verification Photo` - : `
- -

No verification photo available

-
` - } -
- - -
-
-

Location Name

-

${record.location_name}

-
- -
-

Distance from QR

-

${parseFloat(record.location_accuracy).toFixed(3)} miles

-
- -
-

Verification Status

-

- - - Pending Review - -

-
- -
-

Device

-

${record.device_info || "Unknown"}

-
-
- - -
-

Check-in Address

-

${record.checked_in_address || "No address recorded"}

-
- - -
- - -
-
- `; - - modalBody.innerHTML = content; -} - -/** - * Close verification photo modal - */ -function closeVerificationPhotoModal() { - const modal = document.getElementById("verificationPhotoModal"); - if (modal) { - modal.style.display = "none"; - } -} - -/** - * Update verification status (approve/reject) - * @param {number} recordId - The attendance record ID - * @param {string} status - The new status ('approved' or 'rejected') - */ -function updateVerificationStatus(recordId, status) { - if ( - !confirm( - `Are you sure you want to ${status} this verification?\n\nThis action will be logged for audit purposes.` - ) - ) { - return; - } - - console.log(`Updating verification status for record ${recordId} to ${status}`); - - // Show loading state - const modalBody = document.getElementById("verificationPhotoModalBody"); - modalBody.innerHTML = ` -
- -

Updating verification status...

-
- `; - - // Send update request - fetch(`/verification-review/${recordId}/update`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Requested-With": "XMLHttpRequest", - }, - body: JSON.stringify({ - status: status, - note: `Verification ${status} from attendance report review`, - }), - }) - .then((response) => response.json()) - .then((data) => { - if (data.success) { - alert( - `Verification ${status} successfully!\n\nThe page will reload to show the updated status.` - ); - closeVerificationPhotoModal(); - // Reload the page to show updated status - window.location.reload(); - } else { - throw new Error(data.message || "Failed to update verification status"); - } - }) - .catch((error) => { - console.error("Error updating verification status:", error); - alert(`Error: ${error.message}\n\nPlease try again.`); - // Reload modal to show previous state - openVerificationPhotoModal(recordId); - }); -} - -// Close verification modal when clicking outside -window.addEventListener("click", function (event) { - const verificationModal = document.getElementById("verificationPhotoModal"); - - if (event.target === verificationModal) { - closeVerificationPhotoModal(); - } -}); - -// Keyboard shortcut for closing verification modal -document.addEventListener("keydown", function (event) { - if (event.key === "Escape") { - closeVerificationPhotoModal(); - } }); \ No newline at end of file diff --git a/templates/attendance_report.html b/templates/attendance_report.html index 7ea3bba..45856dd 100644 --- a/templates/attendance_report.html +++ b/templates/attendance_report.html @@ -267,19 +267,19 @@ {% if record.verification_required and record.verification_status == 'pending' %} - + {% elif record.verification_status == 'approved' %} - +
@@ -289,7 +289,7 @@
{% elif record.verification_status == 'rejected' %} - +
@@ -339,12 +339,12 @@
{% if record.verification_required and record.verification_status == 'pending' %} - - + {% endif %} {% if session.role in ['admin', 'payroll', 'accounting'] %} @@ -399,7 +399,7 @@
- +