NASER develope task section

This commit is contained in:
mobinesaghi
2026-04-23 14:55:07 +03:30
parent 2bcf9f1b7e
commit b348e476f1
3 changed files with 180 additions and 208 deletions
@@ -97,8 +97,11 @@ class AttendanceController extends Controller
// $until_now = Carbon::now()->diffInSeconds(Carbon::parse($break->break_start)); // $until_now = Carbon::now()->diffInSeconds(Carbon::parse($break->break_start));
} }
$user->tasks = Tasks::where('created_by', $user->id)
->orWhereJsonContains('assignees', (string) $user->id)
->orderBy('target_date', 'desc')
->get();
//dd($user->tasks);
// dd($until_now); // dd($until_now);
return view('attendance.index', compact('user'))->with([ return view('attendance.index', compact('user'))->with([
's' => $s, 's' => $s,
+2
View File
@@ -5,6 +5,8 @@ namespace App\Http\Controllers;
use App\Models\Setting; use App\Models\Setting;
use App\Models\User; use App\Models\User;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Carbon\Carbon;
use Morilog\Jalali\Jalalian;
abstract class Controller abstract class Controller
{ {
+173 -206
View File
@@ -68,7 +68,7 @@ if (count($user->attendanceBreaks) > 0) {
{{-- </button>--}} {{-- </button>--}}
@if (!isset($_COOKIE['token-0'])) @if (!isset($_COOKIE['token-0']))
<form id="attendanceForm" class="form items-center" {{--action="{{ url('attendance/enter') }}" method="post"--}} <form id="attendanceForm" class="form items-center" action="{{ url('attendance/enter') }}" method="post"
style="text-align: left"> style="text-align: left">
@csrf @csrf
<p class="title text-center font-bold text-lg mb-2">سامانه گذارش کار</p> <p class="title text-center font-bold text-lg mb-2">سامانه گذارش کار</p>
@@ -89,11 +89,7 @@ if (count($user->attendanceBreaks) > 0) {
style="width: 100%"> style="width: 100%">
ورود/خروج ورود/خروج
</button> </button>
sghl<button id="submitBtn" disabled type="button"
class="bg-gray-400 text-white rounded cursor-not-allowed rounded-xl py-2 px-4 max-w-60 self-center"
style="width: 100%">
ورود/خروج
</button>
<div id="output" class="text-red-500 text-sm mt-2 text-center"></div> <div id="output" class="text-red-500 text-sm mt-2 text-center"></div>
<div class="flex justify-flex max-w-[240px]" style="align-items: center;"> <div class="flex justify-flex max-w-[240px]" style="align-items: center;">
@@ -117,13 +113,13 @@ if (count($user->attendanceBreaks) > 0) {
</button> </button>
{{--<p>{{Carbon::now()}}</p>--}} {{--<p>{{Carbon::now()}}</p>--}}
</form> </form>
{{-- @if(session('open_task_modal'))--}} {{-- @if(session('open_task_modal'))--}}
{{-- <script>--}} {{-- <script>--}}
{{-- window.onload = () => {--}} {{-- window.onload = () => {--}}
{{-- openTaskModal();--}} {{-- openTaskModal();--}}
{{-- };--}} {{-- };--}}
{{-- </script>--}} {{-- </script>--}}
{{-- @endif--}} {{-- @endif--}}
@else @else
@@ -145,162 +141,6 @@ if (count($user->attendanceBreaks) > 0) {
@endif @endif
<script>
const existed = {{ $existed ? 'true' : 'false' }};
{{--const today = {{$today}};--}}
const distanceMessage = document.getElementById("distanceMessage");
const referenceLat = 32.6504751;
const referenceLng = 51.6936045;
const submitBtn = document.getElementById("submitBtn");
const latInput = document.getElementById("latitude");
const lngInput = document.getElementById("longitude");
const distanceInput = document.getElementById("distance");
const form = document.getElementById("attendanceForm");
// naser
if (existed) {
submitBtn.textContent = "خروج";
} else {
submitBtn.textContent = "ورود";
}
function getDistanceInMeters(lat1, lon1, lat2, lon2) {
const earthRadius = 6371000; // meters
const toRad = deg => deg * Math.PI / 180;
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
lat1 = toRad(lat1);
lat2 = toRad(lat2);
const a = Math.sin(dLat / 2) ** 2 +
Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) ** 2;
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return earthRadius * c;
}
navigator.geolocation.getCurrentPosition(
position => {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
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 = `مکان: خارج از شرکت`;
}
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");
},
error => {
const lat = 32.6520532791536; // از مختصات ثابت استفاده کنید
const lng = 51.68472614422439; // از مختصات ثابت استفاده کنید
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
distanceInput.value = dist;
distanceMessage.textContent = `مکان: خارج از شرکت`;
distanceMessage.classList.remove("bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500", "cursor-not-allowed");
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
}
);
const attendanceForm = document.getElementById('attendanceForm');
const output = document.getElementById('output');
const challenge = new Uint8Array(32);
window.crypto.getRandomValues(challenge);
const createOptions = {
challenge,
rp: {
name: "مثال سایت شما"
},
user: {
id: Uint8Array.from('user-id', c => c.charCodeAt(0)),
name: "user@example.com",
displayName: "نام کاربر"
},
pubKeyCredParams: [{
type: 'public-key',
alg: -7
}],
authenticatorSelection: {
authenticatorAttachment: "platform",
userVerification: "required"
},
timeout: 60000,
attestation: "direct"
};
submitBtn.addEventListener('click', async () => {
// ابتدا اعتبارسنجی اثر انگشت توسط WebAuthn
if (!window.PublicKeyCredential) {
alert('مرورگر شما از احراز هویت بیومتریک پشتیبانی نمی‌کند.');
return;
}
// یک چالش تصادفی بسازید
const challenge = new Uint8Array(32);
window.crypto.getRandomValues(challenge);
// گزینه‌های احراز هویت (Authentication)
const getOptions = {
publicKey: {
challenge: challenge,
timeout: 60000,
rpId: window.location.hostname, // اختیاری؛ برای لوکال‌هاست خالی بگذارید
userVerification: "required",
allowCredentials: [] // خالی بگذارید تا هر کلید موجود نمایش داده شود
}
};
try {
// به جای create از get استفاده کنید
const assertion = await navigator.credentials.get(getOptions);
console.log('احراز هویت موفق:', assertion);
// اینجا فرم را سابمیت کنید یا هر عملیات دیگر
attendanceForm.submit();
} catch (error) {
const message = error.name === 'NotAllowedError'
? 'احراز هویت لغو شد یا دسترسی مجاز نبود.'
: 'عملیات ناموفق بود. لطفاً دوباره امتحان کنید.';
const output = document.getElementById('output');
if (output) {
output.textContent = message;
} else {
alert(message);
}
console.error(error);
}
});
</script>
<div class="w-full flex justify-center mt-4 pb-20" style=""> <div class="w-full flex justify-center mt-4 pb-20" style="">
<div class="overflow-hidden rounded-[23px] max-w-[350px]"> <div class="overflow-hidden rounded-[23px] max-w-[350px]">
@@ -413,38 +253,6 @@ if (count($user->attendanceBreaks) > 0) {
@endif @endif
</div> </div>
<script>
function openCity(evt, Month) {
var i, tabcontent, tablinks;
// مخفی کردن تمام محتواها
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
// غیرفعال کردن تمام تب‌ها
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active bg-gray-300 font-bold", "");
// اضافه کردن استایل دیفالت اگر کلاس اکتیو حذف شد
if (!tablinks[i].className.includes("active")) {
tablinks[i].style.backgroundColor = "";
}
}
// نمایش تب انتخاب شده
document.getElementById(Month).style.display = "block";
evt.currentTarget.className += " active bg-gray-300 font-bold";
}
// باز کردن اولین تب به صورت خودکار هنگام لود صفحه
document.addEventListener("DOMContentLoaded", function () {
let firstTab = document.querySelector(".tablinks");
if (firstTab) {
firstTab.click();
}
});
</script>
@endif @endif
@@ -598,23 +406,23 @@ if (count($user->attendanceBreaks) > 0) {
<div class="inline-flex items-center bg-gray-300 hover:bg-cyan-600 rounded-lg m-2 p-2 justify-between" <div class="inline-flex items-center bg-gray-300 hover:bg-cyan-600 rounded-lg m-2 p-2 justify-between"
style="display: flex"> style="display: flex">
@if($user->active_task) @if($user->active_task)
@if( $user->active_task == $item->name ) @if( $user->active_task == $item->title )
<input type="radio" name="task_id" checked="true" <input type="radio" name="task_id" checked="true"
value="{{ $item->task_id }}" value="{{ $item->id }}"
class=""> class="">
@else @else
<input type="radio" name="task_id" value="{{ $item->task_id }}" class="" <input type="radio" name="task_id" value="{{ $item->id }}" class=""
disabled> disabled>
@endif @endif
@else @else
<input type="radio" name="task_id" value="{{ $item->task_id }}" class=""> <input type="radio" name="task_id" value="{{ $item->id }}" class="">
{{-- <input type="hidden" name="task_name" value="{{$item->name}}">--}} {{-- <input type="hidden" name="task_name" value="{{$item->name}}">--}}
@endif @endif
<div class="px-2.5" style="direction: rtl">{{ $item->name }}</div> <div class="px-2.5" style="direction: rtl">{{ $item->title }}</div>
<span class="block h-1.5 w-1.5 flex-shrink-0 rounded-full" style="background-color: <span class="block h-1.5 w-1.5 flex-shrink-0 rounded-full" style="background-color:
{!! array_key_exists($item->state,$state) ? $state[$item->state] : "" !!};"></span> {!! array_key_exists($item->state,$state) ? $state[$item->state] : "" !!};"></span>
@@ -666,6 +474,165 @@ if (count($user->attendanceBreaks) > 0) {
@section('script') @section('script')
<script> <script>
const existed = {{ $existed ? 'true' : 'false' }};
{{--const today = {{$today}};--}}
const distanceMessage = document.getElementById("distanceMessage");
const referenceLat = 32.6504751;
const referenceLng = 51.6936045;
const submitBtn = document.getElementById("submitBtn");
const latInput = document.getElementById("latitude");
const lngInput = document.getElementById("longitude");
const distanceInput = document.getElementById("distance");
const form = document.getElementById("attendanceForm");
// naser
if (existed) {
submitBtn.textContent = "خروج";
} else {
submitBtn.textContent = "ورود";
}
function getDistanceInMeters(lat1, lon1, lat2, lon2) {
const earthRadius = 6371000; // meters
const toRad = deg => deg * Math.PI / 180;
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
lat1 = toRad(lat1);
lat2 = toRad(lat2);
const a = Math.sin(dLat / 2) ** 2 +
Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) ** 2;
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return earthRadius * c;
}
navigator.geolocation.getCurrentPosition(
position => {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
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 = `مکان: خارج از شرکت`;
}
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");
},
error => {
const lat = 32.6520532791536; // از مختصات ثابت استفاده کنید
const lng = 51.68472614422439; // از مختصات ثابت استفاده کنید
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
distanceInput.value = dist;
distanceMessage.textContent = `مکان: خارج از شرکت`;
distanceMessage.classList.remove("bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500", "cursor-not-allowed");
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
}
);
const attendanceForm = document.getElementById('attendanceForm');
const output = document.getElementById('output');
const challenge = new Uint8Array(32);
window.crypto.getRandomValues(challenge);
const createOptions = {
challenge,
rp: {
name: "مثال سایت شما"
},
user: {
id: Uint8Array.from('user-id', c => c.charCodeAt(0)),
name: "user@example.com",
displayName: "نام کاربر"
},
pubKeyCredParams: [{
type: 'public-key',
alg: -7
}],
authenticatorSelection: {
authenticatorAttachment: "platform",
userVerification: "required"
},
timeout: 60000,
attestation: "direct"
};
submitBtn.addEventListener('click', async () => {
// ابتدا اعتبارسنجی اثر انگشت توسط WebAuthn
if (!window.PublicKeyCredential) {
alert('مرورگر شما از احراز هویت بیومتریک پشتیبانی نمی‌کند.');
return;
}
try {
const credential = await navigator.credentials.create({
publicKey: createOptions
});
attendanceForm.submit();
} catch (error) {
output.textContent = error.name === 'NotAllowedError' ? 'احراز هویت لغو شد یا دسترسی مجاز نبود.' : 'عملیات ناموفق بود. لطفاً دوباره امتحان کنید.';
}
});
function openCity(evt, Month) {
var i, tabcontent, tablinks;
// مخفی کردن تمام محتواها
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
// غیرفعال کردن تمام تب‌ها
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active bg-gray-300 font-bold", "");
// اضافه کردن استایل دیفالت اگر کلاس اکتیو حذف شد
if (!tablinks[i].className.includes("active")) {
tablinks[i].style.backgroundColor = "";
}
}
// نمایش تب انتخاب شده
document.getElementById(Month).style.display = "block";
evt.currentTarget.className += " active bg-gray-300 font-bold";
}
// باز کردن اولین تب به صورت خودکار هنگام لود صفحه
document.addEventListener("DOMContentLoaded", function () {
let firstTab = document.querySelector(".tablinks");
if (firstTab) {
firstTab.click();
}
});
{{-- TASKS MODAL--}} {{-- TASKS MODAL--}}
function openTaskModal(task) { function openTaskModal(task) {