Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.73% covered (success)
97.73%
43 / 44
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
GetDealKanbanQuery
97.73% covered (success)
97.73%
43 / 44
50.00% covered (danger)
50.00%
1 / 2
4
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 execute
97.67% covered (success)
97.67%
42 / 43
0.00% covered (danger)
0.00%
0 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Sales\Application\Queries;
6
7use ImovelZapAi\Sales\Application\Services\DefaultPipelineSeeder;
8use ImovelZapAi\Sales\Infrastructure\Persistence\Eloquent\DealModel;
9
10final class GetDealKanbanQuery
11{
12    public function __construct(
13        private readonly DefaultPipelineSeeder $pipelineSeeder,
14    ) {}
15
16    /**
17     * @return array{pipeline_id: string, columns: list<array<string, mixed>>}
18     */
19    public function execute(string $tenantId): array
20    {
21        $pipeline = $this->pipelineSeeder->ensureForTenant($tenantId);
22        $stages = $pipeline->stages;
23
24        $deals = DealModel::query()
25            ->where('tenant_id', $tenantId)
26            ->where('pipeline_id', $pipeline->id)
27            ->with(['contact', 'property', 'responsible', 'stage'])
28            ->orderByDesc('last_activity_at')
29            ->limit(500)
30            ->get()
31            ->groupBy('stage_id');
32
33        $columns = [];
34        foreach ($stages as $stage) {
35            $cards = ($deals->get($stage->id) ?? collect())->map(function (DealModel $deal) use ($stage) {
36                $stalledHours = $deal->stage_entered_at
37                    ? (int) $deal->stage_entered_at->diffInHours(now())
38                    : null;
39
40                return [
41                    'id' => $deal->id,
42                    'title' => $deal->title,
43                    'contact' => $deal->contact?->display_name,
44                    'property' => $deal->property?->title,
45                    'amount' => $deal->amount,
46                    'responsible' => $deal->responsible?->name,
47                    'stage' => $stage->name,
48                    'priority' => $deal->priority->value ?? $deal->priority,
49                    'next_action_at' => $deal->next_action_at?->toIso8601String(),
50                    'next_action_type' => $deal->next_action_type->value ?? $deal->next_action_type,
51                    'stalled_hours' => $stalledHours,
52                ];
53            })->values()->all();
54
55            $columns[] = [
56                'stage_id' => $stage->id,
57                'key' => $stage->key,
58                'name' => $stage->name,
59                'color' => $stage->color,
60                'is_won' => $stage->is_won,
61                'is_lost' => $stage->is_lost,
62                'cards' => $cards,
63            ];
64        }
65
66        return [
67            'pipeline_id' => $pipeline->id,
68            'columns' => $columns,
69        ];
70    }
71}