NASER develope task section
This commit is contained in:
Notes:
mobinesaghi
2026-04-23 14:42:17 +03:30
NASER add task functions undepended
@@ -97,8 +97,11 @@ class AttendanceController extends Controller
|
||||
// $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);
|
||||
return view('attendance.index', compact('user'))->with([
|
||||
's' => $s,
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace App\Http\Controllers;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Carbon\Carbon;
|
||||
use Morilog\Jalali\Jalalian;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
|
||||
@@ -68,7 +68,7 @@ if (count($user->attendanceBreaks) > 0) {
|
||||
{{-- </button>--}}
|
||||
@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">
|
||||
@csrf
|
||||
<p class="title text-center font-bold text-lg mb-2">سامانه گذارش کار</p>
|
||||
@@ -89,11 +89,7 @@ if (count($user->attendanceBreaks) > 0) {
|
||||
style="width: 100%">
|
||||
ورود/خروج
|
||||
</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 class="flex justify-flex max-w-[240px]" style="align-items: center;">
|
||||
@@ -117,13 +113,13 @@ if (count($user->attendanceBreaks) > 0) {
|
||||
</button>
|
||||
{{--<p>{{Carbon::now()}}</p>--}}
|
||||
</form>
|
||||
{{-- @if(session('open_task_modal'))--}}
|
||||
{{-- <script>--}}
|
||||
{{-- window.onload = () => {--}}
|
||||
{{-- openTaskModal();--}}
|
||||
{{-- };--}}
|
||||
{{-- </script>--}}
|
||||
{{-- @endif--}}
|
||||
{{-- @if(session('open_task_modal'))--}}
|
||||
{{-- <script>--}}
|
||||
{{-- window.onload = () => {--}}
|
||||
{{-- openTaskModal();--}}
|
||||
{{-- };--}}
|
||||
{{-- </script>--}}
|
||||
{{-- @endif--}}
|
||||
|
||||
@else
|
||||
|
||||
@@ -145,162 +141,6 @@ if (count($user->attendanceBreaks) > 0) {
|
||||
@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="overflow-hidden rounded-[23px] max-w-[350px]">
|
||||
@@ -413,38 +253,6 @@ if (count($user->attendanceBreaks) > 0) {
|
||||
@endif
|
||||
</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
|
||||
|
||||
@@ -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"
|
||||
style="display: flex">
|
||||
@if($user->active_task)
|
||||
@if( $user->active_task == $item->name )
|
||||
@if( $user->active_task == $item->title )
|
||||
<input type="radio" name="task_id" checked="true"
|
||||
value="{{ $item->task_id }}"
|
||||
value="{{ $item->id }}"
|
||||
class="">
|
||||
|
||||
@else
|
||||
<input type="radio" name="task_id" value="{{ $item->task_id }}" class=""
|
||||
<input type="radio" name="task_id" value="{{ $item->id }}" class=""
|
||||
disabled>
|
||||
|
||||
@endif
|
||||
@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}}">--}}
|
||||
@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:
|
||||
{!! array_key_exists($item->state,$state) ? $state[$item->state] : "" !!};"></span>
|
||||
|
||||
@@ -666,6 +474,165 @@ if (count($user->attendanceBreaks) > 0) {
|
||||
@section('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--}}
|
||||
function openTaskModal(task) {
|
||||
|
||||
Reference in New Issue
Block a user