183 خطوط
8.9 KiB
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();
|
|
}
|
|
}
|