PM_Console/backend/app/Services/CalendarService.php

183 خطوط
8.9 KiB
PHP

<?php
namespace App\Services;
use App\Models\ActionItem;
use App\Models\Meeting;
use App\Models\Project;
use App\Models\Sprint;
use App\Models\Task;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Collection;
class CalendarService
{
public function __construct(private readonly ResourceAccessService $access) {}
public function events(User $user, Carbon $from, Carbon $to, array $types = []): Collection
{
$enabled = fn (string $type): bool => $types === [] || in_array($type, $types, true);
$events = collect();
if ($enabled('meeting')) {
$this->access->meetings($user)->with(['project:id,title', 'sprint:id,title'])
->whereBetween('date', [$from->toDateString(), $to->toDateString()])
->orderBy('date')
->orderBy('start_time')
->get()
->each(function (Meeting $meeting) use ($events) {
$start = $meeting->date->format('Y-m-d').'T'.($meeting->start_time ?: '00:00:00');
$end = $meeting->end_time ? $meeting->date->format('Y-m-d').'T'.$meeting->end_time : null;
$events->push([
'id' => "meeting-{$meeting->id}",
'event_type' => 'meeting',
'entity_type' => 'meeting',
'entity_id' => $meeting->id,
'title' => $meeting->title,
'start_at' => $start,
'end_at' => $end,
'all_day' => ! $meeting->start_time,
'status' => $meeting->status ?? 'scheduled',
'color_token' => 'info',
'project' => $meeting->project?->only(['id', 'title']),
'sprint' => $meeting->sprint?->only(['id', 'title']),
'preview' => [
'زمان' => $meeting->start_time ? substr($meeting->start_time, 0, 5) : 'تمام روز',
'مکان' => $meeting->location,
'هدف' => $meeting->objective,
],
'target_url' => "/meetings/{$meeting->id}/workspace",
]);
});
}
if ($enabled('task')) {
$this->access->tasks($user)->with('project:id,title')
->whereNotNull('due_date')
->whereBetween('due_date', [$from->toDateString(), $to->toDateString()])
->where('status', '!=', 'done')
->orderBy('due_date')
->get()
->each(fn (Task $task) => $events->push([
'id' => "task-{$task->id}",
'event_type' => 'task',
'entity_type' => 'task',
'entity_id' => $task->id,
'title' => $task->title,
'start_at' => $task->due_date?->format('Y-m-d'),
'end_at' => null,
'all_day' => true,
'status' => $task->status,
'color_token' => $task->priority === 'urgent' ? 'danger' : 'warning',
'project' => $task->project?->only(['id', 'title']),
'sprint' => null,
'preview' => [
'وضعیت' => $task->status,
'اولویت' => $task->priority,
],
'target_url' => "/tasks?preview={$task->id}",
]));
}
if ($enabled('sprint')) {
$this->access->sprints($user)->with('project:id,title')
->where(function ($query) use ($from, $to) {
$query->whereBetween('start_date', [$from->toDateString(), $to->toDateString()])
->orWhereBetween('end_date', [$from->toDateString(), $to->toDateString()]);
})
->get()
->each(function (Sprint $sprint) use ($events, $from, $to) {
foreach ([['start_date', 'شروع'], ['end_date', 'پایان']] as [$field, $label]) {
$date = $sprint->{$field};
if (! $date || $date->lt($from) || $date->gt($to)) {
continue;
}
$events->push([
'id' => "sprint-{$field}-{$sprint->id}",
'event_type' => 'sprint',
'entity_type' => 'sprint',
'entity_id' => $sprint->id,
'title' => "{$label} {$sprint->title}",
'start_at' => $date->format('Y-m-d'),
'end_at' => null,
'all_day' => true,
'status' => $sprint->status,
'color_token' => $field === 'start_date' ? 'success' : 'primary',
'project' => $sprint->project?->only(['id', 'title']),
'sprint' => $sprint->only(['id', 'title']),
'preview' => [
'هدف' => $sprint->goal,
'ظرفیت' => $sprint->capacity_hours,
],
'target_url' => "/sprints/{$sprint->id}/workspace",
]);
}
});
}
if ($enabled('project')) {
$this->access->projects($user)->where(function ($query) use ($from, $to) {
$query->whereBetween('start_date', [$from->toDateString(), $to->toDateString()])
->orWhereBetween('end_date', [$from->toDateString(), $to->toDateString()]);
})->get()->each(function (Project $project) use ($events, $from, $to) {
foreach ([['start_date', 'شروع'], ['end_date', 'پایان']] as [$field, $label]) {
$date = $project->{$field};
if (! $date || Carbon::parse($date)->lt($from) || Carbon::parse($date)->gt($to)) {
continue;
}
$events->push([
'id' => "project-{$field}-{$project->id}",
'event_type' => 'project',
'entity_type' => 'project',
'entity_id' => $project->id,
'title' => "{$label} {$project->title}",
'start_at' => Carbon::parse($date)->format('Y-m-d'),
'end_at' => null,
'all_day' => true,
'status' => $project->status,
'color_token' => 'secondary',
'project' => $project->only(['id', 'title']),
'sprint' => null,
'preview' => ['پیشرفت' => "{$project->progress}%"],
'target_url' => "/projects/{$project->id}",
]);
}
});
}
if ($enabled('action_item') && class_exists(ActionItem::class)) {
ActionItem::with(['project:id,title', 'sprint:id,title'])
->when(! $this->access->canSeeAll($user), function ($query) use ($user) {
$query->where(function ($scope) use ($user) {
$scope->where('owner_id', $user->id)
->orWhere('created_by', $user->id)
->orWhereIn('project_id', $this->access->projects($user)->select('projects.id'));
});
})
->whereNotNull('due_date')
->whereBetween('due_date', [$from->toDateString(), $to->toDateString()])
->whereNotIn('status', ['completed', 'cancelled'])
->get()
->each(fn (ActionItem $item) => $events->push([
'id' => "action-item-{$item->id}",
'event_type' => 'action_item',
'entity_type' => 'action_item',
'entity_id' => $item->id,
'title' => $item->title,
'start_at' => $item->due_date?->format('Y-m-d'),
'end_at' => null,
'all_day' => true,
'status' => $item->status,
'color_token' => $item->due_date?->isPast() ? 'danger' : 'warning',
'project' => $item->project?->only(['id', 'title']),
'sprint' => $item->sprint?->only(['id', 'title']),
'preview' => ['اولویت' => $item->priority, 'وضعیت' => $item->status],
'target_url' => $item->sprint_id ? "/sprints/{$item->sprint_id}/workspace?tab=action-items" : '/meetings',
]));
}
return $events->sortBy('start_at')->values();
}
}