Implement photo verification for violation check-in/out

This commit is contained in:
2025-12-15 13:55:46 -05:00
parent 3af4d2d9f6
commit cfd8cfb57f
6 changed files with 1385 additions and 5 deletions
+349 -1
View File
@@ -385,6 +385,146 @@
transform: none !important;
}
/* Camera Verification Styles */
.verification-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
z-index: 10000;
justify-content: center;
align-items: center;
}
.verification-modal.active {
display: flex;
}
.verification-content {
background: white;
border-radius: var(--border-radius);
padding: 2rem;
max-width: 600px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
}
.verification-header {
text-align: center;
margin-bottom: 1.5rem;
}
.verification-header h2 {
color: var(--text-primary);
margin-bottom: 0.5rem;
}
.verification-header .distance-warning {
color: #dc2626;
font-weight: 600;
margin-top: 0.5rem;
}
.camera-preview {
position: relative;
width: 100%;
background: #000;
border-radius: var(--border-radius);
overflow: hidden;
margin: 1rem 0;
}
.camera-preview video {
width: 100%;
height: auto;
display: block;
}
.camera-preview canvas {
display: none;
}
.camera-preview img {
width: 100%;
height: auto;
display: block;
}
.camera-controls {
display: flex;
gap: 1rem;
justify-content: center;
margin-top: 1rem;
}
.camera-btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: var(--border-radius);
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.camera-btn.capture {
background: var(--primary-color);
color: white;
}
.camera-btn.capture:hover {
background: var(--primary-dark);
}
.camera-btn.retake {
background: #f59e0b;
color: white;
}
.camera-btn.retake:hover {
background: #d97706;
}
.camera-btn.submit-verification {
background: #10b981;
color: white;
}
.camera-btn.submit-verification:hover {
background: #059669;
}
.camera-btn.cancel {
background: #6b7280;
color: white;
}
.camera-btn.cancel:hover {
background: #4b5563;
}
.camera-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.verification-instructions {
background: #fef3c7;
border-left: 4px solid #f59e0b;
padding: 1rem;
margin-bottom: 1rem;
border-radius: 4px;
}
.verification-instructions p {
margin: 0.5rem 0;
color: #78350f;
}
.success-card {
background: rgba(255, 255, 255, 0.95);
border-radius: var(--border-radius);
@@ -583,6 +723,159 @@
background: #15803d;
}
/* Camera Verification Styles */
.verification-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
z-index: 10000;
justify-content: center;
align-items: center;
}
.verification-modal.active {
display: flex;
}
.verification-content {
background: white;
border-radius: var(--border-radius);
padding: 2rem;
max-width: 600px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
}
.verification-header h2 {
color: var(--text-primary);
margin-bottom: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.verification-header .distance-warning {
color: #dc2626;
font-weight: 600;
margin-top: 0.5rem;
font-size: 1rem;
}
.camera-preview {
position: relative;
width: 100%;
background: #000;
border-radius: var(--border-radius);
overflow: hidden;
margin: 1rem 0;
min-height: 300px;
display: flex;
align-items: center;
justify-content: center;
}
.camera-preview video {
width: 100%;
height: auto;
display: block;
}
.camera-preview canvas {
display: none;
}
.camera-preview img {
width: 100%;
height: auto;
display: block;
}
.camera-controls {
display: flex;
gap: 1rem;
justify-content: center;
margin-top: 1rem;
flex-wrap: wrap;
}
.camera-btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: var(--border-radius);
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 0.5rem;
}
.camera-btn.capture {
background: var(--primary-color);
color: white;
}
.camera-btn.capture:hover {
background: var(--primary-hover);
}
.camera-btn.retake {
background: #f59e0b;
color: white;
}
.camera-btn.retake:hover {
background: #d97706;
}
.camera-btn.submit-verification {
background: #10b981;
color: white;
}
.camera-btn.submit-verification:hover {
background: #059669;
}
.camera-btn.cancel {
background: #6b7280;
color: white;
}
.camera-btn.cancel:hover {
background: #4b5563;
}
.camera-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.verification-instructions {
background: #fef3c7;
border-left: 4px solid #f59e0b;
padding: 1rem;
margin-bottom: 1rem;
border-radius: 4px;
}
.verification-instructions p {
margin: 0.5rem 0;
color: #78350f;
font-size: 0.95rem;
}
.verification-instructions strong {
color: #92400e;
}
/* Responsive Design */
@media (max-width: 640px) {
.destination-container {
@@ -918,7 +1211,54 @@
</div>
</div>
</div>
<!-- Camera Verification Modal -->
<div id="verificationModal" class="verification-modal">
<div class="verification-content">
<div class="verification-header">
<h2>
<i class="fas fa-camera"></i>
Photo Verification Required
</h2>
<p class="verification-instructions">
<i class="fas fa-exclamation-triangle"></i>
<strong>You are checking in from a location that is too far from the designated area.</strong>
</p>
<p class="distance-warning">
Distance: <span id="verificationDistance">--</span> miles
(Threshold: <span id="verificationThreshold">0.3</span> miles)
</p>
</div>
<div class="verification-instructions">
<p><strong>Please take a photo of your current location:</strong></p>
<p>• Make sure the photo clearly shows your surroundings</p>
<p>• The photo will be reviewed by management</p>
<p>• You can retake the photo if needed</p>
</div>
<div class="camera-preview" id="cameraPreview">
<video id="cameraVideo" autoplay playsinline></video>
<canvas id="cameraCanvas"></canvas>
<img id="capturedPhoto" style="display: none;" />
</div>
<div class="camera-controls">
<button type="button" class="camera-btn capture" id="captureBtn">
<i class="fas fa-camera"></i> Take Photo
</button>
<button type="button" class="camera-btn retake" id="retakeBtn" style="display: none;">
<i class="fas fa-redo"></i> Retake
</button>
<button type="button" class="camera-btn submit-verification" id="submitVerificationBtn" style="display: none;">
<i class="fas fa-check"></i> Submit with Photo
</button>
<button type="button" class="camera-btn cancel" id="cancelVerificationBtn">
<i class="fas fa-times"></i> Cancel
</button>
</div>
</div>
</div>
</div>
<!-- Enhanced JavaScript with preserved functionality -->
<script src="{{ url_for('static', filename='js/qr_destination.js') }}"></script>
<script src="{{ url_for('static', filename='js/android_location_handler.js') }}"></script>
@@ -1206,6 +1546,14 @@
"success"
);
showEnhancedSuccessCard(data);
} else if (data.requires_verification) {
// Photo verification required
console.log('⚠️ Photo verification required');
verificationAttemptData = formData; // Store for retry with photo
showVerificationModal(
data.distance || 0,
data.threshold || 0.3
);
} else {
showStatusMessage(
data.message || "Submission failed / Envío fallido",
@@ -1346,4 +1694,4 @@
}
</script>
</body>
</html>
</html>