Просмотр исходного кода

reconcile feature and fix branches

Gustavo Mantovani 2 недель назад
Родитель
Сommit
a3e70add5a
81 измененных файлов с 1875 добавлено и 1676 удалено
  1. 9 16
      app/Enums/NotificationTypeEnum.php
  2. 2 0
      app/Http/Controllers/AddressController.php
  3. 2 0
      app/Http/Controllers/CityController.php
  4. 2 0
      app/Http/Controllers/ClientController.php
  5. 2 0
      app/Http/Controllers/ClientFavoriteProviderController.php
  6. 2 0
      app/Http/Controllers/ClientProviderBlockController.php
  7. 40 34
      app/Http/Controllers/CustomScheduleController.php
  8. 13 13
      app/Http/Controllers/DashboardController.php
  9. 9 25
      app/Http/Controllers/NotificationController.php
  10. 0 47
      app/Http/Controllers/PaymentController.php
  11. 2 0
      app/Http/Controllers/ProviderClientBlockController.php
  12. 39 35
      app/Http/Controllers/ProviderWithdrawalController.php
  13. 32 30
      app/Http/Controllers/ReviewController.php
  14. 50 44
      app/Http/Controllers/ScheduleController.php
  15. 159 150
      app/Http/Controllers/UserController.php
  16. 17 14
      app/Http/Requests/ProviderPaymentMethodRequest.php
  17. 34 34
      app/Http/Requests/ScheduleRequest.php
  18. 6 6
      app/Http/Requests/UpdateMeRequest.php
  19. 1 0
      app/Http/Requests/UserAppsRequest.php
  20. 3 1
      app/Http/Resources/AddressResource.php
  21. 1 1
      app/Http/Resources/CartItemResource.php
  22. 1 1
      app/Http/Resources/CartResource.php
  23. 11 8
      app/Http/Resources/CustomScheduleResource.php
  24. 11 11
      app/Http/Resources/DashboardClienteResource.php
  25. 0 2
      app/Http/Resources/PaymentResource.php
  26. 3 3
      app/Http/Resources/ProviderClientBlockResource.php
  27. 13 3
      app/Http/Resources/ReviewDetailResource.php
  28. 1 0
      app/Http/Resources/ReviewResource.php
  29. 13 10
      app/Http/Resources/UserResource.php
  30. 1 0
      app/Http/Resources/UserTypeResource.php
  31. 17 6
      app/Models/Cart.php
  32. 12 2
      app/Models/CartItem.php
  33. 11 9
      app/Models/Client.php
  34. 2 2
      app/Models/Notification.php
  35. 13 11
      app/Models/Payment.php
  36. 12 2
      app/Models/PaymentSchedule.php
  37. 76 87
      app/Models/Provider.php
  38. 4 4
      app/Models/ProviderClientBlock.php
  39. 14 10
      app/Models/Review.php
  40. 4 4
      app/Models/ReviewMedia.php
  41. 2 0
      app/Models/Schedule.php
  42. 4 4
      app/Models/ScheduleProposal.php
  43. 4 4
      app/Models/ScheduleRefuse.php
  44. 23 1
      app/Models/SupportRequest.php
  45. 10 4
      app/Models/User.php
  46. 14 12
      app/Services/AddressService.php
  47. 2 0
      app/Services/CityService.php
  48. 34 16
      app/Services/ClientCalendarService.php
  49. 2 0
      app/Services/ClientFavoriteProviderService.php
  50. 2 0
      app/Services/ClientPaymentMethodService.php
  51. 2 0
      app/Services/ClientProviderBlockService.php
  52. 15 11
      app/Services/ClientService.php
  53. 188 171
      app/Services/CustomScheduleService.php
  54. 9 7
      app/Services/CustomScheduleSpecialityService.php
  55. 275 193
      app/Services/DashboardService.php
  56. 23 13
      app/Services/DistanceService.php
  57. 22 22
      app/Services/EmailService.php
  58. 29 23
      app/Services/GeminiService.php
  59. 19 14
      app/Services/MediaService.php
  60. 7 5
      app/Services/NotificationService.php
  61. 2 0
      app/Services/Pagarme/Concerns/FormatsPagarmeData.php
  62. 9 5
      app/Services/Pagarme/Concerns/SendsPagarmeRequests.php
  63. 0 32
      app/Services/PaymentService.php
  64. 34 16
      app/Services/ProviderCalendarService.php
  65. 5 0
      app/Services/ProviderClientBlockService.php
  66. 77 71
      app/Services/ProviderService.php
  67. 60 49
      app/Services/ProviderWithdrawalService.php
  68. 1 0
      app/Services/PushNotificationDispatcher.php
  69. 11 9
      app/Services/PushNotificationService.php
  70. 154 138
      app/Services/ReviewService.php
  71. 2 0
      app/Services/ScheduleCartService.php
  72. 66 144
      app/Services/ScheduleService.php
  73. 42 19
      app/Services/SearchService.php
  74. 2 0
      app/Services/SupportRequestService.php
  75. 20 17
      app/Services/UserService.php
  76. 24 22
      app/Services/WebhookService.php
  77. 1 1
      routes/authRoutes/device_token.php
  78. 3 5
      routes/authRoutes/notifications.php
  79. 1 4
      routes/authRoutes/payment.php
  80. 22 10
      routes/authRoutes/review.php
  81. 9 9
      routes/noAuthRoutes/auth.php

+ 9 - 16
app/Enums/NotificationTypeEnum.php

@@ -5,25 +5,18 @@ namespace App\Enums;
 enum NotificationTypeEnum: string
 {
 
-    // Notificação cliente
-    case SCHEDULE_CLIENT_PROVIDER_ACCEPTED = 'schedule_client_provider_accepted';
-
-    case SCHEDULE_CLIENT_PROVIDER_REFUSED = 'schedule_client_provider_refused';
+    // Notificação CLIENTE
 
+    case SCHEDULE_CLIENT_PROVIDER_ACCEPTED  = 'schedule_client_provider_accepted';
+    case SCHEDULE_CLIENT_PROVIDER_REFUSED   = 'schedule_client_provider_refused';
     case SCHEDULE_CLIENT_PROVIDER_CANCELLED = 'schedule_client_provider_cancelled';
+    case SCHEDULE_CLIENT_PROVIDER_COMING    = 'schedule_client_provider_coming';
+    case SCHEDULE_CLIENT_PROVIDER_FINISHED  = 'schedule_client_provider_finished';
 
-    case SCHEDULE_CLIENT_PROVIDER_COMING = 'schedule_client_provider_coming';
-
-    case SCHEDULE_CLIENT_PROVIDER_FINISHED = 'schedule_client_provider_finished';
-
-
-
-        // Notificação PRESTADO
-    case SCHEDULE_PROVIDER_CLIENT_NEW_SOLICITATION = 'schedule_provider_client_new_solicitation';
-
-    case SCHEDULE_PROVIDER_START = 'schedule_provider_start';
-
-    case SCHEDULE_PROVIDER_CLIENT_CANCELLED = 'schedule_provider_client_cancelled';
+    // Notificação PRESTADOR
 
+    case SCHEDULE_PROVIDER_CLIENT_NEW_SOLICITATION  = 'schedule_provider_client_new_solicitation';
+    case SCHEDULE_PROVIDER_START                    = 'schedule_provider_start';
+    case SCHEDULE_PROVIDER_CLIENT_CANCELLED         = 'schedule_provider_client_cancelled';
     case SCHEDULE_PROVIDER_CLIENT_PROPOSAL_ACCEPTED = 'schedule_provider_client_proposal_accepted';
 }

+ 2 - 0
app/Http/Controllers/AddressController.php

@@ -65,6 +65,8 @@ class AddressController extends Controller
         );
     }
 
+    //
+
     public function showPrimaryBySource(AddressRequest $request): JsonResponse
     {
         $address = $this->addressService->findPrimaryBySource($request->validated());

+ 2 - 0
app/Http/Controllers/CityController.php

@@ -59,6 +59,8 @@ class CityController extends Controller
         );
     }
 
+    //
+
     public function searchStateCityByDescription(Request $request): JsonResponse
     {
         $state = $request->query('stateUf');

+ 2 - 0
app/Http/Controllers/ClientController.php

@@ -60,6 +60,8 @@ class ClientController extends Controller
         );
     }
 
+    //
+
     public function register(RegisterClientRequest $request): JsonResponse
     {
         $result = $this->service->register($request->validated());

+ 2 - 0
app/Http/Controllers/ClientFavoriteProviderController.php

@@ -62,6 +62,8 @@ class ClientFavoriteProviderController extends Controller
         );
     }
 
+    //
+
     public function getFavoritedProviders(int $clientId): JsonResponse
     {
         $providerIds = $this->service->getFavoritedProviderIds($clientId);

+ 2 - 0
app/Http/Controllers/ClientProviderBlockController.php

@@ -43,6 +43,8 @@ class ClientProviderBlockController extends Controller
         );
     }
 
+    //
+
     public function getBlockedProviders(int $clientId): JsonResponse
     {
         $providerIds = $this->service->getBlockedProviderIds($clientId);

+ 40 - 34
app/Http/Controllers/CustomScheduleController.php

@@ -88,12 +88,7 @@ class CustomScheduleController extends Controller
         }
     }
 
-    public function groupedByClientCustom(): JsonResponse
-    {
-        $grouped = $this->customScheduleService->getSchedulesCustomGroupedByClient();
-
-        return $this->successResponse($grouped);
-    }
+    //
 
     public function available(CustomScheduleAvailableRequest $request)
     {
@@ -114,25 +109,26 @@ class CustomScheduleController extends Controller
         }
     }
 
-    public function providerProposals(CustomScheduleRequest $request)
+    public function getProvidersProposalsAndOpportunities($providerId)
     {
         try {
-            $providerId = $request->query('provider_id');
-
-            if (! $providerId) {
-                return $this->errorResponse('Provider ID é obrigatório', 400);
-            }
-
-            $proposals = $this->customScheduleService->getProviderProposals($providerId);
+            $data = $this->customScheduleService->getProvidersProposalsAndOpportunities($providerId);
 
-            return $this->successResponse($proposals);
+            return $this->successResponse($data);
         } catch (\Exception $e) {
-            Log::error('Error fetching provider proposals: '.$e->getMessage());
+            Log::error('Error fetching provider proposals and opportunities: '.$e->getMessage());
 
             return $this->errorResponse($e->getMessage(), 500);
         }
     }
 
+    public function groupedByClientCustom(): JsonResponse
+    {
+        $grouped = $this->customScheduleService->getSchedulesCustomGroupedByClient();
+
+        return $this->successResponse($grouped);
+    }
+
     public function opportunityProposals($scheduleId)
     {
         try {
@@ -146,6 +142,27 @@ class CustomScheduleController extends Controller
         }
     }
 
+    public function providerProposals(CustomScheduleRequest $request)
+    {
+        try {
+            $providerId = $request->query('provider_id');
+
+            if (! $providerId) {
+                return $this->errorResponse('Provider ID é obrigatório', 400);
+            }
+
+            $proposals = $this->customScheduleService->getProviderProposals($providerId);
+
+            return $this->successResponse($proposals);
+        } catch (\Exception $e) {
+            Log::error('Error fetching provider proposals: '.$e->getMessage());
+
+            return $this->errorResponse($e->getMessage(), 500);
+        }
+    }
+
+    //
+
     public function propose(CustomScheduleProposeRequest $request, $scheduleId)
     {
         try {
@@ -191,19 +208,21 @@ class CustomScheduleController extends Controller
         }
     }
 
-    public function getProvidersProposalsAndOpportunities($providerId)
+    public function refuseOpportunity($scheduleId, CustomScheduleRefuseOpportunityRequest $request)
     {
         try {
-            $data = $this->customScheduleService->getProvidersProposalsAndOpportunities($providerId);
+            $this->customScheduleService->refuseOpportunity($scheduleId, $request->input('provider_id'));
 
-            return $this->successResponse($data);
+            return $this->successResponse(null, 'Oportunidade recusada.');
         } catch (\Exception $e) {
-            Log::error('Error fetching provider proposals and opportunities: '.$e->getMessage());
+            Log::error('Error refusing opportunity: '.$e->getMessage());
 
-            return $this->errorResponse($e->getMessage(), 500);
+            return $this->errorResponse($e->getMessage(), 400);
         }
     }
 
+    //
+
     public function verifyCode($scheduleId, CustomScheduleVerifyCodeRequest $request)
     {
         try {
@@ -216,17 +235,4 @@ class CustomScheduleController extends Controller
             return $this->errorResponse($e->getMessage(), 500);
         }
     }
-
-    public function refuseOpportunity($scheduleId, CustomScheduleRefuseOpportunityRequest $request)
-    {
-        try {
-            $this->customScheduleService->refuseOpportunity($scheduleId, $request->input('provider_id'));
-
-            return $this->successResponse(null, 'Oportunidade recusada.');
-        } catch (\Exception $e) {
-            Log::error('Error refusing opportunity: '.$e->getMessage());
-
-            return $this->errorResponse($e->getMessage(), 400);
-        }
-    }
 }

+ 13 - 13
app/Http/Controllers/DashboardController.php

@@ -33,19 +33,6 @@ class DashboardController extends Controller
         }
     }
 
-    public function scheduleClienteDetails(int $id): JsonResponse
-    {
-        try {
-            $dados = $this->service->getScheduleClienteDetails($id);
-
-            return $this->successResponse(payload: $dados);
-        } catch (\Exception $e) {
-            Log::error('Erro ao obter detalhes do schedule do cliente: '.$e->getMessage());
-
-            return $this->errorResponse(message: __('messages.error_fetching_data'), code: 500);
-        }
-    }
-
     public function dadosDashboardPrestador(): JsonResponse
     {
         try {
@@ -64,4 +51,17 @@ class DashboardController extends Controller
             return $this->errorResponse(message: __('messages.error_fetching_data'), code: 500);
         }
     }
+
+    public function scheduleClienteDetails(int $id): JsonResponse
+    {
+        try {
+            $dados = $this->service->getScheduleClienteDetails($id);
+
+            return $this->successResponse(payload: $dados);
+        } catch (\Exception $e) {
+            Log::error('Erro ao obter detalhes do schedule do cliente: '.$e->getMessage());
+
+            return $this->errorResponse(message: __('messages.error_fetching_data'), code: 500);
+        }
+    }
 }

+ 9 - 25
app/Http/Controllers/NotificationController.php

@@ -11,37 +11,23 @@ class NotificationController extends Controller
 {
     public function index(): JsonResponse
     {
-
-
         $user = Auth::user();
 
-
-
         $notifications = Notification::where('user_id', $user->id)
             ->orderBy('read', 'asc')
             ->orderBy('created_at', 'desc')
             ->limit(50)
             ->get()
             ->map(function ($notification) {
-
                 return [
-                    'id' => $notification->id,
-
-                    'title' => $notification->title,
-
+                    'id'          => $notification->id,
+                    'title'       => $notification->title,
                     'description' => $notification->description,
-
-                    'origin' => $notification->origin,
-
-                    'origin_id' => $notification->origin_id,
-
-                    'type' => $notification->type,
-
-                    'read' => $notification->read,
-
-                    'time' => Carbon::parse(
-                        $notification->created_at
-                    )->diffForHumans(),
+                    'origin'      => $notification->origin,
+                    'origin_id'   => $notification->origin_id,
+                    'type'        => $notification->type,
+                    'read'        => $notification->read,
+                    'time'        => Carbon::parse($notification->created_at)->diffForHumans(),
                 ];
             });
 
@@ -57,8 +43,7 @@ class NotificationController extends Controller
             ->firstOrFail();
 
         $notification->update([
-            'read' => true,
-
+            'read'    => true,
             'read_at' => now(),
         ]);
 
@@ -72,8 +57,7 @@ class NotificationController extends Controller
         Notification::where('user_id', Auth::id())
             ->where('read', false)
             ->update([
-                'read' => true,
-
+                'read'    => true,
                 'read_at' => now(),
             ]);
 

+ 0 - 47
app/Http/Controllers/PaymentController.php

@@ -58,27 +58,6 @@ class PaymentController extends Controller
 
     //
 
-    /*
-    public function payCart(Request $request): JsonResponse
-    {
-        $validated = $request->validate([
-            'cart_id'                  => ['required', 'integer', 'exists:carts,id'],
-            'payment_method'           => ['required', 'in:credit_card,pix'],
-            'client_payment_method_id' => ['nullable', 'integer', 'exists:client_payment_methods,id'],
-            'card_id'                  => ['nullable', 'string', 'max:255'],
-            'phone'                    => ['nullable', 'string', 'max:20'],
-        ]);
-
-        $items = $this->service->payCart($validated, Auth::id());
-
-        return $this->successResponse(
-            payload: PaymentResource::collection($items),
-            message: __('messages.created'),
-            code:    201,
-        );
-    }
-    */
-
     public function paySchedule(Request $request, Schedule $schedule): JsonResponse
     {
         $validated = $request->validate([
@@ -118,32 +97,6 @@ class PaymentController extends Controller
     }
 
     //
-    public function payCart(Request $request): JsonResponse
-    {
-        $validated = $request->validate([
-            'schedule_ids'             => ['required', 'array', 'min:1'],
-            'schedule_ids.*'           => ['required', 'integer', 'distinct', 'exists:schedules,id'],
-            'payment_method'           => ['required', 'in:credit_card,pix'],
-            'client_payment_method_id' => ['nullable', 'integer', 'exists:client_payment_methods,id'],
-            'card_id'                  => ['nullable', 'string', 'max:255'],
-            'phone'                    => ['nullable', 'string', 'max:20'],
-        ]);
-
-        $item = $this->service->payCart($validated, (int) Auth::id());
-
-        if ($item->status === PaymentStatusEnum::FAILED) {
-            return response()->json([
-                'payload' => new PaymentResource($item),
-                'message' => $item->failure_message ?: $this->paymentMessage($item->status->value),
-            ], 422);
-        }
-
-        return $this->successResponse(
-            payload: new PaymentResource($item),
-            message: $this->paymentMessage($item->status->value),
-            code:    201,
-        );
-    }
 
     public function getSchedulePix(Schedule $schedule): JsonResponse
     {

+ 2 - 0
app/Http/Controllers/ProviderClientBlockController.php

@@ -43,6 +43,8 @@ class ProviderClientBlockController extends Controller
         );
     }
 
+    //
+
     public function getBlockedClients(int $providerId): JsonResponse
     {
         $clientIds = $this->service->getBlockedClientIds($providerId);

+ 39 - 35
app/Http/Controllers/ProviderWithdrawalController.php

@@ -15,41 +15,6 @@ class ProviderWithdrawalController extends Controller
         private readonly ProviderWithdrawalService $service,
     ) {}
 
-    private function resolveProvider()
-    {
-        $provider = Auth::user()->provider;
-
-        if (! $provider) {
-            abort(403, 'Apenas prestadores podem acessar este recurso.');
-        }
-
-        return $provider;
-    }
-
-    public function balance(): JsonResponse
-    {
-        $provider = $this->resolveProvider();
-
-        return $this->successResponse(payload: [
-            'available' => $this->service->getAvailableBalance($provider),
-            'pending'   => $this->service->getPendingBalance($provider),
-        ]);
-    }
-
-    public function fees(): JsonResponse
-    {
-        return $this->successResponse(payload: $this->service->getWithdrawalFees());
-    }
-
-    public function splits(): JsonResponse
-    {
-        $provider = $this->resolveProvider();
-
-        $splits = $this->service->getPaymentSplits($provider);
-
-        return $this->successResponse(payload: PaymentSplitResource::collection($splits));
-    }
-
     public function index(): JsonResponse
     {
         $provider = $this->resolveProvider();
@@ -87,4 +52,43 @@ class ProviderWithdrawalController extends Controller
 
         return $this->successResponse(payload: new ProviderWithdrawalResource($withdrawal));
     }
+
+    //
+
+    public function balance(): JsonResponse
+    {
+        $provider = $this->resolveProvider();
+
+        return $this->successResponse(payload: [
+            'available' => $this->service->getAvailableBalance($provider),
+            'pending'   => $this->service->getPendingBalance($provider),
+        ]);
+    }
+
+    public function fees(): JsonResponse
+    {
+        return $this->successResponse(payload: $this->service->getWithdrawalFees());
+    }
+
+    public function splits(): JsonResponse
+    {
+        $provider = $this->resolveProvider();
+
+        $splits = $this->service->getPaymentSplits($provider);
+
+        return $this->successResponse(payload: PaymentSplitResource::collection($splits));
+    }
+
+    //
+
+    private function resolveProvider()
+    {
+        $provider = Auth::user()->provider;
+
+        if (! $provider) {
+            abort(403, 'Apenas prestadores podem acessar este recurso.');
+        }
+
+        return $provider;
+    }
 }

+ 32 - 30
app/Http/Controllers/ReviewController.php

@@ -23,88 +23,90 @@ class ReviewController extends Controller
         );
     }
 
-    public function indexBySchedule(int $scheduleId): JsonResponse
+    public function store(ReviewRequest $request): JsonResponse
     {
-        $reviews = $this->service->getByScheduleId($scheduleId);
+        $review = $this->service->create($request->validated());
 
         return $this->successResponse(
-            payload: ReviewResource::collection($reviews),
+            payload: new ReviewResource($review),
+            message: __('messages.created'),
+            code: 201,
         );
     }
 
-    public function indexByOrigin(string $origin, int $originId): JsonResponse
+    public function update(ReviewRequest $request, int $id): JsonResponse
     {
-        $reviews = $this->service->getByOrigin($origin, $originId);
+        $review = $this->service->update($id, $request->validated());
 
         return $this->successResponse(
-            payload: ReviewResource::collection($reviews),
+            payload: new ReviewResource($review),
+            message: __('messages.updated'),
         );
     }
 
-    public function clientSent(int $clientId): JsonResponse
+    public function destroy(int $id): JsonResponse
     {
-        $reviews = $this->service->getByClientSent($clientId);
+        $this->service->delete($id);
 
         return $this->successResponse(
-            payload: ReviewDetailResource::collection($reviews),
+            message: __('messages.deleted'),
+            code: 204,
         );
     }
 
-    public function clientReceived(int $clientId): JsonResponse
+    //
+
+    public function indexByOrigin(string $origin, int $originId): JsonResponse
     {
-        $reviews = $this->service->getByClientReceived($clientId);
+        $reviews = $this->service->getByOrigin($origin, $originId);
 
         return $this->successResponse(
-            payload: ReviewDetailResource::collection($reviews),
+            payload: ReviewResource::collection($reviews),
         );
     }
 
-    public function providerSent(int $providerId): JsonResponse
+    public function indexBySchedule(int $scheduleId): JsonResponse
     {
-        $reviews = $this->service->getByProviderSent($providerId);
+        $reviews = $this->service->getByScheduleId($scheduleId);
 
         return $this->successResponse(
-            payload: ReviewDetailResource::collection($reviews),
+            payload: ReviewResource::collection($reviews),
         );
     }
 
-    public function providerReceived(int $providerId): JsonResponse
+    public function clientReceived(int $clientId): JsonResponse
     {
-        $reviews = $this->service->getByProviderReceived($providerId);
+        $reviews = $this->service->getByClientReceived($clientId);
 
         return $this->successResponse(
             payload: ReviewDetailResource::collection($reviews),
         );
     }
 
