Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
GetListingKanbanQuery
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
2 / 2
5
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Owners\Application\Queries;
6
7use ImovelZapAi\Owners\Application\Services\DefaultListingPipelineSeeder;
8use ImovelZapAi\Owners\Infrastructure\Persistence\Eloquent\ListingOpportunityModel;
9
10final class GetListingKanbanQuery
11{
12    public function __construct(
13        private readonly DefaultListingPipelineSeeder $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        $grouped = ListingOpportunityModel::query()
23            ->where('tenant_id', $tenantId)
24            ->where('pipeline_id', $pipeline->id)
25            ->with(['owner', 'property', 'responsible', 'stage'])
26            ->orderByDesc('last_activity_at')
27            ->limit(500)
28            ->get()
29            ->groupBy('stage_id');
30
31        $columns = [];
32        foreach ($pipeline->stages as $stage) {
33            $cards = ($grouped->get($stage->id) ?? collect())->map(fn (ListingOpportunityModel $opp) => [
34                'id' => $opp->id,
35                'title' => $opp->title,
36                'owner' => $opp->owner?->name,
37                'property' => $opp->property?->title,
38                'asking' => $opp->owner_asking_price,
39                'recommended' => $opp->agency_recommended_price,
40                'responsible' => $opp->responsible?->name,
41                'stalled_hours' => $opp->stage_entered_at
42                    ? (int) $opp->stage_entered_at->diffInHours(now())
43                    : null,
44            ])->values()->all();
45
46            $columns[] = [
47                'stage_id' => $stage->id,
48                'key' => $stage->key instanceof \BackedEnum ? $stage->key->value : $stage->key,
49                'name' => $stage->name,
50                'color' => $stage->color,
51                'cards' => $cards,
52            ];
53        }
54
55        return ['pipeline_id' => $pipeline->id, 'columns' => $columns];
56    }
57}