name ?? 'نامشخص'; $titleBefore = (string)($before['title'] ?? ''); $titleAfter = (string)($after['title'] ?? $task->title ?? ''); $lines[] = "ویرایش تسک: «{$titleAfter}»"; $lines[] = "توسط: {$actorName}"; $appBefore = $before['app'] ?? null; $appAfter = $after['app'] ?? $task->app ?? null; $a1 = App::find($appBefore)?->name_fa ?? ($appBefore ?? '-'); $a2 = App::find($appAfter)?->name_fa ?? ($appAfter ?? '-'); $lines[] = ($appBefore != $appAfter) ? "اپ: {$a1} ← {$a2}" : "اپ: {$a2}"; $lines[] = (($before['title'] ?? null) !== ($after['title'] ?? null)) ? "عنوان: {$titleBefore} ← {$titleAfter}" : "عنوان: {$titleAfter}"; $priorityFa = [ 'none' => 'ندارد', 'low' => 'کم', 'medium' => 'عادی', 'high' => 'زیاد', 'urgent' => 'فوری', ]; $p1 = $before['priority'] ?? null; $p2 = $after['priority'] ?? null; $lines[] = ($p1 !== $p2) ? "اولویت: " . ($priorityFa[$p1] ?? ($p1 ?? '-')) . " ← " . ($priorityFa[$p2] ?? ($p2 ?? '-')) : "اولویت: " . ($priorityFa[$p2] ?? ($p2 ?? '-')); $stateFa = [ 1 => 'انباشته', 2 => 'برای انجام', 3 => 'درحال انجام', 4 => 'پایان یافته', ]; $s1 = (int)($before['state'] ?? 0); $s2 = (int)($after['state'] ?? 0); $lines[] = ($s1 !== $s2 && $s2) ? "وضعیت: " . ($stateFa[$s1] ?? ($before['state'] ?? '-')) . " ← " . ($stateFa[$s2] ?? ($after['state'] ?? '-')) : "وضعیت: " . ($stateFa[$s2] ?? ($after['state'] ?? '-')); $d1 = $before['start_date'] ?? null; $d2 = $after['start_date'] ?? null; $lines[] = ($d1 !== $d2) ? "شروع: " . ($d1 ?: '-') . " ← " . ($d2 ?: '-') : "شروع: " . ($d2 ?: '-'); $t1 = $before['target_date'] ?? null; $t2 = $after['target_date'] ?? null; $lines[] = ($t1 !== $t2) ? "ددلاین: " . ($t1 ?: '-') . " ← " . ($t2 ?: '-') : "ددلاین: " . ($t2 ?: '-'); $desc1 = trim((string)($before['description'] ?? '')); $desc2 = trim((string)($after['description'] ?? '')); $lines[] = ($desc1 !== $desc2) ? "توضیحات: " . (!empty($desc2) ? 'تغییر کرد' : 'پاک شد') : "توضیحات: بدون تغییر"; $aBefore = $before['assignees'] ?? null; $aAfter = $after['assignees'] ?? null; $arr1 = is_string($aBefore) ? json_decode($aBefore, true) : $aBefore; $arr2 = is_string($aAfter) ? json_decode($aAfter, true) : $aAfter; $arr1 = is_array($arr1) ? array_map('strval', $arr1) : []; $arr2 = is_array($arr2) ? array_map('strval', $arr2) : []; sort($arr1); sort($arr2); $names1 = User::whereIn('id', $arr1)->pluck('name')->all(); $names2 = User::whereIn('id', $arr2)->pluck('name')->all(); $lines[] = ($arr1 !== $arr2) ? "مسئولین: " . (!empty($names1) ? implode('، ', $names1) : '-') . " ← " . (!empty($names2) ? implode('، ', $names2) : '-') : "مسئولین: " . (!empty($names2) ? implode('، ', $names2) : '-'); return implode("\n", $lines); } private function sumDurationSeconds($durations, ?int $userId = null): int { $totalSeconds = 0; foreach ($durations as $item) { if ($userId !== null && (int)$item->user_id !== $userId) { continue; } if ($item->duration === null) { continue; } try { $totalSeconds += CarbonInterval::createFromFormat('H:i:s', $item->duration)->totalSeconds; } catch (\Throwable) { continue; } } return $totalSeconds; } private function resolveAssigneeNames(Tasks $task, array $usersById = []): array { $assignees = is_string($task->assignees) ? json_decode($task->assignees, true) : $task->assignees; $assignees = is_array($assignees) ? array_map('strval', $assignees) : []; if (empty($assignees)) { return []; } if (!empty($usersById)) { $names = []; foreach ($assignees as $id) { if (!empty($usersById[$id])) { $names[] = $usersById[$id]; } } return $names; } return User::whereIn('id', $assignees)->pluck('name')->all(); } private function buildTasksPayload($tasks, array $usersById = []): array { $payload = []; foreach ($tasks as $task) { $payload[$task->id] = [ 'id' => $task->id, 'title' => $task->title, 'description' => $task->description, 'start_date' => $task->start_date, 'target_date' => $task->target_date, 'priority' => $task->priority, 'state' => $task->state, 'app' => $task->app, 'active' => (bool)$task->active, 'creator_name' => $task->creator_name, 'created_at' => $task->created_at, 'assignee_names' => $task->assignee_names ?? $this->resolveAssigneeNames($task, $usersById), 'total_duration' => $task->total_duration ?? '00:00:00', 'comments' => [], 'duration' => [], ]; } return $payload; } private function userCanAccessTask(Tasks $task, User $user): bool { if ($user->is_admin == 1) { return true; } $assignees = is_string($task->assignees) ? json_decode($task->assignees, true) : $task->assignees; $assignees = is_array($assignees) ? array_map('strval', $assignees) : []; return (int)$task->created_by === (int)$user->id || in_array((string)$user->id, $assignees, true); } private function buildTaskDetailPayload(Tasks $task, bool $isAdmin, User $user): array { $activeQuery = TaskDuration::query() ->where('task_id', $task->id) ->whereNull('end'); if (!$isAdmin) { $activeQuery->where('user_id', $user->id); } return [ 'id' => $task->id, 'title' => $task->title, 'description' => $task->description, 'start_date' => $task->start_date, 'target_date' => $task->target_date, 'priority' => $task->priority, 'state' => $task->state, 'app' => $task->app, 'active' => $activeQuery->exists(), 'creator_name' => $task->creator?->name ?? 'نامشخص', 'created_at' => $task->created_at, 'assignee_names' => $this->resolveAssigneeNames($task), 'duration' => $task->duration->map(fn ($d) => [ 'id' => $d->id, 'task_id' => $d->task_id, 'user_id' => $d->user_id, 'start' => $d->start, 'end' => $d->end, 'duration' => $d->duration, ])->values()->all(), 'comments' => $task->comments->map(fn ($c) => [ 'id' => $c->id, 'body' => $c->body, 'created_at' => $c->created_at, 'user' => $c->user ? ['name' => $c->user->name] : null, ])->values()->all(), ]; } private function loadTasksPageData(User $user): array { $isAdmin = $user->is_admin == 1; $query = Tasks::query() ->select([ 'id', 'title', 'description', 'start_date', 'target_date', 'priority', 'state', 'app', 'created_by', 'assignees', 'created_at', 'updated_at', ]) ->with([ 'appName:id,name_fa,type', 'creator:id,name', ]) ->orderByRaw('target_date IS NULL') ->orderBy('target_date', 'asc') ->orderBy('updated_at', 'desc'); if (!$isAdmin) { $query->where(function ($q) use ($user) { $q->where('created_by', $user->id) ->orWhereJsonContains('assignees', (string)$user->id); }); } $tasks = $query->get(); $taskIds = $tasks->pluck('id'); $assigneeIds = []; foreach ($tasks as $task) { $ids = is_string($task->assignees) ? json_decode($task->assignees, true) : $task->assignees; if (is_array($ids)) { $assigneeIds = array_merge($assigneeIds, array_map('strval', $ids)); } } $usersById = !empty($assigneeIds) ? User::whereIn('id', array_unique($assigneeIds)) ->get() ->mapWithKeys(fn ($user) => [(string)$user->id => $user->name]) ->all() : []; if ($taskIds->isEmpty()) { return [ 'tasks' => $tasks, 'tasksPayload' => [], 'assigneeFilterOptions' => [], ]; } $activeQuery = TaskDuration::query() ->select(['task_id']) ->whereIn('task_id', $taskIds) ->whereNull('end'); if (!$isAdmin) { $activeQuery->where('user_id', $user->id); } $activeTaskIds = $activeQuery->pluck('task_id')->flip(); $durationQuery = TaskDuration::query() ->select(['task_id', 'user_id', 'duration']) ->whereIn('task_id', $taskIds) ->whereNotNull('duration'); if (!$isAdmin) { $durationQuery->where('user_id', $user->id); } $durationsByTaskId = $durationQuery->get()->groupBy('task_id'); foreach ($tasks as $task) { $task->active = $activeTaskIds->has($task->id); $task->creator_name = $task->creator?->name ?? 'نامشخص'; $task->assignee_names = $this->resolveAssigneeNames($task, $usersById); $ids = is_string($task->assignees) ? json_decode($task->assignees, true) : $task->assignees; $task->assignee_ids = is_array($ids) ? array_map('strval', $ids) : []; $totalSeconds = $this->sumDurationSeconds( $durationsByTaskId->get($task->id, collect()), $isAdmin ? null : $user->id ); $task->total_duration = CarbonInterval::seconds($totalSeconds)->cascade()->format('%H:%I:%S'); } $assigneeFilterOptions = collect($usersById) ->map(fn ($name, $id) => ['id' => (string)$id, 'name' => $name]) ->sortBy('name', SORT_NATURAL) ->values() ->all(); return [ 'tasks' => $tasks, 'tasksPayload' => $this->buildTasksPayload($tasks, $usersById), 'assigneeFilterOptions' => $assigneeFilterOptions, ]; } // public function tasks() // { // $user = User::where('user_name', $_COOKIE['user_identity'])->first(); // $tasks = $user->tasks; // $users = User::get(); // // $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", // ]; // // $work_item = $tasks->whereNull('parent')->sortBy(fn($t) => array_search($t->state, $state_order)); // $sub_work_items = $tasks->whereNotNull('parent')->sortBy(fn($t) => array_search($t->state, $state_order)); // // foreach ($work_item as $w) { // foreach ($sub_work_items as $t) { // foreach ($users as $u) { // if ($t->created_by == $u->plain_id) // $t->created_by = $u->name; // if ($w->created_by == $u->plain_id) // $w->created_by = $u->name; // } // } // } // // return view('tasks.tasks', compact('tasks', 'work_item', 'sub_work_items')); // } public function tasks() { if (!isset($_COOKIE['user_identity'])) { return redirect('/')->with('message', [ 'color' => 'red', 'text' => 'ابتدا باید وارد شوید!' ]); } $user = User::where('user_name', $_COOKIE['user_identity'])->first(); if ($user) { ['tasks' => $tasks, 'tasksPayload' => $tasksPayload, 'assigneeFilterOptions' => $assigneeFilterOptions] = $this->loadTasksPageData($user); $count = $tasks->count(); return view('tasks.tasks', [ 'tasks' => (new Tasks())->filterByState($tasks), 'tasksPayload' => $tasksPayload, 'assigneeFilterOptions' => $assigneeFilterOptions, 'count' => $count, 'user_id' => $user->id, 'isAdminLevel1' => $user->is_admin == 1, 'mentionUsers' => User::select('id', 'name', 'user_name')->get(), 'appsList' => App::select('id', 'name_fa', 'type')->get(), 'priorityLabels' => [ 'urgent' => 'فوری', 'high' => 'بالا', 'medium' => 'متوسط', 'low' => 'پایین' ], 'priorityColors' => [ 'urgent' => 'bg-red-100 text-red-800', 'high' => 'bg-orange-100 text-orange-800', 'medium' => 'bg-yellow-100 text-yellow-800', 'low' => 'bg-green-100 text-green-800' ], ]); } else { return back()->with('message', [ 'color' => 'red', 'text' => 'کاربر یافت نشد!' ]); } } public function showTask($id) { $task = Tasks::with(['comments.user'])->find($id ?? null); return view('tasks.create', ['task' => $task]); } public function taskDetailsJson($id) { if (!isset($_COOKIE['user_identity'])) { return response()->json(['error' => 'Unauthorized'], 401); } $user = User::where('user_name', $_COOKIE['user_identity'])->first(); if (!$user) { return response()->json(['error' => 'User not found'], 404); } $isAdmin = $user->is_admin == 1; $task = Tasks::query() ->with([ 'creator:id,name', 'comments.user:id,name', 'duration' => function ($q) use ($isAdmin, $user) { if (!$isAdmin) { $q->where('user_id', $user->id); } }, ]) ->find($id); if (!$task || !$this->userCanAccessTask($task, $user)) { return response()->json(['error' => 'Not found'], 404); } return response()->json($this->buildTaskDetailPayload($task, $isAdmin, $user)); } public function editTask(Request $request) { // dd($request->all()); if (!isset($_COOKIE['user_identity'])) { return back()->with('message', [ 'color' => 'red', 'text' => 'ابتدا باید وارد شوید!' ]); } $actor = User::where('user_name', $_COOKIE['user_identity'])->first(); $data = $request->all(); $task = Tasks::find($data['id'] ?? null); if (!$task) { return back()->with('message', [ 'color' => 'red', 'text' => 'وظیفه یافت نشد!' ]); } $before = $task->toArray(); $data['updated_by'] = $actor?->id; if (array_key_exists('assignees', $data)) { $data['assignees'] = !empty($data['assignees']) ? json_encode($data['assignees']) : null; } else { unset($data['assignees']); } unset($data['_token'], $data['id'], $data['start_date_display'], $data['target_date_display']); $task->update($data); $after = $task->toArray(); $title = $task->title; $message = "تسک «{$title}» برای شما به‌روزرسانی شد."; $message_admin = $this->taskChangeMessage($task, $before, $after, $actor); if (!empty($request->assignees)) { foreach ($request->assignees as $assignee) { $assigneeUser = User::where('id', $assignee)->first(); if ($assigneeUser && $assigneeUser->bale !== null) { $this->send_bale($assigneeUser->bale, $message); } } } // dd($data, $message, $message_admin); $this->send_level1_admins($message_admin); return back()->with('message', [ 'color' => 'green', 'text' => $message ]); } public function createTask() { return view('tasks.create'); } public function deleteTask($id) { Tasks::destroy($id); return response()->json([ "success" => true, "message" => "Task deleted successfully.", "task_id" => 45 ]); } public function storeTask(Request $request) { // dd($request->all()); if (!isset($_COOKIE['user_identity'])) { return back()->with('message', [ 'color' => 'red', 'text' => 'ابتدا باید وارد شوید!' ]); } $user = User::where('user_name', $_COOKIE['user_identity'])->first(); $data = $request->all(); $data['created_by'] = $user->id; $data['assignees'] = !empty($data['assignees']) ? json_encode($data['assignees']) : null; $task = Tasks::create($data); $message = "تسک جدیدی با عنوان {$data['title']} برای شما ایجاد شد."; $deadline = !empty($data['target_date']) ? Jalalian::fromCarbon(Carbon::parse($data['target_date']))->format('Y-m-d') : null; $app = App::find($data['app']); $message_admin = "ایجاد تسک: «{$data['title']}»"; $message_admin .= !empty($app) ? "\nاپ {$app->name_fa}" : null; $message_admin .= !empty($deadline) ? "\nددلاین {$deadline}" : null; $message_admin .= !empty($data['start_date']) ? "\nتاریخ شروع {$data['start_date']}" : null; $message_admin .= !empty($data['state_fa']) ? "\nوضعیت: {$data['state_fa']}" : null; $message_admin .= !empty($data['description']) ? "\nبا توضیحات:\n" . $data['description'] : null; $message_admin .= !empty($data['assignees']) ? "\nتوسط " . $user->name . " ساخته شد و به" : null; if (!empty($data['assignees'])) { foreach ($request->assignees as $assignee) { $user = User::where('id', $assignee)->first(); $message_admin .= "\n$user->name"; if ($user && $user->bale !== null) { $this->send_bale($user->bale, $message); } } $message_admin .= "\nاختصاص یافت."; } else { $message_admin .= "\nتوسط " . $user->name . " ساخته شد"; } // dd($data, $message, $message_admin); $this->send_level1_admins($message_admin); return redirect('/tasks')->with('message', [ 'color' => 'green', 'text' => $message ]); } public function activeTask(Request $request) { // dd($request); if (!isset($_COOKIE['user_identity'])) { return response()->json(['action' => 'unauthorized', 'status' => false], 401); } $user = User::where('user_name', $_COOKIE['user_identity'])->first(); $task = Tasks::find($request->task_id); if (!$task) { return response()->json([ 'action' => 'not_found', 'status' => false ]); } $taskDU = TaskDuration::where('user_id', $user->id) ->where('task_id', $request->task_id) ->orderBy('id', 'desc') ->first(); // اگر رکورد باز داریم => پایان if (!empty($taskDU) && ($taskDU->start !== null && $taskDU->end == null)) { // dd('in if'); $attendance = new Attendance(); $s = Carbon::parse($taskDU->start)->format('H:i:s'); $e = Carbon::parse(now())->format('H:i:s'); $duration = $taskDU->duration; if ($e > $s) { $diff = $attendance->duration($s, $e); if ($duration !== null) { [$h1, $m1, $s1] = explode(':', $duration); $durationSeconds = $h1 * 3600 + $m1 * 60 + $s1; [$h2, $m2, $s2] = explode(':', $diff); $diffSeconds = $h2 * 3600 + $m2 * 60 + $s2; $totalSeconds = $durationSeconds + $diffSeconds; $hours = floor($totalSeconds / 3600); $minutes = floor(($totalSeconds % 3600) / 60); $seconds = $totalSeconds % 60; $duration = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds); } else { $duration = $diff; } $taskDU->end = now(); $taskDU->duration = $duration; $user->active_task = null; $taskDU->save(); $user->save(); $this->send_level1_admins("پایان تسک: «{$task->title}»\nتوسط: {$user->name}\nمدت این نوبت: {$duration}"); return response()->json([ 'action' => 'ended', 'status' => true, 'duration' => $duration, 'start' => $taskDU->start, 'end' => $taskDU->end, ]); } else return response()->json([ 'action' => 'end', 'status' => false ]); } else { // شروع => همیشه یک رکورد جدید بساز $new_task_duration = TaskDuration::create([ 'task_id' => $task->id, 'user_id' => $user->id, 'start' => now(), 'end' => null, ]); $user->active_task = $task->title; $user->save(); $this->send_level1_admins("شروع تسک: «{$task->title}»\nتوسط: {$user->name}"); return response()->json([ 'action' => 'start', 'status' => true, 'start' => $new_task_duration->start, 'id' => $new_task_duration->id, ]); } return back()->with('message', ['color' => 'red', 'text' => 'وظیفه یافت نشد✖️']); } public function addTask(Request $request) { if (isset($_COOKIE['user_identity'])) { $user = User::where('user_name', $_COOKIE['user_identity'])->first(); } else { return back()->with('message', [ 'color' => 'red', 'text' => 'auth failed!' ]); } $request->validate([ 'title' => 'required', 'description' => 'required', 'assignees' => 'required', 'start_date' => 'required', 'target_date' => 'required', 'priority' => 'required', 'description_html' => 'required', 'completed_at' => 'required', 'state' => 'required', 'created_at' => 'required', 'updated_at' => 'required', 'app' => 'required', 'task_type' => 'required', ]); $data = $request->all(); $data['user_id'] = $user->id; Tasks::create([$data]); return back()->with('message', [ 'color' => 'green', 'text' => 'وظیفه ایجاد شد.' ]); } public function storeComment(Request $request, $id) { if (!isset($_COOKIE['user_identity'])) { return back()->with('message', [ 'color' => 'red', 'text' => 'ابتدا باید وارد شوید!' ]); } $actor = User::where('user_name', $_COOKIE['user_identity'])->first(); $task = Tasks::find($id ?? null); if (!$task) { return back()->with('message', [ 'color' => 'red', 'text' => 'وظیفه یافت نشد!' ]); } $request->validate([ 'body' => 'required|string', ]); $body = trim((string)$request->input('body')); preg_match_all('/@([\p{L}\p{N}_.-]+)/u', $body, $matches); $usernames = array_values(array_unique($matches[1] ?? [])); $mentionedUsers = !empty($usernames) ? User::whereIn('user_name', $usernames)->get() : collect(); $mentionedIds = $mentionedUsers->pluck('id')->values()->all(); TaskComment::create([ 'task_id' => $task->id, 'user_id' => $actor->id, 'body' => $body, 'mentioned_user_ids' => !empty($mentionedIds) ? $mentionedIds : null, ]); $text = "کامنت جدید روی تسک «{$task->title}»\n" . "توسط: {$actor->name}\n\n" . $body; foreach ($mentionedUsers as $mu) { if ($mu->id === $actor->id) { continue; } if ($mu->bale) { $this->send_bale($mu->bale, $text); } } $this->send_level1_admins($text); return back()->with('message', [ 'color' => 'green', 'text' => 'کامنت ثبت شد.' ]); } public function webhookPlane(Request $request) { $d = $request->all(); // فقط وقتی تغییر state هست if (isset($d['event'], $d['action'], $d['data']['name'], $d['data']['state']['name'])) { $msg = "Event: {$d['event']}\nAction: {$d['action']}\nName: {$d['data']['name']}\nNew Status: {$d['data']['state']['name']}"; // ارسال به تلگرام Http::get("https://api.wwwapp.ir/telegran_send.php", [ 'botToken' => "7467099619:AAF2V6yYcbAb8oSIRSCyH5i2GKfdWVO3a7k", 'chatId' => "-4742642674", 'message' => $msg, ]); } // اگر خواستی ثبت در دیتابیس هم اضافه کنی // DB::table('tst')->insert([ // 'data' => json_encode($d, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), // ]); return response()->json(['status' => 'ok'], 200); } private function send_bale($chat_id, $message) { try { Http::withoutVerifying()->post('https://tapi.bale.ai/1482838710:sYbLzby0tuLRzHvP4d3LsbcNwg2sjSDLTAU/sendMessage', [ "chat_id" => $chat_id, 'text' => $message, ]); } catch (\Exception $e) { } } }