Pārlūkot izejas kodu

feat: add bloqueio automatico de foto de perfil caso nao aprovada e bloqueia novamente mediante atualizacao de foto

Gustavo Mantovani 1 nedēļu atpakaļ
vecāks
revīzija
8c1859762e

+ 9 - 0
app/Enums/GenderEnum.php

@@ -13,6 +13,15 @@ enum GenderEnum: string
     case OTHER             = 'other';
     case PREFER_NOT_TO_SAY = 'prefer_not_to_say';
 
+    public static function labelFor(self|string|null $gender): ?string
+    {
+        if (is_string($gender)) {
+            $gender = self::tryFrom($gender);
+        }
+
+        return $gender?->label();
+    }
+
     public function label(): string
     {
         return __("genders.{$this->value}");

+ 6 - 6
app/Http/Controllers/UserController.php

@@ -97,12 +97,12 @@ class UserController extends Controller
             'user_id'           => auth()->id(),
             'has_avatar'        => $hasAvatar,
             'has_avatar_base64' => $hasBase64,
-            'avatar_base64_len' => $hasBase64 ? strlen(data_get($data, 'avatar_base64'))        : null,
-            'avatar_valid'      => $hasAvatar ? $avatar->isValid()                    : null,
-            'avatar_size'       => $hasAvatar ? $avatar->getSize()                    : null,
-            'avatar_mime'       => $hasAvatar ? $avatar->getMimeType()                : null,
-            'avatar_extension'  => $hasAvatar ? $avatar->getClientOriginalExtension() : null,
-            'avatar_error'      => $hasAvatar ? $avatar->getError()                   : null,
+            'avatar_base64_len' => $hasBase64 ? strlen(data_get($data, 'avatar_base64')) : null,
+            'avatar_valid'      => $hasAvatar ? $avatar->isValid()                       : null,
+            'avatar_size'       => $hasAvatar ? $avatar->getSize()                       : null,
+            'avatar_mime'       => $hasAvatar ? $avatar->getMimeType()                   : null,
+            'avatar_extension'  => $hasAvatar ? $avatar->getClientOriginalExtension()    : null,
+            'avatar_error'      => $hasAvatar ? $avatar->getError()                      : null,
             'method'            => $request->method(),
             'content_type'      => $request->header('Content-Type'),
         ]);

+ 11 - 5
app/Http/Requests/ProviderRequest.php

@@ -63,11 +63,17 @@ class ProviderRequest extends FormRequest
                 },
             ],
 
-            'average_rating'         => 'sometimes|nullable|numeric|min:0|max:5',
-            'total_services'         => 'sometimes|integer|min:0',
-            'birth_date'             => 'sometimes|nullable|date|before:today',
-            'gender'                 => ['sometimes', 'nullable', Rule::enum(GenderEnum::class)],
-            'selfie_verified'        => 'sometimes|boolean',
+            'average_rating' => 'sometimes|nullable|numeric|min:0|max:5',
+            'total_services' => 'sometimes|integer|min:0',
+            'birth_date'     => 'sometimes|nullable|date|before:today',
+            'gender'         => ['sometimes', 'nullable', Rule::enum(GenderEnum::class)],
+
+            'selfie_verified' => [
+                'sometimes',
+                'boolean',
+                Rule::prohibitedIf(fn () => ! $this->user()?->canModerateProviders()),
+            ],
+
             'document_verified'      => 'sometimes|boolean',
             'approval_status'        => ['sometimes', Rule::enum(ApprovalStatusEnum::class)],
             'daily_price_8h'         => "sometimes|nullable|numeric|min:{$dailyPriceMin}|max:500",

+ 3 - 4
app/Http/Resources/ClientFavoriteProviderResource.php

@@ -13,8 +13,7 @@ class ClientFavoriteProviderResource extends JsonResource
     {
         $provider = $this->resource->relationLoaded('provider') ? $this->resource->provider : null;
 
-        $profileMediaPath = $this->profile_media_path
-            ?? ($provider?->relationLoaded('profileMedia') ? $provider->profileMedia?->path : null);
+        $profileMediaPath = $provider?->relationLoaded('profileMedia') ? $provider->profileMedia?->path : null;
 
         $dailyPrice8h = $this->daily_price_8h ?? $provider?->daily_price_8h;
         $dailyPrice6h = $this->daily_price_6h ?? $provider?->daily_price_6h;
@@ -27,9 +26,9 @@ class ClientFavoriteProviderResource extends JsonResource
             'id'             => $this->id,
             'client_id'      => $this->client_id,
             'provider_id'    => $this->provider_id,
-            'provider_name'  => $this->provider_name  ?? ($provider?->relationLoaded('user') ? $provider->user?->name : null),
+            'provider_name'  => $this->provider_name ?? ($provider?->relationLoaded('user') ? $provider->user?->name : null),
             'gender'         => $gender,
-            'gender_label'   => $gender ? GenderEnum::tryFrom($gender)?->label() : null,
+            'gender_label'   => GenderEnum::labelFor($gender),
             'city_name'      => $this->city_name      ?? null,
             'average_rating' => $this->average_rating ?? $provider?->average_rating,
 

+ 1 - 1
app/Http/Resources/ClientProviderBlockResource.php

@@ -18,7 +18,7 @@ class ClientProviderBlockResource extends JsonResource
             'provider_id'       => $this->provider_id,
             'provider_name'     => $this->provider_name     ?? $this->provider?->user?->name,
             'gender'            => $gender,
-            'gender_label'      => $gender ? GenderEnum::tryFrom($gender)?->label() : null,
+            'gender_label'      => GenderEnum::labelFor($gender),
             'provider_email'    => $this->provider_email    ?? $this->provider?->user?->email,
             'provider_phone'    => $this->provider_phone    ?? $this->provider?->user?->phone,
             'provider_district' => $this->provider_district ?? null,

+ 1 - 1
app/Http/Resources/ProviderResource.php

@@ -30,7 +30,7 @@ class ProviderResource extends JsonResource
             'daily_price_6h'                 => $this->daily_price_6h,
             'daily_price_4h'                 => $this->daily_price_4h,
             'daily_price_2h'                 => $this->daily_price_2h,
-            'profile_media_id'               => $this->profile_media_id,
+            'profile_media_id'               => $this->profileMedia?->id,
             'recipient_id'                   => $this->recipient_id,
             'recipient_default_bank_account' => $this->recipient_default_bank_account,
             'profile_media'                  => $this->profileMedia ? new MediaResource($this->profileMedia) : null,

+ 5 - 1
app/Http/Resources/UserResource.php

@@ -46,7 +46,11 @@ class UserResource extends JsonResource
 
   private function resolveProfilePhoto(): ?string
   {
-    $path = $this->provider?->profileMedia?->path ?? $this->client?->profileMedia?->path;
+    if ($this->provider) {
+      $path = $this->provider->profileMedia?->path;
+    } else {
+      $path = $this->client?->profileMedia?->path;
+    }
 
     return $path ? Storage::temporaryUrl($path, now()->addMinutes(60)) : null;
   }

+ 17 - 1
app/Models/Provider.php

@@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use Illuminate\Database\Eloquent\Relations\HasMany;
 use Illuminate\Database\Eloquent\Relations\HasOne;
 use Illuminate\Database\Eloquent\SoftDeletes;
+use Illuminate\Support\Facades\Auth;
 
 /**
  * @property int $id
@@ -152,7 +153,22 @@ class Provider extends Model
 
     public function profileMedia(): BelongsTo
     {
-        return $this->belongsTo(Media::class, "profile_media_id");
+        $authenticatedUserId = Auth::id();
+
+        return $this->belongsTo(Media::class, 'profile_media_id')
+            ->join('providers as profile_media_providers', function ($join) {
+                $join->on('profile_media_providers.id', '=', 'media.source_id')
+                    ->on('profile_media_providers.profile_media_id', '=', 'media.id');
+            })
+            ->where('media.source', 'provider')
+            ->where(function ($query) use ($authenticatedUserId) {
+                $query->where('profile_media_providers.selfie_verified', true);
+
+                if ($authenticatedUserId !== null) {
+                    $query->orWhere('profile_media_providers.user_id', $authenticatedUserId);
+                }
+            })
+            ->select('media.*');
     }
 
     //

+ 17 - 15
app/Models/User.php

@@ -86,11 +86,18 @@ class User extends Authenticatable
         ];
     }
 
+    //
+
     public function isAdmin(): bool
     {
         return $this->type === UserTypeEnum::ADMIN;
     }
 
+    public function canModerateProviders(): bool
+    {
+        return $this->isAdmin();
+    }
+
     //
 
     public function client()
@@ -117,9 +124,8 @@ class User extends Authenticatable
 
     //
 
-    /**
-     * Create a new access token for the user.
-     */
+    // Create a new access token for the user.
+
     public function createAccessToken(string $deviceId): string
     {
         return $this->createToken(
@@ -129,9 +135,8 @@ class User extends Authenticatable
         )->plainTextToken;
     }
 
-    /**
-     * Create a new refresh token for the user.
-     */
+    // Create a new refresh token for the user.
+
     public function createRefreshToken(string $deviceId): string
     {
         return $this->createToken(
@@ -141,9 +146,8 @@ class User extends Authenticatable
         )->plainTextToken;
     }
 
-    /**
-     * Delete all tokens (access and refresh) for a specific device.
-     */
+    // Delete all tokens (access and refresh) for a specific device.
+
     public function deleteTokensByDevice(string $deviceId): void
     {
         $this->tokens()
@@ -161,9 +165,8 @@ class User extends Authenticatable
         );
     }
 
