Updated location address accuracy
This commit is contained in:
@@ -663,10 +663,55 @@ def process_location_data(location_data):
|
|||||||
|
|
||||||
return processed
|
return processed
|
||||||
|
|
||||||
|
def reverse_geocode_coordinates(latitude, longitude):
|
||||||
|
"""
|
||||||
|
Convert GPS coordinates to human-readable address using reverse geocoding
|
||||||
|
Returns address string or None if failed
|
||||||
|
"""
|
||||||
|
if not latitude or not longitude:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(f"🌍 Reverse geocoding coordinates: {latitude}, {longitude}")
|
||||||
|
|
||||||
|
# Using Nominatim (OpenStreetMap) reverse geocoding service
|
||||||
|
url = "https://nominatim.openstreetmap.org/reverse"
|
||||||
|
params = {
|
||||||
|
'lat': latitude,
|
||||||
|
'lon': longitude,
|
||||||
|
'format': 'json',
|
||||||
|
'addressdetails': 1,
|
||||||
|
'zoom': 18 # High detail level
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'User-Agent': 'QR-Attendance-System/1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get(url, params=params, headers=headers, timeout=10)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if data and 'display_name' in data:
|
||||||
|
address = data['display_name']
|
||||||
|
print(f"✅ Reverse geocoded address: {address}")
|
||||||
|
return address
|
||||||
|
else:
|
||||||
|
print(f"⚠️ No address found for coordinates")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
print(f"⚠️ Reverse geocoding API returned status: {response.status_code}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error in reverse geocoding: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
def process_location_data_enhanced(form_data):
|
def process_location_data_enhanced(form_data):
|
||||||
"""
|
"""
|
||||||
Enhanced processing of location data from form submission
|
Enhanced processing of location data from form submission
|
||||||
Validates and cleans location data for storage
|
Validates and cleans location data for storage, including reverse geocoding
|
||||||
"""
|
"""
|
||||||
processed = {
|
processed = {
|
||||||
'latitude': None,
|
'latitude': None,
|
||||||
@@ -707,13 +752,33 @@ def process_location_data_enhanced(form_data):
|
|||||||
alt = float(form_data['altitude'])
|
alt = float(form_data['altitude'])
|
||||||
processed['altitude'] = alt
|
processed['altitude'] = alt
|
||||||
|
|
||||||
# Process address (limit length for database storage)
|
# Process address - First check if address was provided
|
||||||
if form_data.get('address'):
|
if form_data.get('address'):
|
||||||
address = form_data['address'].strip()
|
address = form_data['address'].strip()
|
||||||
if address and address not in ['null', '', 'undefined']:
|
if address and address not in ['null', '', 'undefined']:
|
||||||
|
# Check if the address is just coordinates (like "38.8104192000, -77.1850240000")
|
||||||
|
if re.match(r'^-?\d+\.\d+,?\s*-?\d+\.\d+$', address.replace(' ', '')):
|
||||||
|
print(f"🔍 Detected coordinate-format address: {address}")
|
||||||
|
# This is just coordinates, we need to reverse geocode
|
||||||
|
processed['address'] = None # Reset so reverse geocoding will trigger
|
||||||
|
else:
|
||||||
|
# This is a real address
|
||||||
processed['address'] = address[:500] # Limit to 500 characters
|
processed['address'] = address[:500] # Limit to 500 characters
|
||||||
|
print(f"✅ Using provided address: {processed['address'][:100]}...")
|
||||||
|
|
||||||
print(f"📍 Processed location data:")
|
# CRITICAL: If we have coordinates but no real address, perform reverse geocoding
|
||||||
|
if (processed['latitude'] is not None and processed['longitude'] is not None
|
||||||
|
and not processed['address']):
|
||||||
|
print(f"🌍 Performing reverse geocoding for coordinates: {processed['latitude']}, {processed['longitude']}")
|
||||||
|
reverse_geocoded_address = reverse_geocode_coordinates(processed['latitude'], processed['longitude'])
|
||||||
|
if reverse_geocoded_address:
|
||||||
|
processed['address'] = reverse_geocoded_address[:500]
|
||||||
|
print(f"✅ Reverse geocoded address: {processed['address']}")
|
||||||
|
else:
|
||||||
|
print(f"⚠️ Could not reverse geocode coordinates, keeping coordinates as fallback")
|
||||||
|
processed['address'] = f"{processed['latitude']:.10f}, {processed['longitude']:.10f}"
|
||||||
|
|
||||||
|
print(f"📍 Final processed location data:")
|
||||||
print(f" Coordinates: {processed['latitude']}, {processed['longitude']}")
|
print(f" Coordinates: {processed['latitude']}, {processed['longitude']}")
|
||||||
print(f" GPS Accuracy: {processed['accuracy']}m")
|
print(f" GPS Accuracy: {processed['accuracy']}m")
|
||||||
print(f" Source: {processed['source']}")
|
print(f" Source: {processed['source']}")
|
||||||
@@ -1932,6 +1997,7 @@ def qr_checkin(qr_url):
|
|||||||
"""
|
"""
|
||||||
Enhanced staff check-in with location accuracy calculation
|
Enhanced staff check-in with location accuracy calculation
|
||||||
Allows multiple check-ins with minimum 30-minute intervals between them
|
Allows multiple check-ins with minimum 30-minute intervals between them
|
||||||
|
PRESERVES coordinate-to-address conversion functionality
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
print(f"\n🚀 STARTING ENHANCED CHECK-IN PROCESS")
|
print(f"\n🚀 STARTING ENHANCED CHECK-IN PROCESS")
|
||||||
@@ -1980,23 +2046,23 @@ def qr_checkin(qr_url):
|
|||||||
# Check if 30 minutes have passed since the last check-in
|
# Check if 30 minutes have passed since the last check-in
|
||||||
if recent_checkin_datetime > thirty_minutes_ago:
|
if recent_checkin_datetime > thirty_minutes_ago:
|
||||||
minutes_remaining = 30 - int((current_time - recent_checkin_datetime).total_seconds() / 60)
|
minutes_remaining = 30 - int((current_time - recent_checkin_datetime).total_seconds() / 60)
|
||||||
print(f"⚠️ Too soon for another submission for {employee_id}")
|
print(f"⚠️ Too soon for another check-in for {employee_id}")
|
||||||
print(f" Last submission: {recent_checkin.check_in_time.strftime('%H:%M')}")
|
print(f" Last check-in: {recent_checkin.check_in_time.strftime('%H:%M')}")
|
||||||
print(f" Minutes remaining: {minutes_remaining}")
|
print(f" Minutes remaining: {minutes_remaining}")
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': f'You can submit again in {minutes_remaining} minutes. Last submission was at {recent_checkin.check_in_time.strftime("%H:%M")}.'
|
'message': f'You can check in again in {minutes_remaining} minutes. Last check-in was at {recent_checkin.check_in_time.strftime("%H:%M")}.'
|
||||||
}), 400
|
}), 400
|
||||||
else:
|
else:
|
||||||
print(f"✅ 30-minute interval satisfied. Allowing new check-in for {employee_id}")
|
print(f"✅ 30-minute interval satisfied. Allowing new check-in for {employee_id}")
|
||||||
else:
|
else:
|
||||||
print(f"✅ First submission today for {employee_id}")
|
print(f"✅ First check-in today for {employee_id}")
|
||||||
|
|
||||||
# Process location data
|
# PRESERVED: Process location data with coordinate-to-address conversion
|
||||||
location_data = process_location_data_enhanced(request.form)
|
location_data = process_location_data_enhanced(request.form)
|
||||||
|
|
||||||
# Get device and network info
|
# PRESERVED: Get device and network info
|
||||||
user_agent_string = request.headers.get('User-Agent', '')
|
user_agent_string = request.headers.get('User-Agent', '')
|
||||||
device_info = detect_device_info(user_agent_string)
|
device_info = detect_device_info(user_agent_string)
|
||||||
client_ip = get_client_ip()
|
client_ip = get_client_ip()
|
||||||
@@ -2005,7 +2071,7 @@ def qr_checkin(qr_url):
|
|||||||
print(f"🌐 IP Address: {client_ip}")
|
print(f"🌐 IP Address: {client_ip}")
|
||||||
print(f"📍 Location Data: {location_data}")
|
print(f"📍 Location Data: {location_data}")
|
||||||
|
|
||||||
# Create attendance record
|
# PRESERVED: Create attendance record
|
||||||
print(f"\n💾 CREATING ATTENDANCE RECORD:")
|
print(f"\n💾 CREATING ATTENDANCE RECORD:")
|
||||||
|
|
||||||
attendance = AttendanceData(
|
attendance = AttendanceData(
|
||||||
@@ -2022,13 +2088,13 @@ def qr_checkin(qr_url):
|
|||||||
accuracy=location_data['accuracy'],
|
accuracy=location_data['accuracy'],
|
||||||
altitude=location_data['altitude'],
|
altitude=location_data['altitude'],
|
||||||
location_source=location_data['source'],
|
location_source=location_data['source'],
|
||||||
address=location_data['address'],
|
address=location_data['address'], # This now includes converted address
|
||||||
status='present'
|
status='present'
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"✅ Created base attendance record")
|
print(f"✅ Created base attendance record")
|
||||||
|
|
||||||
# Calculate location accuracy
|
# PRESERVED: Calculate location accuracy
|
||||||
print(f"\n🎯 CALCULATING LOCATION ACCURACY...")
|
print(f"\n🎯 CALCULATING LOCATION ACCURACY...")
|
||||||
location_accuracy = None
|
location_accuracy = None
|
||||||
|
|
||||||
@@ -2050,13 +2116,13 @@ def qr_checkin(qr_url):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Error in location accuracy calculation: {e}")
|
print(f"❌ Error in location accuracy calculation: {e}")
|
||||||
|
|
||||||
# Save to database
|
# PRESERVED: Save to database
|
||||||
try:
|
try:
|
||||||
db.session.add(attendance)
|
db.session.add(attendance)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
print(f"✅ Successfully saved attendance record with ID: {attendance.id}")
|
print(f"✅ Successfully saved attendance record with ID: {attendance.id}")
|
||||||
|
|
||||||
# Count total check-ins for today for this employee at this location
|
# NEW: Count total check-ins for today for this employee at this location
|
||||||
today_checkin_count = AttendanceData.query.filter_by(
|
today_checkin_count = AttendanceData.query.filter_by(
|
||||||
qr_code_id=qr_code.id,
|
qr_code_id=qr_code.id,
|
||||||
employee_id=employee_id.upper(),
|
employee_id=employee_id.upper(),
|
||||||
@@ -2073,7 +2139,7 @@ def qr_checkin(qr_url):
|
|||||||
'message': 'Database error occurred.'
|
'message': 'Database error occurred.'
|
||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
# Return success response
|
# ENHANCED: Return success response with sequence information
|
||||||
response_data = {
|
response_data = {
|
||||||
'success': True,
|
'success': True,
|
||||||
'message': f'Check-in successful! {checkin_sequence_text} for today.',
|
'message': f'Check-in successful! {checkin_sequence_text} for today.',
|
||||||
@@ -2101,6 +2167,7 @@ def qr_checkin(qr_url):
|
|||||||
print(f" Employee: {attendance.employee_id}")
|
print(f" Employee: {attendance.employee_id}")
|
||||||
print(f" Time: {attendance.check_in_time}")
|
print(f" Time: {attendance.check_in_time}")
|
||||||
print(f" Location: {attendance.location_name}")
|
print(f" Location: {attendance.location_name}")
|
||||||
|
print(f" Address: {attendance.address}")
|
||||||
print(f" Today's count: {today_checkin_count}")
|
print(f" Today's count: {today_checkin_count}")
|
||||||
|
|
||||||
return jsonify(response_data), 200
|
return jsonify(response_data), 200
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const translations = {
|
|||||||
error: "Error en el registro. Por favor intente de nuevo.",
|
error: "Error en el registro. Por favor intente de nuevo.",
|
||||||
duplicate: "Ya se ha registrado hoy.",
|
duplicate: "Ya se ha registrado hoy.",
|
||||||
tooSoon: "Por favor espere antes de registrarse nuevamente.",
|
tooSoon: "Por favor espere antes de registrarse nuevamente.",
|
||||||
multipleSuccess: "Submitted successful!!",
|
multipleSuccess: "Submitted successfully!!",
|
||||||
invalidId: "Por favor ingrese un ID de empleado válido.",
|
invalidId: "Por favor ingrese un ID de empleado válido.",
|
||||||
locationError: "No se pudo obtener datos de ubicación.",
|
locationError: "No se pudo obtener datos de ubicación.",
|
||||||
networkError: "Error de red. Verifique su conexión.",
|
networkError: "Error de red. Verifique su conexión.",
|
||||||
@@ -225,7 +225,7 @@ function handleCheckinResponse(data) {
|
|||||||
|
|
||||||
// ENHANCED SUCCESS HANDLING WITH MULTIPLE CHECK-IN INFO
|
// ENHANCED SUCCESS HANDLING WITH MULTIPLE CHECK-IN INFO
|
||||||
function handleCheckinSuccess(data) {
|
function handleCheckinSuccess(data) {
|
||||||
console.log("✅ Submitted successful!");
|
console.log("✅ Submitted successfully!");
|
||||||
|
|
||||||
const responseData = data.data || data || {};
|
const responseData = data.data || data || {};
|
||||||
const checkinCount = responseData.checkin_count_today || 1;
|
const checkinCount = responseData.checkin_count_today || 1;
|
||||||
@@ -515,9 +515,32 @@ function handleLocationError(error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function reverseGeocode(lat, lng) {
|
function reverseGeocode(lat, lng) {
|
||||||
// This would typically use a geocoding service
|
console.log(`🌍 Starting reverse geocoding for: ${lat}, ${lng}`);
|
||||||
// For now, just set a placeholder
|
|
||||||
|
// Using Nominatim (OpenStreetMap) reverse geocoding service
|
||||||
|
const url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=json&addressdetails=1&zoom=18`;
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"User-Agent": "QR-Attendance-System/1.0",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data && data.display_name) {
|
||||||
|
userLocation.address = data.display_name;
|
||||||
|
console.log(`✅ Reverse geocoded address: ${userLocation.address}`);
|
||||||
|
} else {
|
||||||
|
console.log(`⚠️ No address found, using coordinates as fallback`);
|
||||||
userLocation.address = `${lat.toFixed(10)}, ${lng.toFixed(10)}`;
|
userLocation.address = `${lat.toFixed(10)}, ${lng.toFixed(10)}`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(`❌ Reverse geocoding error:`, error);
|
||||||
|
// Fallback to coordinates if reverse geocoding fails
|
||||||
|
userLocation.address = `${lat.toFixed(10)}, ${lng.toFixed(10)}`;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Language functionality remains unchanged
|
// Language functionality remains unchanged
|
||||||
|
|||||||
+123
-40
@@ -1,10 +1,13 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>{{ qr_code.location_event }} - Check In</title>
|
<title>{{ qr_code.location_event }} - Check In</title>
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
<link
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
<style>
|
<style>
|
||||||
/* PRESERVED: All existing styles remain unchanged */
|
/* PRESERVED: All existing styles remain unchanged */
|
||||||
:root {
|
:root {
|
||||||
@@ -30,7 +33,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
@@ -93,7 +96,11 @@
|
|||||||
.header-icon {
|
.header-icon {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--primary-color),
|
||||||
|
var(--primary-hover)
|
||||||
|
);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -333,9 +340,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes successPulse {
|
@keyframes successPulse {
|
||||||
0% { transform: scale(0.8); opacity: 0; }
|
0% {
|
||||||
50% { transform: scale(1.1); }
|
transform: scale(0.8);
|
||||||
100% { transform: scale(1); opacity: 1; }
|
opacity: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-title {
|
.success-title {
|
||||||
@@ -380,7 +395,11 @@
|
|||||||
|
|
||||||
/* NEW: Enhanced styles for multiple check-in information */
|
/* NEW: Enhanced styles for multiple check-in information */
|
||||||
.checkin-sequence {
|
.checkin-sequence {
|
||||||
background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--primary-color),
|
||||||
|
var(--primary-hover)
|
||||||
|
);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
@@ -465,12 +484,17 @@
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="destination-container">
|
<div class="destination-container">
|
||||||
<!-- Language Selector -->
|
<!-- Language Selector -->
|
||||||
<div class="language-selector">
|
<div class="language-selector">
|
||||||
<button id="languageToggle" class="language-toggle" type="button" aria-label="Switch Language">
|
<button
|
||||||
|
id="languageToggle"
|
||||||
|
class="language-toggle"
|
||||||
|
type="button"
|
||||||
|
aria-label="Switch Language"
|
||||||
|
>
|
||||||
<i class="fas fa-globe"></i>
|
<i class="fas fa-globe"></i>
|
||||||
<span id="languageText">EN</span>
|
<span id="languageText">EN</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -501,26 +525,43 @@
|
|||||||
<div class="checkin-header">
|
<div class="checkin-header">
|
||||||
<h2>
|
<h2>
|
||||||
<i class="fas fa-user-check"></i>
|
<i class="fas fa-user-check"></i>
|
||||||
<span data-en="Attendance Tracking" data-es="Seguimiento de Asistencia">Attendance Tracking</span>
|
<span
|
||||||
|
data-en="Attendance Tracking"
|
||||||
|
data-es="Seguimiento de Asistencia"
|
||||||
|
>Attendance Tracking</span
|
||||||
|
>
|
||||||
</h2>
|
</h2>
|
||||||
<p data-en="Please enter your Employee ID to check in/out" data-es="Por favor ingrese su ID de empleado para registrar su entrada/salida">Please enter your Employee ID to check in/out</p>
|
<p
|
||||||
|
data-en="Please enter your Employee ID to check in/out"
|
||||||
|
data-es="Por favor ingrese su ID de empleado para registrar su entrada/salida"
|
||||||
|
>
|
||||||
|
Please enter your Employee ID to check in/out
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="checkinForm" class="checkin-form">
|
<form id="checkinForm" class="checkin-form">
|
||||||
<!-- HIDDEN LOCATION FIELDS -->
|
<!-- HIDDEN LOCATION FIELDS -->
|
||||||
<input type="hidden" id="latitude" name="latitude">
|
<input type="hidden" id="latitude" name="latitude" />
|
||||||
<input type="hidden" id="longitude" name="longitude">
|
<input type="hidden" id="longitude" name="longitude" />
|
||||||
<input type="hidden" id="accuracy" name="accuracy">
|
<input type="hidden" id="accuracy" name="accuracy" />
|
||||||
<input type="hidden" id="altitude" name="altitude">
|
<input type="hidden" id="altitude" name="altitude" />
|
||||||
<input type="hidden" id="locationSource" name="location_source" value="manual">
|
<input
|
||||||
<input type="hidden" id="address" name="address">
|
type="hidden"
|
||||||
|
id="locationSource"
|
||||||
|
name="location_source"
|
||||||
|
value="manual"
|
||||||
|
/>
|
||||||
|
<input type="hidden" id="address" name="address" />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="employee_id">
|
<label for="employee_id">
|
||||||
<i class="fas fa-id-badge"></i>
|
<i class="fas fa-id-badge"></i>
|
||||||
<span data-en="Employee ID" data-es="ID de Empleado">Employee ID</span>
|
<span data-en="Employee ID" data-es="ID de Empleado"
|
||||||
|
>Employee ID</span
|
||||||
|
>
|
||||||
</label>
|
</label>
|
||||||
<input type="text"
|
<input
|
||||||
|
type="text"
|
||||||
id="employee_id"
|
id="employee_id"
|
||||||
name="employee_id"
|
name="employee_id"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
@@ -528,7 +569,8 @@
|
|||||||
data-es-placeholder="Ingrese su ID de Empleado"
|
data-es-placeholder="Ingrese su ID de Empleado"
|
||||||
placeholder="Enter your Employee ID"
|
placeholder="Enter your Employee ID"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
required>
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" id="submitCheckin" class="btn btn-primary">
|
<button type="submit" id="submitCheckin" class="btn btn-primary">
|
||||||
@@ -545,10 +587,18 @@
|
|||||||
<i class="fas fa-check"></i>
|
<i class="fas fa-check"></i>
|
||||||
</div>
|
</div>
|
||||||
<h2 class="success-title">
|
<h2 class="success-title">
|
||||||
<span data-en="Submitted Successful!" data-es="Enviado exitosamente!">Submitted Successful!</span>
|
<span
|
||||||
|
data-en="Submitted Successfully!"
|
||||||
|
data-es="Enviado exitosamente!"
|
||||||
|
>Submitted Successfully!</span
|
||||||
|
>
|
||||||
</h2>
|
</h2>
|
||||||
<p class="success-subtitle">
|
<p class="success-subtitle">
|
||||||
<span data-en="Your attendance has been recorded" data-es="Su asistencia ha sido registrada">Your attendance has been recorded</span>
|
<span
|
||||||
|
data-en="Your attendance has been recorded"
|
||||||
|
data-es="Su asistencia ha sido registrada"
|
||||||
|
>Your attendance has been recorded</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -559,39 +609,72 @@
|
|||||||
|
|
||||||
<div class="success-details">
|
<div class="success-details">
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="detail-label" data-en="Employee ID" data-es="ID Empleado">Employee ID</span>
|
<span
|
||||||
|
class="detail-label"
|
||||||
|
data-en="Employee ID"
|
||||||
|
data-es="ID Empleado"
|
||||||
|
>Employee ID</span
|
||||||
|
>
|
||||||
<span class="detail-value" id="successEmployeeId">-</span>
|
<span class="detail-value" id="successEmployeeId">-</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="detail-label" data-en="Location/Building" data-es="Ubicación/Edificio">Location/Building</span>
|
<span
|
||||||
|
class="detail-label"
|
||||||
|
data-en="Location/Building"
|
||||||
|
data-es="Ubicación/Edificio"
|
||||||
|
>Location/Building</span
|
||||||
|
>
|
||||||
<span class="detail-value" id="successLocation">-</span>
|
<span class="detail-value" id="successLocation">-</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="detail-label" data-en="Event" data-es="Evento">Event</span>
|
<span class="detail-label" data-en="Event" data-es="Evento"
|
||||||
|
>Event</span
|
||||||
|
>
|
||||||
<span class="detail-value" id="successEvent">-</span>
|
<span class="detail-value" id="successEvent">-</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="detail-label" data-en="Submission Time" data-es="Hora de presentación">Submission Time</span>
|
<span
|
||||||
|
class="detail-label"
|
||||||
|
data-en="Submission Time"
|
||||||
|
data-es="Hora de presentación"
|
||||||
|
>Submission Time</span
|
||||||
|
>
|
||||||
<span class="detail-value" id="successCheckInTime">-</span>
|
<span class="detail-value" id="successCheckInTime">-</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="detail-label" data-en="Date" data-es="Fecha">Date</span>
|
<span class="detail-label" data-en="Date" data-es="Fecha"
|
||||||
|
>Date</span
|
||||||
|
>
|
||||||
<span class="detail-value" id="successCheckInDate">-</span>
|
<span class="detail-value" id="successCheckInDate">-</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row" style="display: none;" id="deviceInfoRow">
|
<div class="detail-row" style="display: none" id="deviceInfoRow">
|
||||||
<span class="detail-label" data-en="Device" data-es="Dispositivo">Device</span>
|
<span class="detail-label" data-en="Device" data-es="Dispositivo"
|
||||||
|
>Device</span
|
||||||
|
>
|
||||||
<span class="detail-value" id="successDeviceInfo">-</span>
|
<span class="detail-value" id="successDeviceInfo">-</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row" style="display: none;" id="coordinatesRow">
|
<div class="detail-row" style="display: none" id="coordinatesRow">
|
||||||
<span class="detail-label" data-en="Coordinates" data-es="Coordenadas">Coordinates</span>
|
<span
|
||||||
|
class="detail-label"
|
||||||
|
data-en="Coordinates"
|
||||||
|
data-es="Coordenadas"
|
||||||
|
>Coordinates</span
|
||||||
|
>
|
||||||
<span class="detail-value" id="successCoordinates">-</span>
|
<span class="detail-value" id="successCoordinates">-</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row" style="display: none;" id="addressRow">
|
<div class="detail-row" style="display: none" id="addressRow">
|
||||||
<span class="detail-label" data-en="Address" data-es="Dirección">Address</span>
|
<span class="detail-label" data-en="Address" data-es="Dirección"
|
||||||
|
>Address</span
|
||||||
|
>
|
||||||
<span class="detail-value" id="successAddress">-</span>
|
<span class="detail-value" id="successAddress">-</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row" style="display: none;" id="accuracyRow">
|
<div class="detail-row" style="display: none" id="accuracyRow">
|
||||||
<span class="detail-label" data-en="Location Accuracy" data-es="Precisión de Ubicación">Location Accuracy</span>
|
<span
|
||||||
|
class="detail-label"
|
||||||
|
data-en="Location Accuracy"
|
||||||
|
data-es="Precisión de Ubicación"
|
||||||
|
>Location Accuracy</span
|
||||||
|
>
|
||||||
<span class="detail-value" id="successLocationAccuracy">-</span>
|
<span class="detail-value" id="successLocationAccuracy">-</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -602,5 +685,5 @@
|
|||||||
|
|
||||||
<!-- JavaScript -->
|
<!-- JavaScript -->
|
||||||
<script src="{{ url_for('static', filename='js/qr_destination.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/qr_destination.js') }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user