398 خطوط
14 KiB
PHP
398 خطوط
14 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\StoreTaskRequest;
|
|
use App\Http\Requests\UpdateTaskRequest;
|
|
use App\Http\Resources\TaskResource;
|
|
use App\Models\Task;
|
|
use App\Services\ActivityLogService;
|
|
use App\Services\NotificationService;
|
|
use App\Services\ProjectProgressService;
|
|
use App\Services\ResourceAccessService;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class TaskController extends Controller
|
|
{
|
|
public function __construct(
|
|
protected ActivityLogService $activityLogService,
|
|
protected NotificationService $notificationService,
|
|
protected ProjectProgressService $projectProgressService,
|
|
protected ResourceAccessService $resourceAccess,
|
|
) {}
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
try {
|
|
$query = $this->resourceAccess->tasks($request->user())
|
|
->with(['project', 'assignee', 'reporter'])
|
|
->withCount(['comments', 'files']);
|
|
|
|
if ($request->filled('project_id')) {
|
|
$query->where('project_id', $request->project_id);
|
|
}
|
|
if ($request->filled('assignee_id')) {
|
|
$query->where('assignee_id', $request->assignee_id);
|
|
}
|
|
if ($request->filled('status')) {
|
|
$query->where('status', $request->status);
|
|
}
|
|
if ($request->filled('priority')) {
|
|
$query->where('priority', $request->priority);
|
|
}
|
|
if ($request->filled('search')) {
|
|
$search = $request->search;
|
|
$query->where('title', 'like', "%{$search}%");
|
|
}
|
|
|
|
$perPage = min((int) $request->input('per_page', 15), 100);
|
|
$sortBy = in_array($request->input('sort_by'), ['id', 'title', 'status', 'priority', 'created_at', 'updated_at', 'due_date', 'sort_order'], true)
|
|
? $request->input('sort_by')
|
|
: 'created_at';
|
|
$sortDir = $request->input('sort_dir') === 'asc' ? 'asc' : 'desc';
|
|
$tasks = $query->orderBy($sortBy, $sortDir)->paginate($perPage);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => TaskResource::collection($tasks),
|
|
'meta' => [
|
|
'current_page' => $tasks->currentPage(),
|
|
'last_page' => $tasks->lastPage(),
|
|
'per_page' => $tasks->perPage(),
|
|
'total' => $tasks->total(),
|
|
],
|
|
'message' => 'لیست وظایف',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در دریافت لیست وظایف',
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function show(Task $task): JsonResponse
|
|
{
|
|
try {
|
|
$task->load(['project', 'assignee', 'reporter', 'checklists', 'subtasks', 'comments.user', 'files']);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => new TaskResource($task),
|
|
'message' => 'اطلاعات وظیفه',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در دریافت اطلاعات وظیفه',
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function store(StoreTaskRequest $request): JsonResponse
|
|
{
|
|
try {
|
|
$data = $request->validated();
|
|
abort_unless(
|
|
$this->resourceAccess->projects($request->user())->whereKey($data['project_id'])->exists(),
|
|
403,
|
|
'شما به پروژه انتخابشده دسترسی ندارید.'
|
|
);
|
|
$data['reporter_id'] = $request->user()->id;
|
|
$data['created_by'] = $request->user()->id;
|
|
$task = Task::create($data);
|
|
|
|
$this->projectProgressService->calculateProgress($task->project_id);
|
|
|
|
$this->activityLogService->log(
|
|
$request->user()->id,
|
|
'create_task',
|
|
"وظیفه {$task->title} ایجاد شد",
|
|
'task',
|
|
$task->id,
|
|
$task->project_id,
|
|
$task->id,
|
|
['task_title' => $task->title]
|
|
);
|
|
|
|
if ($task->assignee_id) {
|
|
$this->notificationService->create(
|
|
$task->assignee_id,
|
|
'task_assigned',
|
|
[
|
|
'task_id' => $task->id,
|
|
'project_id' => $task->project_id,
|
|
'url' => '/kanban',
|
|
'notifiable_type' => 'App\\Models\\Task',
|
|
'notifiable_id' => $task->id,
|
|
],
|
|
"وظیفه جدید {$task->title} به شما اختصاص داده شد"
|
|
);
|
|
}
|
|
|
|
$task->load(['project', 'assignee', 'reporter']);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => new TaskResource($task),
|
|
'message' => 'وظیفه با موفقیت ایجاد شد',
|
|
], 201);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در ایجاد وظیفه',
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function update(UpdateTaskRequest $request, Task $task): JsonResponse
|
|
{
|
|
try {
|
|
$validated = $request->validated();
|
|
if (isset($validated['project_id'])) {
|
|
abort_unless(
|
|
$this->resourceAccess->projects($request->user())->whereKey($validated['project_id'])->exists(),
|
|
403,
|
|
'شما به پروژه انتخابشده دسترسی ندارید.'
|
|
);
|
|
}
|
|
$oldStatus = $task->status;
|
|
$task->update($validated);
|
|
|
|
if ($task->wasChanged('status')) {
|
|
$this->activityLogService->log(
|
|
$request->user()->id,
|
|
'update_task_status',
|
|
"وضعیت وظیفه {$task->title} از {$oldStatus} به {$task->status} تغییر یافت",
|
|
'task',
|
|
$task->id,
|
|
$task->project_id,
|
|
$task->id,
|
|
['old_status' => $oldStatus, 'new_status' => $task->status]
|
|
);
|
|
}
|
|
|
|
$this->projectProgressService->calculateProgress($task->project_id);
|
|
$task->load(['project', 'assignee', 'reporter']);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => new TaskResource($task->fresh()),
|
|
'message' => 'وظیفه با موفقیت بهروزرسانی شد',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در بهروزرسانی وظیفه',
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function destroy(Request $request, Task $task): JsonResponse
|
|
{
|
|
try {
|
|
$projectId = $task->project_id;
|
|
$taskId = $task->id;
|
|
$taskTitle = $task->title;
|
|
|
|
$this->activityLogService->log(
|
|
$request->user()->id,
|
|
'delete_task',
|
|
"وظیفه {$taskTitle} حذف شد",
|
|
'task',
|
|
$taskId,
|
|
$projectId,
|
|
null,
|
|
['task_title' => $taskTitle]
|
|
);
|
|
|
|
$task->delete();
|
|
$this->projectProgressService->calculateProgress($projectId);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'وظیفه با موفقیت حذف شد',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در حذف وظیفه',
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function updateStatus(Request $request, Task $task): JsonResponse
|
|
{
|
|
try {
|
|
$request->validate(['status' => 'required|string']);
|
|
|
|
$oldStatus = $task->status;
|
|
$task->update(['status' => $request->status]);
|
|
|
|
$this->projectProgressService->calculateProgress($task->project_id);
|
|
|
|
$this->activityLogService->log(
|
|
$request->user()->id,
|
|
'update_task_status',
|
|
"وضعیت وظیفه {$task->title} از {$oldStatus} به {$task->status} تغییر یافت",
|
|
'task',
|
|
$task->id,
|
|
$task->project_id,
|
|
$task->id,
|
|
['old_status' => $oldStatus, 'new_status' => $task->status]
|
|
);
|
|
|
|
if ($task->assignee_id) {
|
|
$this->notificationService->create(
|
|
$task->assignee_id,
|
|
'task_status_changed',
|
|
['task_id' => $task->id, 'status' => $task->status],
|
|
"وضعیت وظیفه {$task->title} به {$task->status} تغییر یافت"
|
|
);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => new TaskResource($task->fresh()->load(['project', 'assignee'])),
|
|
'message' => 'وضعیت وظیفه بهروزرسانی شد',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در بهروزرسانی وضعیت',
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function updateAssignee(Request $request, Task $task): JsonResponse
|
|
{
|
|
$data = $request->validate(['assignee_id' => 'required|exists:users,id']);
|
|
$task->loadMissing('project.members:id');
|
|
|
|
$assigneeId = (int) $data['assignee_id'];
|
|
$isApprovedProjectMember = $assigneeId === (int) $task->project?->project_manager_id
|
|
|| $task->project?->members->contains('id', $assigneeId);
|
|
|
|
if (! $isApprovedProjectMember) {
|
|
throw ValidationException::withMessages([
|
|
'assignee_id' => 'مسئول جدید باید از اعضای تأییدشده همین پروژه باشد.',
|
|
]);
|
|
}
|
|
|
|
try {
|
|
$oldAssigneeId = $task->assignee_id;
|
|
$task->update(['assignee_id' => $assigneeId]);
|
|
|
|
if ($assigneeId !== (int) $oldAssigneeId) {
|
|
$this->notificationService->create(
|
|
$assigneeId,
|
|
'task_assigned',
|
|
[
|
|
'task_id' => $task->id,
|
|
'project_id' => $task->project_id,
|
|
'url' => '/kanban',
|
|
'notifiable_type' => 'App\\Models\\Task',
|
|
'notifiable_id' => $task->id,
|
|
],
|
|
"وظیفه {$task->title} به شما اختصاص داده شد"
|
|
);
|
|
}
|
|
|
|
$this->activityLogService->log(
|
|
$request->user()->id,
|
|
'update_task_assignee',
|
|
"مسئول وظیفه {$task->title} تغییر یافت",
|
|
'task',
|
|
$task->id,
|
|
$task->project_id,
|
|
$task->id,
|
|
['old_assignee' => $oldAssigneeId, 'new_assignee' => $assigneeId]
|
|
);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => new TaskResource($task->fresh()->load(['project', 'assignee'])),
|
|
'message' => 'مسئول وظیفه بهروزرسانی شد',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در بهروزرسانی مسئول',
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function reorder(Request $request): JsonResponse
|
|
{
|
|
try {
|
|
$request->validate([
|
|
'tasks' => 'required|array',
|
|
'tasks.*.id' => 'required|exists:tasks,id',
|
|
'tasks.*.sort_order' => 'required|integer',
|
|
]);
|
|
|
|
foreach ($request->tasks as $item) {
|
|
Task::where('id', $item['id'])->update(['sort_order' => $item['sort_order']]);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'ترتیب وظایف بهروزرسانی شد',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در مرتبسازی وظایف',
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function myTasks(Request $request): JsonResponse
|
|
{
|
|
try {
|
|
$tasks = Task::with(['project', 'assignee'])
|
|
->where('assignee_id', $request->user()->id)
|
|
->where('status', '!=', 'done')
|
|
->orderBy('due_date')
|
|
->get();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => TaskResource::collection($tasks),
|
|
'message' => 'وظایف من',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در دریافت وظایف من',
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function delayedTasks(): JsonResponse
|
|
{
|
|
try {
|
|
$tasks = Task::with(['project', 'assignee'])
|
|
->where('due_date', '<', Carbon::now())
|
|
->where('status', '!=', 'done')
|
|
->orderBy('due_date')
|
|
->get();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => TaskResource::collection($tasks),
|
|
'message' => 'وظایف عقب افتاده',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در دریافت وظایف عقب افتاده',
|
|
], 500);
|
|
}
|
|
}
|
|
}
|