-    /**
-     * Create a new access token for the user in the app.
-     */
+    // Create a new access token for the user in the app.
+
     public function createAccessTokenApp(string $deviceId): string
     {
         return $this->createToken(
@@ -173,9 +176,8 @@ class User extends Authenticatable
         )->plainTextToken;
     }
 
-    /**
-     * Create a new refresh token for the user in the app.
-     */
+    // Create a new refresh token for the user in the app.
+
     public function createRefreshTokenApp(string $deviceId): string
     {
         return $this->createToken(

+ 15 - 0
app/Observers/ProviderObserver.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Observers;
+
+use App\Models\Provider;
+
+class ProviderObserver
+{
+    public function updating(Provider $provider): void
+    {
+        if ($provider->isDirty('profile_media_id')) {
+            $provider->selfie_verified = false;
+        }
+    }
+}

+ 4 - 0
app/Providers/AppServiceProvider.php

@@ -2,7 +2,9 @@
 
 namespace App\Providers;
 
+use App\Models\Provider;
 use App\Models\Schedule;
+use App\Observers\ProviderObserver;
 use App\Observers\ScheduleObserver;
 use Illuminate\Support\ServiceProvider;
 
@@ -30,6 +32,8 @@ class AppServiceProvider extends ServiceProvider
      */
     public function boot(): void
     {
+        Provider::observe(ProviderObserver::class);
+
         if ($this->app->environment(['local', 'development', 'dev'])) {
             Schedule::observe(ScheduleObserver::class);
         }

+ 31 - 22
app/Services/ClientCalendarService.php

@@ -4,6 +4,7 @@ namespace App\Services;
 
 use App\Enums\GenderEnum;
 use App\Models\Client;
+use App\Models\Provider;
 use App\Models\Schedule;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\DB;
@@ -36,17 +37,6 @@ class ClientCalendarService
             'providers.average_rating',
             'providers.total_services',
 
-            DB::raw("
-                (
-                    SELECT media.path
-                    FROM media
-                    WHERE media.source_id = providers.id
-                    AND media.source = 'provider'
-                    AND media.deleted_at IS NULL
-                    LIMIT 1
-                ) AS provider_photo
-            "),
-
             DB::raw("
                 EXISTS (
                     SELECT 1
@@ -71,14 +61,6 @@ class ClientCalendarService
             "),
         ];
 
-        $signPhoto = fn ($item) => tap($item, function ($i) {
-            $i->gender_label = $i->gender ? GenderEnum::tryFrom($i->gender)?->label() : null;
-
-            $i->provider_photo = $i->provider_photo
-                ? Storage::temporaryUrl($i->provider_photo, now()->addMinutes(60))
-                : null;
-        });
-
         $upcomingSchedules = Schedule::with('address:district,address,number,source_id,source,id')
             ->where('schedules.client_id', $client->id)
             ->whereIn('schedules.status', ['pending', 'accepted', 'paid', 'started'])
@@ -89,8 +71,7 @@ class ClientCalendarService
             ->select($selectFields)
             ->orderBy('schedules.date', 'asc')
             ->orderBy('schedules.start_time', 'asc')
-            ->get()
-            ->map($signPhoto);
+            ->get();
 
         $completedSchedules = Schedule::with('address:district,address,number,source_id,source,id')
             ->where('schedules.client_id', $client->id)
@@ -101,8 +82,36 @@ class ClientCalendarService
             ->select($selectFields)
             ->orderBy('schedules.date', 'desc')
             ->orderBy('schedules.start_time', 'desc')
+            ->get();
+
+        $providerPhotos = Provider::query()
+            ->select('id', 'profile_media_id')
+            ->with('profileMedia')
+            ->whereIn(
+                'id',
+                $upcomingSchedules->pluck('provider_id')
+                    ->merge($completedSchedules->pluck('provider_id'))
+                    ->filter()
+                    ->unique(),
+            )
             ->get()
-            ->map($signPhoto);
+            ->mapWithKeys(fn (Provider $provider) => [
+                $provider->id => $provider->profileMedia?->path,
+            ]);
+
+        $signPhoto = fn ($item) => tap($item, function ($i) use ($providerPhotos) {
+            $i->gender_label = GenderEnum::labelFor($i->gender);
+
+            $path = $providerPhotos->get($i->provider_id);
+
+            $i->provider_photo = $path
+                ? Storage::temporaryUrl($path, now()->addMinutes(60))
+                : null;
+        });
+
+        $upcomingSchedules->transform($signPhoto);
+
+        $completedSchedules->transform($signPhoto);
 
         return [
             'upcomingSchedules'  => $upcomingSchedules,

+ 1 - 2
app/Services/ClientFavoriteProviderService.php

@@ -11,12 +11,12 @@ class ClientFavoriteProviderService
     public function getByClientId(int $clientId): Collection
     {
         return ClientFavoriteProvider::where('client_favorite_providers.client_id', $clientId)
+            ->with('provider.profileMedia')
             ->leftJoin('providers', 'providers.id', '=', 'client_favorite_providers.provider_id')
             ->whereNotNull('providers.recipient_default_bank_account')
             ->whereRaw("providers.recipient_default_bank_account::text <> '{}'")
             ->whereRaw("providers.recipient_default_bank_account::text <> '[]'")
             ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
-            ->leftJoin('media as provider_profile_media', 'provider_profile_media.id', '=', 'providers.profile_media_id')
             ->leftJoin('addresses as provider_address', function ($join) {
                 $join->on('provider_address.source_id', '=', 'providers.id')
                     ->where('provider_address.source', 'provider')
@@ -32,7 +32,6 @@ class ClientFavoriteProviderService
                 'client_favorite_providers.updated_at',
                 'provider_user.name as provider_name',
                 'providers.gender',
-                'provider_profile_media.path as profile_media_path',
                 'providers.average_rating',
                 'providers.daily_price_8h',
                 'providers.daily_price_6h',

+ 6 - 9
app/Services/ClientService.php

@@ -78,13 +78,8 @@ class ClientService
         try {
             DB::beginTransaction();
 
-            $user = User::when(! empty(data_get($data, 'email')), function ($q) use ($data) {
-                $q->where('email', data_get($data, 'email'));
-            })
-                ->when(! empty(data_get($data, 'phone')), function ($q) use ($data) {
-                    $q->where('phone', data_get($data, 'phone'));
-                })
-                ->where('type', 'CLIENT')
+            $user = User::where('email', data_get($data, 'email'))
+                ->where('type', UserTypeEnum::CLIENT->value)
                 ->where('code', data_get($data, 'code'))
                 ->first();
 
@@ -94,10 +89,12 @@ class ClientService
 
             if (! empty(data_get($data, 'name'))) {
                 $user->name = data_get($data, 'name');
-
-                $user->save();
             }
 
+            $user->phone = data_get($data, 'phone');
+
+            $user->save();
+
             $client = Client::firstOrCreate(
                 ['user_id' => $user->id], ['document' => data_get($data, 'document')]
             );

+ 380 - 406
app/Services/DashboardService.php

@@ -17,6 +17,7 @@ use App\Models\ScheduleProposal;
 use App\Models\Speciality;
 use App\Rules\ScheduleBusinessRules;
 use Illuminate\Auth\Access\AuthorizationException;
+use Illuminate\Support\Collection;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
@@ -72,63 +73,55 @@ class DashboardService
         $nextSchedules = Schedule::with([
             'address:district,address,source_id,source,id,address_type',
         ])
-            ->where('schedules.client_id', $cliente->id)
-            ->whereIn('schedules.status', ['accepted', 'paid'])
-            ->whereDate('schedules.date', '>=', now()->toDateString())
-            ->where('schedules.date', '>=', now()->toDateString())
-            ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
-            ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
-            ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
-            ->leftJoin('media as provider_media', 'provider_media.id', '=', 'providers.profile_media_id')
-            ->select(
-                'schedules.id',
-                'schedules.provider_id',
-                'provider_user.name as provider_name',
-                'providers.gender',
-                'schedules.date',
-                'schedules.start_time',
-                'schedules.end_time',
-                'schedules.total_amount',
-                'schedules.period_type',
-                'schedules.schedule_type',
-                'schedules.address_id',
-                'custom_schedules.address_type as custom_address_type',
-                'provider_media.path as provider_photo_path',
-
-                DB::raw("
-                    (
+        ->where('schedules.client_id', $cliente->id)
+        ->whereIn('schedules.status', ['accepted', 'paid'])
+        ->whereDate('schedules.date', '>=', now()->toDateString())
+        ->where('schedules.date', '>=', now()->toDateString())
+        ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
+        ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
+        ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
+        ->select(
+            'schedules.id',
+            'schedules.provider_id',
+            'provider_user.name as provider_name',
+            'providers.gender',
+            'schedules.date',
+            'schedules.start_time',
+            'schedules.end_time',
+            'schedules.total_amount',
+            'schedules.period_type',
+            'schedules.schedule_type',
+            'schedules.address_id',
+            'custom_schedules.address_type as custom_address_type',
+
+            DB::raw("
+                (
+                    SELECT spi.service_package_id
+                    FROM service_package_items spi
+                    WHERE spi.schedule_id = schedules.id
+                    LIMIT 1
+                ) AS service_package_id
+            "),
+
+            DB::raw("
+                (
+                    SELECT COUNT(*)
+                    FROM service_package_items spi_count
+                    WHERE spi_count.service_package_id = (
                         SELECT spi.service_package_id
                         FROM service_package_items spi
                         WHERE spi.schedule_id = schedules.id
                         LIMIT 1
-                    ) AS service_package_id
-                "),
-
-                DB::raw("
-                    (
-                        SELECT COUNT(*)
-                        FROM service_package_items spi_count
-                        WHERE spi_count.service_package_id = (
-                            SELECT spi.service_package_id
-                            FROM service_package_items spi
-                            WHERE spi.schedule_id = schedules.id
-                            LIMIT 1
-                        )
-                    ) AS service_package_items_count
-                "),
-            )
-            ->orderBy('schedules.date', 'asc')
-            ->limit(5)
-            ->get();
+                    )
+                ) AS service_package_items_count
+            "),
+        )
+        ->orderBy('schedules.date', 'asc')
+        ->limit(5)
+        ->get();
 
         $nextSchedules->each(function ($item) {
-            $item->gender_label = $item->gender ? GenderEnum::tryFrom($item->gender)?->label() : null;
-
-            $item->provider_photo = $item->provider_photo_path
-                ? Storage::temporaryUrl($item->provider_photo_path, now()->addMinutes(60))
-                : null;
-
-            unset($item->provider_photo_path);
+            $item->gender_label = GenderEnum::labelFor($item->gender);
         });
 
         $latestPerProvider = Schedule::where('client_id', $cliente->id)
@@ -139,36 +132,28 @@ class DashboardService
         $lastDoneSchedules = Schedule::joinSub($latestPerProvider, 'latest', function ($join) {
             $join->on('schedules.id', '=', 'latest.max_id');
         })
-            ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
-            ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
-            ->leftJoinSub(
-                Address::preferredForProvider(),
-                'provider_address',
-                fn($join) =>
-                $join->on('provider_address.source_id', '=', 'providers.id')
-                    ->where('provider_address.rn', 1)
-            )
-            ->leftJoin('media as provider_media', 'provider_media.id', '=', 'providers.profile_media_id')
-            ->select(
-                'schedules.id',
-                'schedules.provider_id',
-                'provider_user.name as provider_name',
-                'providers.gender',
-                'provider_address.district as provider_district',
-                'provider_media.path as provider_photo_path',
-            )
-            ->orderBy('schedules.date', 'desc')
-            ->limit(5)
-            ->get();
+        ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
+        ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
+        ->leftJoinSub(
+            Address::preferredForProvider(),
+            'provider_address',
+            fn($join) =>
+            $join->on('provider_address.source_id', '=', 'providers.id')
+                ->where('provider_address.rn', 1)
+        )
+        ->select(
+            'schedules.id',
+            'schedules.provider_id',
+            'provider_user.name as provider_name',
+            'providers.gender',
+            'provider_address.district as provider_district',
+        )
+        ->orderBy('schedules.date', 'desc')
+        ->limit(5)
+        ->get();
 
         $lastDoneSchedules->each(function ($item) {
-            $item->gender_label = $item->gender ? GenderEnum::tryFrom($item->gender)?->label() : null;
-
-            $item->provider_photo = $item->provider_photo_path
-                ? Storage::temporaryUrl($item->provider_photo_path, now()->addMinutes(60))
-                : null;
-
-            unset($item->provider_photo_path);
+            $item->gender_label = GenderEnum::labelFor($item->gender);
         });
 
         $favoriteProviders = ClientFavoriteProvider::where('client_favorite_providers.client_id', $cliente->id)
@@ -184,27 +169,19 @@ class DashboardService
                 $join->on('provider_address.source_id', '=', 'providers.id')
                     ->where('provider_address.rn', 1)
             )
-            ->leftJoin('media as provider_media', 'provider_media.id', '=', 'providers.profile_media_id')
             ->select(
                 'providers.id as provider_id',
                 'provider_user.name as provider_name',
                 'providers.gender',
                 'providers.average_rating',
                 'provider_address.district as provider_district',
-                'provider_media.path as provider_photo_path',
             )
             ->orderBy('client_favorite_providers.created_at', 'desc')
             ->limit(5)
             ->get();
 
         $favoriteProviders->each(function ($item) {
-            $item->gender_label = $item->gender ? GenderEnum::tryFrom($item->gender)?->label() : null;
-
-            $item->provider_photo = $item->provider_photo_path
-                ? Storage::temporaryUrl($item->provider_photo_path, now()->addMinutes(60))
-                : null;
-
-            unset($item->provider_photo_path);
+            $item->gender_label = GenderEnum::labelFor($item->gender);
         });
 
         $blockedProviderIds       = ScheduleBusinessRules::getBlockedProviderIdsForClient($cliente->id);
@@ -229,20 +206,21 @@ class DashboardService
             data_get($clientCoordinates, 'longitude'),
         );
 
-        $providerAddressLatestSubquery = DB::raw("
-                    (
-                        SELECT DISTINCT ON (source_id)
-                            *
-                        FROM addresses
-                        WHERE source = 'provider'
-                        AND deleted_at IS NULL
-                        ORDER BY
-                            source_id,
-                            (latitude IS NOT NULL AND longitude IS NOT NULL) DESC,
-                            is_primary DESC,
-                            id DESC
-                    ) AS provider_address
-                ");
+       $providerAddressLatestSubquery = DB::raw("
+            (
+                SELECT DISTINCT ON (source_id)
+                    *
+                FROM addresses
+                WHERE
+                    source = 'provider'
+                    AND deleted_at IS NULL
+                ORDER BY
+                    source_id,
+                    (latitude IS NOT NULL AND longitude IS NOT NULL) DESC,
+                    is_primary DESC,
+                    id DESC
+            ) AS provider_address
+        ");
 
         $providersCloseDebugQuery = Provider::leftJoin(
             'users as provider_user',
@@ -250,14 +228,14 @@ class DashboardService
             '=',
             'providers.user_id'
         )
-            ->visibleToCustomers()
-            ->leftJoin(
-                $providerAddressLatestSubquery,
-                'provider_address.source_id',
-                '=',
-                'providers.id'
-            )
-            ->whereNull('providers.deleted_at');
+        ->visibleToCustomers()
+        ->leftJoin(
+            $providerAddressLatestSubquery,
+            'provider_address.source_id',
+            '=',
+            'providers.id'
+        )
+        ->whereNull('providers.deleted_at');
 
         Log::info('Contexto de prestadores próximos no dashboard', [
             'client_id'                       => $cliente->id,
@@ -319,60 +297,52 @@ class DashboardService
             '=',
             'providers.user_id'
         )
-            ->visibleToCustomers()
-            ->leftJoin(
-                $providerAddressLatestSubquery,
-                'provider_address.source_id',
-                '=',
-                'providers.id'
-            )
-            ->leftJoin(
-                'media as provider_media',
-                'provider_media.id',
-                '=',
-                'providers.profile_media_id'
-            )
-            ->whereNotNull('provider_address.id')
-            ->when(
-                $clientPrimaryAddress?->city_id,
-                fn ($query, int $cityId) => $query->where('provider_address.city_id', $cityId)
-            )
-            ->whereNotIn('providers.id', $blockedProviderIds)
-            ->whereIn('providers.id', $providersWithWorkingDays)
-            ->whereNull('providers.deleted_at')
-            ->select(
-                'providers.id as provider_id',
-                'provider_user.name as provider_name',
-                'providers.gender',
-                'provider_address.id as address_id',
-                'provider_address.zip_code as provider_zip_code',
-                'provider_address.district',
-                'provider_address.latitude as provider_latitude',
-                'provider_address.longitude as provider_longitude',
-                'providers.average_rating',
-                'providers.total_services',
-                'providers.daily_price_8h',
-                'providers.daily_price_6h',
-                'providers.daily_price_4h',
-                'providers.daily_price_2h',
-
-                DB::raw("
-                    (
-                        SELECT COUNT(*)
-                        FROM reviews
-                        LEFT JOIN schedules
-                            ON schedules.id = reviews.schedule_id
-                        WHERE reviews.origin = 'provider'
-                        AND schedules.provider_id = providers.id
-                    ) AS total_reviews
-                "),
-
-                $providersCloseDistanceSelect,
-
-                'provider_media.path as provider_photo_path',
-            )
-            ->orderByRaw('distance_km ASC NULLS LAST')
-            ->get();
+        ->visibleToCustomers()
+        ->leftJoin(
+            $providerAddressLatestSubquery,
+            'provider_address.source_id',
+            '=',
+            'providers.id'
+        )
+        ->whereNotNull('provider_address.id')
+        ->when(
+            $clientPrimaryAddress?->city_id,
+            fn ($query, int $cityId) => $query->where('provider_address.city_id', $cityId)
+        )
+        ->whereNotIn('providers.id', $blockedProviderIds)
+        ->whereIn('providers.id', $providersWithWorkingDays)
+        ->whereNull('providers.deleted_at')
+        ->select(
+            'providers.id as provider_id',
+            'provider_user.name as provider_name',
+            'providers.gender',
+            'provider_address.id as address_id',
+            'provider_address.zip_code as provider_zip_code',
+            'provider_address.district',
+            'provider_address.latitude as provider_latitude',
+            'provider_address.longitude as provider_longitude',
+            'providers.average_rating',
+            'providers.total_services',
+            'providers.daily_price_8h',
+            'providers.daily_price_6h',
+            'providers.daily_price_4h',
+            'providers.daily_price_2h',
+
+            DB::raw("
+                (
+                    SELECT COUNT(*)
+                    FROM reviews
+                    LEFT JOIN schedules
+                        ON schedules.id = reviews.schedule_id
+                    WHERE reviews.origin = 'provider'
+                    AND schedules.provider_id = providers.id
+                ) AS total_reviews
+            "),
+
+            $providersCloseDistanceSelect,
+        )
+        ->orderByRaw('distance_km ASC NULLS LAST')
+        ->get();
 
         Log::info('Resultado da consulta de prestadores próximos no dashboard', [
             'count'                => $providersClose->count(),
@@ -401,7 +371,7 @@ class DashboardService
         );
 
         $providersClose->each(function ($item) use ($clientDistanceAddress) {
-            $item->gender_label = $item->gender ? GenderEnum::tryFrom($item->gender)?->label() : null;
+            $item->gender_label = GenderEnum::labelFor($item->gender);
 
             if ($item->distance_km === null) {
                 $item->distance_km = $this->zipCodeCoordinatesService->calculateDistance(
@@ -414,11 +384,7 @@ class DashboardService
                 );
             }
 
-            $item->provider_photo = $item->provider_photo_path
-                ? Storage::temporaryUrl($item->provider_photo_path, now()->addMinutes(60))
-                : null;
-
-            unset($item->provider_photo_path, $item->provider_zip_code);
+            unset($item->provider_zip_code);
 
             $item->daily_price_8h_base = $item->daily_price_8h;
             $item->daily_price_6h_base = $item->daily_price_6h;
@@ -448,79 +414,70 @@ class DashboardService
         $pendingSchedules = Schedule::with([
             'address:district,address,number,source_id,source,id,address_type',
         ])
-            ->where('schedules.client_id', $cliente->id)
-            ->whereIn('schedules.status', ['pending', 'accepted'])
-            ->where('schedules.schedule_type', 'default')
-            ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
-            ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
-            ->leftJoin('media as provider_media', 'provider_media.id', '=', 'providers.profile_media_id')
-            ->select(
-                'schedules.id',
-                'schedules.provider_id',
-                'provider_user.name as provider_name',
-                'providers.gender',
-                'schedules.date',
-                'schedules.address_id',
-                'schedules.status',
-                'schedules.total_amount',
-                'schedules.start_time',
-                'schedules.end_time',
-
-                DB::raw("
-                    CASE
-                        WHEN (NOW() - schedules.created_at) < INTERVAL '1 hour' THEN
-                            CONCAT(
-                                ROUND(EXTRACT(EPOCH FROM (NOW() - schedules.created_at)) / 60),
-                                'min'
-                            )
-                        WHEN (NOW() - schedules.created_at) < INTERVAL '1 day' THEN
-                            CONCAT(
-                                ROUND(EXTRACT(EPOCH FROM (NOW() - schedules.created_at)) / 3600),
-                                'h'
-                            )
-                        ELSE
-                            CONCAT(
-                                ROUND(EXTRACT(EPOCH FROM (NOW() - schedules.created_at)) / 86400),
-                                'd'
-                            )
-                    END AS time_since_request
-                "),
-
-                'provider_media.path as provider_photo_path',
-
-                DB::raw("
-                    (
+        ->where('schedules.client_id', $cliente->id)
+        ->whereIn('schedules.status', ['pending', 'accepted'])
+        ->where('schedules.schedule_type', 'default')
+        ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
+        ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
+        ->select(
+            'schedules.id',
+            'schedules.provider_id',
+            'provider_user.name as provider_name',
+            'providers.gender',
+            'schedules.date',
+            'schedules.address_id',
+            'schedules.status',
+            'schedules.total_amount',
+            'schedules.start_time',
+            'schedules.end_time',
+
+            DB::raw("
+                CASE
+                    WHEN (NOW() - schedules.created_at) < INTERVAL '1 hour' THEN
+                        CONCAT(
+                            ROUND(EXTRACT(EPOCH FROM (NOW() - schedules.created_at)) / 60),
+                            'min'
+                        )
+                    WHEN (NOW() - schedules.created_at) < INTERVAL '1 day' THEN
+                        CONCAT(
+                            ROUND(EXTRACT(EPOCH FROM (NOW() - schedules.created_at)) / 3600),
+                            'h'
+                        )
+                    ELSE
+                        CONCAT(
+                            ROUND(EXTRACT(EPOCH FROM (NOW() - schedules.created_at)) / 86400),
+                            'd'
+                        )
+                END AS time_since_request
+            "),
+
+            DB::raw("
+                (
+                    SELECT spi.service_package_id
+                    FROM service_package_items spi
+                    WHERE spi.schedule_id = schedules.id
+                    LIMIT 1
+                ) AS service_package_id
+            "),
+
+            DB::raw("
+                (
+                    SELECT COUNT(*)
+                    FROM service_package_items spi_count
+                    WHERE spi_count.service_package_id = (
                         SELECT spi.service_package_id
                         FROM service_package_items spi
                         WHERE spi.schedule_id = schedules.id
                         LIMIT 1
-                    ) AS service_package_id
-                "),
-
-                DB::raw("
-                    (
-                        SELECT COUNT(*)
-                        FROM service_package_items spi_count
-                        WHERE spi_count.service_package_id = (
-                            SELECT spi.service_package_id
-                            FROM service_package_items spi
-                            WHERE spi.schedule_id = schedules.id
-                            LIMIT 1
-                        )
-                    ) AS service_package_items_count
-                "),
-            )
-            ->orderBy('schedules.date', 'asc')
-            ->get();
+                    )
+                ) AS service_package_items_count
+            "),
+        )
+        ->orderBy('schedules.date', 'asc')
+        ->get();
 
         $pendingSchedules->each(function ($item) {
-            $item->gender_label = $item->gender ? GenderEnum::tryFrom($item->gender)?->label() : null;
-
-            $item->provider_photo = $item->provider_photo_path
-                ? Storage::temporaryUrl($item->provider_photo_path, now()->addMinutes(60))
-                : null;
-
-            unset($item->provider_photo_path);
+            $item->gender_label = GenderEnum::labelFor($item->gender);
         });
 
         $proposalsDistanceSelect = DistanceService::sqlExpression(
@@ -560,12 +517,6 @@ class DashboardService
                 '=',
                 'providers.id'
             )
-            ->leftJoin(
-                'media as provider_media',
-                'provider_media.id',
-                '=',
-                'providers.profile_media_id'
-            )
             ->where('schedules.client_id', $cliente->id)
             ->where('schedules.schedule_type', 'custom')
             ->where('schedules.status', 'pending')
@@ -598,8 +549,6 @@ class DashboardService
                 'provider_address.zip_code as provider_zip_code',
 
                 $proposalsDistanceSelect,
-
-                'provider_media.path as provider_photo_path',
             ])
             ->get();
 
@@ -615,7 +564,7 @@ class DashboardService
             ->get();
 
         $schedulesProposals->each(function ($item) use ($clientDistanceAddress) {
-            $item->gender_label = $item->gender ? GenderEnum::tryFrom($item->gender)?->label() : null;
+            $item->gender_label = GenderEnum::labelFor($item->gender);
 
             if ($item->distance_km === null) {
                 $item->distance_km = $this->zipCodeCoordinatesService->calculateDistance(
@@ -628,12 +577,7 @@ class DashboardService
                 );
             }
 
-            $item->provider_photo = $item->provider_photo_path
-                ? Storage::temporaryUrl($item->provider_photo_path, now()->addMinutes(60))
-                : null;
-
             unset(
-                $item->provider_photo_path,
                 $item->provider_latitude,
                 $item->provider_longitude,
                 $item->provider_zip_code,
@@ -643,76 +587,89 @@ class DashboardService
         $todaySchedules = Schedule::with([
             'address:district,address,number,source_id,source,id,address_type',
         ])
-            ->where('schedules.client_id', $cliente->id)
-            ->whereIn('schedules.status', ['accepted', 'paid', 'started', 'finished'])
-            ->whereDate('schedules.date', now()->toDateString())
-            ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
-            ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
-            ->leftJoin('media', 'media.id', '=', 'providers.profile_media_id')
-            ->select(
-                'schedules.id',
-                'schedules.provider_id',
-                'provider_user.name as provider_name',
-                'providers.gender',
-                'schedules.date',
-                'schedules.start_time',
-                'schedules.end_time',
-                'schedules.total_amount',
-                'schedules.period_type',
-                'schedules.schedule_type',
-                'schedules.address_id',
-                'schedules.status',
-                'schedules.code_verified',
-                'schedules.code',
-                'media.path as provider_photo',
-
-                DB::raw("
-                    (
+        ->where('schedules.client_id', $cliente->id)
+        ->whereIn('schedules.status', ['accepted', 'paid', 'started', 'finished'])
+        ->whereDate('schedules.date', now()->toDateString())
+        ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
+        ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
+        ->select(
+            'schedules.id',
+            'schedules.provider_id',
+            'provider_user.name as provider_name',
+            'providers.gender',
+            'schedules.date',
+            'schedules.start_time',
+            'schedules.end_time',
+            'schedules.total_amount',
+            'schedules.period_type',
+            'schedules.schedule_type',
+            'schedules.address_id',
+            'schedules.status',
+            'schedules.code_verified',
+            'schedules.code',
+
+            DB::raw("
+                (
+                    SELECT spi.service_package_id
+                    FROM service_package_items spi
+                    WHERE spi.schedule_id = schedules.id
+                    LIMIT 1
+                ) AS service_package_id
+            "),
+
+            DB::raw("
+                (
+                    SELECT COUNT(*)
+                    FROM service_package_items spi_count
+                    WHERE spi_count.service_package_id = (
                         SELECT spi.service_package_id
                         FROM service_package_items spi
                         WHERE spi.schedule_id = schedules.id
                         LIMIT 1
-                    ) AS service_package_id
-                "),
+                    )
+                ) AS service_package_items_count
+            "),
+
+            DB::raw("
+                EXISTS (
+                    SELECT 1
+                    FROM reviews
+                    WHERE reviews.schedule_id = schedules.id
+                    AND reviews.origin = 'client'
+                    AND reviews.origin_id = {$cliente->id}
+                    AND reviews.deleted_at IS NULL
+                ) AS client_reviewed
+            "),
+        )
+        ->orderBy('schedules.start_time', 'asc')
+        ->get()
+        ->map(function ($item) {
+                $item->gender_label = GenderEnum::labelFor($item->gender);
 
-                DB::raw("
-                    (
-                        SELECT COUNT(*)
-                        FROM service_package_items spi_count
-                        WHERE spi_count.service_package_id = (
-                            SELECT spi.service_package_id
-                            FROM service_package_items spi
-                            WHERE spi.schedule_id = schedules.id
-                            LIMIT 1
-                        )
-                    ) AS service_package_items_count
-                "),
+            return $item;
+        });
 
-                DB::raw("
-                    EXISTS (
-                        SELECT 1
-                        FROM reviews
-                        WHERE reviews.schedule_id = schedules.id
-                        AND reviews.origin = 'client'
-                        AND reviews.origin_id = {$cliente->id}
-                        AND reviews.deleted_at IS NULL
-                    ) AS client_reviewed
-                "),
-            )
-            ->orderBy('schedules.start_time', 'asc')
-            ->get()
-            ->map(function ($item) {
-                $item->gender_label = $item->gender ? GenderEnum::tryFrom($item->gender)?->label() : null;
+        $providerCollections = collect([
+            $nextSchedules,
+            $lastDoneSchedules,
+            $favoriteProviders,
+            $providersClose,
+            $pendingSchedules,
+            $schedulesProposals,
+            $todaySchedules,
+        ]);
 
-                $item->provider_photo = $item->provider_photo
-                    ? Storage::temporaryUrl(
-                        $item->provider_photo,
-                        now()->addMinutes(60)
-                    )
-                    : null;
+        $providerPhotoUrls = $this->providerPhotoUrls(
+            $providerCollections->flatMap(
+                fn (Collection $items) => $items->pluck('provider_id'),
+            ),
+        );
 
-                return $item;
+        $providerCollections->each(function (Collection $items) use ($providerPhotoUrls) {
+            $items->each(function ($item) use ($providerPhotoUrls) {
+                $item->provider_photo = $providerPhotoUrls->get($item->provider_id);
             });
+        });
 
         $notifications = Notification::where('user_id', $user->id)
             ->orderBy('read', 'asc')
@@ -791,57 +748,57 @@ class DashboardService
         $solicitations = Schedule::with([
             'address:district,source_id,source,id,zip_code,latitude,longitude',
         ])
-            ->where('schedules.provider_id', $provider->id)
-            ->where('schedules.status', 'pending')
-            ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
-            ->leftJoin('users as client_user', 'client_user.id', '=', 'clients.user_id')
-            ->leftJoin('media as client_media', 'client_media.id', '=', 'clients.profile_media_id')
-            ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
-            ->select(
-                'schedules.id',
-                'client_user.name as client_name',
-                'clients.average_rating',
-                'schedules.date',
-
-                DB::raw("
-                    TO_CHAR(schedules.date, 'DD/MM/YYYY') AS formatted_date
-                "),
-
-                'schedules.start_time',
-                'schedules.end_time',
-                'schedules.total_amount',
-                'schedules.period_type',
-                'schedules.schedule_type',
-                'schedules.address_id',
-                'schedules.status',
-                'custom_schedules.offers_meal',
-                'client_media.path as customer_photo_path',
-
-                DB::raw("
-                    CASE
-                        WHEN (NOW() - schedules.created_at) < INTERVAL '1 day' THEN
-                            CONCAT(
-                                ROUND(
-                                    EXTRACT(
-                                        EPOCH FROM (NOW() - schedules.created_at)
-                                    ) / 3600
-                                ),
-                                ' hours ago'
-                            )
-                        ELSE
-                            CONCAT(
-                                ROUND(
-                                    EXTRACT(
-                                        EPOCH FROM (NOW() - schedules.created_at)
-                                    ) / 86400
-                                ),
-                                ' days ago'
-                            )
-                    END AS time_since_request
-                "),
-            )
-            ->orderBy('schedules.date', 'asc')
-            ->get();
+        ->where('schedules.provider_id', $provider->id)
+        ->where('schedules.status', 'pending')
+        ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
+        ->leftJoin('users as client_user', 'client_user.id', '=', 'clients.user_id')
+        ->leftJoin('media as client_media', 'client_media.id', '=', 'clients.profile_media_id')
+        ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
+        ->select(
+            'schedules.id',
+            'client_user.name as client_name',
+            'clients.average_rating',
+            'schedules.date',
+
+            DB::raw("
+                TO_CHAR(schedules.date, 'DD/MM/YYYY') AS formatted_date
+            "),
+
+            'schedules.start_time',
+            'schedules.end_time',
+            'schedules.total_amount',
+            'schedules.period_type',
+            'schedules.schedule_type',
+            'schedules.address_id',
+            'schedules.status',
+            'custom_schedules.offers_meal',
+            'client_media.path as customer_photo_path',
+
+            DB::raw("
+                CASE
+                    WHEN (NOW() - schedules.created_at) < INTERVAL '1 day' THEN
+                        CONCAT(
+                            ROUND(
+                                EXTRACT(
+                                    EPOCH FROM (NOW() - schedules.created_at)
+                                ) / 3600
+                            ),
+                            ' hours ago'
+                        )
+                    ELSE
+                        CONCAT(
+                            ROUND(
+                                EXTRACT(
+                                    EPOCH FROM (NOW() - schedules.created_at)
+                                ) / 86400
+                            ),
+                            ' days ago'
+                        )
+                END AS time_since_request
+            "),
+        )
+        ->orderBy('schedules.date', 'asc')
+        ->get();
 
         $solicitations->each(function ($solicitation) use ($address) {
             $solicitation->customer_photo = $solicitation->customer_photo_path
@@ -863,43 +820,43 @@ class DashboardService
         $todayServices = Schedule::with([
             'address:district,address,number,source_id,source,id',
         ])
-            ->where('schedules.provider_id', $provider->id)
-            ->whereIn('schedules.status', ['accepted', 'paid', 'started', 'finished'])
-            ->whereDate('schedules.date', now()->toDateString())
-            ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
-            ->leftJoin('users as client_user', 'client_user.id', '=', 'clients.user_id')
-            ->leftJoin('media as client_media', 'client_media.id', '=', 'clients.profile_media_id')
-            ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
-            ->select(
-                'schedules.id',
-                'schedules.client_id',
-                'client_user.name as client_name',
-                'schedules.date',
-                'schedules.start_time',
-                'schedules.end_time',
-                'schedules.total_amount',
-                'schedules.period_type',
-                'schedules.address_id',
-                'schedules.schedule_type',
-                'schedules.status',
-                'schedules.code_verified',
-                'schedules.code',
-                'custom_schedules.offers_meal',
-                'client_media.path as customer_photo_path',
-
-                DB::raw("
-                    EXISTS (
-                        SELECT 1
-                        FROM reviews
-                        WHERE reviews.schedule_id = schedules.id
-                        AND reviews.origin = 'provider'
-                        AND reviews.origin_id = {$provider->id}
-                        AND reviews.deleted_at IS NULL
-                    ) AS provider_reviewed
-                "),
-            )
-            ->orderBy('schedules.start_time', 'asc')
-            ->get();
+        ->where('schedules.provider_id', $provider->id)
+        ->whereIn('schedules.status', ['accepted', 'paid', 'started', 'finished'])
+        ->whereDate('schedules.date', now()->toDateString())
+        ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
+        ->leftJoin('users as client_user', 'client_user.id', '=', 'clients.user_id')
+        ->leftJoin('media as client_media', 'client_media.id', '=', 'clients.profile_media_id')
+        ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
+        ->select(
+            'schedules.id',
+            'schedules.client_id',
+            'client_user.name as client_name',
+            'schedules.date',
+            'schedules.start_time',
+            'schedules.end_time',
+            'schedules.total_amount',
+            'schedules.period_type',
+            'schedules.address_id',
+            'schedules.schedule_type',
+            'schedules.status',
+            'schedules.code_verified',
+            'schedules.code',
+            'custom_schedules.offers_meal',
+            'client_media.path as customer_photo_path',
+
+            DB::raw("
+                EXISTS (
+                    SELECT 1
+                    FROM reviews
+                    WHERE reviews.schedule_id = schedules.id
+                    AND reviews.origin = 'provider'
+                    AND reviews.origin_id = {$provider->id}
+                    AND reviews.deleted_at IS NULL
+                ) AS provider_reviewed
+            "),
+        )
+        ->orderBy('schedules.start_time', 'asc')
+        ->get();
 
         $todayServices->each(function ($s) {
             $s->customer_photo = $s->customer_photo_path
@@ -998,17 +955,18 @@ class DashboardService
             ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
             ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
             ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
-            ->leftJoin('media', 'media.id', '=', 'providers.profile_media_id')
             ->select(
                 'schedules.provider_id',
                 'provider_user.name as provider_name',
                 'providers.birth_date as provider_birth_date',
                 'providers.gender',
-                'media.path as provider_photo',
                 'custom_schedules.offers_meal',
             )
             ->firstOrFail();
 
+        $providerPhoto = $this->providerPhotoUrls([$schedule->provider_id])
+            ->get($schedule->provider_id);
+
         $allSpecialities = Speciality::where('active', true)
             ->select('id', 'description')
             ->orderBy('description')
@@ -1028,16 +986,32 @@ class DashboardService
             'provider_name'       => $schedule->provider_name,
             'provider_birth_date' => $schedule->provider_birth_date,
             'gender'              => $schedule->gender,
-            'gender_label'        => $schedule->gender ? GenderEnum::tryFrom($schedule->gender)?->label() : null,
+            'gender_label'        => GenderEnum::labelFor($schedule->gender),
             'offers_meal'         => $schedule->offers_meal,
             'specialities'        => $specialities,
 
-            'provider_photo' => $schedule->provider_photo
-                ? Storage::temporaryUrl($schedule->provider_photo, now()->addMinutes(60))
-                : null,
+            'provider_photo' => $providerPhoto,
         ];
     }
 
+    // gera urls apenas para fotos verificadas ou visiveis ao proprio prestador.
+
+    private function providerPhotoUrls(iterable $providerIds): Collection
+    {
+        return Provider::query()
+            ->select('id', 'profile_media_id')
+            ->with('profileMedia')
+            ->whereIn('id', collect($providerIds)->filter()->unique())
+            ->get()
+            ->mapWithKeys(function (Provider $provider) {
+                $path = $provider->profileMedia?->path;
+
+                return [
+                    $provider->id => $path ? Storage::temporaryUrl($path, now()->addMinutes(60)) : null,
+                ];
+            });
+    }
+
     //
 
     private function applyCreditCardFee(?float $price): ?float

+ 7 - 2
app/Services/Pagarme/Concerns/FormatsPagarmeData.php

@@ -60,14 +60,19 @@ trait FormatsPagarmeData
         $addressLine   = trim((string) data_get($data, 'address', ''));
         $segments      = array_map('trim', explode(',', $addressLine));
         $streetSegment = data_get($segments, 0, '');
+        $streetNumber  = data_get($data, 'number');
+
+        if ($streetNumber === null) {
+            $matches = [];
 
-        if ((data_get($data, 'number')) === null) {
             preg_match('/^(\d+)/', $streetSegment, $matches);
+
+            $streetNumber = data_get($matches, 1, 'S/N');
         }
 
         return [
             'street'          => $streetSegment,
-            'street_number'   => (string) data_get($data, 'number', data_get($matches, 1, 'S/N')),
+            'street_number'   => (string) $streetNumber,
             'neighborhood'    => (string) data_get($data, 'district', data_get($segments, 1, 'N/A')),
             'reference_point' => (string) data_get($data, 'reference_point', 'N/A'),
             'complementary'   => (string) data_get($data, 'complement', 'N/A'),

+ 10 - 15
app/Services/SearchService.php

@@ -5,7 +5,6 @@ namespace App\Services;
 use App\Enums\GenderEnum;
 use App\Models\Address;
 use App\Models\Client;
-use App\Models\Media;
 use App\Models\Provider;
 use App\Rules\ScheduleBusinessRules;
 use Illuminate\Support\Facades\Auth;
@@ -83,6 +82,7 @@ class SearchService
             ->whereNotNull('providers.daily_price_2h')
             ->when($name, fn ($q) => $q->where('provider_user.name', 'ILIKE', "%{$name}%"))
             ->select(
+                'providers.id',
                 'providers.id as provider_id',
                 'providers.profile_media_id',
                 'provider_user.name as provider_name',
@@ -159,23 +159,16 @@ class SearchService
             )->values()
         );
 
-        $mediaIds = $filtered->pluck('profile_media_id')->filter()->unique()->values();
+        $filtered->load('profileMedia');
 
-        $mediaUrls = [];
-
-        if ($mediaIds->isNotEmpty()) {
-            Media::whereIn('id', $mediaIds)->get()->each(function ($media) use (&$mediaUrls) {
-                $mediaUrls[$media->id] = $media->path
-                    ? Storage::temporaryUrl($media->path, now()->addMinutes(60))
-                    : null;
-            });
-        }
-
-        return $filtered->map(function ($item) use ($mediaUrls) {
+        return $filtered->map(function ($item) {
             $arr = is_array($item) ? $item : $item->toArray();
 
-            $arr['profile_media_url']   = data_get($mediaUrls, data_get($arr, 'profile_media_id'));
-            $arr['gender_label']        = data_get($arr, 'gender') ? GenderEnum::tryFrom(data_get($arr, 'gender'))?->label() : null;
+            $arr['profile_media_url'] = $item->profileMedia?->path
+                ? Storage::temporaryUrl($item->profileMedia->path, now()->addMinutes(60))
+                : null;
+
+            $arr['gender_label']        = GenderEnum::labelFor(data_get($arr, 'gender'));
             $arr['daily_price_8h_base'] = data_get($arr, 'daily_price_8h');
             $arr['daily_price_6h_base'] = data_get($arr, 'daily_price_6h');
             $arr['daily_price_4h_base'] = data_get($arr, 'daily_price_4h');
@@ -186,7 +179,9 @@ class SearchService
             $arr['daily_price_2h']      = $this->applyCreditCardFee(data_get($arr, 'daily_price_2h'));
 
             unset(
+                $arr['id'],
                 $arr['profile_media_id'],
+                $arr['profile_media'],
                 $arr['provider_zip_code'],
             );