diff --git a/resources/views/admin/index.blade.php b/resources/views/admin/index.blade.php
index 5f5e397b..5abcd99d 100755
--- a/resources/views/admin/index.blade.php
+++ b/resources/views/admin/index.blade.php
@@ -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
}
);
diff --git a/resources/views/attendance/index.blade.php b/resources/views/attendance/index.blade.php
index 8cb81bcc..e7bf56ca 100755
--- a/resources/views/attendance/index.blade.php
+++ b/resources/views/attendance/index.blade.php
@@ -311,12 +311,12 @@ if (!empty($user->tasks)) {
{{-- زمان ورود --}}
-
+ |
{{ $item->entered_at ? \Carbon\Carbon::parse($item->entered_at)->format('H:i') : '-' }}
|
{{-- زمان خروج --}}
-
+ |
{{ $item->exited_at ? \Carbon\Carbon::parse($item->exited_at)->format('H:i') : '-' }}
|
@@ -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');