Files
a.montask/resources/views/admin/index.blade.php
T
2026-07-22 17:48:22 +03:30

979 lines
52 KiB
PHP
Executable File

<?php
use App\Models\Attendance;
use App\Models\User;
use App\Models\ai;
use Morilog\Jalali\Jalalian;
use Carbon\Carbon;
use Illuminate\Support\Collection;
$Attendance = isset($_COOKIE['user_identity'])
? ($_COOKIE['user_identity'] == 1370
? Attendance::all()
: Attendance::where('user_identity', $_COOKIE['user_identity'])->get())
: collect();
$jalalian_object = Jalalian::now();
$first_day_of_month = $jalalian_object->subDays($jalalian_object->getDay() - 1);
$carbon_gregorian = $first_day_of_month->toCarbon();
$thismonth = $carbon_gregorian->format('Y-m-d 00:00:00');
$ids = json_decode(request()->cookie('seen_angizeh_ids', '[]'), true) ?: [];
$s = ai::where('key', 'angizeh')
->when($ids, fn($q) => $q->whereNotIn('id', $ids))
->orderByDesc('id')
->first()
?? ai::where('key', 'angizeh')->orderByDesc('id')->first();
$ids = array_merge($ids, [$s->id]);
Cookie::queue('seen_angizeh_ids', json_encode($ids), 14400);
//total time
$now = Jalalian::fromCarbon(Carbon::now());
$tsec = 0;
//dd(->user_profile);
if (isset($_COOKIE['token01'])) {
$a = json_decode($_COOKIE['token01']);
$user = User::where('user_name', $a->user_profile)->with('attendances')->first();
} else {
$user = isset($_COOKIE['user_identity']) ? User::where('user_name', $_COOKIE['user_identity'])->with('attendances')->first() : null;
}
//dd($user);
if (!empty($user->attendances)) {
foreach ($user->attendances as $att) {
$date = $att->entered_at != null ? \App\Support\JalaliHelper::fromDateTime($att->entered_at) : null;
$att->date = $date != null ? $date->format('Y/m/d') : null;
if ($att->duration != null && $date != null && $now->getMonth() == $date->getMonth()) {
[$h, $m, $sec] = explode(':', $att->duration);
$tsec += $h * 3600 + $m * 60 + $sec;
}
}
}
$hours = floor($tsec / 3600);
$minutes = floor(($tsec % 3600) / 60);
$totalTime = sprintf('%02d:%02d', $hours, $minutes);
$formaction = (isset($_COOKIE['ad']) && $_COOKIE['ad'] == 1)
? 'admin/'
: 'attendance/';
$now = Carbon::now()->format('Y-m-d 00:00:00');
$existed = $Attendance->whereNull('exited_at')->where('entered_at', '>=', $now)->first();
//$requested = $Attendance->whereNull('exited_at')->where('requested', 'true')->first();
//$forgot = $Attendance->whereNull('exited_at')->where('entered_at', '<', $now)->isNotEmpty();
//$today = $Attendance->whereNull('exited_at')->where('entered_at', '>=', $now)->isNotEmpty();
//dd($existed);
if (isset($_COOKIE['token-0'])) {
$hash = md5(strtolower(trim($user->email)));
$gravatarUrl = "https://www.gravatar.com/avatar/$hash?s=200&d=identicon";
}
if (!empty($user->tasks)) {
$backlog = <<<SVG
<svg height="12px" width="12px" viewBox="0 0 16 16" class="flex-shrink-0 flex-shrink-0 size-4" xmlns="http://www.w3.org/2000/svg"><g>
<g transform="translate(8 8) rotate(-90)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(-66)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(-42)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(-18)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(6)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(30)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(54)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(78)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(102)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(126)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(150)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(174)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(198)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(222)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g><g transform="translate(8 8) rotate(246)"><line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#60646C" stroke-width="1.21" stroke-linecap="round">
</line></g></g>
</svg>
SVG;
$todo = <<<SVG
<svg width="12px" height="12px" viewBox="0 0 16 16" class="flex-shrink-0 flex-shrink-0 size-4"><g></g>
<circle cx="8" cy="8" r="6" fill="none" stroke="#60646C" stroke-width="1.5" stroke-dasharray="37.69911184307752" stroke-dashoffset="0" stroke-linecap="round" transform="rotate(-90 8 8)">
</circle></svg>
SVG;
$error = <<<SVG
<svg width="12px" height="12px" viewBox="0 0 16 16" class="flex-shrink-0 flex-shrink-0 size-4"><g><g transform="translate(8 8) rotate(102)">
<line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#eb144c" stroke-width="1.21" stroke-linecap="round"></line></g><g transform="translate(8 8) rotate(126)">
<line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#eb144c" stroke-width="1.21" stroke-linecap="round"></line></g><g transform="translate(8 8) rotate(150)">
<line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#eb144c" stroke-width="1.21" stroke-linecap="round"></line></g><g transform="translate(8 8) rotate(174)">
<line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#eb144c" stroke-width="1.21" stroke-linecap="round"></line></g><g transform="translate(8 8) rotate(198)">
<line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#eb144c" stroke-width="1.21" stroke-linecap="round"></line></g><g transform="translate(8 8) rotate(222)">
<line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#eb144c" stroke-width="1.21" stroke-linecap="round"></line></g><g transform="translate(8 8) rotate(246)">
<line x1="5.75" y1="0" x2="6.5" y2="0" stroke="#eb144c" stroke-width="1.21" stroke-linecap="round"></line></g></g><circle cx="6" cy="6" r="3" stroke-width="1.5" stroke-linecap="round" fill="none" transform="rotate(-90 8 6)" stroke-dasharray="37.69911184307752" stroke-dashoffset="0" stroke="#eb144c">
</circle><circle cx="8" cy="8" r="6" fill="none" stroke="#eb144c" stroke-width="1.5" stroke-dasharray="37.69911184307752" stroke-dashoffset="18.84955592153876" stroke-linecap="round" transform="rotate(-90 8 8)"></circle></svg>
SVG;
$in_progress = <<<SVG
<svg width="12px" height="12px" viewBox="0 0 16 16" class="flex-shrink-0 flex-shrink-0 size-4"><g></g><circle cx="6" cy="6" r="3" stroke-width="1.5" stroke-linecap="round" fill="none" transform="rotate(-90 8 6)" stroke-dasharray="37.69911184307752" stroke-dashoffset="0" stroke="#F59E0B"></circle><circle cx="8" cy="8" r="6" fill="none" stroke="#F59E0B" stroke-width="1.5" stroke-dasharray="37.69911184307752" stroke-dashoffset="0" stroke-linecap="round" transform="rotate(-90 8 8)"></circle></svg>
SVG;
$done = <<<SVG
<svg height="12px" width="12px" class="flex-shrink-0 flex-shrink-0 size-4" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" percentage="1"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 15C11.866 15 15 11.866 15 8C15 4.13401 11.866 1 8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15ZM11.3587 6.18828C11.6007 5.85214 11.5244 5.38343 11.1882 5.14141C10.8521 4.89938 10.3834 4.97568 10.1414 5.31183L7.03706 9.62335L5.25956 7.97751C4.95563 7.69609 4.4811 7.71434 4.19968 8.01828C3.91826 8.32221 3.93651 8.79673 4.24045 9.07815L6.64045 11.3004C6.79816 11.4464 7.01095 11.5178 7.22481 11.4963C7.43868 11.4749 7.63307 11.3627 7.75865 11.1883L11.3587 6.18828Z" fill="#46A758"></path></svg>
SVG;
$cancelled = <<<SVG
<svg height="12px" width="12px" class="flex-shrink-0 flex-shrink-0 size-4" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" percentage="1"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 15C11.866 15 15 11.866 15 8C15 4.13401 11.866 1 8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15ZM11.1018 4.89826C11.3947 5.19115 11.3947 5.66603 11.1018 5.95892L9.06068 8.00002L11.1018 10.0411C11.3947 10.334 11.3947 10.8089 11.1018 11.1018C10.8089 11.3947 10.334 11.3947 10.0411 11.1018L8.00002 9.06068L5.95892 11.1018C5.66603 11.3947 5.19115 11.3947 4.89826 11.1018C4.60537 10.8089 4.60537 10.334 4.89826 10.0411L6.93936 8.00002L4.89826 5.95892C4.60537 5.66603 4.60537 5.19115 4.89826 4.89826C5.19115 4.60537 5.66603 4.60537 5.95892 4.89826L8.00002 6.93936L10.0411 4.89826C10.334 4.60537 10.8089 4.60537 11.1018 4.89826Z" fill="#9AA4BC"></path></svg>
SVG;
$state = [
"c73c2044-ea14-42cb-b08d-cecf3d1fdfb5" => "$in_progress",// in progress
"a9ce312f-cfca-491e-8e3d-1975aa6ebc10" => "$todo",//to do
"dea1fb57-a281-4fe7-8f3e-ee5567637e35" => "$error",// error
"770fbe42-99f6-41db-9b9e-773d9cced1a5" => "$backlog",//backlog
"b0e464e1-34eb-41ea-b2f7-3ef7483bc9f0" => "$done",//done
"d61d14a3-72f7-4f44-894b-efe8b2a75be1" => "$cancelled",//canceled
];
$state_order = [
"c73c2044-ea14-42cb-b08d-cecf3d1fdfb5",
"a9ce312f-cfca-491e-8e3d-1975aa6ebc10",
"dea1fb57-a281-4fe7-8f3e-ee5567637e35",
"770fbe42-99f6-41db-9b9e-773d9cced1a5",
"b0e464e1-34eb-41ea-b2f7-3ef7483bc9f0",
"d61d14a3-72f7-4f44-894b-efe8b2a75be1",
];
$user->tasks = $user->tasks->sortBy(function ($task) use ($state_order) {
return array_search($task->state, $state_order);
});
$tasks_with_started = $user->tasks->filter(function ($task) {
return $task->pivot && $task->pivot->started_at != null;
});
}
//dd($tasks_with_started);
//dd($user->tasks);
//dd($user->tasks->where('started_at', !null));
?>
@extends('app')
@section('content')
<div class="container flex-col"
style="letter-spacing: 0;display: flex;justify-content: center;align-items: center; margin : 90px 0 10px 0">
{{-- <button type="button"--}}
{{-- class="fixed max-w-[350px] bottom-24 right-9 z-50--}}
{{-- w-16 h-16 flex items-center justify-center--}}
{{-- text-white rounded-full--}}
{{-- bg-blue-500 hover:bg-blue-600--}}
{{-- shadow-lg cursor-pointer transition-all duration-300"--}}
{{-- onclick="openModal('ثبت روز کاری جدید', 'new')">--}}
{{-- ثبت روز جدید--}}
{{-- </button>--}}
@if (!isset($_COOKIE['token-0']))
<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>
<p class="message text-right text-sm text-gray-700">{{ $s->response }}</p>
@php
$user_identity = $_COOKIE['user_identity'] ?? '';
@endphp
<input type="hidden" name="latitude" id="latitude">
<input type="hidden" name="longitude" id="longitude">
<input type="hidden" name="distance" id="distance">
<p id="distanceMessage" class="text-center mb-2 font-semibold text-gray-600">در حال بررسی موقعیت...</p>
<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 class="flex justify-flex max-w-[240px]" style="align-items: center;">
<button type="button"
class="w-1/2 p-2 text-sm text-white text-center rounded-xl bg-cyan-500 hover:bg-cyan-600 cursor-pointer"
onclick="openModal('ثبت روز کاری جدید', 'new')">
ثبت روز کاری
</button>
<button type="button" id="breakAttendance"
class="w-1/2 p-2 text-sm text-white text-center rounded-xl bg-cyan-500 hover:bg-cyan-600
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 cursor-pointer mx-auto"
href="">
استراحت
</button>
</div>
<button type="button" id="tasksBtn"
class="text-white rounded-xl py-2 px-4 max-w-60 self-center bg-cyan-500 hover:bg-cyan-700 cursor-pointer w-full"
onclick="openTaskModal()">
وظیفه‌ها
</button>
</form>
@if(session('open_task_modal'))
<script>
window.onload = () => {
openTaskModal();
};
</script>
@endif
@else
<div class="ui-panel flex p-4 rounded-xl max-w-[350px] flex-col items-center">
<img src="{{$user->picture ?? $gravatarUrl}}" alt="Profile Picture" class="w-16 h-16 rounded-full ">
<label>
{{$user->name}}
</label>
<button type="button"
class="w-auto p-2 text-sm text-white text-center rounded-xl bg-blue-500 hover:bg-blue-600 cursor-pointer"
onclick="openModal('ثبت روز کاری جدید', 'new')">
ثبت روز کاری
</button>
</div>
@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 => 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' ? 'احراز هویت لغو شد یا دسترسی مجاز نبود.' : 'عملیات ناموفق بود. لطفاً دوباره امتحان کنید.';
}
});
</script>
<div class="w-full flex justify-center mt-4" style="">
<div class="overflow-hidden border border-gray-300 rounded-[23px] ui-panel max-w-[350px]">
<div class=" border-b border-gray-300 text-center w-full">
<!-- Your Table Code Here -->
<div class="w-[350px]">
@if(isset($_COOKIE['user_identity']))
<table class="min-w-full ui-attendance-table table-auto">
<thead class=" text-black sticky top-0 z-10"
style="background: rgba(244,244,244,0.82);border-bottom: 1px solid;border-color: #e1e1e1 ">
<tr>
@if($_COOKIE['user_identity'] == 1370)
<th class="py-3 px-6 text-right font-medium text-black uppercase tracking-wider"
style="color: #2c2c2c">نام
</th>
@endif
{{-- {{dd($user)}}--}}
<th class="py-3 px-6 text-right font-medium text-black uppercase tracking-wider"
style="color: #2c2c2c">تاریخ
<th class="py-3 px-6 text-right font-medium text-black uppercase tracking-wider"
style="color: #2c2c2c">ورود
</th>
<th class="py-3 px-6 text-center font-medium text-black uppercase tracking-wider"
style="color: #2c2c2c">
خروج
</th>
{{-- <th class="py-3 px-6 text-center font-medium text-black uppercase tracking-wider"--}}
{{-- style="color: #2c2c2c">--}}
{{-- استراحت--}}
{{-- </th>--}}
<th class="py-3 px-6 text-center font-medium text-black uppercase tracking-wider"
style="color: #2c2c2c">جمع
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
@if(!empty($user->attendances))
@foreach($user->attendances as $item)
{{--{{dd($item)}}--}}
<tr class="hover:bg-red-100 cursor-pointer "
style="background-color: {{$item->requested ? '#bfdbfe' : '#ffffff' }}"
onclick="openModal('{{ $item->id }}', '{{ $item->id }}' , '{{ $item->entered_at }}', '{{ $item->exited_at }} ')">
@if($_COOKIE['user_identity'] == 1370)
<td class="py-4 px-6 text-sm text-gray-900 whitespace-nowrap">
{{$item->user_identity}}
</td>
@endif
<td class="py-4 px-6 text-sm text-gray-900 whitespace-nowrap text-right"
style="background-color: {{$item->requested ? '' :($item->distance < 10 ? '#d1fae5' : ($item->distance < 50 ? '#dbeafe' : ($item->distance > 2000 ? '#ffedd5' : '#f3f4f6'))) }}; color: #111827;">
{{ \App\Support\JalaliHelper::format($item->entered_at, 'm/d') }}
</td>
<td class="py-4 px-6 text-sm text-gray-900 whitespace-nowrap text-right"
style="background-color: {{ $item->requested ? '' :($item->distance < 10 ? '#d1fae5' : ($item->distance < 50 ? '#dbeafe' : ($item->distance > 2000 ? '#ffedd5' : '#f3f4f6'))) }}; color: #111827;">
{{ $item->entered_at ? Carbon::parse($item->entered_at)->format('H:i') : '-' }}
</td>
<td class="py-4 px-6 text-sm text-gray-900 whitespace-nowrap text-center"
style="background-color: {{ $item->requested ? '' :($item->distance_exit < 10 ? '#d1fae5' : ($item->distance_exit < 50 ? '#dbeafe' : ($item->distance_exit > 2000 ? '#ffedd5' : '#f3f4f6'))) }}; color: #111827;">
{{ $item->exited_at ? Carbon::parse($item->exited_at)->format('H:i') : '-' }}
</td>
{{-- <td class="py-4 px-6 text-sm text-gray-900 whitespace-nowrap text-center"--}}
{{-- style="background-color: {{ $item->requested ? '' :($item->distance_exit < 10 ? '#d1fae5' : ($item->distance_exit < 50 ? '#dbeafe' : ($item->distance_exit > 2000 ? '#ffedd5' : '#f3f4f6'))) }}; color: #111827;">--}}
{{-- میزان استراحت--}}
{{-- </td>--}}
<td class="py-4 px-6 text-sm text-gray-900 whitespace-nowrap">
{{ $item->entered_at && $item->exited_at ? Carbon::parse($item->duration)->format('H:i') : '-' }}
</td>
</tr>
<tr class="spacer">
</tr>
@endforeach
@endif
</tbody>
</table>
@endif
</div>
</div>
</div>
<!-- ATTENDANCE Modal -->
<div id="modal-container"
class="ui-modal-backdrop centered hidden">
<div id="modal-content" class="ui-modal-panel max-w-sm w-full">
<h2 id="modal-title" class="ui-title"></h2>
<p id="modal-body" class="ui-subtitle"></p>
<div class="flex flex-col gap-4" style="align-items: center">
<div class="flex justify-center gap-2">
<div id="panel-access-link" href="#" class="" title="Panel Access"></div>
</div>
<form id="edit-form" method="post" action="{{$formaction.'edit'}}"
class="inline-block flex-col gap-2 flex items-center">
@csrf
<p id="new-date-label" style="display: none; text-align: right; ">تاریخ:</p>
<div id="new-date-group" class="input-group" style="margin: 20px; display: none;">
<input type="text" name="display_date" id="display_date"
class=" example1 form-control border p-2 rounded date-picker-input" value=""
readonly>
</div>
<p style=" ">ساعت شروع:</p>
<div class="input-group clockpicker border-2 border-gray-300 rounded-lg"
style="margin: 20px;">
<span class="input-group-addon bg-gray-200 align-middle text-stone-600 rounded-r-md p-2 pt-2.5">
<i class="fa-solid fa-clock"></i>
</span>
<input type="text" name="starttime" id="starttime"
class="form-control cursor-default p-2 w-36 "
placeholder="09:00
value="" readonly>
<button type="button" onclick="clearTime('starttime')"
class="p-2 bg-gray-200 hover:bg-gray-300 text-red-600 rounded-l-md w-9
title=" خالی کردن ساعت
">
&times;
</button>
</div>
<p>ساعت پایان:</p>
<div class="input-group clockpicker border-2 border-gray-300 rounded-lg"
style="margin: 20px;">
<span class="input-group-addon bg-gray-200 align-middle text-stone-600 rounded-r-md p-2 pt-2.5">
<i class="fa-solid fa-clock"></i>
</span>
<input type="text" name="endtime" id="endtime"
class="form-control cursor-default p-2 w-36"
placeholder="17:30"
value="" readonly>
<button type="button" onclick="clearTime('endtime')"
class="p-2 bg-gray-200 hover:bg-gray-300 text-red-600 rounded-l-md w-9
title=" خالی کردن ساعت
">
&times;
</button>
</div>
<input type="hidden" name="uname" id="uname" value="{{$user->user_name ?? null}}">
<input type="hidden" name="date" id="date" value="">
<input type="hidden" name="id" id="edit_id" value="">
<button type="submit" id="submit-edit-button" style="display: none;"
onclick="return confirmSubmit()">
ثبت تغییرات
</button>
</form>
<form id="delete-form" action="{{$formaction.'delete'}}" method="post" style="display: none;">
@csrf
<input type="hidden" name="id" id="delete_id" value="">
<button type="submit" id="submit-delete-button" onclick="return confirmSubmit()">
حذف
</button>
</form>
<div class="flex justify-center gap-2 mt-2 px-4">
<button type="button" class="bg-blue-500 text-white rounded p-2 **w-[70%]**"
onclick="document.getElementById('edit-form').submit(); return confirmSubmit()">
@if(isset($_COOKIE['ad']) && $_COOKIE['ad'] == 1)
ثبت تغییرات
@else
ارسال برای تایید
@endif
</button>
<button type="button" class="bg-red-500 text-white rounded p-2 **w-[30%]**"
onclick="document.getElementById('delete-form').submit(); return confirmSubmit()">
<i class="fas fa-trash text-white text-lg"></i>
</button>
</div>
</div>
</div>
</div>
</div>
{{-- START TASK MODAL --}}
<div id="taskModal"
class="ui-modal-backdrop hidden">
<div class="ui-modal-panel">
<button type="button" onclick="closeTaskModal()" class="ui-modal-close" aria-label="بستن">
&times;
</button>
<h2 class="ui-title" id="modalTitle">انتخاب وظیفه</h2>
<p class="ui-subtitle" id="modalDesc">یکی از وظایف زیر را انتخاب کنید:</p>
<div class="grid flex-col gap-2 grid-cols-2 pb-3 items-center justify-items-end">
@if(!empty($user->tasks))
@foreach($user->tasks as $item)
<div class="inline-flex items-center ml-auto">
{!! array_key_exists($item->state,$state) ? $state[$item->state] : "" !!}
{{-- <div class="grid flex-col gap-2 grid-cols-2 pb-3 items-center justify-items-center">--}}
<p class="w-full">{{ $item->name }}</p>
</div>
<button
class="actionBtn px-4 py-2 rounded-lg font-semibold text-white transition w-20
{{ ($item->pivot->started_at && !$item->pivot->ended_at) ? 'bg-red-600 hover:bg-red-700' : 'bg-blue-600 hover:bg-blue-700' }}"
id="statusBtn"
onclick="handleTaskAction(
'{{ $item->task_id }}',
'{{ $item->pivot->id ?? '' }}',
'{{ $item->pivot->started_at ?? '' }}',
'{{ $item->pivot->ended_at ?? '' }}',
'{{ $item->pivot->ended_at ? 'start' : ($item->pivot->started_at ? 'end' : 'start') }}'
)">
شروع/پایان
</button>
{{-- </div>--}}
{{-- @if(!$loop->last)--}}
{{-- <hr class="border-t border-gray-300 border-dashed w-full grid-span-2 col-end-2">--}}
{{-- @endif--}}
@endforeach
@endif
</div>
</div>
</div>
@endsection
@section('script')
<script>
{{-- TASKS MODAL--}}
function openTaskModal(task) {
const modal = document.getElementById('taskModal');
modal.classList.remove('hidden');
modal.classList.add('flex');
// آپدیت متن دکمه بر اساس وضعیت تسک
updateStatusButtonText(task.started_at, task.ended_at);
// تنظیم عملکرد دکمه بر اساس تسک فعلی
const statusBtn = document.getElementById('statusBtn');
statusBtn.onclick = () => handleTaskAction(
task.id,
task.p_id,
task.started_at,
task.ended_at
);
}
// -------------------------------
// بستن مودال
// -------------------------------
function closeTaskModal() {
const modal = document.getElementById('taskModal');
modal.classList.add('hidden');
modal.classList.remove('flex');
}
// -------------------------------
// آپدیت متن دکمه شروع/پایان
// -------------------------------
function updateStatusButtonText(started_at, ended_at) {
const statusBtn = document.getElementById('statusBtn');
let text = "شروع";
if (started_at && !ended_at) {
text = "پایان";
}
statusBtn.textContent = text;
}
// -------------------------------
// هندل عملیات شروع / پایان تسک
// -------------------------------
function handleTaskAction(taskId, p_id, started_at, ended_at) {
// ... (منطق تعیین actionType و زمان‌ها) ...
// ارسال داده به سرور
fetch('{{ url("/task-manager") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
task_id: taskId,
p_id: p_id,
started_at: started_at,
ended_at: ended_at,
action_type: actionType
})
})
.then(res => {
// ابتدا بررسی وضعیت HTTP
if (!res.ok) {
// اگر کد وضعیت 4xx یا 5xx باشد
throw new Error(`HTTP error! status: ${res.status}`);
}
// در صورت موفقیت (200)، تلاش برای خواندن JSON
return res.json();
})
.then(data => {
// 💡 اصلاح: بررسی دقیق فیلد success
if (data && data.success === true) {
console.log('✅ Task updated successfully:', data);
closeTaskModal();
// 💡 رفع مشکل عدم به‌روزرسانی وضعیت دکمه در بار بعدی:
// برای سادگی، صفحه را رفرش کنید تا داده‌های جدید واکشی شوند.
window.location.reload();
} else {
// سرور با کد 200 جواب داده اما success: false بوده یا ساختار پاسخ مشکل دارد.
console.error('❌ Operation failed based on server data:', data);
alert('❌ خطا در انجام عملیات! پاسخ سرور را در کنسول بررسی کنید.');
}
})
.catch(err => {
// شامل خطای ارتباط، خطای HTTP و خطای JSON نامعتبر
console.error('⚠️ Server or JSON parsing error:', err);
alert(`⚠️ خطا در ارتباط با سرور یا پردازش داده! جزئیات در کنسول.`);
});
}
{{-- BREAK MODAL--}}
let breakStarted = false;
let breakStartTime = null;
let breakTimer = null;
const breakBtn = document.getElementById('breakAttendance');
function formatLocalDatetime(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
function formatDuration(totalSeconds) {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const h = String(hours);
const i = String(minutes).padStart(2, '0');
return `${h}:${i}`;
}
breakBtn.addEventListener('click', () => {
if (!breakStarted) {
// شروع استراحت 🚀
breakStarted = true;
breakStartTime = new Date();
// تغییر ظاهر دکمه و شروع تایمر (همچنان M:S نمایش داده می شود)
breakBtn.classList.remove('bg-gray-500', 'hover:bg-gray-600');
breakBtn.classList.add('bg-red-500', 'hover:bg-red-600');
breakBtn.textContent = '00:00';
let seconds = 0;
breakTimer = setInterval(() => {
seconds++;
// اینجا همچنان به صورت دقیقه:ثانیه نمایش داده می شود
const m = String(Math.floor(seconds / 60)).padStart(2, '0');
const s = String(seconds % 60).padStart(2, '0');
breakBtn.textContent = ` ${m}:${s}`;
}, 1000);
//send start time
let formData = new FormData();
formData.append('start_time', formatLocalDatetime(breakStartTime));
formData.append('_token', '{{ csrf_token() }}');
fetch("{{ url('attendance/break') }}", {
method: "POST",
body: formData
})
.then(res => res.json())
.then(data => console.log('✅ Break Started Logged:', data))
.catch(err => console.error('💥 Error starting break:', err));
} else {
// پایان استراحت 🛑
breakStarted = false;
const breakEndTime = new Date();
clearInterval(breakTimer);
// 1. محاسبه مدت زمان استراحت (بر حسب ثانیه)
const durationSec = Math.floor((breakEndTime - breakStartTime) / 1000);
// 2. تبدیل زمان های شروع و پایان به فرمت رشته ای
const breakStartTime_str = formatLocalDatetime(breakStartTime);
const breakEndTime_str = formatLocalDatetime(breakEndTime);
// 3. فرمت کردن مدت زمان به H:i (فقط برای نمایش در کنسول/هشدار)
const duration_formatted_h_i = formatDuration(durationSec);
// 4. نمایش دکمه به حالت اولیه
breakBtn.classList.remove('bg-red-500', 'hover:bg-red-600');
breakBtn.classList.add('bg-gray-500', 'hover:bg-gray-600');
breakBtn.textContent = 'استراحت';
// 5. ارسال داده‌ها به سرور
let formData = new FormData();
formData.append('start_time', breakStartTime_str);
formData.append('end_time', breakEndTime_str);
formData.append('duration_seconds', durationSec);
formData.append('_token', '{{ csrf_token() }}');
fetch("{{ url('attendance/break') }}", {
method: "POST",
body: formData
})
.then(res => res.json())
.then(data => console.log('✅ Break saved:', data))
.catch(err => console.error('💥 Error:', err));
}
});
{{-- ATTENDANCE MODAL--}}
function clearTime(fieldId) {
document.getElementById(fieldId).value = '';
}
function closeModal() {
document.getElementById('modal-container').classList.add('hidden');
}
document.getElementById('modal-container').addEventListener('click', (e) => {
if (e.target === document.getElementById('modal-container')) {
closeModal();
}
});
function confirmSubmit() {
return confirm("آیا تغییرات اعمال شوند؟");
}
function openModal(title, id, enteredAt, exitedAt) {
const isNewEntry = id === 'new';
const modalTitleElement = document.getElementById('modal-title');
// ✅ بهترین روش: انتخاب مستقیم فیلد مخفی با ID آن
const hiddenIdInput = document.getElementById('edit_id');
// const hiddenIdInput = document.getElementById('delete_id');
// انتخاب فیلد ID برای فرم حذف
const deleteIdInput = document.getElementById('delete_id');
const submitButton = document.querySelector('#modal-content button[type="submit"]');
const hiddenDateInput = document.querySelector('input[name="date"]');
// مقداردهی به فرم حذف
if (deleteIdInput) {
deleteIdInput.value = id;
}
const newDateLabel = document.getElementById('new-date-label');
const newDateGroup = document.getElementById('new-date-group');
const displayDateInput = document.getElementById('display_date');
const panelLink = document.getElementById('panel-access-link');
panelLink.href = `/go_down/${id}`;
document.getElementById('modal-container').classList.remove('hidden');
const startTimeInput = document.querySelector('input[name="starttime"]');
const endTimeInput = document.querySelector('input[name="endtime"]');
// تنظیم ساعت‌ها
if (enteredAt && !isNaN(new Date(enteredAt).getTime())) {
startTimeInput.value = new Date(enteredAt).toLocaleTimeString('en-GB', {
hour: '2-digit',
minute: '2-digit'
});
} else {
startTimeInput.value = '09:00';
}
if (exitedAt && !isNaN(new Date(exitedAt).getTime())) {
endTimeInput.value = new Date(exitedAt).toLocaleTimeString('en-GB', {
hour: '2-digit',
minute: '2-digit'
});
}
// else {
// endTimeInput.value = '17:30';
// }
if (isNewEntry) {
// حالت ثبت جدید
modalTitleElement.innerText = 'ثبت روز کاری جدید';
newDateLabel.style.display = 'block';
newDateGroup.style.display = 'block';
displayDateInput.value = '';
hiddenDateInput.value = '';
// ✅ اصلاح: مقداردهی 'new' به فیلد ID
if (hiddenIdInput) hiddenIdInput.value = 'new';
submitButton.onclick = function () {
if (hiddenDateInput.value === '') {
alert("لطفاً یک تاریخ را از تقویم انتخاب کنید.");
return false;
}
return confirmSubmit();
};
} else {
// حالت ویرایش
modalTitleElement.innerText = 'تغییر ساعت کاری';
newDateLabel.style.display = 'none';
newDateGroup.style.display = 'none';
// ✅ اصلاح: استفاده از متغیر 'id' که از جدول فرستاده شده است
if (hiddenIdInput) hiddenIdInput.value = id;
if (hiddenDateInput) hiddenDateInput.value = enteredAt;
submitButton.onclick = function () {
return confirmSubmit();
};
}
}
$(document).ready(function () {
// 1. فعال‌سازی Clockpicker
$('.clockpicker').clockpicker({
autoclose: true,
placement: 'center',
align: 'left',
donetext: 'تایید',
vibrate: true,
// donetext: true,
});
$(".example1").persianDatepicker({
format: "YYYY/MM/DD",
altField: '#date',
altFormat: 'YYYY-MM-DD',
calendar: {
persian: {
locale: 'fa',
leapYearMode: "astronomical"
}
},
observer: true,
initialValue: false,
initialValueType: 'gregorian',
autoClose: true,
toolbox: {
calendarSwitch: {enabled: false},
todayButton: {enabled: true, text: {fa: "امروز"}}
},
onSelect: function (unix) {
// تبدیل تاریخ انتخابی به میلادی درست با timezone ایران
const d = new persianDate(unix).toCalendar('gregorian').toLocale('en');
console.log("Gregorian (Fixed):", d.format("YYYY-MM-DD"));
}
});
// 2. فعال‌سازی Persian Datepicker
// $('.example1').pDatepicker({
// format: 'YYYY/MM/DD',
// // placement: 'center',
// altField: '#date', // این فیلد مخفی، تاریخ میلادی تبدیل شده را دریافت می‌کند
// altFormat: 'YYYY-MM-DD', // میلادی ذخیره بشه
// // changeMonth: true,
// // changeYear: true,
// // showOtherMonths: true,
// // selectOtherMonths: true,
// autoClose: true,
//
// });
});
</script>
@endsection