-    public function store(ReviewRequest $request): JsonResponse
+    public function clientSent(int $clientId): JsonResponse
     {
-        $review = $this->service->create($request->validated());
+        $reviews = $this->service->getByClientSent($clientId);
 
         return $this->successResponse(
-            payload: new ReviewResource($review),
-            message: __('messages.created'),
-            code: 201,
+            payload: ReviewDetailResource::collection($reviews),
         );
     }
 
-    public function update(ReviewRequest $request, int $id): JsonResponse
+    public function providerReceived(int $providerId): JsonResponse
     {
-        $review = $this->service->update($id, $request->validated());
+        $reviews = $this->service->getByProviderReceived($providerId);
 
         return $this->successResponse(
-            payload: new ReviewResource($review),
-            message: __('messages.updated'),
+            payload: ReviewDetailResource::collection($reviews),
         );
     }
 
-    public function destroy(int $id): JsonResponse
+    public function providerSent(int $providerId): JsonResponse
     {
-        $this->service->delete($id);
+        $reviews = $this->service->getByProviderSent($providerId);
 
         return $this->successResponse(
-            message: __('messages.deleted'),
-            code: 204,
+            payload: ReviewDetailResource::collection($reviews),
         );
     }
 }

+ 50 - 44
app/Http/Controllers/ScheduleController.php

@@ -48,20 +48,6 @@ class ScheduleController extends Controller
         }
     }
 
-    private function scheduleItems(array $data): array
-    {
-        if (! empty($data['dates'])) {
-            return array_map(
-                fn (string $date) => ['date' => $date],
-                $data['dates'],
-            );
-        }
-
-        return [
-            ['date' => $data['date']],
-        ];
-    }
-
     public function show(string $id): JsonResponse
     {
         $schedule = $this->scheduleService->getById($id);
@@ -95,6 +81,8 @@ class ScheduleController extends Controller
         );
     }
 
+    //
+
     public function finished(): JsonResponse
     {
         $schedules = $this->scheduleService->getFinished();
@@ -104,6 +92,21 @@ class ScheduleController extends Controller
         );
     }
 
+    public function clientProviderBlocks(Request $request): JsonResponse
+    {
+        $validated = $request->validate([
+            'client_id'   => 'required|integer|exists:clients,id',
+            'provider_id' => 'required|integer|exists:providers,id',
+        ]);
+
+        $blocks = $this->scheduleService->getClientProviderBlocks(
+            (int) $validated['client_id'],
+            (int) $validated['provider_id'],
+        );
+
+        return $this->successResponse(payload: $blocks);
+    }
+
     public function groupedByClient(): JsonResponse
     {
         $grouped = $this->scheduleService->getSchedulesDefaultGroupedByClient();
@@ -111,14 +114,16 @@ class ScheduleController extends Controller
         return $this->successResponse($grouped);
     }
 
