fix office location detection radius and GPS accuracy

This commit is contained in:
2026-09-23 13:07:21 +03:30
parent 9e6407d648
commit 694f41968a
2 changed files with 141 additions and 51 deletions
+16 -9
View File
@@ -251,6 +251,8 @@ SVG;
const referenceLat = 32.6504751;
const referenceLng = 51.6936045;
const OFFICE_RADIUS_METERS = 100;
const MAX_ACCURACY_CREDIT_METERS = 80;
const submitBtn = document.getElementById("submitBtn");
const latInput = document.getElementById("latitude");
@@ -278,26 +280,31 @@ SVG;
return earthRadius * c;
}
function isInsideOffice(distanceMeters, accuracyMeters) {
const accuracyCredit = Math.min(Math.max(0, accuracyMeters || 0), MAX_ACCURACY_CREDIT_METERS);
return Math.max(0, distanceMeters - accuracyCredit) <= OFFICE_RADIUS_METERS;
}
navigator.geolocation.getCurrentPosition(
position => {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
const accuracy = position.coords.accuracy || 0;
latInput.value = lat;
lngInput.value = lng;
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
distanceInput.value = dist;
if (dist < 20) {
distanceMessage.textContent = `مکان: شرکت`;
} else {
distanceMessage.textContent = `مکان: خارج از شرکت`;
}
const inside = isInsideOffice(dist, accuracy);
distanceMessage.textContent = inside
? `مکان: شرکت (~${dist}م)`
: `مکان: خارج از شرکت (~${dist}م)`;
distanceMessage.classList.remove("bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500", "cursor-not-allowed");
if (dist < 10) {
distanceMessage.classList.remove("bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500", "cursor-not-allowed", "text-green-500", "text-blue-500", "text-orange-500", "text-gray-400");
if (inside) {
distanceMessage.classList.add("text-green-500");
} else if (dist < 50) {
} else if (dist < OFFICE_RADIUS_METERS * 2) {
distanceMessage.classList.add("text-blue-500");
} else if (dist > 2000) {
distanceMessage.classList.add("text-orange-500");
@@ -311,7 +318,7 @@ SVG;
},
error => alert("⛔ خطا در دریافت موقعیت مکانی: " + error.message), {
enableHighAccuracy: true,
timeout: 10000,
timeout: 20000,
maximumAge: 0
}
);
+125 -42
View File
@@ -311,12 +311,12 @@ if (!empty($user->tasks)) {
</td>
{{-- زمان ورود --}}
<td style="border-right: 1px solid #ddd; padding: 4px; background-color: {{$item->entered_at && $item->distance ? ($item->requested !== null ? '#fad1d1' :($item->distance < 50 ? '#d1fae5' : '#edae80')) : '' }};">
<td style="border-right: 1px solid #ddd; padding: 4px; background-color: {{$item->entered_at && $item->distance ? ($item->requested !== null ? '#fad1d1' :($item->distance < 100 ? '#d1fae5' : '#edae80')) : '' }};">
{{ $item->entered_at ? \Carbon\Carbon::parse($item->entered_at)->format('H:i') : '-' }}
</td>
{{-- زمان خروج --}}
<td style="border-right: 1px solid #ddd; padding: 4px; background-color: {{$item->exited_at && $item->distance_exit ? ($item->requested !== null ? '#fad1d1' :($item->distance_exit < 50 ? '#d1fae5' : '#edae80')) : '' }};">
<td style="border-right: 1px solid #ddd; padding: 4px; background-color: {{$item->exited_at && $item->distance_exit ? ($item->requested !== null ? '#fad1d1' :($item->distance_exit < 100 ? '#d1fae5' : '#edae80')) : '' }};">
{{ $item->exited_at ? \Carbon\Carbon::parse($item->exited_at)->format('H:i') : '-' }}
</td>
@@ -646,8 +646,12 @@ if (!empty($user->tasks)) {
const distanceMessage = document.getElementById("distanceMessage");
// مختصات دفتر / شرکت
const referenceLat = 32.6504751;
const referenceLng = 51.6936045;
// شعاع مجاز (متر) — GPS داخل ساختمان معمولاً ۲۰–۸۰ متر خطا دارد
const OFFICE_RADIUS_METERS = 100;
const MAX_ACCURACY_CREDIT_METERS = 80;
const submitBtn = document.getElementById("submitBtn");
const latInput = document.getElementById("latitude");
@@ -677,57 +681,136 @@ if (!empty($user->tasks)) {
return earthRadius * c;
}
navigator.geolocation.getCurrentPosition(
position => {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
function isInsideOffice(distanceMeters, accuracyMeters) {
const accuracyCredit = Math.min(Math.max(0, accuracyMeters || 0), MAX_ACCURACY_CREDIT_METERS);
const effectiveDistance = Math.max(0, distanceMeters - accuracyCredit);
return effectiveDistance <= OFFICE_RADIUS_METERS;
}
latInput.value = lat;
lngInput.value = lng;
function applyLocationResult(lat, lng, accuracyMeters) {
latInput.value = lat;
lngInput.value = lng;
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
distanceInput.value = dist;
if (dist < 20) {
distanceMessage.textContent = `مکان: شرکت`;
} else {
distanceMessage.textContent = `مکان: خارج از شرکت`;
}
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
distanceInput.value = dist;
const inside = isInsideOffice(dist, accuracyMeters);
const accuracyText = accuracyMeters
? ` · دقت ±${Math.round(accuracyMeters)}م`
: '';
distanceMessage.classList.remove("bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500", "cursor-not-allowed");
if (dist < 10) {
distanceMessage.classList.add("text-green-500");
} else if (dist < 50) {
distanceMessage.classList.add("text-blue-500");
} else if (dist > 2000) {
distanceMessage.classList.add("text-orange-500");
} else {
distanceMessage.classList.add("text-gray-400");
}
submitBtn.disabled = false;
submitBtn.classList.remove("bg-gray-400", "cursor-not-allowed");
submitBtn.classList.add("bg-cyan-500", "hover:bg-cyan-700", "cursor-pointer");
distanceMessage.textContent = inside
? `مکان: شرکت (~${dist}م)${accuracyText}`
: `مکان: خارج از شرکت (~${dist}م)${accuracyText}`;
},
error => {
const lat = 32.6520532791536; // از مختصات ثابت استفاده کنید
const lng = 51.68472614422439; // از مختصات ثابت استفاده کنید
distanceMessage.classList.remove(
"bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500",
"cursor-not-allowed", "text-green-500", "text-blue-500",
"text-orange-500", "text-gray-400"
);
if (inside) {
distanceMessage.classList.add("text-green-500");
} else if (dist <= OFFICE_RADIUS_METERS * 2) {
distanceMessage.classList.add("text-blue-500");
} else if (dist > 2000) {
distanceMessage.classList.add("text-orange-500");
} else {
distanceMessage.classList.add("text-gray-400");
}
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
distanceInput.value = dist;
distanceMessage.textContent = `مکان: خارج از شرکت`;
submitBtn.disabled = false;
submitBtn.classList.remove("bg-gray-400", "cursor-not-allowed");
submitBtn.classList.add("bg-cyan-500", "hover:bg-cyan-700", "cursor-pointer");
}
distanceMessage.classList.remove("bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500", "cursor-not-allowed");
function locateUser() {
if (!navigator.geolocation) {
distanceMessage.textContent = 'موقعیت‌یاب در دسترس نیست';
distanceMessage.classList.add("text-orange-500");
submitBtn.disabled = false;
submitBtn.classList.remove("bg-gray-400", "cursor-not-allowed");
submitBtn.classList.add("bg-cyan-500", "hover:bg-cyan-700", "cursor-pointer");
// alert("⛔ خطا در دریافت موقعیت مکانی: " + error.message), {
// enableHighAccuracy: true,
// timeout: 10000,
// maximumAge: 0
return;
}
);
distanceMessage.textContent = 'در حال بررسی موقعیت...';
distanceMessage.classList.remove("text-green-500", "text-blue-500", "text-orange-500", "text-gray-400");
const geoOptions = {
enableHighAccuracy: true,
timeout: 20000,
maximumAge: 0,
};
let bestReading = null;
let watchId = null;
let finished = false;
const finishWithBest = () => {
if (finished) return;
finished = true;
if (watchId !== null) {
navigator.geolocation.clearWatch(watchId);
}
if (bestReading) {
applyLocationResult(
bestReading.lat,
bestReading.lng,
bestReading.accuracy
);
}
};
const considerReading = (position) => {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
const accuracy = position.coords.accuracy || 9999;
const dist = getDistanceInMeters(referenceLat, referenceLng, lat, lng);
const score = dist + Math.min(accuracy, 150);
if (!bestReading || score < bestReading.score) {
bestReading = { lat, lng, accuracy, dist, score };
}
// اگر با دقت خوب داخل محدوده شدیم، زودتر تمام کن
if (isInsideOffice(dist, accuracy) && accuracy <= 60) {
finishWithBest();
}
};
navigator.geolocation.getCurrentPosition(
(position) => {
considerReading(position);
// چند ثانیه watch برای خواندن دقیق‌تر داخل ساختمان
watchId = navigator.geolocation.watchPosition(
considerReading,
() => finishWithBest(),
geoOptions
);
setTimeout(finishWithBest, 5000);
},
() => {
// تلاش دوم بدون الزام دقت بالا
navigator.geolocation.getCurrentPosition(
(position) => {
considerReading(position);
finishWithBest();
},
() => {
distanceMessage.textContent = 'موقعیت دریافت نشد — می‌توانید ورود بزنید';
distanceMessage.classList.add("text-orange-500");
distanceInput.value = '';
submitBtn.disabled = false;
submitBtn.classList.remove("bg-gray-400", "cursor-not-allowed");
submitBtn.classList.add("bg-cyan-500", "hover:bg-cyan-700", "cursor-pointer");
},
{ enableHighAccuracy: false, timeout: 10000, maximumAge: 60000 }
);
},
geoOptions
);
}
locateUser();
const attendanceForm = document.getElementById('attendanceForm');
const output = document.getElementById('output');