-    public function updateStatus(string $id, ScheduleRequest $request): JsonResponse
+    //
+
+    public function cancelWithReason(string $id, Request $request): JsonResponse
     {
         try {
             $validated = $request->validate([
-                'status' => 'required|in:pending,accepted,rejected,paid,cancelled,started,finished',
+                'cancel_text' => 'required|string|min:5|max:1000',
             ]);
 
-            $schedule = $this->scheduleService->updateStatus($id, $validated['status']);
+            $schedule = $this->scheduleService->cancelWithReason((int) $id, $validated['cancel_text']);
 
             return $this->successResponse(
                 payload: new ScheduleResource($schedule),
@@ -129,14 +134,29 @@ class ScheduleController extends Controller
         }
     }
 
-    public function cancelWithReason(string $id, Request $request): JsonResponse
+    public function storeCart(ScheduleRequest $request, ScheduleCartService $cartService): JsonResponse
+    {
+        try {
+            $schedules = $cartService->create($request->validated());
+
+            return $this->successResponse(
+                payload: ScheduleResource::collection($schedules),
+                message: count($schedules).' '.__('schedules.schedules_created'),
+                code:    201,
+            );
+        } catch (\Exception $e) {
+            return $this->errorResponse($e->getMessage(), 422);
+        }
+    }
+
+    public function updateStatus(string $id, ScheduleRequest $request): JsonResponse
     {
         try {
             $validated = $request->validate([
-                'cancel_text' => 'required|string|min:5|max:1000',
+                'status' => 'required|in:pending,accepted,rejected,paid,cancelled,started,finished',
             ]);
 
-            $schedule = $this->scheduleService->cancelWithReason((int) $id, $validated['cancel_text']);
+            $schedule = $this->scheduleService->updateStatus($id, $validated['status']);
 
             return $this->successResponse(
                 payload: new ScheduleResource($schedule),
@@ -147,33 +167,19 @@ class ScheduleController extends Controller
         }
     }
 
-    public function clientProviderBlocks(Request $request): JsonResponse
-    {
-        $validated = $request->validate([
-            'client_id'   => 'required|integer|exists:clients,id',
-            'provider_id' => 'required|integer|exists:providers,id',
-        ]);
-
-        $blocks = $this->scheduleService->getClientProviderBlocks(
-            (int) $validated['client_id'],
-            (int) $validated['provider_id'],
-        );
+    //
 
-        return $this->successResponse(payload: $blocks);
-    }
-
-    public function storeCart(ScheduleRequest $request, ScheduleCartService $cartService): JsonResponse
+    private function scheduleItems(array $data): array
     {
-        try {
-            $schedules = $cartService->create($request->validated());
-
-            return $this->successResponse(
-                payload: ScheduleResource::collection($schedules),
-                message: count($schedules).' '.__('schedules.schedules_created'),
-                code:    201,
+        if (! empty($data['dates'])) {
+            return array_map(
+                fn (string $date) => ['date' => $date],
+                $data['dates'],
             );
-        } catch (\Exception $e) {
-            return $this->errorResponse($e->getMessage(), 422);
         }
+
+        return [
+            ['date' => $data['date']],
+        ];
     }
 }

+ 159 - 150
app/Http/Controllers/UserController.php

@@ -14,154 +14,163 @@ use Illuminate\Support\Str;
 
 class UserController extends Controller
 {
-  public function __construct(protected UserService $service) {}
-
-  public function me(): JsonResponse
-  {
-    $user = $this->service->me();
-    return $this->successResponse(payload: new UserResource($user));
-  }
-
-  public function index(): JsonResponse
-  {
-    $items = $this->service->getAll();
-    return $this->successResponse(
-      payload: UserResource::collection($items),
-    );
-  }
-
-  public function store(UserRequest $request): JsonResponse
-  {
-    $item = $this->service->create($request->validated());
-    return $this->successResponse(
-      payload: new UserResource($item),
-      message: __("messages.created"),
-      code: 201,
-    );
-  }
-
-  public function show(int $id): JsonResponse
-  {
-    $item = $this->service->findById($id);
-    return $this->successResponse(payload: new UserResource($item));
-  }
-
-  public function update(UserRequest $request, int $id): JsonResponse
-  {
-    $item = $this->service->update($id, $request->validated());
-    return $this->successResponse(
-      payload: new UserResource($item),
-      message: __("messages.updated"),
-    );
-  }
-
-  public function destroy(int $id): JsonResponse
-  {
-      $this->service->delete($id);
-
-      return $this->successResponse(
-          message: __('messages.deleted'),
-          code: 204,
-      );
-  }
-
-  public function updateMe(UpdateMeRequest $request): JsonResponse
-  {
-      $traceId = Str::uuid()->toString();
-      Log::withContext(['trace_id' => $traceId]);
-
-      $hasAvatar = $request->hasFile('avatar');
-      $avatar    = $request->file('avatar');
-
-      $data       = $request->validated();
-      $hasBase64  = ! empty($data['avatar_base64']);
-
-      Log::warning('[avatar-upload] Requisição recebida em updateMe', [
-          'user_id'          => auth()->id(),
-          'has_avatar'       => $hasAvatar,
-          'has_avatar_base64'=> $hasBase64,
-          'avatar_base64_len'=> $hasBase64 ? strlen($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'),
-      ]);
-
-      if ($hasBase64) {
-          $decoded = $this->decodeBase64ToUploadedFile($data['avatar_base64']);
-
-          Log::warning('[avatar-upload] Avatar base64 decodificado', [
-              'decode_ok'   => $decoded !== null,
-              'decoded_size'=> $decoded?->getSize(),
-              'decoded_mime'=> $decoded?->getClientMimeType(),
-          ]);
-
-          if ($decoded) {
-              $data['avatar'] = $decoded;
-          }
-      }
-
-      unset($data['avatar_base64']);
-
-      $user = $this->service->updateMe($data);
-
-      Log::warning('[avatar-upload] updateMe concluído com sucesso', [
-          'user_id'         => $user->id,
-          'client_id'       => $user->client?->id,
-          'profile_media_id'=> $user->client?->profile_media_id,
-          'media_path'      => $user->client?->profileMedia?->path,
-          'media_url'       => $user->client?->profileMedia?->url,
-      ]);
-
-      return $this->successResponse(
-          payload: new UserResource($user),
-          message: __('messages.updated'),
-      );
-  }
-
-  private function decodeBase64ToUploadedFile(string $base64): ?UploadedFile
-  {
-      if (preg_match('/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.+)$/s', $base64, $matches)) {
-          $mime    = $matches[1];
-          $content = base64_decode($matches[2], true);
-      } else {
-          $mime    = 'image/jpeg';
-          $content = base64_decode($base64, true);
-      }
-
-      if ($content === false || $content === '') {
-          return null;
-      }
-
-      $extension = match ($mime) {
-          'image/png'              => 'png',
-          'image/webp'             => 'webp',
-          'image/jpg', 'image/jpeg' => 'jpg',
-          default                  => 'jpg',
-      };
-
-      $tmpPath = tempnam(sys_get_temp_dir(), 'avatar_');
-
-      if ($tmpPath === false || file_put_contents($tmpPath, $content) === false) {
-          return null;
-      }
-
-      return new UploadedFile(
-          path: $tmpPath,
-          originalName: 'avatar.'.$extension,
-          mimeType: $mime,
-          test: true,
-      );
-  }
-
-  public function getUserTypes(): JsonResponse
-  {
-    $user_types = $this->service->getUserTypes();
-    return $this->successResponse(
-      payload: new UserTypeResource($user_types),
-    );
-  }
+    public function __construct(protected UserService $service) {}
+
+    public function index(): JsonResponse
+    {
+        $items = $this->service->getAll();
+
+        return $this->successResponse(
+            payload: UserResource::collection($items),
+        );
+    }
+
+    public function store(UserRequest $request): JsonResponse
+    {
+        $item = $this->service->create($request->validated());
+
+        return $this->successResponse(
+            payload: new UserResource($item),
+            message: __("messages.created"),
+            code: 201,
+        );
+    }
+
+    public function show(int $id): JsonResponse
+    {
+        $item = $this->service->findById($id);
+        return $this->successResponse(payload: new UserResource($item));
+    }
+
+    public function update(UserRequest $request, int $id): JsonResponse
+    {
+        $item = $this->service->update($id, $request->validated());
+
+        return $this->successResponse(
+            payload: new UserResource($item),
+            message: __("messages.updated"),
+        );
+    }
+
+    public function destroy(int $id): JsonResponse
+    {
+        $this->service->delete($id);
+
+        return $this->successResponse(
+            message: __('messages.deleted'),
+            code: 204,
+        );
+    }
+
+    //
+
+    public function getUserTypes(): JsonResponse
+    {
+        $user_types = $this->service->getUserTypes();
+
+        return $this->successResponse(
+            payload: new UserTypeResource($user_types),
+        );
+    }
+
+    public function me(): JsonResponse
+    {
+        $user = $this->service->me();
+        return $this->successResponse(payload: new UserResource($user));
+    }
+
+    public function updateMe(UpdateMeRequest $request): JsonResponse
+    {
+        $traceId = Str::uuid()->toString();
+
+        Log::withContext(['trace_id' => $traceId]);
+
+        $hasAvatar = $request->hasFile('avatar');
+        $avatar    = $request->file('avatar');
+
+        $data       = $request->validated();
+        $hasBase64  = ! empty($data['avatar_base64']);
+
+        Log::warning('[avatar-upload] Requisição recebida em updateMe', [
+            'user_id'           => auth()->id(),
+            'has_avatar'        => $hasAvatar,
+            'has_avatar_base64' => $hasBase64,
+            'avatar_base64_len' => $hasBase64 ? strlen($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'),
+        ]);
+
+        if ($hasBase64) {
+            $decoded = $this->decodeBase64ToUploadedFile($data['avatar_base64']);
+
+            Log::warning('[avatar-upload] Avatar base64 decodificado', [
+                'decode_ok'    => $decoded !== null,
+                'decoded_size' => $decoded?->getSize(),
+                'decoded_mime' => $decoded?->getClientMimeType(),
+            ]);
+
+            if ($decoded) {
+                $data['avatar'] = $decoded;
+            }
+        }
+
+        unset($data['avatar_base64']);
+
+        $user = $this->service->updateMe($data);
+
+        Log::warning('[avatar-upload] updateMe concluído com sucesso', [
+            'user_id'          => $user->id,
+            'client_id'        => $user->client?->id,
+            'profile_media_id' => $user->client?->profile_media_id,
+            'media_path'       => $user->client?->profileMedia?->path,
+            'media_url'        => $user->client?->profileMedia?->url,
+        ]);
+
+        return $this->successResponse(
+            payload: new UserResource($user),
+            message: __('messages.updated'),
+        );
+    }
+
+    //
+
+    private function decodeBase64ToUploadedFile(string $base64): ?UploadedFile
+    {
+        if (preg_match('/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.+)$/s', $base64, $matches)) {
+            $mime    = $matches[1];
+            $content = base64_decode($matches[2], true);
+        } else {
+            $mime    = 'image/jpeg';
+            $content = base64_decode($base64, true);
+        }
+
+        if ($content === false || $content === '') {
+            return null;
+        }
+
+        $extension = match ($mime) {
+            'image/png'               => 'png',
+            'image/webp'              => 'webp',
+            'image/jpg', 'image/jpeg' => 'jpg',
+            default                   => 'jpg',
+        };
+
+        $tmpPath = tempnam(sys_get_temp_dir(), 'avatar_');
+
+        if ($tmpPath === false || file_put_contents($tmpPath, $content) === false) {
+            return null;
+        }
+
+        return new UploadedFile(
+            path: $tmpPath,
+            originalName: 'avatar.'.$extension,
+            mimeType: $mime,
+            test: true,
+        );
+    }
 }

+ 17 - 14
app/Http/Requests/ProviderPaymentMethodRequest.php

@@ -5,7 +5,6 @@ namespace App\Http\Requests;
 use App\Enums\AccountTypeEnum;
 use App\Enums\BankAccountTypeEnum;
 use Illuminate\Foundation\Http\FormRequest;
-use Illuminate\Validation\Rule;
 
 class ProviderPaymentMethodRequest extends FormRequest
 {
@@ -16,30 +15,34 @@ class ProviderPaymentMethodRequest extends FormRequest
 
     public function rules(): array
     {
+        $pixAccountType   = AccountTypeEnum::PIX->value;
+        $bankAccountType  = AccountTypeEnum::BANK_ACCOUNT->value;
+        $accountTypes     = implode(',', [$pixAccountType, $bankAccountType]);
+        $bankAccountTypes = implode(',', [BankAccountTypeEnum::CHECKING->value, BankAccountTypeEnum::SAVINGS->value]);
 
         if ($this->account_type == AccountTypeEnum::PIX->value) {
 
             $rules =  [
-                'account_type' => ['sometimes', Rule::in([AccountTypeEnum::PIX->value, AccountTypeEnum::BANK_ACCOUNT->value])],
-                'pix_key' => ['required', 'string', 'max:255', Rule::requiredIf($this->account_type === AccountTypeEnum::PIX->value)],
-                'bank_account_type' => ['nullable', Rule::in([BankAccountTypeEnum::CHECKING->value, BankAccountTypeEnum::SAVINGS->value]), Rule::requiredIf($this->account_type === AccountTypeEnum::BANK_ACCOUNT->value)],
-                'agency' => ['nullable', 'string', 'max:255', Rule::requiredIf($this->account_type === AccountTypeEnum::BANK_ACCOUNT->value)],
-                'account' => ['nullable', 'string', 'max:255', Rule::requiredIf($this->account_type === AccountTypeEnum::BANK_ACCOUNT->value)],
-                'digit' => ['nullable', 'string', 'max:255', Rule::requiredIf($this->account_type === AccountTypeEnum::BANK_ACCOUNT->value)],
+                'account_type'      => "sometimes|in:{$accountTypes}",
+                'pix_key'           => "required|string|max:255|required_if:account_type,{$pixAccountType}",
+                'bank_account_type' => "nullable|required_if:account_type,{$bankAccountType}|in:{$bankAccountTypes}",
+                'agency'            => "nullable|string|max:255|required_if:account_type,{$bankAccountType}",
+                'account'           => "nullable|string|max:255|required_if:account_type,{$bankAccountType}",
+                'digit'             => "nullable|string|max:255|required_if:account_type,{$bankAccountType}",
             ];
         } else {
             $rules =  [
-                'account_type' => ['sometimes', Rule::in([AccountTypeEnum::PIX->value, AccountTypeEnum::BANK_ACCOUNT->value])],
-                'pix_key' => ['nullable', 'string', 'max:255', Rule::requiredIf($this->account_type === AccountTypeEnum::PIX->value)],
-                'bank_account_type' => ['sometimes', Rule::in([BankAccountTypeEnum::CHECKING->value, BankAccountTypeEnum::SAVINGS->value]), Rule::requiredIf($this->account_type === AccountTypeEnum::BANK_ACCOUNT->value)],
-                'agency' => ['sometimes', 'string', 'max:255', Rule::requiredIf($this->account_type === AccountTypeEnum::BANK_ACCOUNT->value)],
-                'account' => ['sometimes', 'string', 'max:255', Rule::requiredIf($this->account_type === AccountTypeEnum::BANK_ACCOUNT->value)],
-                'digit' => ['sometimes', 'string', 'max:255', Rule::requiredIf($this->account_type === AccountTypeEnum::BANK_ACCOUNT->value)],
+                'account_type'      => "sometimes|in:{$accountTypes}",
+                'pix_key'           => "nullable|string|max:255|required_if:account_type,{$pixAccountType}",
+                'bank_account_type' => "sometimes|required_if:account_type,{$bankAccountType}|in:{$bankAccountTypes}",
+                'agency'            => "sometimes|string|max:255|required_if:account_type,{$bankAccountType}",
+                'account'           => "sometimes|string|max:255|required_if:account_type,{$bankAccountType}",
+                'digit'             => "sometimes|string|max:255|required_if:account_type,{$bankAccountType}",
             ];
         }
 
         if ($this->isMethod('post')) {
-            $rules['provider_id'] =  ['required', 'exists:providers,id'];
+            $rules['provider_id'] = 'required|exists:providers,id';
         };
 
 

+ 34 - 34
app/Http/Requests/ScheduleRequest.php

@@ -31,17 +31,17 @@ class ScheduleRequest extends FormRequest
         ];
 
         if ($this->isMethod('POST')) {
-            $rules['client_id']                = 'required|exists:clients,id';
-            $rules['provider_id']              = 'required|exists:providers,id';
-            $rules['address_id']               = 'required|exists:addresses,id';
-            $rules['date']                     = 'required_without:dates|nullable|date|after_or_equal:today';
-            $rules['dates']                    = 'nullable|array';
-            $rules['dates.*']                  = 'date|after_or_equal:today';
-            $rules['period_type']              = 'required|in:2,4,6,8';
-            $rules['start_time']               = 'required|date_format:H:i';
-            $rules['end_time']                 = 'required|date_format:H:i|after:start_time';
-            $rules['total_amount']             = 'required|numeric|min:0';
-            $rules['status']                   = 'in:pending';
+            $rules['client_id']    = 'required|exists:clients,id';
+            $rules['provider_id']  = 'required|exists:providers,id';
+            $rules['address_id']   = 'required|exists:addresses,id';
+            $rules['date']         = 'required_without:dates|nullable|date|after_or_equal:today';
+            $rules['dates']        = 'nullable|array';
+            $rules['dates.*']      = 'date|after_or_equal:today';
+            $rules['period_type']  = 'required|in:2,4,6,8';
+            $rules['start_time']   = 'required|date_format:H:i';
+            $rules['end_time']     = 'required|date_format:H:i|after:start_time';
+            $rules['total_amount'] = 'required|numeric|min:0';
+            $rules['status']       = 'in:pending';
         }
 
         return $rules;
@@ -50,29 +50,29 @@ class ScheduleRequest extends FormRequest
     public function messages(): array
     {
         return [
-            'client_id.required'                 => 'O cliente é obrigatório.',
-            'client_id.exists'                   => 'Cliente não encontrado.',
-            'provider_id.required'               => 'O prestador é obrigatório.',
-            'provider_id.exists'                 => 'Prestador não encontrado.',
-            'address_id.required'                => 'O endereço é obrigatório.',
-            'address_id.exists'                  => 'Endereço não encontrado.',
-            'date.required_without'              => 'A data é obrigatória quando não há múltiplas datas.',
-            'date.date'                          => 'Data inválida.',
-            'date.after_or_equal'                => 'A data deve ser hoje ou futura.',
-            'dates.array'                        => 'Datas devem ser um array.',
-            'dates.*.date'                       => 'Uma das datas é inválida.',
-            'dates.*.after_or_equal'             => 'Todas as datas devem ser hoje ou futuras.',
-            'period_type.required'               => 'O período é obrigatório.',
-            'period_type.in'                     => 'Período inválido.',
-            'start_time.required'                => 'O horário de início é obrigatório.',
-            'start_time.date_format'             => 'Formato de horário inválido.',
-            'end_time.required'                  => 'O horário de término é obrigatório.',
-            'end_time.date_format'               => 'Formato de horário inválido.',
-            'end_time.after'                     => 'O horário de término deve ser após o horário de início.',
-            'total_amount.required'              => 'O valor total é obrigatório.',
-            'total_amount.numeric'               => 'O valor total deve ser numérico.',
-            'total_amount.min'                   => 'O valor total deve ser maior ou igual a zero.',
-            'status.in'                          => 'Status inválido.',
+            'client_id.required'     => 'O cliente é obrigatório.',
+            'client_id.exists'       => 'Cliente não encontrado.',
+            'provider_id.required'   => 'O prestador é obrigatório.',
+            'provider_id.exists'     => 'Prestador não encontrado.',
+            'address_id.required'    => 'O endereço é obrigatório.',
+            'address_id.exists'      => 'Endereço não encontrado.',
+            'date.required_without'  => 'A data é obrigatória quando não há múltiplas datas.',
+            'date.date'              => 'Data inválida.',
+            'date.after_or_equal'    => 'A data deve ser hoje ou futura.',
+            'dates.array'            => 'Datas devem ser um array.',
+            'dates.*.date'           => 'Uma das datas é inválida.',
+            'dates.*.after_or_equal' => 'Todas as datas devem ser hoje ou futuras.',
+            'period_type.required'   => 'O período é obrigatório.',
+            'period_type.in'         => 'Período inválido.',
+            'start_time.required'    => 'O horário de início é obrigatório.',
+            'start_time.date_format' => 'Formato de horário inválido.',
+            'end_time.required'      => 'O horário de término é obrigatório.',
+            'end_time.date_format'   => 'Formato de horário inválido.',
+            'end_time.after'         => 'O horário de término deve ser após o horário de início.',
+            'total_amount.required'  => 'O valor total é obrigatório.',
+            'total_amount.numeric'   => 'O valor total deve ser numérico.',
+            'total_amount.min'       => 'O valor total deve ser maior ou igual a zero.',
+            'status.in'              => 'Status inválido.',
         ];
     }
 }

+ 6 - 6
app/Http/Requests/UpdateMeRequest.php

@@ -16,12 +16,12 @@ class UpdateMeRequest extends FormRequest
     public function rules(): array
     {
         return [
-            'name'     => 'sometimes|string|nullable|max:255',
-            'email'    => ['sometimes', 'email', 'nullable', Rule::unique('users', 'email')->ignore(Auth::id())],
-            'phone'    => 'sometimes|string|nullable',
-            'language' => 'sometimes|string|nullable',
-            'document' => 'sometimes|string|nullable',
-            'avatar'   => 'sometimes|file|image|mimes:jpg,jpeg,png,webp|max:5120',
+            'name'          => 'sometimes|string|nullable|max:255',
+            'email'         => ['sometimes', 'email', 'nullable', Rule::unique('users', 'email')->ignore(Auth::id())],
+            'phone'         => 'sometimes|string|nullable',
+            'language'      => 'sometimes|string|nullable',
+            'document'      => 'sometimes|string|nullable',
+            'avatar'        => 'sometimes|file|image|mimes:jpg,jpeg,png,webp|max:5120',
             'avatar_base64' => 'sometimes|string|nullable',
         ];
     }

+ 1 - 0
app/Http/Requests/UserAppsRequest.php

@@ -17,6 +17,7 @@ class UserAppsRequest extends FormRequest
             $rules['phone'] = 'required|string|max:20';
             $rules['email'] = 'nullable';
         }
+
         if (! $this->has('phone')) {
             $rules['email'] = 'required|email';
             $rules['phone'] = 'nullable';

+ 3 - 1
app/Http/Resources/AddressResource.php

@@ -29,12 +29,14 @@ class AddressResource extends JsonResource
             'is_primary'     => $this->is_primary,
             'city_id'        => $this->city_id,
             'state_id'       => $this->state_id,
-            'address_full'   => implode(', ', array_filter([
+
+            'address_full' => implode(', ', array_filter([
                 $this->address,
                 $this->number ? "nº {$this->number}" : null,
                 $this->district,
                 $this->city ? "{$this->city->name}/{$this->state?->code}" : null,
             ])),
+
             'latitude'     => $this->latitude,
             'longitude'    => $this->longitude,
             'city'         => $this->whenLoaded('city'),

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

@@ -2,10 +2,10 @@
 
 namespace App\Http\Resources;
 
+use App\Models\CartItem;
 use Illuminate\Http\Request;
 use Illuminate\Http\Resources\Json\JsonResource;
 use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
-use App\Models\CartItem;
 
 class CartItemResource extends JsonResource
 {

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

@@ -2,10 +2,10 @@
 
 namespace App\Http\Resources;
 
+use App\Models\Cart;
 use Illuminate\Http\Request;
 use Illuminate\Http\Resources\Json\JsonResource;
 use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
-use App\Models\Cart;
 
 class CartResource extends JsonResource
 {

+ 11 - 8
app/Http/Resources/CustomScheduleResource.php

@@ -37,21 +37,24 @@ class CustomScheduleResource extends JsonResource
                     'id'          => $this->schedule->id,
                     'client_id'   => $this->schedule->client_id,
                     'provider_id' => $this->schedule->provider_id,
-                    'client_name'    => $this->schedule->client?->user?->name,
+                    'client_name' => $this->schedule->client?->user?->name,
+
                     'customer_photo' => (function () {
                         $path = $this->schedule->client?->profileMedia?->path;
                         return $path ? Storage::temporaryUrl($path, now()->addMinutes(60)) : null;
                     })(),
+
                     'address_id'  => $this->schedule->address_id,
 
                     'address' => $this->schedule->address ? [
-                        'address'      => $this->schedule->address->address,
-                        'number'       => $this->schedule->address->number,
-                        'district'     => $this->schedule->address->district,
-                        'complement'   => $this->schedule->address->complement,
-                        'zip_code'     => $this->schedule->address->zip_code,
-                        'city'         => $this->schedule->address->city,
-                        'state'        => $this->schedule->address->state,
+                        'address'    => $this->schedule->address->address,
+                        'number'     => $this->schedule->address->number,
+                        'district'   => $this->schedule->address->district,
+                        'complement' => $this->schedule->address->complement,
+                        'zip_code'   => $this->schedule->address->zip_code,
+                        'city'       => $this->schedule->address->city,
+                        'state'      => $this->schedule->address->state,
+
                         'address_full' => implode(', ', array_filter([
                             $this->schedule->address->address,
                             $this->schedule->address->number ? "nº {$this->schedule->address->number}" : null,

+ 11 - 11
app/Http/Resources/DashboardClienteResource.php

@@ -15,18 +15,18 @@ class DashboardClienteResource extends JsonResource
     public function toArray(Request $request): array
     {
         return [
-            'headerBar'           => $this['headerBar'],
-            'summaryInfos'        => $this['summaryInfos'],
-            'pendingSchedules'    => $this['pendingSchedules'],
-            'nextSchedules'       => $this['nextSchedules'],
-            'lastDoneSchedules'   => $this['lastDoneSchedules'],
-            'favoriteProviders'   => $this['favoriteProviders'],
-            'providersClose'      => $this['providersClose'],
-            'schedulesProposals'  => $this['schedulesProposals'],
+            'headerBar'                  => $this['headerBar'],
+            'summaryInfos'               => $this['summaryInfos'],
+            'pendingSchedules'           => $this['pendingSchedules'],
+            'nextSchedules'              => $this['nextSchedules'],
+            'lastDoneSchedules'          => $this['lastDoneSchedules'],
+            'favoriteProviders'          => $this['favoriteProviders'],
+            'providersClose'             => $this['providersClose'],
+            'schedulesProposals'         => $this['schedulesProposals'],
             'customSchedulesNoProposals' => $this['customSchedulesNoProposals'],
-            'todaySchedules'      => $this['todaySchedules'],
-            'notifications'       => $this['notifications'],
-            'has_payment_methods' => $this['has_payment_methods'],
+            'todaySchedules'             => $this['todaySchedules'],
+            'notifications'              => $this['notifications'],
+            'has_payment_methods'        => $this['has_payment_methods'],
         ];
     }
 }

+ 0 - 2
app/Http/Resources/PaymentResource.php

@@ -73,8 +73,6 @@ class PaymentResource extends JsonResource
         $charge      = $this->gateway_payload['charges'][0] ?? [];
         $transaction = $charge['last_transaction']          ?? [];
 
-        $transaction = $charge['last_transaction'] ?? [];
-
         $expiresAt = $charge['expires_at']
             ?? $transaction['expires_at']
             ?? $this->expires_at?->toISOString();

+ 3 - 3
app/Http/Resources/ProviderClientBlockResource.php

@@ -14,9 +14,9 @@ class ProviderClientBlockResource extends JsonResource
             'id'            => $this->id,
             'provider_id'   => $this->provider_id,
             'client_id'     => $this->client_id,
-            'client_name'   => $this->client->user->name ?? null,
-            'client_email'  => $this->client->user->email ?? null,
-            'client_phone'  => $this->client->user->phone ?? null,
+            'client_name'   => $this->client->user->name     ?? null,
+            'client_email'  => $this->client->user->email    ?? null,
+            'client_phone'  => $this->client->user->phone    ?? null,
             'client_rating' => $this->client->average_rating ?? null,
             'client_photo'  => $this->resolveClientPhoto(),
             'created_at'    => $this->created_at?->format('Y-m-d H:i'),

+ 13 - 3
app/Http/Resources/ReviewDetailResource.php

@@ -10,7 +10,9 @@ class ReviewDetailResource extends JsonResource
     public function toArray(Request $request): array
     {
         $schedule = $this->schedule;
+
         $customSchedule = $schedule?->customSchedule;
+
         $address = $schedule?->address;
 
         return [
@@ -20,12 +22,14 @@ class ReviewDetailResource extends JsonResource
             'stars'      => $this->stars,
             'comment'    => $this->comment,
             'created_at' => $this->created_at?->format('Y-m-d H:i'),
+
             'improvements' => $this->whenLoaded('improvements', function () {
                 return $this->improvements->map(fn($imp) => [
                     'id'          => $imp->id,
                     'description' => $imp->description,
                 ]);
             }),
+
             'schedule' => $schedule ? [
                 'id'            => $schedule->id,
                 'schedule_type' => $schedule->schedule_type,
@@ -36,19 +40,22 @@ class ReviewDetailResource extends JsonResource
                 'period_type'   => $schedule->period_type,
                 'total_amount'  => $schedule->total_amount,
                 'code'          => $schedule->code,
-                'client'        => $schedule->client ? [
+
+                'client' => $schedule->client ? [
                     'id'    => $schedule->client->id,
                     'name'  => $schedule->client->user?->name,
                     'email' => $schedule->client->user?->email,
                     'phone' => $schedule->client->user?->phone,
                 ] : null,
-                'provider'      => $schedule->provider ? [
+
+                'provider' => $schedule->provider ? [
                     'id'    => $schedule->provider->id,
                     'name'  => $schedule->provider->user?->name,
                     'email' => $schedule->provider->user?->email,
                     'phone' => $schedule->provider->user?->phone,
                 ] : null,
-                'address'       => $address ? [
+
+                'address'  => $address ? [
                     'street'       => $address->street,
                     'number'       => $address->number,
                     'complement'   => $address->complement,
@@ -56,15 +63,18 @@ class ReviewDetailResource extends JsonResource
                     'city'         => $address->city?->name,
                     'state'        => $address->state?->name,
                 ] : null,
+
                 'custom_schedule' => $customSchedule ? [
                     'address_type' => $customSchedule->address_type,
                     'service_type' => $customSchedule->serviceType?->description,
+
                     'specialities' => $customSchedule->specialities
                         ? $customSchedule->specialities->map(fn($s) => [
                             'id'          => $s->id,
                             'description' => $s->description,
                         ])
                         : [],
+
                     'min_price'   => $customSchedule->min_price,
                     'max_price'   => $customSchedule->max_price,
                     'offers_meal' => $customSchedule->offers_meal,

+ 1 - 0
app/Http/Resources/ReviewResource.php

@@ -44,6 +44,7 @@ class ReviewResource extends JsonResource
                         : null,
                 ]);
             }),
+
             'created_at' => $this->created_at?->format('Y-m-d H:i'),
             'updated_at' => $this->updated_at?->format('Y-m-d H:i'),
         ];

+ 13 - 10
app/Http/Resources/UserResource.php

@@ -18,23 +18,26 @@ class UserResource extends JsonResource
   public function toArray(Request $request): array
   {
     return [
-      'id' => $this->id,
-      'name' => $this->name,
-      'email' => $this->email,
-      'phone' => $this->phone,
-      'language' => $this->language,
-      'type' => $this->type,
+      'id'          => $this->id,
+      'name'        => $this->name,
+      'email'       => $this->email,
+      'phone'       => $this->phone,
+      'language'    => $this->language,
+      'type'        => $this->type,
       'provider_id' => $this->provider?->id,
+
       'provider_daily_price_8h' => $this->provider?->daily_price_8h,
       'provider_daily_price_6h' => $this->provider?->daily_price_6h,
       'provider_daily_price_4h' => $this->provider?->daily_price_4h,
       'provider_daily_price_2h' => $this->provider?->daily_price_2h,
-      'client_id' => $this->client?->id,
-      'profile_photo' => $this->resolveProfilePhoto(),
+      'client_id'               => $this->client?->id,
+      'profile_photo'           => $this->resolveProfilePhoto(),
       'client_document'         => $this->client?->document,
       'registration_complete'   => $this->registration_complete,
-      'provider'                => new ProviderResource($this->whenLoaded('provider')),
-      'client'                  => new ClientResource($this->whenLoaded('client')),
+
+      'provider' => new ProviderResource($this->whenLoaded('provider')),
+      'client'   => new ClientResource($this->whenLoaded('client')),
+
       'created_at' => Carbon::parse($this->created_at)->format('Y-m-d H:i'),
       'updated_at' => Carbon::parse($this->updated_at)->format('Y-m-d H:i'),
     ];

+ 1 - 0
app/Http/Resources/UserTypeResource.php

@@ -18,6 +18,7 @@ class UserTypeResource extends JsonResource
         // ]
 
         $return = [];
+
         foreach ($this->resource as $value) {
             $return[$value] = $value;
         }

+ 17 - 6
app/Models/Cart.php

@@ -8,8 +8,13 @@ use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\SoftDeletes;
 
 /**
+ * @property int $id
+ * @property int $client_id
  * @property CartStatusEnum $status
- * @property-read \App\Models\Client|null $client
+ * @property \Illuminate\Support\Carbon|null $created_at
+ * @property \Illuminate\Support\Carbon|null $updated_at
+ * @property \Illuminate\Support\Carbon|null $deleted_at
+ * @property-read \App\Models\Client $client
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CartItem> $items
  * @property-read int|null $items_count
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Schedule> $schedules
@@ -18,6 +23,12 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart onlyTrashed()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart query()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart whereClientId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart whereCreatedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart whereDeletedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart whereId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart whereStatus($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart whereUpdatedAt($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart withTrashed(bool $withTrashed = true)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Cart withoutTrashed()
  * @mixin \Eloquent
@@ -45,14 +56,14 @@ class Cart extends Model
         return $this->belongsTo(Client::class);
     }
 
-    public function schedules()
+    public function items()
     {
-        return $this->belongsToMany(Schedule::class, 'cart_items')
-            ->withTimestamps();
+        return $this->hasMany(CartItem::class);
     }
 
-    public function items()
+    public function schedules()
     {
-        return $this->hasMany(CartItem::class);
+        return $this->belongsToMany(Schedule::class, 'cart_items')
+            ->withTimestamps();
     }
 }

+ 12 - 2
app/Models/CartItem.php

@@ -6,11 +6,21 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 
 /**
- * @property-read \App\Models\Cart|null $cart
- * @property-read \App\Models\Schedule|null $schedule
+ * @property int $id
+ * @property int $cart_id
+ * @property int $schedule_id
+ * @property \Illuminate\Support\Carbon|null $created_at
+ * @property \Illuminate\Support\Carbon|null $updated_at
+ * @property-read \App\Models\Cart $cart
+ * @property-read \App\Models\Schedule $schedule
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CartItem newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CartItem newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CartItem query()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CartItem whereCartId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CartItem whereCreatedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CartItem whereId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CartItem whereScheduleId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CartItem whereUpdatedAt($value)
  * @mixin \Eloquent
  */
 class CartItem extends Model

+ 11 - 9
app/Models/Client.php

@@ -74,10 +74,7 @@ class Client extends Model
         return $this->belongsTo(User::class)->select('id', 'name', 'email', 'phone');
     }
 
-    public function profileMedia(): BelongsTo
-    {
-        return $this->belongsTo(Media::class, 'profile_media_id');
-    }
+    //
 
     public function blockedByProviders(): HasMany
     {
@@ -89,6 +86,16 @@ class Client extends Model
         return $this->hasMany(ClientProviderBlock::class);
     }
 
+    public function profileMedia(): BelongsTo
+    {
+        return $this->belongsTo(Media::class, 'profile_media_id');
+    }
+
+    public function schedules()
+    {
+        return $this->hasMany(Schedule::class);
+    }
+
     public function updateAverageRating(float $newRating): void
     {
         $totalReviews = Review::where('reviews.origin', 'provider')
@@ -107,11 +114,6 @@ class Client extends Model
         $this->save();
     }
 
-    public function schedules()
-    {
-        return $this->hasMany(Schedule::class);
-    }
-
     //
 
     public function ensureGatewayCode(): string

+ 2 - 2
app/Models/Notification.php

@@ -56,8 +56,8 @@ class Notification extends Model
     ];
 
     protected $casts = [
-        'read' => 'boolean',
-        'read_at' => 'datetime',
+        'read'       => 'boolean',
+        'read_at'    => 'datetime',
         'created_at' => 'datetime',
         'updated_at' => 'datetime',
         'deleted_at' => 'datetime',

+ 13 - 11
app/Models/Payment.php

@@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
 
 /**
  * @property int $id
- * @property int $schedule_id
+ * @property int|null $schedule_id
  * @property int $client_id
  * @property int|null $provider_id
  * @property int|null $client_payment_method_id
@@ -42,7 +42,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @property-read \App\Models\Client $client
  * @property-read \App\Models\ClientPaymentMethod|null $clientPaymentMethod
  * @property-read \App\Models\Provider|null $provider
- * @property-read \App\Models\Schedule $schedule
+ * @property-read \App\Models\Schedule|null $schedule
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Schedule> $schedules
  * @property-read int|null $schedules_count
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\PaymentSplit> $splits
@@ -145,27 +145,29 @@ class Payment extends Model
         'deleted_at'          => 'datetime',
     ];
 
-    public function schedule()
+    public function client()
     {
-        return $this->belongsTo(Schedule::class);
+        return $this->belongsTo(Client::class);
     }
 
-    public function schedules()
+    public function provider()
     {
-        return $this->belongsToMany(Schedule::class, 'payment_schedules')
-            ->withTimestamps();
+        return $this->belongsTo(Provider::class);
     }
 
-    public function client()
+    public function schedule()
     {
-        return $this->belongsTo(Client::class);
+        return $this->belongsTo(Schedule::class);
     }
 
-    public function provider()
+    public function schedules()
     {
-        return $this->belongsTo(Provider::class);
+        return $this->belongsToMany(Schedule::class, 'payment_schedules')
+            ->withTimestamps();
     }
 
+    //
+
     public function clientPaymentMethod()
     {
         return $this->belongsTo(ClientPaymentMethod::class);

+ 12 - 2
app/Models/PaymentSchedule.php

@@ -7,11 +7,21 @@ use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
 /**
- * @property-read \App\Models\Payment|null $payment
- * @property-read \App\Models\Schedule|null $schedule
+ * @property int $id
+ * @property int $payment_id
+ * @property int $schedule_id
+ * @property \Illuminate\Support\Carbon|null $created_at
+ * @property \Illuminate\Support\Carbon|null $updated_at
+ * @property-read \App\Models\Payment $payment
+ * @property-read \App\Models\Schedule $schedule
  * @method static \Illuminate\Database\Eloquent\Builder<static>|PaymentSchedule newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|PaymentSchedule newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|PaymentSchedule query()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|PaymentSchedule whereCreatedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|PaymentSchedule whereId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|PaymentSchedule wherePaymentId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|PaymentSchedule whereScheduleId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|PaymentSchedule whereUpdatedAt($value)
  * @mixin \Eloquent
  */
 class PaymentSchedule extends Model

+ 76 - 87
app/Models/Provider.php

@@ -59,6 +59,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider onlyTrashed()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider query()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider visibleToCustomers()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereApprovalStatus($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereAverageRating($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereBirthDate($value)
@@ -98,11 +99,11 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  */
 class Provider extends Model
 {
-  use HasFactory, SoftDeletes;
+    use HasFactory, SoftDeletes;
 
-  protected $table = "providers";
+    protected $table = "providers";
 
-  protected $guarded = ["id", "recipient_code"];
+    protected $guarded = ["id", "recipient_code"];
 
     /**
      * Get the attributes that should be cast.
@@ -134,104 +135,92 @@ class Provider extends Model
         return $this->belongsTo(User::class, 'user_id');
     }
 
-  /**
-   * @return BelongsTo
-   */
-  public function profileMedia(): BelongsTo
-  {
-    return $this->belongsTo(Media::class, "profile_media_id");
-  }
-
-  public function documentFrontMedia(): BelongsTo
-  {
-    return $this->belongsTo(Media::class, 'document_front_media_id');
-  }
-
-  public function documentBackMedia(): BelongsTo
-  {
-    return $this->belongsTo(Media::class, 'document_back_media_id');
-  }
-
-  /**
-   * @return HasMany
-     */
+    //
+
+    public function documentFrontMedia(): BelongsTo
+    {
+        return $this->belongsTo(Media::class, 'document_front_media_id');
+    }
+
+    public function documentBackMedia(): BelongsTo
+    {
+        return $this->belongsTo(Media::class, 'document_back_media_id');
+    }
+
+    public function profileMedia(): BelongsTo
+    {
+        return $this->belongsTo(Media::class, "profile_media_id");
+    }
+
+    //
+
     public function addresses(): HasMany
     {
         return $this->hasMany(Address::class, 'source_id')
             ->where('source', 'provider');
     }
 
-    // /**
-    //  * @return HasOne
-    //  */
-    // public function primaryAddress(): HasOne
-    // {
-    //     return $this->hasOne(Address::class, 'source_id')
-    //         ->where('source', 'provider')
-    //         ->where('is_primary', true);
-    // }
+    public function primaryAddress()
+    {
+        return $this->hasOne(Address::class, "source_id")
+            ->where("source", "provider")
+            ->orderBy("is_primary", "desc");
+    }
 
-    /**
-     * @return HasMany
-   */
-  public function blockedClients()
-  {
-    return $this->hasMany(ProviderClientBlock::class);
-  }
-
-  /**
-   * @return \Illuminate\Database\Eloquent\Relations\HasMany
-   */
-  public function blockedByClients()
-  {
-    return $this->hasMany(ClientProviderBlock::class);
-  }
-
-  public function updateAverageRating(float $newRating): void
-  {
-    $totalReviews = Review::where('reviews.origin', 'client')
-      ->leftJoin('schedules', 'schedules.id', '=', 'reviews.schedule_id')
-      ->where('schedules.provider_id', $this->id)
-      ->count();
-
-    if ($totalReviews === 0) {
-      $this->average_rating = $newRating;
-    } else {
-      $currentTotalRating = $this->average_rating * ($totalReviews - 1);
-      $newAverage = ($currentTotalRating + $newRating) / $totalReviews;
-      $this->average_rating = round($newAverage, 2);
+    //
+
+    public function blockedByClients()
+    {
+        return $this->hasMany(ClientProviderBlock::class);
+    }
+
+    public function blockedClients()
+    {
+        return $this->hasMany(ProviderClientBlock::class);
     }
 
-    $this->save();
-  }
+    //
 
-  public function primaryAddress()
-  {
-    return $this->hasOne(Address::class, "source_id")
-      ->where("source", "provider")
-      ->orderBy("is_primary", "desc");
-  }
+    public function updateAverageRating(float $newRating): void
+    {
+        $totalReviews = Review::where('reviews.origin', 'client')
+            ->leftJoin('schedules', 'schedules.id', '=', 'reviews.schedule_id')
+            ->where('schedules.provider_id', $this->id)
+            ->count();
 
-  //
+        if ($totalReviews === 0) {
+            $this->average_rating = $newRating;
+        } else {
+            $currentTotalRating = $this->average_rating * ($totalReviews - 1);
 
-  public function scopeVisibleToCustomers($query)
-  {
-      return $query
-          ->whereNotNull('providers.recipient_default_bank_account')
-          ->whereRaw("providers.recipient_default_bank_account::text <> '{}'")
-          ->whereRaw("providers.recipient_default_bank_account::text <> '[]'");
-  }
+            $newAverage = ($currentTotalRating + $newRating) / $totalReviews;
 
-  public function ensureGatewayCode(): string
-  {
-      if (! empty($this->recipient_code)) {
-          return $this->recipient_code;
-      }
+            $this->average_rating = round($newAverage, 2);
+        }
 
-      $code = 'provider-'.(string) \Illuminate\Support\Str::uuid();
+        $this->save();
+    }
 
-      $this->forceFill(['recipient_code' => $code])->save();
+    //
 
-      return $code;
-  }
+    public function ensureGatewayCode(): string
+    {
+        if (! empty($this->recipient_code)) {
+            return $this->recipient_code;
+        }
+
+        $code = 'provider-'.(string) \Illuminate\Support\Str::uuid();
+
+        $this->forceFill(['recipient_code' => $code])->save();
+
+        return $code;
+    }
+
+    public function scopeVisibleToCustomers($query)
+    {
+        return $query
+            ->whereNotNull('providers.recipient_default_bank_account')
+            ->whereRaw("providers.recipient_default_bank_account::text <> '{}'")
+            ->whereRaw("providers.recipient_default_bank_account::text <> '[]'");
+    }
 }

+ 4 - 4
app/Models/ProviderClientBlock.php

@@ -45,13 +45,13 @@ class ProviderClientBlock extends Model
         'deleted_at' => 'datetime',
     ];
 
-    public function provider(): BelongsTo
+    public function client(): BelongsTo
     {
-        return $this->belongsTo(Provider::class);
+        return $this->belongsTo(Client::class);
     }
 
-    public function client(): BelongsTo
+    public function provider(): BelongsTo
     {
-        return $this->belongsTo(Client::class);
+        return $this->belongsTo(Provider::class);
     }
 }

+ 14 - 10
app/Models/Review.php

@@ -69,25 +69,19 @@ class Review extends Model
         return $this->belongsTo(Schedule::class);
     }
 
-    public function originProvider(): BelongsTo
-    {
-        return $this->belongsTo(Provider::class, 'origin_id');
-    }
+    //
 
     public function originClient(): BelongsTo
     {
         return $this->belongsTo(Client::class, 'origin_id');
     }
 
-    public function reviewsImprovements(): HasMany
+    public function originProvider(): BelongsTo
     {
-        return $this->hasMany(ReviewImprovement::class);
+        return $this->belongsTo(Provider::class, 'origin_id');
     }
 
-    public function reviewMedia(): HasMany
-    {
-        return $this->hasMany(ReviewMedia::class);
-    }
+    //
 
     public function improvements(): BelongsToMany
     {
@@ -98,4 +92,14 @@ class Review extends Model
             'improvement_type_id'
         )->withTimestamps();
     }
+
+    public function reviewsImprovements(): HasMany
+    {
+        return $this->hasMany(ReviewImprovement::class);
+    }
+
+    public function reviewMedia(): HasMany
+    {
+        return $this->hasMany(ReviewMedia::class);
+    }
 }

+ 4 - 4
app/Models/ReviewMedia.php

@@ -31,13 +31,13 @@ class ReviewMedia extends Model
 
     protected $fillable = ['review_id', 'media_id', 'origin'];
 
-    public function review(): BelongsTo
+    public function media(): BelongsTo
     {
-        return $this->belongsTo(Review::class);
+        return $this->belongsTo(Media::class);
     }
 
-    public function media(): BelongsTo
+    public function review(): BelongsTo
     {
-        return $this->belongsTo(Media::class);
+        return $this->belongsTo(Review::class);
     }
 }

+ 2 - 0
app/Models/Schedule.php

@@ -110,6 +110,8 @@ class Schedule extends Model
         return $this->belongsTo(Provider::class);
     }
 
+    //
+
     public function address()
     {
         return $this->belongsTo(Address::class, 'address_id')->select('id', 'district');

+ 4 - 4
app/Models/ScheduleProposal.php

@@ -44,13 +44,13 @@ class ScheduleProposal extends Model
         'deleted_at' => 'datetime',
     ];
 
-    public function schedule(): BelongsTo
+    public function provider(): BelongsTo
     {
-        return $this->belongsTo(Schedule::class);
+        return $this->belongsTo(Provider::class);
     }
 
-    public function provider(): BelongsTo
+    public function schedule(): BelongsTo
     {
-        return $this->belongsTo(Provider::class);
+        return $this->belongsTo(Schedule::class);
     }
 }

+ 4 - 4
app/Models/ScheduleRefuse.php

@@ -44,13 +44,13 @@ class ScheduleRefuse extends Model
         'deleted_at' => 'datetime',
     ];
 
-    public function schedule(): BelongsTo
+    public function provider(): BelongsTo
     {
-        return $this->belongsTo(Schedule::class);
+        return $this->belongsTo(Provider::class);
     }
 
-    public function provider(): BelongsTo
+    public function schedule(): BelongsTo
     {
-        return $this->belongsTo(Provider::class);
+        return $this->belongsTo(Schedule::class);
     }
 }

+ 23 - 1
app/Models/SupportRequest.php

@@ -24,8 +24,30 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
  * @property \Illuminate\Support\Carbon|null $deleted_at
- * @property-read \App\Models\User|null $user
  * @property-read \App\Models\User|null $handler
+ * @property-read \App\Models\User|null $user
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest newModelQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest newQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest onlyTrashed()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest query()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereCategory($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereCreatedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereDeletedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereEmail($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereHandledBy($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereMessage($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereName($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest wherePhone($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereResolvedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereStatus($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereTitle($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereUpdatedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereUserId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest whereUserType($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest withTrashed(bool $withTrashed = true)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|SupportRequest withoutTrashed()
+ * @mixin \Eloquent
  */
 class SupportRequest extends Model
 {

+ 10 - 4
app/Models/User.php

@@ -91,16 +91,20 @@ class User extends Authenticatable
         return $this->type === UserTypeEnum::ADMIN;
     }
 
-    public function provider()
-    {
-        return $this->hasOne(Provider::class, 'user_id');
-    }
+    //
 
     public function client()
     {
         return $this->hasOne(Client::class, 'user_id');
     }
 
+    public function provider()
+    {
+        return $this->hasOne(Provider::class, 'user_id');
+    }
+
+    //
+
     public function deviceTokens()
     {
         return $this->hasMany(DeviceToken::class);
@@ -111,6 +115,8 @@ class User extends Authenticatable
         return $this->hasMany(PushNotificationLog::class);
     }
 
+    //
+
     /**
      * Create a new access token for the user.
      */

+ 14 - 12
app/Services/AddressService.php

@@ -7,18 +7,6 @@ use Illuminate\Database\Eloquent\Collection;
 
 class AddressService
 {
-    public function findBySource(array $data): ?Collection
-    {
-        $allAddresses = Address::where('source', $data['source'])
-            ->where('source_id', $data['source_id'])
-            ->with(['city', 'state'])
-            ->whereNull('deleted_at')
-            ->orderBy('is_primary', 'desc')
-            ->get();
-
-        return $allAddresses;
-    }
-
     public function one($id): Address
     {
         return Address::find($id);
@@ -53,6 +41,20 @@ class AddressService
         return $address->delete();
     }
 
+    //
+
+    public function findBySource(array $data): ?Collection
+    {
+        $allAddresses = Address::where('source', $data['source'])
+            ->where('source_id', $data['source_id'])
+            ->with(['city', 'state'])
+            ->whereNull('deleted_at')
+            ->orderBy('is_primary', 'desc')
+            ->get();
+
+        return $allAddresses;
+    }
+
     public function findPrimaryBySource(array $data): ?Address
     {
         return Address::where('source', $data['source'])

+ 2 - 0
app/Services/CityService.php

@@ -47,6 +47,8 @@ class CityService
         return $model->delete();
     }
 
+    //
+
     public function findStateCityByDescription(string $state, string $city): ?array
     {
         $state = State::query()

+ 34 - 16
app/Services/ClientCalendarService.php

@@ -33,22 +33,40 @@ class ClientCalendarService
             'provider_user.name as provider_name',
             '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 FROM reviews
-        WHERE reviews.schedule_id = schedules.id
-          AND reviews.origin = 'client'
-          AND reviews.origin_id = {$client->id}
-          AND reviews.deleted_at IS NULL
-      ) as client_reviewed"),
-            DB::raw("(
-        SELECT reviews.stars FROM reviews
-        WHERE reviews.schedule_id = schedules.id
-          AND reviews.origin = 'client'
-          AND reviews.origin_id = {$client->id}
-          AND reviews.deleted_at IS NULL
-        LIMIT 1
-      ) as client_stars"),
+
+            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
+                    FROM reviews
+                    WHERE reviews.schedule_id = schedules.id
+                    AND reviews.origin = 'client'
+                    AND reviews.origin_id = {$client->id}
+                    AND reviews.deleted_at IS NULL
+                ) AS client_reviewed
+            "),
+
+            DB::raw("
+                (
+                    SELECT reviews.stars
+                    FROM reviews
+                    WHERE reviews.schedule_id = schedules.id
+                    AND reviews.origin = 'client'
+                    AND reviews.origin_id = {$client->id}
+                    AND reviews.deleted_at IS NULL
+                    LIMIT 1
+                ) AS client_stars
+            "),
         ];
 
         $signPhoto = fn ($item) => tap($item, fn ($i) => $i->provider_photo = $i->provider_photo

+ 2 - 0
app/Services/ClientFavoriteProviderService.php

@@ -76,6 +76,8 @@ class ClientFavoriteProviderService
         return $favorite->delete();
     }
 
+    //
+
     public function getFavoritedProviderIds(int $clientId): array
     {
         return ClientFavoriteProvider::where('client_id', $clientId)

+ 2 - 0
app/Services/ClientPaymentMethodService.php

@@ -68,6 +68,8 @@ class ClientPaymentMethodService
         return $paymentMethod->delete();
     }
 
+    //
+
     private function deactivateOtherCards(int $clientId, ?int $exceptId = null): void
     {
         $query = ClientPaymentMethod::where('client_id', $clientId);

+ 2 - 0
app/Services/ClientProviderBlockService.php

@@ -64,6 +64,8 @@ class ClientProviderBlockService
         return $block->delete();
     }
 
+    //
+
     public function getBlockedProviderIds(int $clientId): array
     {
         return ClientProviderBlock::where('client_id', $clientId)

+ 15 - 11
app/Services/ClientService.php

@@ -53,7 +53,9 @@ class ClientService
                 sourceId: $client->id,
                 old: $client->profileMedia,
             );
+
             $data['profile_media_id'] = $media->id;
+
             unset($data['avatar']);
         }
 
@@ -69,6 +71,8 @@ class ClientService
         return $client->delete();
     }
 
+    //
+
     public function register(array $data): ?array
     {
         try {
@@ -107,18 +111,18 @@ class ClientService
             $client->refresh();
 
             $addressData = [
-                'zip_code'       => $data['zip_code'] ?? null,
-                'address'        => $data['address'] ?? null,
-                'number'         => $data['number'] ?? null,
-                'district'       => $data['district'] ?? null,
+                'zip_code'       => $data['zip_code']       ?? null,
+                'address'        => $data['address']        ?? null,
+                'number'         => $data['number']         ?? null,
+                'district'       => $data['district']       ?? null,
                 'has_complement' => $data['has_complement'] ?? false,
-                'complement'     => $data['complement'] ?? null,
-                'nickname'       => $data['nickname'] ?? null,
-                'instructions'   => $data['instructions'] ?? null,
-                'address_type'   => $data['address_type'] ?? 'home',
+                'complement'     => $data['complement']     ?? null,
+                'nickname'       => $data['nickname']       ?? null,
+                'instructions'   => $data['instructions']   ?? null,
+                'address_type'   => $data['address_type']   ?? 'home',
                 'is_primary'     => true,
-                'latitude'       => $data['latitude'] ?? null,
-                'longitude'      => $data['longitude'] ?? null,
+                'latitude'       => $data['latitude']   ?? null,
+                'longitude'      => $data['longitude']  ?? null,
             ];
 
             if (! empty($data['state']) && ! empty($data['city'])) {
@@ -128,7 +132,7 @@ class ClientService
                     $city = City::where('name', $data['city'])->where('state_id', $state->id)->first();
 
                     $addressData['state_id'] = $state->id;
-                    $addressData['city_id'] = $city?->id;
+                    $addressData['city_id']  = $city?->id;
                 }
             }
 

+ 188 - 171
app/Services/CustomScheduleService.php

@@ -54,6 +54,7 @@ class CustomScheduleService
 
         try {
             $quantity = $data['quantity'] ?? 1;
+
             $specialityIds = $data['speciality_ids'] ?? [];
 
             $createdCustomSchedules = [];
@@ -110,7 +111,9 @@ class CustomScheduleService
             return $createdCustomSchedules;
         } catch (\Exception $e) {
             DB::rollBack();
+
             Log::error('Error creating custom schedule: '.$e->getMessage());
+
             throw $e;
         }
     }
@@ -121,6 +124,7 @@ class CustomScheduleService
 
         try {
             $customSchedule = CustomSchedule::findOrFail($id);
+
             $schedule = $customSchedule->schedule;
 
             $scheduleUpdateData = [];
@@ -202,7 +206,9 @@ class CustomScheduleService
             ]);
         } catch (\Exception $e) {
             DB::rollBack();
+
             Log::error('Error updating custom schedule: '.$e->getMessage());
+
             throw $e;
         }
     }
@@ -226,22 +232,14 @@ class CustomScheduleService
             return $customSchedule;
         } catch (\Exception $e) {
             DB::rollBack();
+
             Log::error('Error deleting custom schedule: '.$e->getMessage());
+
             throw $e;
         }
     }
 
-    public function getSchedulesCustomGroupedByClient()
-    {
-        $schedules = Schedule::with(['client.user', 'provider.user', 'address', 'customSchedule.serviceType', 'customSchedule.specialities', 'reviews.reviewsImprovements.improvementType'])
-            ->orderBy('id', 'desc')
-            ->where('schedule_type', 'custom')
-            ->get();
-
-        $grouped = $this->formatCustomSchedules($schedules);
-
-        return $grouped;
-    }
+    //
 
     public function getAvailableOpportunities($providerId)
     {
@@ -252,7 +250,7 @@ class CustomScheduleService
             ->orderBy('is_primary', 'desc')
             ->first();
 
-        $providerLat = $providerAddress?->latitude !== null ? (float) $providerAddress->latitude : null;
+        $providerLat = $providerAddress?->latitude !== null  ? (float) $providerAddress->latitude  : null;
         $providerLng = $providerAddress?->longitude !== null ? (float) $providerAddress->longitude : null;
 
         $opportunities = Schedule::with([
@@ -280,13 +278,16 @@ class CustomScheduleService
                 'schedules.start_time',
                 'schedules.end_time',
                 'schedules.total_amount',
-                DB::raw("CASE
-           WHEN schedules.period_type = '2' THEN {$provider->daily_price_2h}
-           WHEN schedules.period_type = '4' THEN {$provider->daily_price_4h}
-           WHEN schedules.period_type = '6' THEN {$provider->daily_price_6h}
-           WHEN schedules.period_type = '8' THEN {$provider->daily_price_8h}
-           ELSE 0
-         END as total_amount"),
+
+                DB::raw("
+                    CASE
+                        WHEN schedules.period_type = '2' THEN {$provider->daily_price_2h}
+                        WHEN schedules.period_type = '4' THEN {$provider->daily_price_4h}
+                        WHEN schedules.period_type = '6' THEN {$provider->daily_price_6h}
+                        WHEN schedules.period_type = '8' THEN {$provider->daily_price_8h}
+                        ELSE 0
+                    END AS total_amount
+                "),
             )
             ->get();
 
@@ -302,11 +303,12 @@ class CustomScheduleService
             $opportunity->distance_km = DistanceService::calculate(
                 $providerLat,
                 $providerLng,
-                $opportunity->address?->latitude !== null ? (float) $opportunity->address->latitude : null,
+                $opportunity->address?->latitude !== null  ? (float) $opportunity->address->latitude  : null,
                 $opportunity->address?->longitude !== null ? (float) $opportunity->address->longitude : null,
             );
 
             $photoPath = $opportunity->client->profileMedia?->path;
+
             $opportunity->customer_photo = $photoPath
                 ? Storage::temporaryUrl($photoPath, now()->addMinutes(60))
                 : null;
@@ -315,6 +317,26 @@ class CustomScheduleService
         return $availableOpportunities->values();
     }
 
+    public function getOpportunityProposals($scheduleId)
+    {
+        return ScheduleProposal::with(['provider.user'])
+            ->where('schedule_id', $scheduleId)
+            ->orderBy('created_at', 'desc')
+            ->get();
+    }
+
+    public function getProvidersProposalsAndOpportunities($providerId)
+    {
+        $proposals = $this->getProviderProposals($providerId);
+
+        $opportunities = $this->formatCustomSchedules($this->getAvailableOpportunities($providerId));
+
+        return [
+            'proposals'     => $proposals,
+            'opportunities' => $opportunities,
+        ];
+    }
+
     public function getProviderProposals($providerId)
     {
         return ScheduleProposal::with([
@@ -331,14 +353,20 @@ class CustomScheduleService
             ->get();
     }
 
-    public function getOpportunityProposals($scheduleId)
+    public function getSchedulesCustomGroupedByClient()
     {
-        return ScheduleProposal::with(['provider.user'])
-            ->where('schedule_id', $scheduleId)
-            ->orderBy('created_at', 'desc')
+        $schedules = Schedule::with(['client.user', 'provider.user', 'address', 'customSchedule.serviceType', 'customSchedule.specialities', 'reviews.reviewsImprovements.improvementType'])
+            ->orderBy('id', 'desc')
+            ->where('schedule_type', 'custom')
             ->get();
+
+        $grouped = $this->formatCustomSchedules($schedules);
+
+        return $grouped;
     }
 
+    //
+
     public function proposeOpportunity($scheduleId, $providerId)
     {
         $schedule = Schedule::findOrFail($scheduleId);
@@ -376,27 +404,48 @@ class CustomScheduleService
         $notificationService = app(NotificationService::class);
 
         $notificationService->create([
-            'title' => 'Nova proposta recebida!',
-
-            'description' =>
-            $provider->user->name .
-                ' enviou uma proposta para seu agendamento sob medida.',
+            'title'       => 'Nova proposta recebida!',
+            'description' => $provider->user->name . ' enviou uma proposta para seu agendamento sob medida.',
+            'origin'      => 'schedule',
+            'origin_id'   => $schedule->id,
+            'type'        => NotificationTypeEnum::SCHEDULE_PROVIDER_CLIENT_NEW_SOLICITATION->value,
+            'user_id'     => $schedule->client->user_id,
+        ]);
 
-            'origin' => 'schedule',
+        return ScheduleProposal::create([
+            'schedule_id' => $scheduleId,
+            'provider_id' => $providerId,
+        ]);
+    }
 
-            'origin_id' => $schedule->id,
+    public function refuseOpportunity($scheduleId, $providerId)
+    {
 
-            'type' => NotificationTypeEnum::SCHEDULE_PROVIDER_CLIENT_NEW_SOLICITATION->value,
+        $schedule = Schedule::with(['client.user'])->findOrFail($scheduleId);
 
-            'user_id' => $schedule->client->user_id,
-        ]);
+        $provider = Provider::with(['user'])->findOrFail($providerId);
 
-        return ScheduleProposal::create([
+        $schedule_refuse = ScheduleRefuse::create([
             'schedule_id' => $scheduleId,
             'provider_id' => $providerId,
         ]);
+
+        $notificationService = app(NotificationService::class);
+
+        $notificationService->create([
+            'title'       => 'Oportunidade recusada!',
+            'description' => $provider->user->name . ' recusou sua solicitação sob medida.',
+            'origin'      => 'schedule',
+            'origin_id'   => $scheduleId,
+            'type'        => NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_REFUSED->value,
+            'user_id'     => $schedule->client->user_id,
+        ]);
+
+        return $schedule_refuse;
     }
 
+    //
+
     public function acceptProposal($proposalId)
     {
         return DB::transaction(function () use ($proposalId) {
@@ -435,20 +484,16 @@ class CustomScheduleService
             ]);
 
             $schedule->refresh();
+
             $schedule->load(['provider.user', 'client.user']);
 
             $notificationService->create([
-                'title' => 'Proposta aceita!',
-
+                'title'       => 'Proposta aceita!',
                 'description' => 'O cliente aceitou sua proposta de diária.',
-
-                'origin' => 'schedule',
-
-                'origin_id' => $schedule->id,
-
-                'type' => NotificationTypeEnum::SCHEDULE_PROVIDER_CLIENT_PROPOSAL_ACCEPTED->value,
-
-                'user_id' => $provider->user_id,
+                'origin'      => 'schedule',
+                'origin_id'   => $schedule->id,
+                'type'        => NotificationTypeEnum::SCHEDULE_PROVIDER_CLIENT_PROPOSAL_ACCEPTED->value,
+                'user_id'     => $provider->user_id,
             ]);
 
             app(ScheduleService::class)->updateStatus($schedule->id, 'accepted');
@@ -474,19 +519,12 @@ class CustomScheduleService
             $notificationService = app(NotificationService::class);
 
             $notificationService->create([
-                'title' => 'Proposta recusada!',
-
-                'description' =>
-                'O cliente recusou sua proposta de diária.',
-
-                'origin' => 'schedule',
-
-                'origin_id' => $proposal->schedule_id,
-
-                'type' =>
-                NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_REFUSED->value,
-
-                'user_id' => $proposal->provider->user_id,
+                'title'       => 'Proposta recusada!',
+                'description' => 'O cliente recusou sua proposta de diária.',
+                'origin'      => 'schedule',
+                'origin_id'   => $proposal->schedule_id,
+                'type'        => NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_REFUSED->value,
+                'user_id'     => $proposal->provider->user_id,
             ]);
 
 
@@ -496,97 +534,7 @@ class CustomScheduleService
         });
     }
 
-    private function checkProviderAvailability($providerId, $schedule)
-    {
-        $client_id = $schedule->client_id;
-        $provider_id = $providerId;
-
-        $date = Carbon::parse($schedule->date);
-        $dayOfWeek = $date->dayOfWeek; // 0-6
-        $startTime = $schedule->start_time;
-        $endTime = $schedule->end_time;
-        $date_ymd = $date->format('Y-m-d');
-        $period = $startTime < '13:00:00' ? 'morning' : 'afternoon';
-        $period_type = $schedule->period_type; // 2,4,6,8
-
-        // bloqueio 2 schedules por semana para o mesmo client e provider
-        ScheduleBusinessRules::validateWeeklyScheduleLimit(
-            $client_id,
-            $provider_id,
-            $date_ymd
-        );
-
-        // bloqueio provider trabalha no dia/periodo
-        ScheduleBusinessRules::validateWorkingDay(
-            $provider_id,
-            $dayOfWeek,
-            $period
-        );
-
-        // bloqueio provider tem blockedday para dia/hora
-        ScheduleBusinessRules::validateBlockedDay(
-            $provider_id,
-            $date_ymd,
-            $startTime,
-            $endTime
-        );
-
-        // bloqueio daily_price do provider esta fora do range min_price e max_price
-        ScheduleBusinessRules::validatePricePeriod(
-            $provider_id,
-            $schedule->customSchedule->min_price,
-            $schedule->customSchedule->max_price,
-            $period_type
-        );
-
-        // bloqueio provider tem outro agendamento para dia/hora
-        ScheduleBusinessRules::validateConflictingSchedule(
-            $provider_id,
-            $date_ymd,
-            $startTime,
-            $endTime
-        );
-
-        // bloqueio provider tem outra proposta para o mesmo agendamento
-        ScheduleBusinessRules::validateConflictingSameProposal(
-            $provider_id,
-            $schedule->id
-        );
-
-        // bloqueio provider tem outra proposta na mesma data
-        ScheduleBusinessRules::validateConflictingProposalSameDate(
-            $provider_id,
-            $date_ymd,
-            $startTime,
-            $endTime,
-            $schedule->id
-        );
-
-        // bloqueio caso o client tenha bloqueado o provider
-        ScheduleBusinessRules::validateClientNotBlockedByProvider(
-            $client_id,
-            $provider_id
-        );
-
-        // bloqueio caso o provider tenha bloqueado o client
-        ScheduleBusinessRules::validateProviderNotBlockedByClient(
-            $client_id,
-            $provider_id
-        );
-
-        return true;
-    }
-
-    public function getProvidersProposalsAndOpportunities($providerId)
-    {
-        $proposals = $this->getProviderProposals($providerId);
-        $opportunities = $this->formatCustomSchedules($this->getAvailableOpportunities($providerId));
-
-        return [
-            'proposals'     => $proposals,
-            'opportunities' => $opportunities,
-        ];
-    }
+    //
 
     public function formatCustomSchedules($schedules)
     {
@@ -598,9 +546,11 @@ class CustomScheduleService
             return [
                 'client_id'      => $firstSchedule->client_id,
                 'client_name'    => $firstSchedule->client->user->name ?? 'N/A',
+
                 'customer_photo' => $clientPhotoPath
                     ? Storage::temporaryUrl($clientPhotoPath, now()->addMinutes(60))
                     : null,
+
                 'schedules' => $clientSchedules->map(function ($schedule) {
                     $customSchedule = $schedule->customSchedule;
 
@@ -617,7 +567,8 @@ class CustomScheduleService
                         'provider_id'   => $schedule->provider_id,
                         'client_id'     => $schedule->client_id,
                         'provider_name' => $schedule->provider?->user->name ?? 'N/A',
-                        'address'       => $schedule->address ? [
+
+                        'address' => $schedule->address ? [
                             'id'         => $schedule->address->id,
                             'address'    => $schedule->address->address,
                             'complement' => $schedule->address->complement,
@@ -625,7 +576,9 @@ class CustomScheduleService
                             'city'       => $schedule->address->city->name ?? '',
                             'state'      => $schedule->address->city->state->name ?? '',
                         ] : null,
-                        'client_name'     => $schedule->client->user->name ?? 'N/A',
+
+                        'client_name' => $schedule->client->user->name ?? 'N/A',
+
                         'custom_schedule' => $customSchedule ? [
                             'id'                => $customSchedule->id,
                             'address_type'      => $customSchedule->address_type,
@@ -635,13 +588,15 @@ class CustomScheduleService
                             'min_price'         => $customSchedule->min_price,
                             'max_price'         => $customSchedule->max_price,
                             'offers_meal'       => $customSchedule->offers_meal,
-                            'specialities'      => $customSchedule->specialities->map(function ($speciality) {
+
+                            'specialities' => $customSchedule->specialities->map(function ($speciality) {
                                 return [
                                     'id'          => $speciality->id,
                                     'description' => $speciality->description,
                                 ];
                             })->values(),
                         ] : null,
+
                         'reviews' => $schedule->reviews->map(function ($review) {
                             return [
                                 'id'           => $review->id,
@@ -651,6 +606,7 @@ class CustomScheduleService
                                 'origin_id'    => $review->origin_id,
                                 'created_at'   => Carbon::parse($review->created_at)->format('Y-m-d H:i'),
                                 'updated_at'   => Carbon::parse($review->updated_at)->format('Y-m-d H:i'),
+
                                 'improvements' => $review->reviewsImprovements->map(function ($ri) {
                                     return [
                                         'id'                    => $ri->id,
@@ -687,35 +643,96 @@ class CustomScheduleService
         return $schedule;
     }
 
-    public function refuseOpportunity($scheduleId, $providerId)
+    //
+
+    private function checkProviderAvailability($providerId, $schedule)
     {
+        $client_id   = $schedule->client_id;
+        $provider_id = $providerId;
 
-        $schedule = Schedule::with(['client.user'])->findOrFail($scheduleId);
+        $date = Carbon::parse($schedule->date);
 
-        $provider = Provider::with(['user'])->findOrFail($providerId);
-        
-        $schedule_refuse = ScheduleRefuse::create([
-            'schedule_id' => $scheduleId,
-            'provider_id' => $providerId,
-        ]);
+        $dayOfWeek   = $date->dayOfWeek; // 0-6
+        $startTime   = $schedule->start_time;
+        $endTime     = $schedule->end_time;
+        $date_ymd    = $date->format('Y-m-d');
+        $period      = $startTime < '13:00:00' ? 'morning' : 'afternoon';
+        $period_type = $schedule->period_type; // 2,4,6,8
 
-        $notificationService = app(NotificationService::class);
+        // bloqueio 2 schedules por semana para o mesmo client e provider
 
-        $notificationService->create([
-            'title' => 'Oportunidade recusada!',
+        ScheduleBusinessRules::validateWeeklyScheduleLimit(
+            $client_id,
+            $provider_id,
+            $date_ymd
+        );
 
-            'description' => $provider->user->name . ' recusou sua solicitação sob medida.',
+        // bloqueio provider trabalha no dia/periodo
 
-            'origin' => 'schedule',
+        ScheduleBusinessRules::validateWorkingDay(
+            $provider_id,
+            $dayOfWeek,
+            $period
+        );
 
-            'origin_id' => $scheduleId,
+        // bloqueio provider tem blockedday para dia/hora
 
-            'type' =>
-            NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_REFUSED->value,
+        ScheduleBusinessRules::validateBlockedDay(
+            $provider_id,
+            $date_ymd,
+            $startTime,
+            $endTime
+        );
 
-            'user_id' => $schedule->client->user_id,
-        ]);
+        // bloqueio daily_price do provider esta fora do range min_price e max_price
 
-        return $schedule_refuse;
+        ScheduleBusinessRules::validatePricePeriod(
+            $provider_id,
+            $schedule->customSchedule->min_price,
+            $schedule->customSchedule->max_price,
+            $period_type
+        );
+
+        // bloqueio provider tem outro agendamento para dia/hora
+
+        ScheduleBusinessRules::validateConflictingSchedule(
+            $provider_id,
+            $date_ymd,
+            $startTime,
+            $endTime
+        );
+
+        // bloqueio provider tem outra proposta para o mesmo agendamento
+
+        ScheduleBusinessRules::validateConflictingSameProposal(
+            $provider_id,
+            $schedule->id
+        );
+
+        // bloqueio provider tem outra proposta na mesma data
+
+        ScheduleBusinessRules::validateConflictingProposalSameDate(
+            $provider_id,
+            $date_ymd,
+            $startTime,
+            $endTime,
+            $schedule->id
+        );
+
+        // bloqueio caso o client tenha bloqueado o provider
+
+        ScheduleBusinessRules::validateClientNotBlockedByProvider(
+            $client_id,
+            $provider_id
+        );
+
+        // bloqueio caso o provider tenha bloqueado o client
+
+        ScheduleBusinessRules::validateProviderNotBlockedByClient(
+            $client_id,
+            $provider_id
+        );
+
+        return true;
     }
 }

+ 9 - 7
app/Services/CustomScheduleSpecialityService.php

@@ -18,13 +18,6 @@ class CustomScheduleSpecialityService
         return CustomScheduleSpeciality::with(['customSchedule', 'speciality'])->findOrFail($id);
     }
 
-    public function getByCustomScheduleId($customScheduleId)
-    {
-        return CustomScheduleSpeciality::with(['speciality'])
-            ->where('custom_schedule_id', $customScheduleId)
-            ->get();
-    }
-
     public function create(array $data)
     {
         return CustomScheduleSpeciality::create($data);
@@ -47,4 +40,13 @@ class CustomScheduleSpecialityService
 
         return $customScheduleSpeciality;
     }
+
+    //
+
+    public function getByCustomScheduleId($customScheduleId)
+    {
+        return CustomScheduleSpeciality::with(['speciality'])
+            ->where('custom_schedule_id', $customScheduleId)
+            ->get();
+    }
 }

+ 275 - 193
app/Services/DashboardService.php

@@ -66,15 +66,17 @@ class DashboardService
                 ->count(),
         ];
 
-        $nextSchedules = Schedule::with('address:district,address,source_id,source,id,address_type')
+        $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')
-            ->where('schedules.date', '>=', now()->toDateString())
             ->select(
                 'schedules.id',
                 'schedules.provider_id',
@@ -89,17 +91,27 @@ class DashboardService
                 'custom_schedules.address_type as custom_address_type',
                 'provider_media.path as provider_photo_path',
 
-                DB::raw("(SELECT ci.cart_id FROM cart_items ci WHERE ci.schedule_id = schedules.id LIMIT 1) as cart_id"),
-
-                DB::raw(
-                    "(SELECT COUNT(*) FROM cart_items ci_count
+                DB::raw("
+                    (
+                        SELECT ci.cart_id
+                        FROM cart_items ci
+                        WHERE ci.schedule_id = schedules.id
+                        LIMIT 1
+                    ) AS cart_id
+                "),
+
+                DB::raw("
+                    (
+                        SELECT COUNT(*)
+                        FROM cart_items ci_count
                         WHERE ci_count.cart_id = (
-                            SELECT ci.cart_id FROM cart_items ci
-                                WHERE ci.schedule_id = schedules.id
-                                    LIMIT 1
+                            SELECT ci.cart_id
+                            FROM cart_items ci
+                            WHERE ci.schedule_id = schedules.id
+                            LIMIT 1
                         )
-                    ) as cart_items_count"
-                ),
+                    ) AS cart_items_count
+                "),
             )
             ->orderBy('schedules.date', 'asc')
             ->limit(5)
@@ -196,21 +208,34 @@ class DashboardService
             $clientPrimaryAddress?->longitude !== null ? (float) $clientPrimaryAddress->longitude : null,
         );
 
-        $providersClose = Provider::leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
+        $providersClose = Provider::leftJoin(
+            'users as provider_user',
+            'provider_user.id',
+            '=',
+            'providers.user_id'
+        )
             ->visibleToCustomers()
             ->leftJoin(
-                DB::raw(
-                    "(SELECT DISTINCT ON (source_id) * FROM addresses
-                    WHERE source = 'provider'
+                DB::raw("
+                    (
+                        SELECT DISTINCT ON (source_id)
+                            *
+                        FROM addresses
+                        WHERE source = 'provider'
                         AND deleted_at IS NULL
-                            ORDER BY source_id, is_primary DESC
-                ) as provider_address"
-                ),
+                        ORDER BY source_id, is_primary DESC
+                    ) AS provider_address
+                "),
                 'provider_address.source_id',
                 '=',
                 'providers.id'
             )
-            ->leftJoin('media as provider_media', 'provider_media.id', '=', 'providers.profile_media_id')
+            ->leftJoin(
+                'media as provider_media',
+                'provider_media.id',
+                '=',
+                'providers.profile_media_id'
+            )
             ->whereNotNull('provider_address.id')
             ->where('provider_address.city_id', $clientPrimaryAddress?->city_id)
             ->whereNotIn('providers.id', $blockedProviderIds)
@@ -230,13 +255,16 @@ class DashboardService
                 '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"
-                ),
+                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,
 
@@ -258,7 +286,9 @@ class DashboardService
             $item->daily_price_2h = $this->applyCreditCardFee($item->daily_price_2h);
         });
 
-        $pendingSchedules = Schedule::with('address:district,address,number,source_id,source,id,address_type')
+        $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')
@@ -276,27 +306,49 @@ class DashboardService
                 '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("
+                    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("(SELECT ci.cart_id FROM cart_items ci WHERE ci.schedule_id = schedules.id LIMIT 1) as cart_id"),
-
-                DB::raw(
-                    "(SELECT COUNT(*) FROM cart_items ci_count
+                DB::raw("
+                    (
+                        SELECT ci.cart_id
+                        FROM cart_items ci
+                        WHERE ci.schedule_id = schedules.id
+                        LIMIT 1
+                    ) AS cart_id
+                "),
+
+                DB::raw("
+                    (
+                        SELECT COUNT(*)
+                        FROM cart_items ci_count
                         WHERE ci_count.cart_id = (
-                            SELECT ci.cart_id FROM cart_items ci
-                                WHERE ci.schedule_id = schedules.id
-                                    LIMIT 1
+                            SELECT ci.cart_id
+                            FROM cart_items ci
+                            WHERE ci.schedule_id = schedules.id
+                            LIMIT 1
                         )
-                    ) as cart_items_count"
-                ),
+                    ) AS cart_items_count
+                "),
             )
             ->orderBy('schedules.date', 'asc')
             ->get();
@@ -315,25 +367,43 @@ class DashboardService
         );
 
         $schedulesProposals = ScheduleProposal::query()
-            ->leftJoin('schedules', 'schedule_proposals.schedule_id', '=', 'schedules.id')
-            ->leftJoin('providers', 'schedule_proposals.provider_id', '=', 'providers.id')
+            ->leftJoin(
+                'schedules',
+                'schedule_proposals.schedule_id',
+                '=',
+                'schedules.id'
+            )
+            ->leftJoin(
+                'providers',
+                'schedule_proposals.provider_id',
+                '=',
+                'providers.id'
+            )
             ->whereNotNull('providers.recipient_default_bank_account')
             ->whereRaw("providers.recipient_default_bank_account::text <> '{}'")
             ->whereRaw("providers.recipient_default_bank_account::text <> '[]'")
             ->leftJoin('users', 'providers.user_id', '=', 'users.id')
             ->leftJoin(
-                DB::raw(
-                    "(SELECT DISTINCT ON (source_id) * FROM addresses
+                DB::raw("
+                    (
+                        SELECT DISTINCT ON (source_id)
+                            *
+                        FROM addresses
                         WHERE source = 'provider'
-                            AND deleted_at IS NULL
-                                ORDER BY source_id, is_primary DESC
-                    ) as provider_address"
-                ),
+                        AND deleted_at IS NULL
+                        ORDER BY source_id, is_primary DESC
+                    ) AS provider_address
+                "),
                 'provider_address.source_id',
                 '=',
                 'providers.id'
             )
-            ->leftJoin('media as provider_media', 'provider_media.id', '=', 'providers.profile_media_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')
@@ -343,7 +413,9 @@ class DashboardService
             ->select([
                 'schedule_proposals.id',
 
-                DB::raw("DATE_PART('year', AGE(providers.birth_date)) as idade"),
+                DB::raw("
+                    DATE_PART('year', AGE(providers.birth_date)) AS idade
+                "),
 
                 'providers.id as provider_id',
                 'schedules.id as schedule_id',
@@ -379,7 +451,9 @@ class DashboardService
             unset($item->provider_photo_path);
         });
 
-        $todaySchedules = Schedule::with('address:district,address,number,source_id,source,id,address_type')
+        $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())
@@ -402,33 +476,47 @@ class DashboardService
                 'schedules.code',
                 'media.path as provider_photo',
 
-                DB::raw("(SELECT ci.cart_id FROM cart_items ci WHERE ci.schedule_id = schedules.id LIMIT 1) as cart_id"),
-
-                DB::raw(
-                    "(SELECT COUNT(*) FROM cart_items ci_count
+                DB::raw("
+                    (
+                        SELECT ci.cart_id
+                        FROM cart_items ci
+                        WHERE ci.schedule_id = schedules.id
+                        LIMIT 1
+                    ) AS cart_id
+                "),
+
+                DB::raw("
+                    (
+                        SELECT COUNT(*)
+                        FROM cart_items ci_count
                         WHERE ci_count.cart_id = (
-                            SELECT ci.cart_id FROM cart_items ci
-                                WHERE ci.schedule_id = schedules.id
-                                    LIMIT 1
+                            SELECT ci.cart_id
+                            FROM cart_items ci
+                            WHERE ci.schedule_id = schedules.id
+                            LIMIT 1
                         )
-                    ) as cart_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"
-                ),
+                    ) AS cart_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->provider_photo = $item->provider_photo
-                    ? Storage::temporaryUrl($item->provider_photo, now()->addMinutes(60))
+                    ? Storage::temporaryUrl(
+                        $item->provider_photo,
+                        now()->addMinutes(60)
+                    )
                     : null;
 
                 return $item;
@@ -453,81 +541,21 @@ class DashboardService
         $hasPaymentMethods = ClientPaymentMethod::where('client_id', $cliente->id)->exists();
 
         return [
-            'headerBar'           => $headerBar,
-            'summaryInfos'        => $summaryInfos,
-            'pendingSchedules'    => $pendingSchedules,
-            'nextSchedules'       => $nextSchedules,
-            'lastDoneSchedules'   => $lastDoneSchedules,
-            'favoriteProviders'   => $favoriteProviders,
-            'providersClose'      => $providersClose,
-            'todaySchedules'      => $todaySchedules,
-            'schedulesProposals'  => $schedulesProposals,
+            'headerBar'                  => $headerBar,
+            'summaryInfos'               => $summaryInfos,
+            'pendingSchedules'           => $pendingSchedules,
+            'nextSchedules'              => $nextSchedules,
+            'lastDoneSchedules'          => $lastDoneSchedules,
+            'favoriteProviders'          => $favoriteProviders,
+            'providersClose'             => $providersClose,
+            'todaySchedules'             => $todaySchedules,
+            'schedulesProposals'         => $schedulesProposals,
             'customSchedulesNoProposals' => $custom_schedules_with_no_proposals,
-            'notifications'       => $notifications,
-            'has_payment_methods' => $hasPaymentMethods,
+            'notifications'              => $notifications,
+            'has_payment_methods'        => $hasPaymentMethods,
         ];
     }
 
-    public function getScheduleClienteDetails(int $scheduleId): array
-    {
-        $user = Auth::user();
-
-        $cliente = Client::where('user_id', $user->id)->firstOrFail();
-
-        $schedule = Schedule::where('schedules.id', $scheduleId)
-            ->where('schedules.client_id', $cliente->id)
-            ->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',
-                'media.path as provider_photo',
-                'custom_schedules.offers_meal',
-            )
-            ->firstOrFail();
-
-        $allSpecialities = Speciality::where('active', true)
-            ->select('id', 'description')
-            ->orderBy('description')
-            ->get();
-
-        $providerSpecialityIds = ProviderSpeciality::where('provider_id', $schedule->provider_id)
-            ->pluck('speciality_id')
-            ->all();
-
-        $specialities = $allSpecialities->map(fn($sp) => [
-            'id'             => $sp->id,
-            'description'    => $sp->description,
-            'has_speciality' => in_array($sp->id, $providerSpecialityIds),
-        ])->values();
-
-        return [
-            'provider_name'       => $schedule->provider_name,
-            'provider_birth_date' => $schedule->provider_birth_date,
-            'offers_meal'         => $schedule->offers_meal,
-            'specialities'        => $specialities,
-
-            'provider_photo' => $schedule->provider_photo
-                ? Storage::temporaryUrl($schedule->provider_photo, now()->addMinutes(60))
-                : null,
-        ];
-    }
-
-    // aplicação da taxa 
-    private function applyCreditCardFee(?float $price): ?float
-    {
-        if ($price === null) {
-            return null;
-        }
-
-        $rate = (float) config('services.pagarme.platform_credit_card_fee_rate');
-
-        return round($price * (1 + $rate), 2);
-    }
-
     public function dadosDashboardPrestador(): array
     {
         $user = Auth::user();
@@ -568,7 +596,9 @@ class DashboardService
             'your_price'    => $priceActual,
         ];
 
-        $solicitations = Schedule::with('address:district,source_id,source,id,latitude,longitude')
+        $solicitations = Schedule::with([
+            'address:district,source_id,source,id,latitude,longitude',
+        ])
             ->where('schedules.provider_id', $provider->id)
             ->where('schedules.status', 'pending')
             ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
@@ -581,7 +611,9 @@ class DashboardService
                 'clients.average_rating',
                 'schedules.date',
 
-                DB::raw("TO_CHAR(schedules.date, 'DD/MM/YYYY') as formatted_date"),
+                DB::raw("
+                    TO_CHAR(schedules.date, 'DD/MM/YYYY') AS formatted_date
+                "),
 
                 'schedules.start_time',
                 'schedules.end_time',
@@ -593,12 +625,28 @@ class DashboardService
                 '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"
-                ),
+                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();
@@ -621,7 +669,9 @@ class DashboardService
             );
         });
 
-        $todayServices = Schedule::with('address:district,address,number,source_id,source,id')
+        $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())
@@ -646,15 +696,16 @@ class DashboardService
                 '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"
-                ),
+                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();
@@ -707,36 +758,6 @@ class DashboardService
             );
         });
 
-        // $opportunities = Schedule::with('address:district,source_id,source,id')
-        //   ->where('schedules.schedule_type', 'custom')
-        //   ->where('schedules.status', 'pending')
-        //   ->whereDate('schedules.date', '>=', now()->toDateString())
-        //   ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
-        //   ->leftJoin('users as client_user', 'client_user.id', '=', 'clients.user_id')
-        //   ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
-        //   ->select(
-        //     'schedules.id',
-        //     'custom_schedules.id as custom_schedule_id',
-        //     'client_user.name as client_name',
-        //     'clients.average_rating',
-        //     'schedules.date',
-        //     'schedules.start_time',
-        //     'schedules.end_time',
-        //     'schedules.period_type',
-        //     'schedules.schedule_type',
-        //     'schedules.address_id',
-        //     'custom_schedules.address_type',
-        //     DB::raw("CASE
-        //       WHEN schedules.period_type = '2' THEN {$provider->daily_price_2h}
-        //       WHEN schedules.period_type = '4' THEN {$provider->daily_price_4h}
-        //       WHEN schedules.period_type = '6' THEN {$provider->daily_price_6h}
-        //       WHEN schedules.period_type = '8' THEN {$provider->daily_price_8h}
-        //       ELSE 0
-        //     END as total_amount"),
-        //   )
-        //   ->orderBy('schedules.date', 'asc')
-        //   ->get();
-
         $notifications = Notification::where('user_id', $user->id)
             ->orderBy('read', 'asc')
             ->orderBy('created_at', 'desc')
@@ -773,6 +794,67 @@ class DashboardService
         ];
     }
 
+    public function getScheduleClienteDetails(int $scheduleId): array
+    {
+        $user = Auth::user();
+
+        $cliente = Client::where('user_id', $user->id)->firstOrFail();
+
+        $schedule = Schedule::where('schedules.id', $scheduleId)
+            ->where('schedules.client_id', $cliente->id)
+            ->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',
+                'media.path as provider_photo',
+                'custom_schedules.offers_meal',
+            )
+            ->firstOrFail();
+
+        $allSpecialities = Speciality::where('active', true)
+            ->select('id', 'description')
+            ->orderBy('description')
+            ->get();
+
+        $providerSpecialityIds = ProviderSpeciality::where('provider_id', $schedule->provider_id)
+            ->pluck('speciality_id')
+            ->all();
+
+        $specialities = $allSpecialities->map(fn($sp) => [
+            'id'             => $sp->id,
+            'description'    => $sp->description,
+            'has_speciality' => in_array($sp->id, $providerSpecialityIds),
+        ])->values();
+
+        return [
+            'provider_name'       => $schedule->provider_name,
+            'provider_birth_date' => $schedule->provider_birth_date,
+            'offers_meal'         => $schedule->offers_meal,
+            'specialities'        => $specialities,
+
+            'provider_photo' => $schedule->provider_photo
+                ? Storage::temporaryUrl($schedule->provider_photo, now()->addMinutes(60))
+                : null,
+        ];
+    }
+
+    //
+
+    private function applyCreditCardFee(?float $price): ?float
+    {
+        if ($price === null) {
+            return null;
+        }
+
+        $rate = (float) config('services.pagarme.platform_credit_card_fee_rate');
+
+        return round($price * (1 + $rate), 2);
+    }
+
     private function distanceSelect(?float $clientLatitude, ?float $clientLongitude): \Illuminate\Contracts\Database\Query\Expression
     {
         return DistanceService::sqlExpression($clientLatitude, $clientLongitude);

+ 23 - 13
app/Services/DistanceService.php

@@ -33,7 +33,7 @@ class DistanceService
         ?float $clientLongitude,
         string $targetLatCol = 'provider_address.latitude',
         string $targetLngCol = 'provider_address.longitude',
-        string $alias = 'distance_km'
+        string $alias        = 'distance_km'
     ): Expression {
         if ($clientLatitude === null || $clientLongitude === null) {
             return DB::raw("NULL as {$alias}");
@@ -43,19 +43,29 @@ class DistanceService
             CASE
                 WHEN {$targetLatCol} IS NOT NULL
                 AND {$targetLngCol} IS NOT NULL
-                THEN ROUND((
-                    ".self::EARTH_RADIUS_KM." * acos(
-                        least(1, greatest(-1,
-                            cos(radians({$clientLatitude}))
-                            * cos(radians({$targetLatCol}))
-                            * cos(radians({$targetLngCol}) - radians({$clientLongitude}))
-                            + sin(radians({$clientLatitude}))
-                            * sin(radians({$targetLatCol}))
-                        ))
-                    )
-                )::numeric, 1)
+                THEN ROUND(
+                    (
+                        " . self::EARTH_RADIUS_KM . " * ACOS(
+                            LEAST(
+                                1,
+                                GREATEST(
+                                    -1,
+                                    COS(RADIANS({$clientLatitude}))
+                                        * COS(RADIANS({$targetLatCol}))
+                                        * COS(
+                                            RADIANS({$targetLngCol})
+                                            - RADIANS({$clientLongitude})
+                                        )
+                                        + SIN(RADIANS({$clientLatitude}))
+                                        * SIN(RADIANS({$targetLatCol}))
+                                )
+                            )
+                        )
+                    )::numeric,
+                    1
+                )
                 ELSE NULL
-            END as {$alias}
+            END AS {$alias}
         ");
     }
 }

+ 22 - 22
app/Services/EmailService.php

@@ -9,16 +9,20 @@ use Illuminate\Support\Facades\Mail;
 
 class EmailService
 {
-    /**
-     * Envia o e-mail com o código de verificação para o usuário.
-     *
-     * @param  string  $email  Endereço de e-mail do destinatário.
-     * @param  string  $code  Código de 6 dígitos gerado.
-     * @param  string  $recipientName  Nome do destinatário (opcional).
-     */
-    public function sendVerificationCode(string $email, string $code, string $recipientName = ''): void
+    public function sendEmailReceipt(
+        string $email,
+        object $schedule,
+        string $client_name,
+        string $service_date,
+        string $start_time,
+        string $end_time,
+        string $address,
+        string $total_amount,
+        string $service_fee,
+        string $final_amount,
+        string $payment_method): void
     {
-        Mail::to($email)->send(new SendCodeMail($code, $recipientName));
+        Mail::to($email)->send(new EmailReceipt($schedule, $client_name, $service_date, $start_time, $end_time, $address, $total_amount, $service_fee, $final_amount, $payment_method));
     }
 
     public function sendProviderApproved(string $email, string $recipientName = '', ?string $locale = null): void
@@ -32,19 +36,15 @@ class EmailService
         $pendingMail->send(new ProviderApprovedMail($recipientName));
     }
 
-    public function sendEmailReceipt(
-        string $email,
-        object $schedule,
-        string $client_name,
-        string $service_date,
-        string $start_time,
-        string $end_time,
-        string $address,
-        string $total_amount,
-        string $service_fee,
-        string $final_amount,
-        string $payment_method): void
+    /**
+     * Envia o e-mail com o código de verificação para o usuário.
+     *
+     * @param  string $email          Endereço de e-mail do destinatário.
+     * @param  string $code           Código de 6 dígitos gerado.
+     * @param  string $recipientName  Nome do destinatário (opcional).
+     */
+    public function sendVerificationCode(string $email, string $code, string $recipientName = ''): void
     {
-        Mail::to($email)->send(new EmailReceipt($schedule, $client_name, $service_date, $start_time, $end_time, $address, $total_amount, $service_fee, $final_amount, $payment_method));
+        Mail::to($email)->send(new SendCodeMail($code, $recipientName));
     }
 }

+ 29 - 23
app/Services/GeminiService.php

@@ -23,13 +23,16 @@ class GeminiService
     {
         $apiKey = config('services.gemini.api_key');
         $model  = config('services.gemini.model');
+
         $contents = $this->buildContents($history, $message);
 
         $payload = [
             'systemInstruction' => [
                 'parts' => [['text' => $this->loadContext($userType)]],
             ],
+
             'contents' => $contents,
+
             'generationConfig' => [
                 'temperature'     => 0.4,
                 'maxOutputTokens' => 512,
@@ -38,7 +41,7 @@ class GeminiService
 
         try {
             $response = $this->client->post($this->buildApiUrl($model), [
-                'json' => $payload,
+                'json'  => $payload,
                 'query' => ['key' => $apiKey],
             ]);
 
@@ -53,39 +56,20 @@ class GeminiService
         }
     }
 
+    //
+
     private function buildApiUrl(string $model): string
     {
         return self::API_BASE_URL . '/' . rawurlencode($model) . ':generateContent';
     }
 
-    private function logGeminiError(GuzzleException $e, string $model): void
-    {
-        $context = [
-            'model'   => $model,
-            'message' => $this->sanitizeApiKey($e->getMessage()),
-        ];
-
-        if ($e instanceof RequestException && $e->hasResponse()) {
-            $response = $e->getResponse();
-
-            $context['status'] = $response->getStatusCode();
-            $context['body']   = $this->sanitizeApiKey((string) $response->getBody());
-        }
-
-        Log::error('Gemini API error', $context);
-    }
-
-    private function sanitizeApiKey(string $message): string
-    {
-        return preg_replace('/([?&]key=)[^&\s"]+/i', '$1[REDACTED]', $message);
-    }
-
     private function buildContents(array $history, string $newMessage): array
     {
         $contents = [];
 
         foreach ($history as $entry) {
             $role = $entry['role'] === 'model' ? 'model' : 'user';
+
             $contents[] = [
                 'role'  => $role,
                 'parts' => [['text' => $entry['text']]],
@@ -115,4 +99,26 @@ class GeminiService
 
         return $restrictions . "\n\n" . $context;
     }
+
+    private function logGeminiError(GuzzleException $e, string $model): void
+    {
+        $context = [
+            'model'   => $model,
+            'message' => $this->sanitizeApiKey($e->getMessage()),
+        ];
+
+        if ($e instanceof RequestException && $e->hasResponse()) {
+            $response = $e->getResponse();
+
+            $context['status'] = $response->getStatusCode();
+            $context['body']   = $this->sanitizeApiKey((string) $response->getBody());
+        }
+
+        Log::error('Gemini API error', $context);
+    }
+
+    private function sanitizeApiKey(string $message): string
+    {
+        return preg_replace('/([?&]key=)[^&\s"]+/i', '$1[REDACTED]', $message);
+    }
 }

+ 19 - 14
app/Services/MediaService.php

@@ -55,17 +55,19 @@ class MediaService
         return $model->delete();
     }
 
+    //
+
     public function createFromFile(UploadedFile $file, string $folder, string $source, int $sourceId, ?string $filename = null): Media
     {
         Log::warning('[avatar-upload] MediaService::createFromFile — iniciando upload para S3', [
-            'folder'            => $folder,
-            'source'            => $source,
-            'source_id'         => $sourceId,
-            'file_original_name'=> $file->getClientOriginalName(),
-            'file_size'         => $file->getSize(),
-            'file_mime'         => $file->getMimeType(),
-            'file_extension'    => $file->getClientOriginalExtension(),
-            'file_is_valid'     => $file->isValid(),
+            'folder'             => $folder,
+            'source'             => $source,
+            'source_id'          => $sourceId,
+            'file_original_name' => $file->getClientOriginalName(),
+            'file_size'          => $file->getSize(),
+            'file_mime'          => $file->getMimeType(),
+            'file_extension'     => $file->getClientOriginalExtension(),
+            'file_is_valid'      => $file->isValid(),
         ]);
 
         $path = $this->uploadFile($file, $folder, $filename);
@@ -95,18 +97,21 @@ class MediaService
     public function replaceFile(UploadedFile $newFile, string $folder, string $source, int $sourceId, ?Media $old = null, ?string $filename = null): Media
     {
         Log::warning('[avatar-upload] MediaService::replaceFile iniciado', [
-            'folder'       => $folder,
-            'source'       => $source,
-            'source_id'    => $sourceId,
-            'has_old_media'=> $old !== null,
-            'old_media_id' => $old?->id,
-            'old_path'     => $old?->path,
+            'folder'        => $folder,
+            'source'        => $source,
+            'source_id'     => $sourceId,
+            'has_old_media' => $old !== null,
+            'old_media_id'  => $old?->id,
+            'old_path'      => $old?->path,
         ]);
 
         if ($old) {
             Log::warning('[avatar-upload] Removendo arquivo antigo do S3', ['old_path' => $old->path]);
+
             $this->removeArchiveByPath($old->path);
+
             $old->delete();
+
             Log::warning('[avatar-upload] Arquivo antigo removido do S3 e Media deletado do banco');
         }
 

+ 7 - 5
app/Services/NotificationService.php

@@ -25,6 +25,13 @@ class NotificationService
         return Notification::create($data);
     }
 
+    public function delete(Notification $notification): void
+    {
+        $notification->delete();
+    }
+
+    //
+
     public function markAsRead(Notification $notification): Notification
     {
         $notification->update([
@@ -51,9 +58,4 @@ class NotificationService
             ->where('read', false)
             ->count();
     }
-
-    public function delete(Notification $notification): void
-    {
-        $notification->delete();
-    }
 }

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

@@ -12,6 +12,8 @@ trait FormatsPagarmeData
         return preg_replace('/\D+/', '', (string) $value) ?? '';
     }
 
+    //
+
     protected function customerDocument(?string $value): string
     {
         $document = trim((string) $value);

+ 9 - 5
app/Services/Pagarme/Concerns/SendsPagarmeRequests.php

@@ -75,6 +75,8 @@ trait SendsPagarmeRequests
         }
     }
 
+    //
+
     protected function pagarmeHttp(string $idempotencyKey)
     {
         $secretKey = config('services.pagarme.secret_key');
@@ -93,11 +95,6 @@ trait SendsPagarmeRequests
             ]);
     }
 
-    protected function pagarmeUrl(string $path): string
-    {
-        return rtrim(config('services.pagarme.base_url'), '/').'/'.ltrim($path, '/');
-    }
-
     protected function pagarmeOriginIp(): ?string
     {
         try {
@@ -106,4 +103,11 @@ trait SendsPagarmeRequests
             return null;
         }
     }
+
+    //
+
+    protected function pagarmeUrl(string $path): string
+    {
+        return rtrim(config('services.pagarme.base_url'), '/').'/'.ltrim($path, '/');
+    }
 }

+ 0 - 32
app/Services/PaymentService.php

@@ -250,38 +250,6 @@ class PaymentService
         return $payment;
     }
 
-    /*
-    public function payCart(array $data, int $userId): SupportCollection
-    {
-        $cart = Cart::query()
-            ->with(['items.schedule.client', 'items.schedule.provider'])
-            ->findOrFail($data['cart_id']);
-
-        $schedules = $this->cartSchedules($cart);
-
-        if ($schedules->contains(fn (Schedule $schedule) => $schedule->client?->user_id !== $userId)) {
-            throw new AuthorizationException;
-        }
-
-        $this->validateCartSchedules($schedules, $data['payment_method']);
-
-        $payments = $schedules->map(fn (Schedule $schedule) => $this->payAcceptedSchedule(
-            schedule:              $schedule,
-            paymentMethod:         $data['payment_method'],
-            clientPaymentMethodId: $data['client_payment_method_id'] ?? null,
-
-            options: [
-                'phone'   => $data['phone']   ?? null,
-                'card_id' => $data['card_id'] ?? null,
-            ],
-        ));
-
-        $this->syncCartStatusAfterPayments($cart);
-
-        return $payments;
-    }
-    */
-
     //
 
     public function getOrCreatePixPayment(Schedule $schedule): Payment

+ 34 - 16
app/Services/ProviderCalendarService.php

@@ -31,22 +31,40 @@ class ProviderCalendarService
             'schedules.address_id',
             'schedules.status',
             'custom_schedules.offers_meal',
-            DB::raw("(SELECT media.path FROM media WHERE media.source_id = clients.id AND media.source = 'client' AND media.deleted_at IS NULL LIMIT 1) as customer_photo"),
-            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"),
-            DB::raw("(
-        SELECT reviews.stars FROM reviews
-        WHERE reviews.schedule_id = schedules.id
-          AND reviews.origin = 'provider'
-          AND reviews.origin_id = {$provider->id}
-          AND reviews.deleted_at IS NULL
-        LIMIT 1
-      ) as provider_stars"),
+
+            DB::raw("
+                (
+                    SELECT media.path
+                    FROM media
+                    WHERE media.source_id = clients.id
+                    AND media.source = 'client'
+                    AND media.deleted_at IS NULL
+                    LIMIT 1
+                ) AS customer_photo
+            "),
+
+            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
+            "),
+
+            DB::raw("
+                (
+                    SELECT reviews.stars
+                    FROM reviews
+                    WHERE reviews.schedule_id = schedules.id
+                    AND reviews.origin = 'provider'
+                    AND reviews.origin_id = {$provider->id}
+                    AND reviews.deleted_at IS NULL
+                    LIMIT 1
+                ) AS provider_stars
+            "),
         ];
 
         $signPhoto = fn ($item) => tap($item, fn ($i) => $i->customer_photo = $i->customer_photo

+ 5 - 0
app/Services/ProviderClientBlockService.php

@@ -18,6 +18,7 @@ class ProviderClientBlockService
     public function create(array $data): ProviderClientBlock
     {
         // Validar duplicidade (ignorando soft-deleted)
+
         $existing = ProviderClientBlock::where('provider_id', $data['provider_id'])
             ->where('client_id', $data['client_id'])
             ->first();
@@ -25,12 +26,14 @@ class ProviderClientBlockService
         if ($existing) {
             if ($existing->trashed()) {
                 // Se foi deletado (desbloqueado), restaura
+
                 $existing->restore();
                 $existing->load(['client.user']);
 
                 return $existing;
             } else {
                 // Se já existe ativo, lança erro
+
                 throw new Exception(__('validation.provider_client_block.already_blocked'));
             }
         }
@@ -49,6 +52,8 @@ class ProviderClientBlockService
         return $block->delete();
     }
 
+    //
+
     public function getBlockedClientIds(int $providerId): array
     {
         return ProviderClientBlock::where('provider_id', $providerId)

+ 77 - 71
app/Services/ProviderService.php

@@ -104,44 +104,36 @@ class ProviderService
 
     //
 
-    public function getPending(int $page = 1, int $perPage = 10): LengthAwarePaginator
-    {
-        return Provider::query()
-            ->where('approval_status', ApprovalStatusEnum::PENDING->value)
-            ->with(['user', 'profileMedia'])
-            ->orderBy('created_at', 'asc')
-            ->paginate($perPage, ['*'], 'page', $page);
-    }
-
-    //
-
-    public function approve(int $id): Provider
+    public function getPaymentMethods(int $id): array
     {
-        [$provider, $wasAccepted] = DB::transaction(function () use ($id) {
-            $provider = Provider::findOrFail($id);
-            $wasAccepted = $provider->approval_status === ApprovalStatusEnum::ACCEPTED;
-
-            $provider->update(['approval_status' => ApprovalStatusEnum::ACCEPTED->value]);
-
-            return [$provider->fresh(['user', 'profileMedia']), $wasAccepted];
-        });
+        $provider = Provider::find($id);
 
-        if (! $wasAccepted) {
-            $this->sendApprovedEmail($provider);
+        if (! $provider || empty($provider->recipient_default_bank_account)) {
+            return [];
         }
 
-        return $provider;
+        $bankAccount = $provider->recipient_default_bank_account;
+
+        return [[
+            'id'                              => $provider->id,
+            'provider_id'                     => $provider->id,
+            'account_type'                    => 'bank_account',
+            'pix_key'                         => $bankAccount['pix_key']             ?? null,
+            'bank_account_type'               => $bankAccount['type']                ?? null,
+            'agency'                          => $bankAccount['branch_number']       ?? null,
+            'account'                         => $bankAccount['account_number']      ?? null,
+            'digit'                           => $bankAccount['account_check_digit'] ?? null,
+            'recipient_default_bank_account'  => $bankAccount,
+        ]];
     }
 
-    public function reject(int $id): Provider
+    public function getPending(int $page = 1, int $perPage = 10): LengthAwarePaginator
     {
-        return DB::transaction(function () use ($id) {
-            $provider = Provider::findOrFail($id);
-
-            $provider->update(['approval_status' => ApprovalStatusEnum::REJECTED->value]);
-
-            return $provider->fresh(['user', 'profileMedia']);
-        });
+        return Provider::query()
+            ->where('approval_status', ApprovalStatusEnum::PENDING->value)
+            ->with(['user', 'profileMedia'])
+            ->orderBy('created_at', 'asc')
+            ->paginate($perPage, ['*'], 'page', $page);
     }
 
     //
@@ -153,7 +145,7 @@ class ProviderService
 
             $email = $data['email'] ?? null;
             $phone = $data['phone'] ?? null;
-            $code = $data['code'] ?? null;
+            $code  = $data['code']  ?? null;
 
             $user = User::query()
                 ->where('type', UserTypeEnum::PROVIDER->value)
@@ -195,18 +187,19 @@ class ProviderService
                 $provider->restore();
             }
 
-            $provider->rg = $data['rg'] ?? null;
-            $provider->document = $this->sanitizeDigits($data['document'] ?? null);
-            $provider->birth_date = $data['birth_date'] ?? null;
-            $provider->daily_price_8h = $data['daily_price_8h'] ?? null;
-            $provider->daily_price_6h = $data['daily_price_6h'] ?? null;
-            $provider->daily_price_4h = $data['daily_price_4h'] ?? null;
-            $provider->daily_price_2h = $data['daily_price_2h'] ?? null;
+            $provider->rg              = $data['rg'] ?? null;
+            $provider->document        = $this->sanitizeDigits($data['document'] ?? null);
+            $provider->birth_date      = $data['birth_date']     ?? null;
+            $provider->daily_price_8h  = $data['daily_price_8h'] ?? null;
+            $provider->daily_price_6h  = $data['daily_price_6h'] ?? null;
+            $provider->daily_price_4h  = $data['daily_price_4h'] ?? null;
+            $provider->daily_price_2h  = $data['daily_price_2h'] ?? null;
             $provider->approval_status = ApprovalStatusEnum::PENDING->value;
 
             $provider->save();
 
             $provider->refresh();
+
             $provider->load('profileMedia', 'documentFrontMedia', 'documentBackMedia');
 
             $selfie = $this->mediaService->replaceFile(
@@ -244,6 +237,7 @@ class ProviderService
             $provider->save();
 
             $bankAccount = $data['recipient_default_bank_account'] ?? [];
+
             $hasBankData = ! empty($bankAccount['bank']) && ! empty($bankAccount['account_number']);
 
             if (! empty($data['recipient_name']) && $hasBankData && empty($provider->recipient_id)) {
@@ -251,12 +245,15 @@ class ProviderService
             }
 
             Address::where('source', 'provider')->where('source_id', $provider->id)->delete();
+
             $this->createProviderAddress($provider->id, $data);
 
             ProviderServicesType::where('provider_id', $provider->id)->delete();
+
             $this->createProviderServicesTypes($provider->id, $data);
 
             ProviderWorkingDay::where('provider_id', $provider->id)->delete();
+
             $this->createProviderWorkingDays($provider->id, $data);
 
             if (empty($user->email) || empty($user->code)) {
@@ -266,7 +263,8 @@ class ProviderService
             $user->registration_complete = true;
 
             $user->validated_code = true;
-            $user->code = null;
+            $user->code           = null;
+
             $user->save();
 
             DB::commit();
@@ -307,29 +305,39 @@ class ProviderService
         return $provider->fresh(['user', 'profileMedia']);
     }
 
-    public function getPaymentMethods(int $id): array
+    //
+
+    public function approve(int $id): Provider
     {
-        $provider = Provider::find($id);
+        [$provider, $wasAccepted] = DB::transaction(function () use ($id) {
+            $provider = Provider::findOrFail($id);
+            $wasAccepted = $provider->approval_status === ApprovalStatusEnum::ACCEPTED;
 
-        if (! $provider || empty($provider->recipient_default_bank_account)) {
-            return [];
+            $provider->update(['approval_status' => ApprovalStatusEnum::ACCEPTED->value]);
+
+            return [$provider->fresh(['user', 'profileMedia']), $wasAccepted];
+        });
+
+        if (! $wasAccepted) {
+            $this->sendApprovedEmail($provider);
         }
 
-        $bankAccount = $provider->recipient_default_bank_account;
+        return $provider;
+    }
 
-        return [[
-            'id'                              => $provider->id,
-            'provider_id'                     => $provider->id,
-            'account_type'                    => 'bank_account',
-            'pix_key'                         => $bankAccount['pix_key'] ?? null,
-            'bank_account_type'               => $bankAccount['type'] ?? null,
-            'agency'                          => $bankAccount['branch_number'] ?? null,
-            'account'                         => $bankAccount['account_number'] ?? null,
-            'digit'                           => $bankAccount['account_check_digit'] ?? null,
-            'recipient_default_bank_account'  => $bankAccount,
-        ]];
+    public function reject(int $id): Provider
+    {
+        return DB::transaction(function () use ($id) {
+            $provider = Provider::findOrFail($id);
+
+            $provider->update(['approval_status' => ApprovalStatusEnum::REJECTED->value]);
+
+            return $provider->fresh(['user', 'profileMedia']);
+        });
     }
 
+    //
+
     private function buildRecipientDataFromProvider(Provider $provider, array $bankAccountData): array
     {
         $address = $provider->addresses->first();
@@ -352,12 +360,10 @@ class ProviderService
         ];
     }
 
-    //
-
     private function createProviderAddress(int $providerId, array $data): void
     {
         $state = null;
-        $city = null;
+        $city  = null;
 
         if (! empty($data['state'])) {
             $state = State::query()
@@ -378,17 +384,17 @@ class ProviderService
 
         $address = new Address;
 
-        $address->source = 'provider';
-        $address->source_id = $providerId;
-        $address->zip_code = $this->sanitizeDigits($data['zip_code'] ?? null);
-        $address->address = $data['address'] ?? null;
+        $address->source         = 'provider';
+        $address->source_id      = $providerId;
+        $address->zip_code       = $this->sanitizeDigits($data['zip_code'] ?? null);
+        $address->address        = $data['address'] ?? null;
         $address->has_complement = (bool) ($data['has_complement'] ?? false);
-        $address->complement = $data['complement'] ?? null;
-        $address->nickname = $data['nickname'] ?? null;
-        $address->instructions = $data['instructions'] ?? null;
-        $address->address_type = $data['address_type'] ?? 'home';
-        $address->state_id = $state?->id;
-        $address->city_id = $city?->id;
+        $address->complement     = $data['complement']   ?? null;
+        $address->nickname       = $data['nickname']     ?? null;
+        $address->instructions   = $data['instructions'] ?? null;
+        $address->address_type   = $data['address_type'] ?? 'home';
+        $address->state_id       = $state?->id;
+        $address->city_id        = $city?->id;
 
         $address->save();
     }
@@ -438,6 +444,8 @@ class ProviderService
         }
     }
 
+    //
+
     private function sanitizeDigits(?string $value): ?string
     {
         if ($value === null) {
@@ -449,8 +457,6 @@ class ProviderService
         return $digits === '' ? null : $digits;
     }
 
-    //
-
     private function sendApprovedEmail(Provider $provider): void
     {
         if (! empty($provider->user?->email)) {

+ 60 - 49
app/Services/ProviderWithdrawalService.php

@@ -19,13 +19,51 @@ class ProviderWithdrawalService
         private readonly PagarmeTransferService $pagarmeTransfer,
     ) {}
 
-    public function getWithdrawalFees(): array
+    public function handleTransferWebhook(array $data): void
     {
-        return [
-            'payment_transfer_fee_amount' => $this->paymentTransferFeeAmount(),
-        ];
+        $transferId = $data['id'] ?? null;
+
+        if (empty($transferId)) {
+            return;
+        }
+
+        $withdrawal = ProviderWithdrawal::query()
+            ->where('transfer_id', $transferId)
+            ->first();
+
+        if (! $withdrawal) {
+            Log::channel('pagarme')->warning('ProviderWithdrawal not found for transfer webhook', [
+                'transfer_id' => $transferId,
+            ]);
+
+            return;
+        }
+
+        $status = $data['status'] ?? null;
+
+        if ($status === ProviderWithdrawalStatusEnum::TRANSFERRED->value) {
+            $withdrawal->forceFill([
+                'status'       => ProviderWithdrawalStatusEnum::TRANSFERRED,
+                'completed_at' => now(),
+            ])->save();
+        } elseif (in_array($status, [ProviderWithdrawalStatusEnum::FAILED->value, ProviderWithdrawalStatusEnum::CANCELED->value], true)) {
+            $withdrawal->paymentSplits()
+                ->update(['provider_withdrawal_id' => null]);
+
+            $withdrawal->forceFill([
+                'status'        => ProviderWithdrawalStatusEnum::fromString($status),
+                'failed_at'     => $status === ProviderWithdrawalStatusEnum::FAILED->value ? now() : null,
+                'bank_response' => $status === ProviderWithdrawalStatusEnum::FAILED->value ? ($data['bank_response'] ?? null) : null,
+            ])->save();
+        } elseif ($status === ProviderWithdrawalStatusEnum::PROCESSING->value) {
+            $withdrawal->forceFill([
+                'status' => ProviderWithdrawalStatusEnum::PROCESSING,
+            ])->save();
+        }
     }
 
+    //
+
     public function getAvailableBalance(Provider $provider): float
     {
         $earnings = (float) PaymentSplit::query()
@@ -55,6 +93,8 @@ class ProviderWithdrawalService
             ->sum('gross_amount');
     }
 
+    //
+
     public function getPaymentSplits(Provider $provider): Collection
     {
         return PaymentSplit::query()
@@ -64,6 +104,8 @@ class ProviderWithdrawalService
             ->get();
     }
 
+    //
+
     public function requestWithdrawal(Provider $provider): ProviderWithdrawal
     {
         if (empty($provider->recipient_id)) {
@@ -160,6 +202,8 @@ class ProviderWithdrawalService
         });
     }
 
+    //
+
     public function getWithdrawals(Provider $provider): Collection
     {
         return ProviderWithdrawal::query()
@@ -184,49 +228,22 @@ class ProviderWithdrawalService
             ->firstOrFail();
     }
 
-    public function handleTransferWebhook(array $data): void
-    {
-        $transferId = $data['id'] ?? null;
+    //
 
-        if (empty($transferId)) {
-            return;
-        }
-
-        $withdrawal = ProviderWithdrawal::query()
-            ->where('transfer_id', $transferId)
-            ->first();
-
-        if (! $withdrawal) {
-            Log::channel('pagarme')->warning('ProviderWithdrawal not found for transfer webhook', [
-                'transfer_id' => $transferId,
-            ]);
-
-            return;
-        }
-
-        $status = $data['status'] ?? null;
-
-        if ($status === ProviderWithdrawalStatusEnum::TRANSFERRED->value) {
-            $withdrawal->forceFill([
-                'status'       => ProviderWithdrawalStatusEnum::TRANSFERRED,
-                'completed_at' => now(),
-            ])->save();
-        } elseif (in_array($status, [ProviderWithdrawalStatusEnum::FAILED->value, ProviderWithdrawalStatusEnum::CANCELED->value], true)) {
-            $withdrawal->paymentSplits()
-                ->update(['provider_withdrawal_id' => null]);
+    public function getWithdrawalFees(): array
+    {
+        return [
+            'payment_transfer_fee_amount' => $this->paymentTransferFeeAmount(),
+        ];
+    }
 
-            $withdrawal->forceFill([
-                'status'        => ProviderWithdrawalStatusEnum::fromString($status),
-                'failed_at'     => $status === ProviderWithdrawalStatusEnum::FAILED->value ? now() : null,
-                'bank_response' => $status === ProviderWithdrawalStatusEnum::FAILED->value ? ($data['bank_response'] ?? null) : null,
-            ])->save();
-        } elseif ($status === ProviderWithdrawalStatusEnum::PROCESSING->value) {
-            $withdrawal->forceFill([
-                'status' => ProviderWithdrawalStatusEnum::PROCESSING,
-            ])->save();
-        }
+    private function paymentTransferFeeAmount(): float
+    {
+        return round((float) config('services.pagarme.transfer_fee_amount'), 2);
     }
 
+    //
+
     private function availableScheduleQuery($query)
     {
         return $query
@@ -259,10 +276,4 @@ class ProviderWithdrawalService
 
         return Carbon::now()->subDays($releaseDays)->format('Y-m-d H:i:s');
     }
-
-    private function paymentTransferFeeAmount(): float
-    {
-        return round((float) config('services.pagarme.transfer_fee_amount'), 2);
-    }
-
 }

+ 1 - 0
app/Services/PushNotificationDispatcher.php

@@ -116,6 +116,7 @@ class PushNotificationDispatcher
         $userIds = $users->pluck('id');
 
         $blockedByCategory = collect();
+
         if ($notification->categoryCooldownDays() > 0) {
             $blockedByCategory = PushNotificationLog::whereIn('user_id', $userIds)
                 ->where('target', $notification->target()->value)

+ 11 - 9
app/Services/PushNotificationService.php

@@ -59,6 +59,17 @@ class PushNotificationService
         }
     }
 
+    //
+
+    private function deactivateInvalidTokens(array $tokens): void
+    {
+        if (empty($tokens)) {
+            return;
+        }
+
+        DeviceToken::whereIn('token', $tokens)->update(['active' => false]);
+    }
+
     private function log(User $user, BasePushNotification $notification): void
     {
         PushNotificationLog::create([
@@ -69,13 +80,4 @@ class PushNotificationService
             'sent_at'  => now(),
         ]);
     }
-
-    private function deactivateInvalidTokens(array $tokens): void
-    {
-        if (empty($tokens)) {
-            return;
-        }
-
-        DeviceToken::whereIn('token', $tokens)->update(['active' => false]);
-    }
 }

+ 154 - 138
app/Services/ReviewService.php

@@ -26,152 +26,140 @@ class ReviewService
             ->get();
     }
 
-    public function getByScheduleId(int $scheduleId)
+    public function create(array $data): Review
     {
-        return Review::with(['schedule.client.user', 'schedule.provider.user'])
-            ->where('schedule_id', $scheduleId)
-            ->orderBy('created_at', 'desc')
-            ->get();
-    }
+        try {
+            DB::beginTransaction();
 
-    public function getByOrigin(string $origin, int $originId)
-    {
-        return Review::with(['schedule.client.user', 'schedule.provider.user'])
-            ->where('origin', $origin)
-            ->where('origin_id', $originId)
-            ->orderBy('created_at', 'desc')
-            ->get();
-    }
+            $review = new Review();
 
-  public function create(array $data): Review
-  {
-    try {
-      DB::beginTransaction();
-      $review = new Review();
-      $review->fill($data);
-      $review->save();
-      $review->refresh();
-  
-      if (isset($data['origin']) && isset($data['origin_id'])) {
-        switch ($data['origin']) {
-          case 'client':
-            $schedule = Schedule::find($data['schedule_id']);
-            $provider = Provider::find($schedule->provider_id);
-
-            $provider->updateAverageRating((float) $data['stars']);
-            break;
-          case 'provider':
-            $schedule = Schedule::find($data['schedule_id']);
-            $client = Client::find($schedule->client_id);
-
-            $client->updateAverageRating((float) $data['stars']);
-            break;
-        }
-      }
-      if (isset($data['improvements_ids'])) {
-        $review->improvements()->sync($data['improvements_ids']);
-      }
-  
-      if (! empty($data['block_provider'])) {
-        $schedule = Schedule::find($data['schedule_id']);
-
-        $alreadyBlocked = ClientProviderBlock::where('client_id', $schedule->client_id)
-          ->where('provider_id', $schedule->provider_id)
-          ->whereNull('deleted_at')
-          ->exists();
-
-        if (! $alreadyBlocked) {
-          ClientProviderBlock::create([
-            'client_id'   => $schedule->client_id,
-            'provider_id' => $schedule->provider_id,
-          ]);
-        }
-      }
+            $review->fill($data);
+            $review->save();
+            $review->refresh();
 
-      if (! empty($data['block_client'])) {
-        $schedule = Schedule::find($data['schedule_id']);
+            if (isset($data['origin']) && isset($data['origin_id'])) {
+                switch ($data['origin']) {
+                    case 'client':
+                        $schedule = Schedule::find($data['schedule_id']);
+                        $provider = Provider::find($schedule->provider_id);
 
-        $alreadyBlocked = ProviderClientBlock::where('provider_id', $schedule->provider_id)
-          ->where('client_id', $schedule->client_id)
-          ->whereNull('deleted_at')
-          ->exists();
+                        $provider->updateAverageRating((float) $data['stars']);
 
-        if (! $alreadyBlocked) {
-          ProviderClientBlock::create([
-            'provider_id' => $schedule->provider_id,
-            'client_id'   => $schedule->client_id,
-          ]);
-        }
-      }
+                        break;
 
-      if (! empty($data['favorite_provider'])) {
-        $schedule = Schedule::find($data['schedule_id']);
+                    case 'provider':
+                        $schedule = Schedule::find($data['schedule_id']);
+                        $client   = Client::find($schedule->client_id);
 
-        $alreadyFavorited = ClientFavoriteProvider::where('client_id', $schedule->client_id)
-          ->where('provider_id', $schedule->provider_id)
-          ->whereNull('deleted_at')
-          ->exists();
+                        $client->updateAverageRating((float) $data['stars']);
 
-        if (! $alreadyFavorited) {
-          ClientFavoriteProvider::create([
-            'client_id'   => $schedule->client_id,
-            'provider_id' => $schedule->provider_id,
-          ]);
-        }
-      }
-
-      if (! empty($data['photos'])) {
-        $schedule = Schedule::find($data['schedule_id']);
-        $origin = $data['origin'];
-
-        foreach ($data['photos'] as $photo) {
-          $media = $this->mediaService->createFromFile(
-            file: $photo,
-            folder: "review/{$schedule->id}/{$origin}",
-            source: 'review',
-            sourceId: $review->id,
-          );
-
-          ReviewMedia::create([
-            'review_id' => $review->id,
-            'media_id'  => $media->id,
-            'origin'    => $origin,
-          ]);
+                        break;
+                }
+            }
+
+            if (isset($data['improvements_ids'])) {
+                $review->improvements()->sync($data['improvements_ids']);
+            }
+
+            if (! empty($data['block_provider'])) {
+                $schedule = Schedule::find($data['schedule_id']);
+
+                $alreadyBlocked = ClientProviderBlock::where('client_id', $schedule->client_id)
+                    ->where('provider_id', $schedule->provider_id)
+                    ->whereNull('deleted_at')
+                    ->exists();
+
+                if (! $alreadyBlocked) {
+                    ClientProviderBlock::create([
+                        'client_id'   => $schedule->client_id,
+                        'provider_id' => $schedule->provider_id,
+                    ]);
+                }
+            }
+
+            if (! empty($data['block_client'])) {
+                $schedule = Schedule::find($data['schedule_id']);
+
+                $alreadyBlocked = ProviderClientBlock::where('provider_id', $schedule->provider_id)
+                    ->where('client_id', $schedule->client_id)
+                    ->whereNull('deleted_at')
+                    ->exists();
+
+                if (! $alreadyBlocked) {
+                    ProviderClientBlock::create([
+                        'provider_id' => $schedule->provider_id,
+                        'client_id'   => $schedule->client_id,
+                    ]);
+                }
+            }
+
+            if (! empty($data['favorite_provider'])) {
+                $schedule = Schedule::find($data['schedule_id']);
+
+                $alreadyFavorited = ClientFavoriteProvider::where('client_id', $schedule->client_id)
+                    ->where('provider_id', $schedule->provider_id)
+                    ->whereNull('deleted_at')
+                    ->exists();
+
+                if (! $alreadyFavorited) {
+                    ClientFavoriteProvider::create([
+                        'client_id'   => $schedule->client_id,
+                        'provider_id' => $schedule->provider_id,
+                    ]);
+                }
+            }
+
+            if (! empty($data['photos'])) {
+                $schedule = Schedule::find($data['schedule_id']);
+
+                $origin = $data['origin'];
+
+                foreach ($data['photos'] as $photo) {
+                    $media = $this->mediaService->createFromFile(
+                        file: $photo,
+                        folder: "review/{$schedule->id}/{$origin}",
+                        source: 'review',
+                        sourceId: $review->id,
+                    );
+
+                    ReviewMedia::create([
+                        'review_id' => $review->id,
+                        'media_id'  => $media->id,
+                        'origin'    => $origin,
+                    ]);
+                }
+            }
+
+            DB::commit();
+
+            return $review;
+        } catch (Exception $e) {
+            DB::rollBack();
+
+            Log::error('Error creating review: ' . $e->getMessage());
+
+            throw $e;
         }
-      }
-  
-      DB::commit();
-      return $review;
-    } catch (Exception $e) {
-      DB::rollBack();
-      Log::error('Error creating review: ' . $e->getMessage());
-      throw $e;
     }
-  }
 
-    private function detailEagerLoads(): array
+    public function update(int $id, array $data): Review
     {
-        return [
-            'schedule.client.user',
-            'schedule.client.profileMedia',
-            'schedule.provider.user',
-            'schedule.address.city',
-            'schedule.address.state',
-            'schedule.customSchedule.serviceType',
-            'schedule.customSchedule.specialities',
-            'improvements',
-        ];
+        $review = Review::findOrFail($id);
+
+        $review->update($data);
+
+        return $review->fresh();
     }
 
-    public function getByClientSent(int $clientId)
+    public function delete(int $id): bool
     {
-        return Review::with($this->detailEagerLoads())
-            ->whereIn('origin', ['client', 'clients'])
-            ->where('origin_id', $clientId)
-            ->orderBy('created_at', 'desc')
-            ->get();
+        $review = Review::findOrFail($id);
+
+        return $review->delete();
     }
 
+    //
+
     public function getByClientReceived(int $clientId)
     {
         return Review::with($this->detailEagerLoads())
@@ -181,11 +169,11 @@ class ReviewService
             ->get();
     }
 
-    public function getByProviderSent(int $providerId)
+    public function getByClientSent(int $clientId)
     {
         return Review::with($this->detailEagerLoads())
-            ->whereIn('origin', ['provider', 'providers'])
-            ->where('origin_id', $providerId)
+            ->whereIn('origin', ['client', 'clients'])
+            ->where('origin_id', $clientId)
             ->orderBy('created_at', 'desc')
             ->get();
     }
@@ -209,19 +197,47 @@ class ReviewService
         return $reviews;
     }
 
-    public function update(int $id, array $data): Review
+    public function getByProviderSent(int $providerId)
     {
-        $review = Review::findOrFail($id);
+        return Review::with($this->detailEagerLoads())
+            ->whereIn('origin', ['provider', 'providers'])
+            ->where('origin_id', $providerId)
+            ->orderBy('created_at', 'desc')
+            ->get();
+    }
 
-        $review->update($data);
+    //
 
-        return $review->fresh();
+    public function getByOrigin(string $origin, int $originId)
+    {
+        return Review::with(['schedule.client.user', 'schedule.provider.user'])
+            ->where('origin', $origin)
+            ->where('origin_id', $originId)
+            ->orderBy('created_at', 'desc')
+            ->get();
     }
 
-    public function delete(int $id): bool
+    public function getByScheduleId(int $scheduleId)
     {
-        $review = Review::findOrFail($id);
+        return Review::with(['schedule.client.user', 'schedule.provider.user'])
+            ->where('schedule_id', $scheduleId)
+            ->orderBy('created_at', 'desc')
+            ->get();
+    }
 
-        return $review->delete();
+    //
+
+    private function detailEagerLoads(): array
+    {
+        return [
+            'schedule.client.user',
+            'schedule.client.profileMedia',
+            'schedule.provider.user',
+            'schedule.address.city',
+            'schedule.address.state',
+            'schedule.customSchedule.serviceType',
+            'schedule.customSchedule.specialities',
+            'improvements',
+        ];
     }
 }

+ 2 - 0
app/Services/ScheduleCartService.php

@@ -18,6 +18,8 @@ class ScheduleCartService
         );
     }
 
+    //
+
     private function baseScheduleData(array $data): array
     {
         return Arr::except($data, [

+ 66 - 144
app/Services/ScheduleService.php

@@ -2,13 +2,13 @@
 
 namespace App\Services;
 
+use App\Enums\UserTypeEnum;
 use App\Enums\NotificationTypeEnum;
 use App\Jobs\StartScheduleJob;
 use App\Models\Provider;
 use App\Models\Schedule;
 use App\Rules\ScheduleBusinessRules;
 use App\Services\NotificationService;
-use App\Enums\UserTypeEnum;
 use Carbon\Carbon;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\DB;
@@ -80,10 +80,10 @@ class ScheduleService
 
     public function update($id, array $data)
     {
-
         unset($data['status']);
 
         $schedule = Schedule::with(['provider.user', 'client.user', 'address'])->findOrFail($id);
+
         if (isset($data['provider_id']) || isset($data['period_type'])) {
             $providerId = $data['provider_id'] ?? $schedule->provider_id;
             $periodType = $data['period_type'] ?? $schedule->period_type;
@@ -119,10 +119,11 @@ class ScheduleService
     {
         try {
             DB::beginTransaction();
+
             $schedule = Schedule::with(['provider.user', 'client.user', 'address'])->findOrFail($id);
 
             $allowedTransitions = [
-                'pending' => ['accepted', 'rejected', 'cancelled'],
+                'pending'   => ['accepted', 'rejected', 'cancelled'],
                 'accepted'  => ['paid', 'cancelled'],
                 'paid'      => ['cancelled', 'started'],
                 'started'   => ['finished'],
@@ -145,57 +146,36 @@ class ScheduleService
             $schedule->refresh();
 
             $currentStatus = $schedule->status;
-        
-            switch ($status) {
 
+            switch ($status) {
                 case 'pending':
                     break;
 
                 case 'accepted':
-
                     $notificationService = app(NotificationService::class);
 
                     switch (Auth::user()->type) {
-
                         case UserTypeEnum::PROVIDER:
-
                             $notificationService->create([
-                                'title' => 'Agendamento aceito!',
-
-                                'description' =>
-                                $schedule->provider->user->name .
-                                    ' aceitou sua solicitação de diária.',
-
-                                'origin' => 'schedule',
-
-                                'origin_id' => $schedule->id,
-
-                                'type' =>
-                                NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_ACCEPTED->value,
-
-                                'user_id' => $schedule->client->user_id,
+                                'title'       => 'Agendamento aceito!',
+                                'description' => $schedule->provider->user->name . ' aceitou sua solicitação de diária.',
+                                'origin'      => 'schedule',
+                                'origin_id'   => $schedule->id,
+                                'type'        => NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_ACCEPTED->value,
+                                'user_id'     => $schedule->client->user_id,
                             ]);
 
                             break;
 
                         case UserTypeEnum::CLIENT:
-
                             if ($schedule->provider_id) {
-
                                 $notificationService->create([
-                                    'title' => 'Proposta aceita!',
-
-                                    'description' =>
-                                    'O cliente aceitou sua proposta de diária.',
-
-                                    'origin' => 'schedule',
-
-                                    'origin_id' => $schedule->id,
-
-                                    'type' =>
-                                    NotificationTypeEnum::SCHEDULE_PROVIDER_CLIENT_PROPOSAL_ACCEPTED->value,
-
-                                    'user_id' => $schedule->provider->user_id,
+                                    'title'       => 'Proposta aceita!',
+                                    'description' => 'O cliente aceitou sua proposta de diária.',
+                                    'origin'      => 'schedule',
+                                    'origin_id'   => $schedule->id,
+                                    'type'        => NotificationTypeEnum::SCHEDULE_PROVIDER_CLIENT_PROPOSAL_ACCEPTED->value,
+                                    'user_id'     => $schedule->provider->user_id,
                                 ]);
                             }
 
@@ -208,48 +188,29 @@ class ScheduleService
                     break;
 
                 case 'cancelled':
-
                     $notificationService = app(NotificationService::class);
 
                     switch (Auth::user()->type) {
-
                         case UserTypeEnum::CLIENT:
-
                             $notificationService->create([
-                                'title' => 'Agendamento cancelado!',
-
-                                'description' =>
-                                'O cliente cancelou o agendamento.',
-
-                                'origin' => 'schedule',
-
-                                'origin_id' => $schedule->id,
-
-                                'type' =>
-                                NotificationTypeEnum::SCHEDULE_PROVIDER_CLIENT_CANCELLED->value,
-
-                                'user_id' => $schedule->provider->user_id,
+                                'title'       => 'Agendamento cancelado!',
+                                'description' => 'O cliente cancelou o agendamento.',
+                                'origin'      => 'schedule',
+                                'origin_id'   => $schedule->id,
+                                'type'        => NotificationTypeEnum::SCHEDULE_PROVIDER_CLIENT_CANCELLED->value,
+                                'user_id'     => $schedule->provider->user_id,
                             ]);
 
                             break;
 
                         case UserTypeEnum::PROVIDER:
-
                             $notificationService->create([
-                                'title' => 'Agendamento cancelado!',
-
-                                'description' =>
-                                $schedule->provider->user->name .
-                                    ' cancelou sua solicitação de diária.',
-
-                                'origin' => 'schedule',
-
-                                'origin_id' => $schedule->id,
-
-                                'type' =>
-                                NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_CANCELLED->value,
-
-                                'user_id' => $schedule->client->user_id,
+                                'title'       => 'Agendamento cancelado!',
+                                'description' => $schedule->provider->user->name . ' cancelou sua solicitação de diária.',
+                                'origin'      => 'schedule',
+                                'origin_id'   => $schedule->id,
+                                'type'        => NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_CANCELLED->value,
+                                'user_id'     => $schedule->client->user_id,
                             ]);
 
                             break;
@@ -261,92 +222,61 @@ class ScheduleService
                     break;
 
                 case 'started':
-
                     $notificationService = app(NotificationService::class);
 
                     // CLIENTE
-                    $notificationService->create([
-                        'title' => 'Diarista a caminho!',
-
-                        'description' =>
-                        'Informe o código ' . $schedule->code . ' para liberar o início do serviço.',
-
-                        'origin' => 'schedule',
-
-                        'origin_id' => $schedule->id,
-
-                        'type' =>
-                        NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_COMING->value,
 
-                        'user_id' => $schedule->client->user_id,
+                    $notificationService->create([
+                        'title'       => 'Diarista a caminho!',
+                        'description' => 'Informe o código ' . $schedule->code . ' para liberar o início do serviço.',
+                        'origin'      => 'schedule',
+                        'origin_id'   => $schedule->id,
+                        'type'        => NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_COMING->value,
+                        'user_id'     => $schedule->client->user_id,
                     ]);
 
                     // PRESTADOR
-                    $notificationService->create([
-                        'title' => 'Início do serviço!',
-
-                        'description' =>
-                        'Solicite o código ao cliente para iniciar a diária.',
-
-                        'origin' => 'schedule',
-
-                        'origin_id' => $schedule->id,
 
-                        'type' =>
-                        NotificationTypeEnum::SCHEDULE_PROVIDER_START->value,
-
-                        'user_id' => $schedule->provider->user_id,
+                    $notificationService->create([
+                        'title'       => 'Início do serviço!',
+                        'description' => 'Solicite o código ao cliente para iniciar a diária.',
+                        'origin'      => 'schedule',
+                        'origin_id'   => $schedule->id,
+                        'type'        => NotificationTypeEnum::SCHEDULE_PROVIDER_START->value,
+                        'user_id'     => $schedule->provider->user_id,
                     ]);
 
                     break;
 
                 case 'finished':
-
                     $notificationService = app(NotificationService::class);
 
                     // CLIENTE
-                    $notificationService->create([
-                        'title' => 'Serviço finalizado!',
 
-                        'description' =>
-                        'Sua diária foi finalizada com sucesso.',
-
-                        'origin' => 'schedule',
-
-                        'origin_id' => $schedule->id,
-
-                        'type' =>
-                        NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_FINISHED->value,
-
-                        'user_id' => $schedule->client->user_id,
+                    $notificationService->create([
+                        'title'       => 'Serviço finalizado!',
+                        'description' => 'Sua diária foi finalizada com sucesso.',
+                        'origin'      => 'schedule',
+                        'origin_id'   => $schedule->id,
+                        'type'        => NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_FINISHED->value,
+                        'user_id'     => $schedule->client->user_id,
                     ]);
 
                     break;
 
                 case 'paid':
-
                     $notificationService = app(NotificationService::class);
 
                     switch (Auth::user()->type) {
-
                         case UserTypeEnum::CLIENT:
-
                             if ($schedule->provider_id) {
-
                                 $notificationService->create([
-                                    'title' => 'Pagamento confirmado!',
-
-                                    'description' =>
-                                    'O cliente confirmou o pagamento da diária.',
-
-                                    'origin' => 'schedule',
-
-                                    'origin_id' => $schedule->id,
-
-                                    'type' =>
-                                    NotificationTypeEnum::SCHEDULE_PROVIDER_START->value,
-
-                                    'user_id' => $schedule->provider->user_id,
+                                    'title'       => 'Pagamento confirmado!',
+                                    'description' => 'O cliente confirmou o pagamento da diária.',
+                                    'origin'      => 'schedule',
+                                    'origin_id'   => $schedule->id,
+                                    'type'        => NotificationTypeEnum::SCHEDULE_PROVIDER_START->value,
+                                    'user_id'     => $schedule->provider->user_id,
                                 ]);
                             }
 
@@ -369,42 +299,34 @@ class ScheduleService
                     break;
 
                 case 'rejected':
-
                     $notificationService = app(NotificationService::class);
 
                     $notificationService->create([
-                        'title' => 'Agendamento recusado!',
-
-                        'description' =>
-                        'O diarista não poderá atender. Veja outros profissionais disponíveis.',
-
-                        'origin' => 'schedule',
-
-                        'origin_id' => $schedule->id,
-
-                        'type' =>
-                        NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_REFUSED->value,
-
-                        'user_id' => $schedule->client->user_id,
+                        'title'       => 'Agendamento recusado!',
+                        'description' => 'O diarista não poderá atender. Veja outros profissionais disponíveis.',
+                        'origin'      => 'schedule',
+                        'origin_id'   => $schedule->id,
+                        'type'        => NotificationTypeEnum::SCHEDULE_CLIENT_PROVIDER_REFUSED->value,
+                        'user_id'     => $schedule->client->user_id,
                     ]);
 
                     break;
             }
 
-
-
-
-
             DB::commit();
 
             return $schedule->fresh(['client.user', 'provider.user', 'address']);
         } catch (\Exception $e) {
             DB::rollBack();
+
             Log::error('Erro ao atualizar status do agendamento: ' . $e->getMessage());
+
             throw new \Exception('Não foi possível atualizar o status do agendamento.');
         }
     }
 
+    //
+
     public function getClientProviderBlocks(int $clientId, int $providerId): array
     {
         $today = Carbon::today()->format('Y-m-d');

+ 42 - 19
app/Services/SearchService.php

@@ -22,6 +22,7 @@ class SearchService
         $cliente = Client::where('user_id', $user->id)->first();
 
         $blockedProviderIds = ScheduleBusinessRules::getBlockedProviderIdsForClient($cliente->id);
+
         $providersWithWorkingDays = ScheduleBusinessRules::getProviderIdsWithWorkingDays();
 
         $clientPrimaryAddress = Address::where('source', 'client')
@@ -30,22 +31,32 @@ class SearchService
             ->first();
 
         $distanceSelect = $this->distanceSelect(
-            $clientPrimaryAddress?->latitude !== null ? (float) $clientPrimaryAddress->latitude : null,
+            $clientPrimaryAddress?->latitude !== null  ? (float) $clientPrimaryAddress->latitude  : null,
             $clientPrimaryAddress?->longitude !== null ? (float) $clientPrimaryAddress->longitude : null,
         );
 
-        $baseQuery = Provider::leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
+        $baseQuery = Provider::leftJoin(
+            'users as provider_user',
+            'provider_user.id',
+            '=',
+            'providers.user_id'
+        )
             ->visibleToCustomers()
-            ->leftJoin(DB::raw("
-        (
-          SELECT DISTINCT ON (source_id)
-            *
-          FROM addresses
-          WHERE source = 'provider'
-          AND deleted_at IS NULL
-          ORDER BY source_id, is_primary DESC
-        ) as provider_address
-      "), 'provider_address.source_id', '=', 'providers.id')
+            ->leftJoin(
+                DB::raw("
+                    (
+                        SELECT DISTINCT ON (source_id)
+                            *
+                        FROM addresses
+                        WHERE source = 'provider'
+                        AND deleted_at IS NULL
+                        ORDER BY source_id, is_primary DESC
+                    ) AS provider_address
+                "),
+                'provider_address.source_id',
+                '=',
+                'providers.id'
+            )
             ->whereNotNull('provider_address.id')
             ->whereNotIn('providers.id', $blockedProviderIds)
             ->whereIn('providers.id', $providersWithWorkingDays)
@@ -69,13 +80,18 @@ class SearchService
                 'providers.daily_price_4h',
                 'providers.daily_price_2h',
                 'providers.created_at',
-                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"),
+
+                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
+                "),
+
                 $distanceSelect,
             )
             ->orderByRaw('distance_km ASC NULLS LAST');
@@ -103,7 +119,9 @@ class SearchService
         );
 
         $mediaIds = $filtered->pluck('profile_media_id')->filter()->unique()->values();
+
         $mediaUrls = [];
+
         if ($mediaIds->isNotEmpty()) {
             Media::whereIn('id', $mediaIds)->get()->each(function ($media) use (&$mediaUrls) {
                 $mediaUrls[$media->id] = $media->path
@@ -114,12 +132,17 @@ class SearchService
 
         return $filtered->map(function ($item) use ($mediaUrls) {
             $arr = is_array($item) ? $item : $item->toArray();
+
             $arr['profile_media_url'] = $mediaUrls[$arr['profile_media_id'] ?? null] ?? null;
+
             unset($arr['profile_media_id']);
+
             return $arr;
         })->values()->toArray();
     }
 
+    //
+
     private function distanceSelect(?float $clientLatitude, ?float $clientLongitude): \Illuminate\Contracts\Database\Query\Expression
     {
         return DistanceService::sqlExpression($clientLatitude, $clientLongitude);

+ 2 - 0
app/Services/SupportRequestService.php

@@ -69,6 +69,8 @@ class SupportRequestService
         return $supportRequest->fresh('handler');
     }
 
+    //
+
     private function sendAlert(SupportRequest $supportRequest): void
     {
         $recipient = config('services.support.email');

+ 20 - 17
app/Services/UserService.php

@@ -16,11 +16,6 @@ class UserService
 {
     public function __construct(private readonly MediaService $mediaService) {}
 
-    public function me(): ?User
-    {
-        return User::with(['provider.profileMedia', 'client.profileMedia'])->find(Auth::id());
-    }
-
     public function getAll(): Collection
     {
         return User::query()->orderBy('created_at', 'desc')->get();
@@ -60,6 +55,18 @@ class UserService
         return $model->delete();
     }
 
+    //
+
+    public function getUserTypes(): array
+    {
+        return UserTypeEnum::toArray();
+    }
+
+    public function me(): ?User
+    {
+        return User::with(['provider.profileMedia', 'client.profileMedia'])->find(Auth::id());
+    }
+
     public function updateMe(array $data): User
     {
         try {
@@ -97,11 +104,11 @@ class UserService
                 $client = $user->client ?? Client::create(['user_id' => $user->id]);
 
                 Log::warning('[avatar-upload] Iniciando replaceFile para avatar do cliente', [
-                    'client_id'    => $client->id,
-                    'folder'       => "client/avatar/{$client->id}",
-                    'has_old_media'=> $client->profileMedia !== null,
-                    'old_media_id' => $client->profileMedia?->id,
-                    'old_media_path'=> $client->profileMedia?->path,
+                    'client_id'      => $client->id,
+                    'folder'         => "client/avatar/{$client->id}",
+                    'has_old_media'  => $client->profileMedia !== null,
+                    'old_media_id'   => $client->profileMedia?->id,
+                    'old_media_path' => $client->profileMedia?->path,
                 ]);
 
                 $media = $this->mediaService->replaceFile(
@@ -119,11 +126,12 @@ class UserService
                 ]);
 
                 $client->profile_media_id = $media->id;
+
                 $client->save();
 
                 Log::warning('[avatar-upload] Client salvo com novo profile_media_id', [
-                    'client_id'       => $client->id,
-                    'profile_media_id'=> $client->profile_media_id,
+                    'client_id'        => $client->id,
+                    'profile_media_id' => $client->profile_media_id,
                 ]);
             } else {
                 Log::warning('[avatar-upload] Avatar NÃO processado no service', [
@@ -161,9 +169,4 @@ class UserService
             throw $e;
         }
     }
-
-    public function getUserTypes(): array
-    {
-        return UserTypeEnum::toArray();
-    }
 }

+ 24 - 22
app/Services/WebhookService.php

@@ -82,10 +82,10 @@ class WebhookService
             Log::channel('pagarme')->warning('Payment not found for Pagar.me webhook', [
                 'hook_id'        => $payload['id'] ?? null,
                 'event'          => $event,
-                'order_id'       => $orderResponse['id'] ?? null,
-                'charge_id'      => $orderResponse['charges'][0]['id'] ?? null,
+                'order_id'       => $orderResponse['id']                                   ?? null,
+                'charge_id'      => $orderResponse['charges'][0]['id']                     ?? null,
                 'transaction_id' => $orderResponse['charges'][0]['last_transaction']['id'] ?? null,
-                'metadata'       => $orderResponse['metadata'] ?? [],
+                'metadata'       => $orderResponse['metadata']                             ?? [],
             ]);
 
             return null;
@@ -159,14 +159,16 @@ class WebhookService
         return $webhook;
     }
 
+    //
+
     private function extractReferences(?string $event, array $data): array
     {
         if (str_starts_with((string) $event, 'order.')) {
             $charge = $data['charges'][0] ?? [];
 
             return [
-                'order_id'       => $data['id'] ?? null,
-                'charge_id'      => $charge['id'] ?? null,
+                'order_id'       => $data['id']                       ?? null,
+                'charge_id'      => $charge['id']                     ?? null,
                 'transaction_id' => $charge['last_transaction']['id'] ?? null,
             ];
         }
@@ -186,27 +188,14 @@ class WebhookService
         ];
     }
 
-    private function normalizeOrderResponse(?string $event, array $data): array
-    {
-        if (str_starts_with((string) $event, 'order.')) {
-            return $data;
-        }
-
-        return [
-            'id'       => $data['order']['id'] ?? $data['order_id'] ?? null,
-            'metadata' => $data['metadata'] ?? $data['order']['metadata'] ?? [],
-            'charges'  => [$data],
-        ];
-    }
-
     private function findPayment(array $orderResponse): ?Payment
     {
         $charge        = $orderResponse['charges'][0] ?? [];
-        $transaction   = $charge['last_transaction'] ?? [];
-        $metadata      = $orderResponse['metadata'] ?? $charge['metadata'] ?? [];
+        $transaction   = $charge['last_transaction']  ?? [];
+        $metadata      = $orderResponse['metadata']   ?? $charge['metadata'] ?? [];
         $metadataId    = filter_var($metadata['payment_id'] ?? null, FILTER_VALIDATE_INT) ?: null;
-        $chargeId      = $charge['id'] ?? null;
-        $transactionId = $transaction['id'] ?? null;
+        $chargeId      = $charge['id']        ?? null;
+        $transactionId = $transaction['id']   ?? null;
         $orderId       = $orderResponse['id'] ?? null;
         $references    = array_filter([$metadataId, $chargeId, $transactionId, $orderId]);
 
@@ -229,4 +218,17 @@ class WebhookService
             ->latest('id')
             ->first();
     }
+
+    private function normalizeOrderResponse(?string $event, array $data): array
+    {
+        if (str_starts_with((string) $event, 'order.')) {
+            return $data;
+        }
+
+        return [
+            'id'       => $data['order']['id'] ?? $data['order_id']          ?? null,
+            'metadata' => $data['metadata']    ?? $data['order']['metadata'] ?? [],
+            'charges'  => [$data],
+        ];
+    }
 }

+ 1 - 1
routes/authRoutes/device_token.php

@@ -3,5 +3,5 @@
 use App\Http\Controllers\DeviceTokenController;
 use Illuminate\Support\Facades\Route;
 
-Route::post('/device-tokens',          [DeviceTokenController::class, 'store']);
+Route::post('/device-tokens',           [DeviceTokenController::class, 'store']);
 Route::delete('/device-tokens/{token}', [DeviceTokenController::class, 'destroy']);

+ 3 - 5
routes/authRoutes/notifications.php

@@ -3,8 +3,6 @@
 use App\Http\Controllers\NotificationController;
 use Illuminate\Support\Facades\Route;
 
-Route::get('/notifications',[NotificationController::class, 'index'])->middleware('permission:notification,view');
-
-Route::put('/notifications/{id}/read',[NotificationController::class, 'markAsRead'])->middleware('permission:notification,add');
-
-Route::put('/notifications/read-all',[NotificationController::class, 'markAllAsRead'])->middleware('permission:notification,add');
+Route::get('/notifications',           [NotificationController::class, 'index'])->middleware('permission:notification,view');
+Route::put('/notifications/{id}/read', [NotificationController::class, 'markAsRead'])->middleware('permission:notification,add');
+Route::put('/notifications/read-all',  [NotificationController::class, 'markAllAsRead'])->middleware('permission:notification,add');

+ 1 - 4
routes/authRoutes/payment.php

@@ -6,10 +6,7 @@ use Illuminate\Support\Facades\Route;
 Route::get('/payment',  [PaymentController::class, 'index'])->middleware('permission:payment,view');
 Route::post('/payment', [PaymentController::class, 'store'])->middleware('permission:payment,add');
 
-Route::get('/payment/platform-fees', [PaymentController::class, 'platformFees']);
-
-// Route::post('/payment/cart/pay', [PaymentController::class, 'payCart']);
-
+Route::get('/payment/platform-fees',            [PaymentController::class, 'platformFees']);
 Route::get('/payment/schedule/{schedule}/pix',  [PaymentController::class, 'getSchedulePix'])->middleware('permission:config.schedule,view');
 Route::post('/payment/schedule/{schedule}/pay', [PaymentController::class, 'paySchedule'])->middleware('permission:config.schedule,edit');
 

+ 22 - 10
routes/authRoutes/review.php

@@ -3,13 +3,25 @@
 use App\Http\Controllers\ReviewController;
 use Illuminate\Support\Facades\Route;
 
-Route::get('/reviews', [ReviewController::class, 'index'])->middleware('permission:config.review,view');
-Route::get('/reviews/schedule/{scheduleId}', [ReviewController::class, 'indexBySchedule'])->middleware('permission:config.review,view');
-Route::get('/reviews/client/{clientId}/sent', [ReviewController::class, 'clientSent'])->middleware('permission:config.review,view');
-Route::get('/reviews/client/{clientId}/received', [ReviewController::class, 'clientReceived'])->middleware('permission:config.review,view');
-Route::get('/reviews/provider/{providerId}/sent', [ReviewController::class, 'providerSent'])->middleware('permission:config.review,view');
-Route::get('/reviews/provider/{providerId}/received', [ReviewController::class, 'providerReceived'])->middleware('permission:config.review,view');
-Route::get('/reviews/{origin}/{originId}', [ReviewController::class, 'indexByOrigin'])->middleware('permission:config.review,view');
-Route::post('/reviews', [ReviewController::class, 'store'])->middleware('permission:config.review,add');
-Route::put('/reviews/{id}', [ReviewController::class, 'update'])->middleware('permission:config.review,edit');
-Route::delete('/reviews/{id}', [ReviewController::class, 'destroy'])->middleware('permission:config.review,delete');
+Route::prefix('reviews')->group(function () {
+    Route::get('/',                           [ReviewController::class, 'index'])->middleware('permission:config.review,view');
+    Route::get('/schedule/{scheduleId}',      [ReviewController::class, 'indexBySchedule'])->middleware('permission:config.review,view');
+    Route::get('/client/{clientId}/sent',     [ReviewController::class, 'clientSent'])->middleware('permission:config.review,view');
+    Route::get('/client/{clientId}/received', [ReviewController::class, 'clientReceived'])->middleware('permission:config.review,view');
+    Route::get('/provider/{providerId}/sent', [ReviewController::class, 'providerSent'])->middleware('permission:config.review,view');
+
+    Route::get('/provider/{providerId}/received', [ReviewController::class, 'providerReceived'])
+        ->middleware('permission:config.review,view');
+
+    Route::get('/{origin}/{originId}', [ReviewController::class, 'indexByOrigin'])
+        ->middleware('permission:config.review,view');
+
+    Route::post('/', [ReviewController::class, 'store'])
+        ->middleware('permission:config.review,add');
+
+    Route::put('/{id}', [ReviewController::class, 'update'])
+        ->middleware('permission:config.review,edit');
+
+    Route::delete('/{id}', [ReviewController::class, 'destroy'])
+        ->middleware('permission:config.review,delete');
+});

+ 9 - 9
routes/noAuthRoutes/auth.php

@@ -6,16 +6,16 @@ use App\Http\Controllers\ClientController;
 use App\Http\Controllers\ProviderController;
 
 // backoffice
-Route::post('/login', [AuthController::class, 'login']);
+Route::post('/login',   [AuthController::class, 'login']);
 Route::post('/refresh', [AuthController::class, 'refresh']);
 
 // app
-Route::post('/login-app', [AuthController::class, 'loginApp']);
-Route::post('/refresh-app', [AuthController::class, 'refreshApp']);
-Route::post('/client-send-code', [AuthController::class, 'clientSendCode']);
-Route::post('/provider-send-code', [AuthController::class, 'providerSendCode']);
-Route::post('/user-validate-code', [AuthController::class, 'validateCode']);
-Route::post('/validate-code-client', [AuthController::class, 'validateCodeClient']);
+Route::post('/login-app',              [AuthController::class, 'loginApp']);
+Route::post('/refresh-app',            [AuthController::class, 'refreshApp']);
+Route::post('/client-send-code',       [AuthController::class, 'clientSendCode']);
+Route::post('/provider-send-code',     [AuthController::class, 'providerSendCode']);
+Route::post('/user-validate-code',     [AuthController::class, 'validateCode']);
+Route::post('/validate-code-client',   [AuthController::class, 'validateCodeClient']);
 Route::post('/validate-code-provider', [AuthController::class, 'validateCodeProvider']);
-Route::post('/register-client', [ClientController::class, 'register']);
-Route::post('/register-provider', [ProviderController::class, 'register']);
+Route::post('/register-client',        [ClientController::class, 'register']);
+Route::post('/register-provider',      [ProviderController::class, 'register']);