Browse Source

feat: :sparkles: feat (calculo-distancias) criado calculo distancias + funcao preencher endereco com maps no perfil

foi criado o calculo de distancias usando metodo matematico + funcao para preencher endereco com google maps no perfil do cliente e prestador quando nao ha lat e long definidos

fase:dev | origin:escopo
Gustavo Zanatta 2 weeks ago
parent
commit
041ee27f24

+ 225 - 224
app/Http/Controllers/AuthController.php

@@ -3,272 +3,273 @@
 namespace App\Http\Controllers;
 
 use App\Http\Requests\AuthRequest;
-use App\Http\Requests\RefreshTokenAppRequest;
 use App\Http\Requests\RefreshTokenRequest;
+use App\Http\Requests\RefreshTokenAppRequest;
 use App\Http\Requests\UserAppsRequest;
 use App\Http\Requests\UserAppsValidateCodeRequest;
+use Illuminate\Http\JsonResponse;
 use App\Http\Resources\AuthResource;
 use App\Services\AuthService;
-use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Facades\Log;
 
 class AuthController extends Controller
 {
-    public function __construct(protected AuthService $authService) {}
-
-    public function login(AuthRequest $request): JsonResponse
-    {
-        $validated = $request->validated();
+  public function __construct(protected AuthService $authService) {}
 
-        $result = $this->authService->login(
-            email: $validated['email'],
-            password: $validated['password'],
-        );
+  public function login(AuthRequest $request): JsonResponse
+  {
+    $validated = $request->validated();
 
-        if (! $result) {
-            return $this->errorResponse(message: __('auth.failed'), code: 401);
-        }
+    $result = $this->authService->login(
+      email: $validated["email"],
+      password: $validated["password"],
+    );
 
-        return $this->successResponse(
-            payload: new AuthResource($result['payload']),
-            message: __('auth.logged_in'),
-        )->withCookie(
-            cookie(
-                'refresh_token',
-                $result['refreshToken'],
-                config('sanctum.rt_expiration') * 60,
-                '/',
-                config('session.domain'),
-                config('session.secure'),
-                true,
-                false,
-                'Lax',
-            ),
-        );
+    if (!$result) {
+      return $this->errorResponse(message: __("auth.failed"), code: 401);
     }
 
-    public function loginApp(AuthRequest $request): JsonResponse
-    {
-        $validated = $request->validated();
-
-        $result = $this->authService->login(
-            email: $validated['email'],
-            password: $validated['password'],
-        );
-
-        if (! $result) {
-            return $this->errorResponse(message: __('auth.failed'), code: 401);
-        }
+    return $this->successResponse(
+      payload: new AuthResource($result["payload"]),
+      message: __("auth.logged_in"),
+    )->withCookie(
+      cookie(
+        "refresh_token",
+        $result["refreshToken"],
+        config("sanctum.rt_expiration") * 60,
+        "/",
+        config("session.domain"),
+        config("session.secure"),
+        true,
+        false,
+        "Lax",
+      ),
+    );
+  }
+
+  public function loginApp(AuthRequest $request): JsonResponse
+  {
+    $validated = $request->validated();
+
+    $result = $this->authService->login(
+      email: $validated["email"],
+      password: $validated["password"],
+    );
+
+    if (!$result) {
+      return $this->errorResponse(message: __("auth.failed"), code: 401);
+    }
 
-        return $this->successResponse(
-            payload: new AuthResource([
-                ...$result['payload'],
-                'refresh_token' => $result['refreshToken'],
-            ]),
-            message: __('auth.logged_in'),
-        );
+    return $this->successResponse(
+      payload: new AuthResource([
+        ...$result["payload"],
+        "refresh_token" => $result["refreshToken"],
+      ]),
+      message: __("auth.logged_in"),
+    );
+  }
+
+  public function logout(): JsonResponse
+  {
+    $this->authService->logout();
+
+    return $this->successResponse(
+      message: __("auth.logout"),
+    )->withoutCookie("refresh_token");
+  }
+
+  public function refresh(RefreshTokenRequest $request): JsonResponse
+  {
+    $refresh_token = $request->cookie("refresh_token");
+
+    if (is_null($refresh_token)) {
+      return $this->errorResponse(
+        code: 403,
+      )->withoutCookie("refresh_token");
     }
 
-    public function logout(): JsonResponse
-    {
-        $this->authService->logout();
+    $result = $this->authService->refresh(
+      $refresh_token
+    );
 
-        return $this->successResponse(
-            message: __('auth.logout'),
-        )->withoutCookie('refresh_token');
+    if (is_null($result)) {
+      return $this->errorResponse(
+        message: __("auth.unauthorized"),
+        code: 403,
+      )->withoutCookie("refresh_token");
     }
 
-    public function refresh(RefreshTokenRequest $request): JsonResponse
-    {
-        $refresh_token = $request->cookie('refresh_token');
-
-        if (is_null($refresh_token)) {
-            return $this->errorResponse(
-                code: 403,
-            )->withoutCookie('refresh_token');
-        }
+    return $this->successResponse(
+      payload: new AuthResource($result["payload"]),
+    )->withCookie(
+      cookie(
+        "refresh_token",
+        $result["refreshToken"],
+        config("sanctum.rt_expiration") * 60,
+        "/",
+        config("session.domain"),
+        config("session.secure"),
+        true,
+        true,
+        "Lax",
+      ),
+    );
+  }
+
+  public function refreshApp(RefreshTokenAppRequest $request): JsonResponse
+  {
+    $refresh_token = $request->validated("refresh_token");
+
+    if (is_null($refresh_token)) {
+      return $this->errorResponse(code: 403);
+    }
+    $result = $this->authService->refresh(
+      $refresh_token
+    );
+
+    if (is_null($result)) {
+      return $this->errorResponse(
+        message: __("auth.unauthorized"),
+        code: 403,
+      );
+    }
+    return $this->successResponse(
+      payload: new AuthResource([
+        ...$result["payload"],
+        "refresh_token" => $result["refreshToken"],
+      ]),
+    );
+  }
+
+  public function clientSendCode(UserAppsRequest $request): JsonResponse
+  {
+    $result = $this->authService->clientSendCode($request->validated());
+
+    if (is_array($result) && isset($result['error'])) {
+      return $this->errorResponse(message: __("auth.{$result['error']}"), code: 403);
+    }
 
-        $result = $this->authService->refresh(
-            $refresh_token
-        );
+    return $this->successResponse(
+      message: __("messages.code_sent"),
+      code: 201,
+      payload: ['isLogin' => $result],
+    );
+  }
 
-        if (is_null($result)) {
-            return $this->errorResponse(
-                message: __('auth.unauthorized'),
-                code: 403,
-            )->withoutCookie('refresh_token');
-        }
+  public function providerSendCode(UserAppsRequest $request): JsonResponse
+  {
+    $result = $this->authService->providerSendCode($request->validated());
 
-        return $this->successResponse(
-            payload: new AuthResource($result['payload']),
-        )->withCookie(
-            cookie(
-                'refresh_token',
-                $result['refreshToken'],
-                config('sanctum.rt_expiration') * 60,
-                '/',
-                config('session.domain'),
-                config('session.secure'),
-                true,
-                true,
-                'Lax',
-            ),
-        );
+    if (is_array($result) && isset($result['error'])) {
+      return $this->errorResponse(message: __("auth.{$result['error']}"), code: 403);
     }
 
-    public function refreshApp(RefreshTokenAppRequest $request): JsonResponse
-    {
-        $refresh_token = $request->validated('refresh_token');
+    return $this->successResponse(
+      message: __("messages.code_sent"),
+      code: 201,
+      payload: ['isLogin' => $result],
+    );
+  }
 
-        if (is_null($refresh_token)) {
-            return $this->errorResponse(code: 403);
-        }
-        $result = $this->authService->refresh(
-            $refresh_token
-        );
+  public function validateCodeClient(UserAppsValidateCodeRequest $request): JsonResponse
+  {
+    $email   = $request->input('email');
+    $phone   = $request->input('phone');
+    $code    = $request->input('code');
+    $isLogin = (bool) $request->input('isLogin', false);
 
-        if (is_null($result)) {
-            return $this->errorResponse(
-                message: __('auth.unauthorized'),
-                code: 403,
-            );
-        }
+    $result = $this->authService->validateCodeClient($request->validated(), $isLogin);
 
-        return $this->successResponse(
-            payload: new AuthResource([
-                ...$result['payload'],
-                'refresh_token' => $result['refreshToken'],
-            ]),
-        );
+    if ($result === false) {
+      return $this->errorResponse(message: __('auth.invalid_code'), code: 400);
     }
 
-    public function clientSendCode(UserAppsRequest $request): JsonResponse
-    {
-        $result = $this->authService->clientSendCode($request->validated());
-
-        if (is_array($result) && isset($result['error'])) {
-            return $this->errorResponse(message: __("auth.{$result['error']}"), code: 403);
-        }
-
-        return $this->successResponse(
-            message: __('messages.code_sent'),
-            code: 201,
-            payload: ['isLogin' => $result],
-        );
+    if (is_array($result) && isset($result['error'])) {
+      return $this->errorResponse(message: __("auth.{$result['error']}"), code: 403);
     }
 
-    public function providerSendCode(UserAppsRequest $request): JsonResponse
-    {
-        $result = $this->authService->providerSendCode($request->validated());
-
-        if (is_array($result) && isset($result['error'])) {
-            return $this->errorResponse(message: __("auth.{$result['error']}"), code: 403);
-        }
-
-        return $this->successResponse(
-            message: __('messages.code_sent'),
-            code: 201,
-            payload: ['isLogin' => $result],
-        );
+    if ($isLogin) {
+      return $this->successResponse(
+        payload: new AuthResource([...$result['payload'], 'refresh_token' => $result['refreshToken']]),
+        message: __('auth.logged_in'),
+      );
     }
 
-    public function validateCodeClient(UserAppsValidateCodeRequest $request): JsonResponse
-    {
-        $email   = $request->input('email');
-        $phone   = $request->input('phone');
-        $code    = $request->input('code');
-        $isLogin = (bool) $request->input('isLogin', false);
-
-        $result = $this->authService->validateCodeClient($request->validated(), $isLogin);
-
-        if ($result === false) {
-            return $this->errorResponse(message: __('auth.invalid_code'), code: 400);
-        }
+    return $this->successResponse(
+      payload: ['email' => $email, 'phone' => $phone, 'code' => $code],
+      message: __('auth.valid_code'),
+    );
+  }
 
-        if (is_array($result) && isset($result['error'])) {
-            return $this->errorResponse(message: __("auth.{$result['error']}"), code: 403);
-        }
+  public function validateCodeProvider(UserAppsValidateCodeRequest $request): JsonResponse
+  {
+    $email   = $request->input('email');
+    $phone   = $request->input('phone');
+    $code    = $request->input('code');
+    $isLogin = (bool) $request->input('isLogin', false);
 
-        if ($isLogin) {
-            return $this->successResponse(
-                payload: new AuthResource([...$result['payload'], 'refresh_token' => $result['refreshToken']]),
-                message: __('auth.logged_in'),
-            );
-        }
+    $result = $this->authService->validateCodeProvider($request->validated(), $isLogin);
 
-        return $this->successResponse(
-            payload: ['email' => $email, 'phone' => $phone, 'code' => $code],
-            message: __('auth.valid_code'),
-        );
+    if ($result === false) {
+      return $this->errorResponse(message: __('auth.invalid_code'), code: 400);
     }
 
-    public function validateCodeProvider(UserAppsValidateCodeRequest $request): JsonResponse
-    {
-        $email   = $request->input('email');
-        $phone   = $request->input('phone');
-        $code    = $request->input('code');
-        $isLogin = (bool) $request->input('isLogin', false);
-
-        $result = $this->authService->validateCodeProvider($request->validated(), $isLogin);
-
-        if ($result === false) {
-            return $this->errorResponse(message: __('auth.invalid_code'), code: 400);
-        }
-
-        if (is_array($result) && isset($result['error'])) {
-            return $this->errorResponse(message: __("auth.{$result['error']}"), code: 403);
-        }
+    if (is_array($result) && isset($result['error'])) {
+      return $this->errorResponse(message: __("auth.{$result['error']}"), code: 403);
+    }
 
-        if ($isLogin) {
-            return $this->successResponse(
-                payload: new AuthResource([...$result['payload'], 'refresh_token' => $result['refreshToken']]),
-                message: __('auth.logged_in'),
-            );
-        }
+    if ($isLogin) {
+      return $this->successResponse(
+        payload: new AuthResource([...$result['payload'], 'refresh_token' => $result['refreshToken']]),
+        message: __('auth.logged_in'),
+      );
+    }
 
+    return $this->successResponse(
+      payload: ['email' => $email, 'phone' => $phone, 'code' => $code],
+      message: __('auth.valid_code'),
+    );
+  }
+
+  public function validateCode(UserAppsValidateCodeRequest $request): JsonResponse
+  {
+    try {
+
+      $email = $request->input("email");
+      $phone = $request->input("phone");
+      $code = $request->input("code");
+      $isLogin = $request->input("isLogin");
+
+      $result = $this->authService->validateCode($request->validated(), $isLogin);
+  
+      if (!$result) {
+        return $this->errorResponse(
+          message: __("auth.invalid_code"),
+          code: 400,
+        );
+      }
+      
+      if($isLogin) {
         return $this->successResponse(
-            payload: ['email' => $email, 'phone' => $phone, 'code' => $code],
-            message: __('auth.valid_code'),
+          payload: new AuthResource([
+            ...$result["payload"],
+            "refresh_token" => $result["refreshToken"],
+          ]),
+          message: __("auth.logged_in"),
         );
-    }
+      } else {
+        return $this->successResponse(
+          payload: ['email' => $email, 'phone' => $phone, 'code' => $code],
+          message: __("auth.valid_code"),
+          code: 200,
+        );
+      }
 
-    public function validateCode(UserAppsValidateCodeRequest $request): JsonResponse
-    {
-        try {
-            $email   = $request->input('email');
-            $phone   = $request->input('phone');
-            $code    = $request->input('code');
-            $isLogin = $request->input('isLogin');
-
-            $result = $this->authService->validateCode($request->validated(), $isLogin);
-
-            if (! $result) {
-                return $this->errorResponse(
-                    message: __('auth.invalid_code'),
-                    code: 400,
-                );
-            }
-
-            if ($isLogin) {
-                return $this->successResponse(
-                    payload: new AuthResource([
-                        ...$result['payload'],
-                        'refresh_token' => $result['refreshToken'],
-                    ]),
-                    message: __('auth.logged_in'),
-                );
-            } else {
-                return $this->successResponse(
-                    payload: ['email' => $email, 'phone' => $phone, 'code' => $code],
-                    message: __('auth.valid_code'),
-                    code: 200,
-                );
-            }
-
-        } catch (\Exception $e) {
-            return $this->errorResponse(
-                message: __('auth.validation_error'),
-                code: 500,
-            );
-        }
+    } catch (\Exception $e) {
+      return $this->errorResponse(
+        message: __("auth.validation_error"),
+        code: 500,
+      );
     }
+  }
 }

+ 16 - 24
app/Http/Controllers/ProviderController.php

@@ -18,7 +18,6 @@ class ProviderController extends Controller
     public function index(): JsonResponse
     {
         $items = $this->service->getAll();
-
         return $this->successResponse(
             payload: ProviderResource::collection($items),
         );
@@ -27,10 +26,9 @@ class ProviderController extends Controller
     public function store(ProviderRequest $request): JsonResponse
     {
         $item = $this->service->create($request->validated());
-
         return $this->successResponse(
             payload: new ProviderResource($item),
-            message: __('messages.created'),
+            message: __("messages.created"),
             code: 201,
         );
     }
@@ -38,14 +36,12 @@ class ProviderController extends Controller
     public function show(int $id): JsonResponse
     {
         $item = $this->service->findById($id);
-
         return $this->successResponse(payload: new ProviderResource($item));
     }
 
     public function update(ProviderRequest $request, int $id): JsonResponse
     {
         $item = $this->service->update($id, $request->validated());
-
         return $this->successResponse(
             payload: new ProviderResource($item),
             message: __('messages.updated'),
@@ -65,17 +61,16 @@ class ProviderController extends Controller
     public function destroy(int $id): JsonResponse
     {
         $this->service->delete($id);
-
         return $this->successResponse(
-            message: __('messages.deleted'),
+            message: __("messages.deleted"),
             code: 204,
         );
     }
 
     public function pending(Request $request): JsonResponse
     {
-        $page      = $request->integer('page', 1);
-        $perPage   = $request->integer('per_page', 10);
+        $page    = $request->integer('page', 1);
+        $perPage = $request->integer('per_page', 10);
         $paginated = $this->service->getPending($page, $perPage);
 
         return $this->successResponse(payload: [
@@ -89,7 +84,6 @@ class ProviderController extends Controller
     public function approve(int $id): JsonResponse
     {
         $item = $this->service->approve($id);
-
         return $this->successResponse(
             payload: new ProviderResource($item),
             message: __('messages.provider_approved'),
@@ -99,7 +93,6 @@ class ProviderController extends Controller
     public function reject(int $id): JsonResponse
     {
         $item = $this->service->reject($id);
-
         return $this->successResponse(
             payload: new ProviderResource($item),
             message: __('messages.provider_rejected'),
@@ -108,18 +101,17 @@ class ProviderController extends Controller
 
     public function register(RegisterProviderRequest $request): JsonResponse
     {
-        $result = $this->service->register($request->validated());
-
-        if (! $result) {
-            return $this->errorResponse(message: __('auth.failed'), code: 401);
-        }
-
-        return $this->successResponse(
-            payload: new AuthResource([
-                ...$result['payload'],
-                'refresh_token' => $result['refreshToken'],
-            ]),
-            message: __('auth.logged_in'),
-        );
+      $result = $this->service->register($request->validated());
+      if (!$result) {
+        return $this->errorResponse(message: __("auth.failed"), code: 401);
+      }
+
+      return $this->successResponse(
+        payload: new AuthResource([
+          ...$result["payload"],
+          "refresh_token" => $result["refreshToken"],
+        ]),
+        message: __("auth.logged_in"),
+      );
     }
 }

+ 2 - 0
app/Http/Requests/AddressRequest.php

@@ -27,6 +27,8 @@ class AddressRequest extends FormRequest
             'state_id'       => 'nullable|integer|exists:states,id',
             'address_type'   => 'sometimes|in:home,commercial,other',
             'is_primary'     => 'sometimes|boolean',
+            'latitude'       => 'nullable|numeric|between:-90,90',
+            'longitude'      => 'nullable|numeric|between:-180,180',
         ];
 
         if ($this->isMethod('post')) {

+ 15 - 14
app/Http/Requests/ProviderRequest.php

@@ -2,9 +2,10 @@
 
 namespace App\Http\Requests;
 
-use App\Enums\ApprovalStatusEnum;
 use Illuminate\Foundation\Http\FormRequest;
 use Illuminate\Validation\Rule;
+use App\Enums\UserTypeEnum;
+use App\Enums\ApprovalStatusEnum;
 
 class ProviderRequest extends FormRequest
 {
@@ -17,8 +18,8 @@ class ProviderRequest extends FormRequest
                 'sometimes',
                 'string',
                 'regex:/^[0-9]{11}$|^[0-9]{14}$/',
-                function ($attribute, $value, $fail) {
-                    if (! $this->isValidCpfCnpj($value)) {
+                function ($attribute, $value, $fail) use ($providerId) {
+                    if (!$this->isValidCpfCnpj($value)) {
                         $fail(__('validation.custom.document.invalid'));
                     }
                 },
@@ -26,7 +27,7 @@ class ProviderRequest extends FormRequest
                     ->ignore($providerId)
                     ->whereNull('deleted_at'),
             ],
-            'rg'      => 'sometimes|nullable|string|max:20',
+            'rg' => 'sometimes|nullable|string|max:20',
             'user_id' => [
                 'sometimes',
                 'exists:users,id',
@@ -38,7 +39,7 @@ class ProviderRequest extends FormRequest
                         ->where('user_id', $value)
                         ->whereNull('deleted_at')
                         ->exists();
-
+                    
                     if ($clientExists) {
                         $fail(__('validation.custom.user_id.already_linked_to_client'));
                     }
@@ -82,7 +83,7 @@ class ProviderRequest extends FormRequest
                 'string',
                 'regex:/^[0-9]{11}$|^[0-9]{14}$/',
                 function ($attribute, $value, $fail) {
-                    if (! $this->isValidCpfCnpj($value)) {
+                    if (!$this->isValidCpfCnpj($value)) {
                         $fail(__('validation.custom.document.invalid'));
                     }
                 },
@@ -116,7 +117,7 @@ class ProviderRequest extends FormRequest
                         ->where('user_id', $value)
                         ->whereNull('deleted_at')
                         ->exists();
-
+                    
                     if ($clientExists) {
                         $fail(__('validation.custom.user_id.already_linked_to_client'));
                     }
@@ -186,11 +187,11 @@ class ProviderRequest extends FormRequest
         }
 
         // Valida primeiro dígito verificador
-        $length  = strlen($cnpj) - 2;
+        $length = strlen($cnpj) - 2;
         $numbers = substr($cnpj, 0, $length);
-        $digits  = substr($cnpj, $length);
-        $sum     = 0;
-        $pos     = $length - 7;
+        $digits = substr($cnpj, $length);
+        $sum = 0;
+        $pos = $length - 7;
 
         for ($i = $length; $i >= 1; $i--) {
             $sum += $numbers[$length - $i] * $pos--;
@@ -206,10 +207,10 @@ class ProviderRequest extends FormRequest
         }
 
         // Valida segundo dígito verificador
-        $length  = $length + 1;
+        $length = $length + 1;
         $numbers = substr($cnpj, 0, $length);
-        $sum     = 0;
-        $pos     = $length - 7;
+        $sum = 0;
+        $pos = $length - 7;
 
         for ($i = $length; $i >= 1; $i--) {
             $sum += $numbers[$length - $i] * $pos--;

+ 84 - 87
app/Http/Requests/RegisterProviderRequest.php

@@ -8,93 +8,90 @@ use Illuminate\Validation\Rule;
 
 class RegisterProviderRequest extends FormRequest
 {
-    public function rules(): array
-    {
-        $rules = [
-            'email'      => 'sometimes|email',
-            'phone'      => 'sometimes|string|nullable|max:20',
-            'name'       => 'required|string|max:255',
-            'code'       => 'required|string|max:6',
-            'document'   => ['required', 'string', 'max:20'],
-            'rg'         => 'required|string|max:20',
-            'birth_date' => 'required|date|before:today',
-
-            'recipient_name'        => 'required|string|max:255',
-            'recipient_email'       => 'required|email|max:255',
-            'recipient_description' => 'required|string',
-            'recipient_document'    => 'required|string|max:20',
-            'recipient_type'        => ['required', Rule::in(['individual', 'company'])],
-            'recipient_code'        => 'required|string|max:255',
-
-            'recipient_payment_mode' => ['required', Rule::in(['bank_transfer'])],
-
-            'recipient_default_bank_account'                     => 'required|array',
-            'recipient_default_bank_account.holder_name'         => 'required|string|max:255',
-            'recipient_default_bank_account.holder_type'         => ['required', Rule::in(['individual', 'company'])],
-            'recipient_default_bank_account.holder_document'     => 'required|string|max:20',
-            'recipient_default_bank_account.bank'                => 'required|string|max:20',
-            'recipient_default_bank_account.branch_number'       => 'required|string|max:20',
-            'recipient_default_bank_account.branch_check_digit'  => 'sometimes|nullable|string|max:10',
-            'recipient_default_bank_account.account_number'      => 'required|string|max:20',
-            'recipient_default_bank_account.account_check_digit' => 'required|string|max:10',
-            'recipient_default_bank_account.type'                => ['required', Rule::in(['checking', 'savings'])],
-            'recipient_default_bank_account.metadata'            => 'sometimes|array',
-            'recipient_default_bank_account.pix_key'             => 'sometimes|nullable|string|max:255',
-
-            'recipient_metadata' => 'sometimes|array',
-
-            'zip_code'       => 'required|string|max:20',
-            'address'        => 'required|string|max:255',
-            'has_complement' => 'sometimes|boolean',
-            'complement'     => 'nullable|string|max:255',
-            'nickname'       => 'nullable|string|max:255',
-            'instructions'   => 'nullable|string',
-            'address_type'   => ['required', Rule::in(['home', 'commercial', 'other'])],
-            'city'           => 'nullable|string|max:255',
-            'state'          => 'nullable|string|max:2',
-
-            'daily_price_8h' => 'required|numeric|min:100|max:500',
-            'daily_price_6h' => 'required|numeric|min:0',
-            'daily_price_4h' => 'required|numeric|min:0',
-            'daily_price_2h' => 'required|numeric|min:0',
-
-            'services_types_ids' => 'sometimes|array',
-
-            'services_types_ids.*' => [
-                'integer',
-                Rule::exists('service_types', 'id')->where(function ($query) {
-                    $query->whereNull('deleted_at')->where('is_active', true);
-                }),
-            ],
-
-            'service_types_ids' => 'sometimes|array',
-
-            'service_types_ids.*' => [
-                'integer',
-                Rule::exists('service_types', 'id')->where(function ($query) {
-                    $query->whereNull('deleted_at')->where('is_active', true);
-                }),
-            ],
-
-            'working_days'          => 'required|array|min:1',
-            'working_days.*.day'    => 'required|integer|min:0|max:6',
-            'working_days.*.period' => ['required', Rule::in([WorkingPeriodEnum::MORNING->value, WorkingPeriodEnum::AFTERNOON->value])],
-
-            'selfie_base64'         => 'required|string',
-            'document_front_base64' => 'required|string',
-            'document_back_base64'  => 'required|string',
-        ];
-
-        if (! $this->has('email')) {
-            $rules['phone'] = 'required|string|max:20';
-            $rules['email'] = 'nullable';
-        }
-
-        if (! $this->has('phone')) {
-            $rules['email'] = 'required|email';
-            $rules['phone'] = 'nullable';
-        }
+  public function rules(): array
+  {
+    $rules = [
+      'email' => 'sometimes|email',
+      'phone' => 'sometimes|string|nullable|max:20',
+      'name' => 'required|string|max:255',
+      'code' => 'required|string|max:6',
+      'document' => ['required', 'string', 'max:20'],
+      'rg' => 'required|string|max:20',
+      'birth_date' => 'required|date|before:today',
+
+      'recipient_name'        => 'required|string|max:255',
+      'recipient_email'       => 'required|email|max:255',
+      'recipient_description' => 'required|string',
+      'recipient_document'    => 'required|string|max:20',
+      'recipient_type'        => ['required', Rule::in(['individual', 'company'])],
+      'recipient_code'        => 'required|string|max:255',
+
+      'recipient_payment_mode' => ['required', Rule::in(['bank_transfer'])],
+
+      'recipient_default_bank_account'                     => 'required|array',
+      'recipient_default_bank_account.holder_name'         => 'required|string|max:255',
+      'recipient_default_bank_account.holder_type'         => ['required', Rule::in(['individual', 'company'])],
+      'recipient_default_bank_account.holder_document'     => 'required|string|max:20',
+      'recipient_default_bank_account.bank'                => 'required|string|max:20',
+      'recipient_default_bank_account.branch_number'       => 'required|string|max:20',
+      'recipient_default_bank_account.branch_check_digit'  => 'sometimes|nullable|string|max:10',
+      'recipient_default_bank_account.account_number'      => 'required|string|max:20',
+      'recipient_default_bank_account.account_check_digit' => 'required|string|max:10',
+      'recipient_default_bank_account.type'                => ['required', Rule::in(['checking', 'savings'])],
+      'recipient_default_bank_account.metadata'            => 'sometimes|array',
+      'recipient_default_bank_account.pix_key'             => 'sometimes|nullable|string|max:255',
+
+      'recipient_metadata' => 'sometimes|array',
+
+      'zip_code'       => 'required|string|max:20',
+      'address'        => 'required|string|max:255',
+      'has_complement' => 'sometimes|boolean',
+      'complement'     => 'nullable|string|max:255',
+      'nickname'       => 'nullable|string|max:255',
+      'instructions'   => 'nullable|string',
+      'address_type'   => ['required', Rule::in(['home', 'commercial', 'other'])],
+      'city'           => 'nullable|string|max:255',
+      'state'          => 'nullable|string|max:2',
+
+      'daily_price_8h' => 'required|numeric|min:100|max:500',
+      'daily_price_6h' => 'required|numeric|min:0',
+      'daily_price_4h' => 'required|numeric|min:0',
+      'daily_price_2h' => 'required|numeric|min:0',
+
+      'services_types_ids' => 'sometimes|array',
+      'services_types_ids.*' => [
+        'integer',
+        Rule::exists('service_types', 'id')->where(function ($query) {
+          $query->whereNull('deleted_at')->where('is_active', true);
+        }),
+      ],
+      'service_types_ids' => 'sometimes|array',
+      'service_types_ids.*' => [
+        'integer',
+        Rule::exists('service_types', 'id')->where(function ($query) {
+          $query->whereNull('deleted_at')->where('is_active', true);
+        }),
+      ],
+
+      'working_days' => 'required|array|min:1',
+      'working_days.*.day' => 'required|integer|min:0|max:6',
+      'working_days.*.period' => ['required', Rule::in([WorkingPeriodEnum::MORNING->value, WorkingPeriodEnum::AFTERNOON->value])],
+
+      'selfie_base64' => 'required|string',
+      'document_front_base64' => 'required|string',
+      'document_back_base64' => 'required|string',
+    ];
+
+    if (!$this->has('email')) {
+      $rules['phone'] = 'required|string|max:20';
+      $rules['email'] = 'nullable';
+    }
 
-        return $rules;
+    if (!$this->has('phone')) {
+      $rules['email'] = 'required|email';
+      $rules['phone'] = 'nullable';
     }
+
+    return $rules;
+  }
 }

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

@@ -35,6 +35,8 @@ class AddressResource extends JsonResource
                 $this->district,
                 $this->city ? "{$this->city->name}/{$this->state?->code}" : null,
             ])),
+            'latitude'     => $this->latitude,
+            'longitude'    => $this->longitude,
             'city'         => $this->whenLoaded('city'),
             'state'        => $this->whenLoaded('state'),
             'address_type' => $this->address_type,

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

@@ -2,11 +2,11 @@
 
 namespace App\Http\Resources;
 
-use App\Models\Provider;
 use Carbon\Carbon;
 use Illuminate\Http\Request;
-use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
 use Illuminate\Http\Resources\Json\JsonResource;
+use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
+use App\Models\Provider;
 
 class ProviderResource extends JsonResource
 {
@@ -38,7 +38,7 @@ class ProviderResource extends JsonResource
     }
 
     /**
-     * @param  \Illuminate\Database\Eloquent\Collection<Provider>  $resource
+     * @param \Illuminate\Database\Eloquent\Collection<Provider> $resource
      * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection<ProviderResource>
      */
     public static function collection($resource): AnonymousResourceCollection

+ 55 - 49
app/Models/Provider.php

@@ -15,15 +15,16 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @property string $document
  * @property string|null $rg
  * @property int $user_id
- * @property numeric|null $average_rating
+ * @property float|null $average_rating
  * @property int $total_services
- * @property \Illuminate\Support\Carbon|null $birth_date
+ * @property string|null $birth_date
  * @property bool $selfie_verified
  * @property bool $document_verified
- * @property numeric|null $daily_price_8h
- * @property numeric|null $daily_price_6h
- * @property numeric|null $daily_price_4h
- * @property numeric|null $daily_price_2h
+ * @property string $approval_status
+ * @property float|null $daily_price_8h
+ * @property float|null $daily_price_6h
+ * @property float|null $daily_price_4h
+ * @property float|null $daily_price_2h
  * @property int|null $profile_media_id
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
@@ -52,7 +53,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @property-read int|null $blocked_clients_count
  * @property-read \App\Models\Address|null $primaryAddress
  * @property-read \App\Models\Media|null $profileMedia
- * @property-read \App\Models\User $user
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider onlyTrashed()
@@ -96,11 +96,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'];
+  protected $guarded = ["id"];
 
     /**
      * Get the attributes that should be cast.
@@ -132,11 +132,17 @@ class Provider extends Model
         return $this->belongsTo(User::class, 'user_id');
     }
 
-    public function profileMedia(): BelongsTo
-    {
-        return $this->belongsTo(Media::class, 'profile_media_id');
-    }
+  /**
+   * @return BelongsTo
+   */
+  public function profileMedia(): BelongsTo
+  {
+    return $this->belongsTo(Media::class, "profile_media_id");
+  }
 
+  /**
+   * @return HasMany
+     */
     public function addresses(): HasMany
     {
         return $this->hasMany(Address::class, 'source_id')
@@ -155,42 +161,42 @@ class Provider extends Model
 
     /**
      * @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 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 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);
-        }
-
-        $this->save();
-    }
+    $this->save();
+  }
 
-    public function primaryAddress()
-    {
-        return $this->hasOne(Address::class, 'source_id')
-            ->where('source', 'provider')
-            ->orderBy('is_primary', 'desc');
-    }
+  public function primaryAddress()
+  {
+    return $this->hasOne(Address::class, "source_id")
+      ->where("source", "provider")
+      ->orderBy("is_primary", "desc");
+  }
 }

+ 265 - 293
app/Services/AuthService.php

@@ -17,117 +17,114 @@ use Illuminate\Support\Str;
 
 class AuthService
 {
-    public function __construct(
-        private readonly EmailService $emailService,
-    ) {}
+  public function __construct(
+    private readonly EmailService $emailService,
+  ) {}
 
-    public function login(string $email, string $password): ?array
-    {
-        $user = User::where('email', $email)->first();
+  public function login(string $email, string $password): ?array
+  {
+    $user = User::where('email', $email)->first();
 
-        if (! $user || ! in_array($user->type, [UserTypeEnum::ADMIN, UserTypeEnum::USER])) {
-            return null;
-        }
-
-        if (! Auth::attempt(['email' => $email, 'password' => $password])) {
-            return null;
-        }
-
-        // $user = User::where('email', $email)->first();
-        $deviceId = Str::uuid()->toString();
-
-        $accessToken  = $user->createAccessToken($deviceId);
-        $refreshToken = $user->createRefreshToken($deviceId);
-
-        return [
-            'payload' => [
-                'access_token' => $accessToken,
-                'user'         => $user,
-            ],
-            'refreshToken' => $refreshToken,
-        ];
+    if (!$user || !in_array($user->type, [UserTypeEnum::ADMIN, UserTypeEnum::USER])) {
+      return null;
     }
 
-    public function refresh(string $refreshToken): ?array
-    {
-        if (! $refreshToken) {
-            return null;
-        }
-
-        $tokenModel = PersonalAccessToken::findToken($refreshToken);
-
-        if (
-            ! $tokenModel ||
-            ! in_array('refresh', $tokenModel->abilities) ||
-            $tokenModel->expires_at < now()
-        ) {
-            return null;
-        }
-
-        $user = $tokenModel->tokenable;
+    if (!Auth::attempt(['email' => $email, 'password' => $password])) {
+      return null;
+    }
 
-        if (! $user) {
-            return null;
-        }
+    // $user = User::where('email', $email)->first();
+    $deviceId = Str::uuid()->toString();
+
+    $accessToken = $user->createAccessToken($deviceId);
+    $refreshToken = $user->createRefreshToken($deviceId);
+
+    return [
+      'payload' => [
+        'access_token' => $accessToken,
+        'user'         => $user,
+      ],
+      'refreshToken' => $refreshToken,
+    ];
+  }
+
+  public function refresh(string $refreshToken): ?array
+  {
+    if (!$refreshToken) {
+      return null;
+    }
 
-        $deviceId = Str::afterLast($tokenModel->name, '_');
+    $tokenModel = PersonalAccessToken::findToken($refreshToken);
 
-        $tokens = $this->refreshTokenTransaction($tokenModel, $user, $deviceId);
+    if (
+      !$tokenModel ||
+      !in_array("refresh", $tokenModel->abilities) ||
+      $tokenModel->expires_at < now()
+    ) {
+      return null;
+    }
 
-        return [
-            'payload' => [
-                'access_token' => $tokens['access_token'],
-                'user'         => $user,
-            ],
-            'refreshToken' => $tokens['refresh_token'],
-        ];
+    $user = $tokenModel->tokenable;
+    if (!$user) {
+      return null;
     }
 
-    public function logout(): void
-    {
-        $user = Auth::user();
+    $deviceId = Str::afterLast($tokenModel->name, "_");
 
-        if (! $user) {
-            return;
-        }
+    $tokens = $this->refreshTokenTransaction($tokenModel, $user, $deviceId);
 
-        $tokenName = $user->currentAccessToken()->name;
-        $deviceId  = Str::afterLast($tokenName, '_');
+    return [
+      "payload" => [
+        "access_token" => $tokens["access_token"],
+        "user" => $user,
+      ],
+      "refreshToken" => $tokens["refresh_token"],
+    ];
+  }
 
-        $user
-            ->tokens()
-            ->where('name', 'like', "%_{$deviceId}")
-            ->delete();
+  public function logout(): void
+  {
+    $user = Auth::user();
+    if (!$user) {
+      return;
     }
 
-    protected function refreshTokenTransaction(
-        PersonalAccessToken $tokenModel,
-        User $user,
-        string $deviceId,
+    $tokenName = $user->currentAccessToken()->name;
+    $deviceId = Str::afterLast($tokenName, "_");
+
+    $user
+      ->tokens()
+      ->where("name", "like", "%_{$deviceId}")
+      ->delete();
+  }
+
+  protected function refreshTokenTransaction(
+    PersonalAccessToken $tokenModel,
+    User $user,
+    string $deviceId,
+  ): array {
+    return DB::transaction(function () use (
+      $tokenModel,
+      $user,
+      $deviceId,
     ): array {
-        return DB::transaction(function () use (
-            $tokenModel,
-            $user,
-            $deviceId,
-        ): array {
-            $tokenModel->update(['expires_at' => Carbon::now()]);
-
-            $accessToken  = $user->createAccessToken($deviceId);
-            $refreshToken = $user->createRefreshToken($deviceId);
-
-            return [
-                'access_token'  => $accessToken,
-                'refresh_token' => $refreshToken,
-            ];
-        });
-    }
+      $tokenModel->update(["expires_at" => Carbon::now()]);
+
+      $accessToken = $user->createAccessToken($deviceId);
+      $refreshToken = $user->createRefreshToken($deviceId);
 
-    public function clientSendCode(array $data): bool|array|null
-    {
-        try {
-            DB::beginTransaction();
+      return [
+        "access_token" => $accessToken,
+        "refresh_token" => $refreshToken,
+      ];
+    });
+  }
 
-            $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
+  public function clientSendCode(array $data): bool|array|null
+  {
+    try {
+      DB::beginTransaction();
+      $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
 
             $user = User::where(function ($query) use ($data) {
                 $query->when(! empty($data['email']), function ($q) use ($data) {
@@ -175,229 +172,204 @@ class AuthService
                 Client::create(['user_id' => $user->id]);
             }
 
-            if (! empty($data['email'])) {
-                $this->emailService->sendVerificationCode(
-                    email: $data['email'],
-                    code: $code,
-                    recipientName: $data['name'] ?? '',
-                );
-            } elseif (! empty($data['phone'])) {
-                Log::info('SMS: envio de código por telefone ainda não implementado.', [
-                    'phone' => $data['phone'],
-                ]);
-            }
-
-            DB::commit();
-
-            return $isLogin;
-        } catch (\Exception $e) {
-            DB::rollBack();
-            Log::error('Erro ao enviar código de verificação.', [
-                'error' => $e->getMessage(),
-                'data'  => $data,
-            ]);
-
-            return false;
-        }
+      if (!empty($data['email'])) {
+        $this->emailService->sendVerificationCode(
+          email: $data['email'],
+          code: $code,
+          recipientName: $data['name'] ?? '',
+        );
+      } elseif (!empty($data['phone'])) {
+        Log::info('SMS: envio de código por telefone ainda não implementado.', [
+          'phone' => $data['phone'],
+        ]);
+      }
+
+      DB::commit();
+      return $isLogin;
+    } catch (\Exception $e) {
+      DB::rollBack();
+      Log::error('Erro ao enviar código de verificação.', [
+        'error' => $e->getMessage(),
+        'data' => $data,
+      ]);
+      return false;
     }
+  }
 
-    public function providerSendCode(array $data): bool|array|null
-    {
-        try {
-            DB::beginTransaction();
-
-            $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
-
-            $user = User::where(function ($query) use ($data) {
-                $query->when(! empty($data['email']), function ($q) use ($data) {
-                    $q->where('email', $data['email']);
-                })
-                    ->when(! empty($data['phone']), function ($q) use ($data) {
-                        $q->where('phone', $data['phone']);
-                    });
-            })
-                ->first();
-
-            $isLogin = false;
-
-            if ($user) {
-                if ($user->type->value !== UserTypeEnum::PROVIDER->value) {
-                    DB::rollBack();
-
-                    return ['error' => 'wrong_user_type'];
-                }
-
-                $provider = Provider::where('user_id', $user->id)->first();
-
-                if ($provider && $provider->approval_status && $provider->approval_status->value !== ApprovalStatusEnum::ACCEPTED->value) {
-                    DB::rollBack();
-
-                    return ['error' => 'provider_not_accepted'];
-                }
-
-                $user->code = $code;
-
-                $user->validated_code = false;
+  public function providerSendCode(array $data): bool|array|null
+  {
+    try {
+      DB::beginTransaction();
+      $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
 
-                $user->save();
-
-                $isLogin = true;
-            } else {
-                $user = new User;
-
-                $user->fill($data);
-
-                $user->code = $code;
-                $user->name = $data['name'] ?? 'Usuário';
-                $user->type = $data['type'] ?? UserTypeEnum::PROVIDER->value;
-
-                $user->save();
-            }
-
-            if (! empty($data['email'])) {
-                $this->emailService->sendVerificationCode(
-                    email: $data['email'],
-                    code: $code,
-                    recipientName: $data['name'] ?? '',
-                );
-            } elseif (! empty($data['phone'])) {
-                Log::info('SMS: envio de código por telefone ainda não implementado.', [
-                    'phone' => $data['phone'],
-                ]);
-            }
-
-            DB::commit();
-
-            return $isLogin;
-        } catch (\Exception $e) {
-            DB::rollBack();
-
-            Log::error('Erro ao enviar código de verificação.', [
-                'error' => $e->getMessage(),
-                'data'  => $data,
-            ]);
-
-            return false;
-        }
-    }
-
-    public function validateCodeClient(array $data, bool $isLogin): bool|array
-    {
-        $email = $data['email'] ?? null;
-        $phone = $data['phone'] ?? null;
-        $code  = $data['code'] ?? '';
-
-        $user = User::where(function ($query) use ($email, $phone) {
-            $query->when($email, fn ($q) => $q->where('email', $email))
-                ->when($phone, fn ($q) => $q->where('phone', $phone));
+      $user = User::where(function ($query) use ($data) {
+        $query->when(!empty($data['email']), function ($q) use ($data) {
+          $q->where('email', $data['email']);
         })
-            ->where('code', $code)
-            ->first();
-
-        if (! $user) {
-            return false;
+          ->when(!empty($data['phone']), function ($q) use ($data) {
+            $q->where('phone', $data['phone']);
+          });
+      })
+        ->first();
+      $isLogin = false;
+      if ($user) {
+        if ($user->type->value !== UserTypeEnum::PROVIDER->value) {
+          DB::rollBack();
+          return ['error' => 'wrong_user_type'];
         }
-
-        if ($isLogin) {
-            return $this->loginWithEmail($user->email, $code);
+        $provider = Provider::where('user_id', $user->id)->first();
+        if($provider && $provider->approval_status->value !== ApprovalStatusEnum::ACCEPTED->value) {
+          DB::rollBack();
+          return ['error' => 'provider_not_accepted'];
         }
-
-        return true;
+         
+        $user->code = $code;
+        $user->validated_code = false;
+        $user->save();
+        $isLogin = true;
+      } else {
+        $user = new User();
+        $user->fill($data);
+        $user->code = $code;
+        $user->name = $data['name'] ?? 'Usuário';
+        $user->type = $data['type'] ?? UserTypeEnum::PROVIDER->value;
+        $user->save();
+      }
+
+      if (!empty($data['email'])) {
+        $this->emailService->sendVerificationCode(
+          email: $data['email'],
+          code: $code,
+          recipientName: $data['name'] ?? '',
+        );
+      } elseif (!empty($data['phone'])) {
+        Log::info('SMS: envio de código por telefone ainda não implementado.', [
+          'phone' => $data['phone'],
+        ]);
+      }
+
+      DB::commit();
+      return $isLogin;
+    } catch (\Exception $e) {
+      DB::rollBack();
+      Log::error('Erro ao enviar código de verificação.', [
+        'error' => $e->getMessage(),
+        'data' => $data,
+      ]);
+      return false;
+    }
+  }
+
+  public function validateCodeClient(array $data, bool $isLogin): bool|array
+  {
+    $email = $data['email'] ?? null;
+    $phone = $data['phone'] ?? null;
+    $code  = $data['code'] ?? '';
+
+    $user = User::where(function ($query) use ($email, $phone) {
+        $query->when($email, fn($q) => $q->where('email', $email))
+              ->when($phone,  fn($q) => $q->where('phone', $phone));
+      })
+      ->where('code', $code)
+      ->first();
+
+    if (!$user) {
+      return false;
     }
 
-    public function validateCodeProvider(array $data, bool $isLogin): bool|array
-    {
-        $email = $data['email'] ?? null;
-        $phone = $data['phone'] ?? null;
-        $code  = $data['code'] ?? '';
-
-        $user = User::where(function ($query) use ($email, $phone) {
-            $query->when($email, fn ($q) => $q->where('email', $email))
-                ->when($phone, fn ($q) => $q->where('phone', $phone));
-        })
-            ->where('code', $code)
-            ->first();
-
-        if (! $user) {
-            return false;
-        }
-
-        if ($isLogin) {
-            $user->load('provider');
-
-            $provider = $user->provider ?? null;
+    if ($isLogin) {
+      return $this->loginWithEmail($user->email, $code);
+    }
 
-            if ($provider && $provider->approval_status === ApprovalStatusEnum::PENDING->value) {
-                return ['error' => 'provider_pending'];
-            }
+    return true;
+  }
 
-            if ($provider && $provider->approval_status === ApprovalStatusEnum::REJECTED->value) {
-                return ['error' => 'provider_rejected'];
-            }
+  public function validateCodeProvider(array $data, bool $isLogin): bool|array
+  {
+    $email = $data['email'] ?? null;
+    $phone = $data['phone'] ?? null;
+    $code  = $data['code'] ?? '';
 
-            return $this->loginWithEmail($user->email, $code);
-        }
+    $user = User::where(function ($query) use ($email, $phone) {
+        $query->when($email, fn($q) => $q->where('email', $email))
+              ->when($phone,  fn($q) => $q->where('phone', $phone));
+      })
+      ->where('code', $code)
+      ->first();
 
-        return true;
+    if (!$user) {
+      return false;
     }
 
-    public function validateCode(array $data, bool $isLogin): bool|array
-    {
-        $email = $data['email'] ?? null;
-        $phone = $data['phone'] ?? null;
-        $code  = $data['code'] ?? '';
+    if ($isLogin) {
+      $user->load('provider');
+      $provider = $user->provider ?? null;
 
-        $user = User::where(function ($query) use ($email, $phone) {
-            $query->when($email, function ($q) use ($email) {
-                $q->where('email', $email);
-            })
-                ->when($phone, function ($q) use ($phone) {
-                    $q->where('phone', $phone);
-                });
-        })
-            ->where('code', $code)
-            ->first();
-
-        if (! $user) {
-            return false;
-        }
+      if ($provider && $provider->approval_status === ApprovalStatusEnum::PENDING->value) {
+        return ['error' => 'provider_pending'];
+      }
 
-        if ($isLogin) {
-            $resultLogin = $this->loginWithEmail($user->email, $code);
+      if ($provider && $provider->approval_status === ApprovalStatusEnum::REJECTED->value) {
+        return ['error' => 'provider_rejected'];
+      }
 
-            return $resultLogin;
-        }
-
-        return true;
+      return $this->loginWithEmail($user->email, $code);
     }
 
-    public function loginWithEmail(string $email, string $code): ?array
-    {
-        $user = User::where('email', $email)
-            ->where('code', $code)
-            ->first();
-
-        if (! $user) {
-            return null;
-        }
-
-        $deviceId = Str::uuid()->toString();
+    return true;
+  }
+
+  public function validateCode(array $data, bool $isLogin): bool|array
+  {
+    $email = $data['email'] ?? null;
+    $phone = $data['phone'] ?? null;
+    $code = $data['code'] ?? '';
+
+    $user = User::where(function ($query) use ($email, $phone) {
+      $query->when($email, function ($q) use ($email) {
+        $q->where('email', $email);
+      })
+        ->when($phone, function ($q) use ($phone) {
+          $q->where('phone', $phone);
+        });
+    })
+      ->where('code', $code)
+      ->first();
 
-        $accessToken  = $user->createAccessTokenApp($deviceId);
-        $refreshToken = $user->createRefreshTokenApp($deviceId);
+    if (!$user) {
+      return false;
+    }
 
-        $user->validated_code = true;
+    if($isLogin) {
+      $resultLogin = $this->loginWithEmail($user->email, $code);
+      return $resultLogin;
+    }
 
-        $user->code = null;
+    return true;
+  }
 
-        $user->save();
+  public function loginWithEmail(string $email, string $code): ?array
+  {
+    $user = User::where('email', $email)
+      ->where('code', $code)
+      ->first();
 
-        return [
-            'payload' => [
-                'access_token' => $accessToken,
-                'user'         => $user,
-            ],
-            'refreshToken' => $refreshToken,
-        ];
+    if (!$user) {
+      return null;
     }
+    $deviceId = Str::uuid()->toString();
+    $accessToken = $user->createAccessTokenApp($deviceId);
+    $refreshToken = $user->createRefreshTokenApp($deviceId);
+    $user->validated_code = true;
+    $user->code = null;
+    $user->save();
+
+    return [
+      "payload" => [
+        "access_token" => $accessToken,
+        "user" => $user,
+      ],
+      "refreshToken" => $refreshToken,
+    ];
+  }
 }

+ 19 - 1
app/Services/CustomScheduleService.php

@@ -2,6 +2,7 @@
 
 namespace App\Services;
 
+use App\Models\Address;
 use App\Models\CustomSchedule;
 use App\Models\CustomScheduleSpeciality;
 use App\Models\Provider;
@@ -9,6 +10,7 @@ use App\Models\Schedule;
 use App\Models\ScheduleProposal;
 use App\Models\ScheduleRefuse;
 use App\Rules\ScheduleBusinessRules;
+use App\Services\DistanceService;
 use Carbon\Carbon;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
@@ -240,9 +242,16 @@ class CustomScheduleService
 
     public function getAvailableOpportunities($providerId)
     {
-
         $provider = Provider::find($providerId);
 
+        $providerAddress = Address::where('source', 'provider')
+            ->where('source_id', $providerId)
+            ->orderBy('is_primary', 'desc')
+            ->first();
+
+        $providerLat = $providerAddress?->latitude !== null ? (float) $providerAddress->latitude : null;
+        $providerLng = $providerAddress?->longitude !== null ? (float) $providerAddress->longitude : null;
+
         $opportunities = Schedule::with([
             'client.user',
             'address',
@@ -285,6 +294,15 @@ class CustomScheduleService
             }
         });
 
+        $availableOpportunities->each(function ($opportunity) use ($providerLat, $providerLng) {
+            $opportunity->distance_km = DistanceService::calculate(
+                $providerLat,
+                $providerLng,
+                $opportunity->address?->latitude !== null ? (float) $opportunity->address->latitude : null,
+                $opportunity->address?->longitude !== null ? (float) $opportunity->address->longitude : null,
+            );
+        });
+
         return $availableOpportunities->values();
     }
 

+ 39 - 24
app/Services/DashboardService.php

@@ -13,6 +13,7 @@ use App\Models\Schedule;
 use App\Models\ScheduleProposal;
 use App\Models\Speciality;
 use App\Rules\ScheduleBusinessRules;
+use App\Services\DistanceService;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\DB;
 
@@ -191,10 +192,23 @@ class DashboardService
             ->orderBy('schedules.date', 'asc')
             ->get();
 
+        $proposalsDistanceSelect = DistanceService::sqlExpression(
+            $clientPrimaryAddress?->latitude !== null ? (float) $clientPrimaryAddress->latitude : null,
+            $clientPrimaryAddress?->longitude !== null ? (float) $clientPrimaryAddress->longitude : null,
+        );
+
         $schedulesProposals = ScheduleProposal::query()
             ->leftJoin('schedules', 'schedule_proposals.schedule_id', '=', 'schedules.id')
             ->leftJoin('providers', 'schedule_proposals.provider_id', '=', 'providers.id')
             ->leftJoin('users', 'providers.user_id', '=', 'users.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')
             ->where('schedules.client_id', $cliente->id)
             ->where('schedules.schedule_type', 'custom')
             ->where('schedules.status', 'pending')
@@ -217,6 +231,7 @@ class DashboardService
                 'providers.total_services',
 
                 'users.name as provider_name',
+                $proposalsDistanceSelect,
             ])
             ->get();
 
@@ -346,7 +361,7 @@ class DashboardService
             'your_price'    => $priceActual,
         ];
 
-        $solicitations = Schedule::with('address:district,source_id,source,id')
+        $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')
@@ -374,6 +389,18 @@ class DashboardService
             ->orderBy('schedules.date', 'asc')
             ->get();
 
+        $providerLat = $address?->latitude !== null ? (float) $address->latitude : null;
+        $providerLng = $address?->longitude !== null ? (float) $address->longitude : null;
+
+        $solicitations->each(function ($solicitation) use ($providerLat, $providerLng) {
+            $solicitation->distance_km = DistanceService::calculate(
+                $providerLat,
+                $providerLng,
+                $solicitation->address?->latitude !== null ? (float) $solicitation->address->latitude : null,
+                $solicitation->address?->longitude !== null ? (float) $solicitation->address->longitude : null,
+            );
+        });
+
         $todayServices = Schedule::with('address:district,address,number,source_id,source,id')
             ->where('schedules.provider_id', $provider->id)
             ->whereIn('schedules.status', ['accepted', 'paid', 'started', 'finished'])
@@ -407,7 +434,7 @@ class DashboardService
             ->orderBy('schedules.start_time', 'asc')
             ->get();
 
-        $nextSchedules = Schedule::with('address:district,address,number,source_id,source,id')
+        $nextSchedules = Schedule::with('address:district,address,number,source_id,source,id,latitude,longitude')
             ->where('schedules.provider_id', $provider->id)
             ->whereIn('schedules.status', ['accepted', 'paid'])
             ->whereDate('schedules.date', '>=', now()->toDateString())
@@ -430,6 +457,15 @@ class DashboardService
             ->orderBy('schedules.date', 'asc')
             ->get();
 
+        $nextSchedules->each(function ($schedule) use ($providerLat, $providerLng) {
+            $schedule->distance_km = DistanceService::calculate(
+                $providerLat,
+                $providerLng,
+                $schedule->address?->latitude !== null ? (float) $schedule->address->latitude : null,
+                $schedule->address?->longitude !== null ? (float) $schedule->address->longitude : null,
+            );
+        });
+
         // $opportunities = Schedule::with('address:district,source_id,source,id')
         //   ->where('schedules.schedule_type', 'custom')
         //   ->where('schedules.status', 'pending')
@@ -475,27 +511,6 @@ class DashboardService
 
     private function distanceSelect(?float $clientLatitude, ?float $clientLongitude): \Illuminate\Contracts\Database\Query\Expression
     {
-        if ($clientLatitude === null || $clientLongitude === null) {
-            return DB::raw('NULL as distance_km');
-        }
-
-        return DB::raw("
-            CASE
-                WHEN provider_address.latitude IS NOT NULL
-                AND provider_address.longitude IS NOT NULL
-                THEN ROUND((
-                    6371 * acos(
-                        least(1, greatest(-1,
-                            cos(radians({$clientLatitude}))
-                            * cos(radians(provider_address.latitude))
-                            * cos(radians(provider_address.longitude) - radians({$clientLongitude}))
-                            + sin(radians({$clientLatitude}))
-                            * sin(radians(provider_address.latitude))
-                        ))
-                    )
-                )::numeric, 1)
-                ELSE NULL
-            END as distance_km
-        ");
+        return DistanceService::sqlExpression($clientLatitude, $clientLongitude);
     }
 }

+ 61 - 0
app/Services/DistanceService.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace App\Services;
+
+use Illuminate\Contracts\Database\Query\Expression;
+use Illuminate\Support\Facades\DB;
+
+class DistanceService
+{
+    private const EARTH_RADIUS_KM = 6371;
+
+    public static function calculate(?float $lat1, ?float $lng1, ?float $lat2, ?float $lng2): ?float
+    {
+        if ($lat1 === null || $lng1 === null || $lat2 === null || $lng2 === null) {
+            return null;
+        }
+
+        $lat1Rad = deg2rad($lat1);
+        $lat2Rad = deg2rad($lat2);
+        $lng1Rad = deg2rad($lng1);
+        $lng2Rad = deg2rad($lng2);
+
+        $cosValue = cos($lat1Rad) * cos($lat2Rad) * cos($lng2Rad - $lng1Rad)
+            + sin($lat1Rad) * sin($lat2Rad);
+
+        $cosValue = min(1.0, max(-1.0, $cosValue));
+
+        return round(self::EARTH_RADIUS_KM * acos($cosValue), 1);
+    }
+
+    public static function sqlExpression(
+        ?float $clientLatitude,
+        ?float $clientLongitude,
+        string $targetLatCol = 'provider_address.latitude',
+        string $targetLngCol = 'provider_address.longitude',
+        string $alias = 'distance_km'
+    ): Expression {
+        if ($clientLatitude === null || $clientLongitude === null) {
+            return DB::raw("NULL as {$alias}");
+        }
+
+        return DB::raw("
+            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)
+                ELSE NULL
+            END as {$alias}
+        ");
+    }
+}

+ 202 - 216
app/Services/ProviderService.php

@@ -24,20 +24,20 @@ class ProviderService
         private readonly PagarmeRecipientService $pagarmeRecipientService,
     ) {}
 
-    public function getAll(): Collection
-    {
-        $providers = Provider::query()
-            ->with(['user', 'profileMedia'])
-            ->orderBy('created_at', 'desc')
-            ->get();
+  public function getAll(): Collection
+  {
+    $providers = Provider::query()
+      ->with(['user', 'profileMedia'])
+      ->orderBy('created_at', 'desc')
+      ->get();
 
-        return $providers;
-    }
+    return $providers;
+  }
 
-    public function findById(int $id): ?Provider
-    {
-        return Provider::with(['user', 'profileMedia'])->find($id);
-    }
+  public function findById(int $id): ?Provider
+  {
+    return Provider::with(['user', 'profileMedia'])->find($id);
+  }
 
     public function create(array $data): Provider
     {
@@ -52,13 +52,13 @@ class ProviderService
         });
     }
 
-    public function update(int $id, array $data): ?Provider
-    {
-        $model = $this->findById($id);
+  public function update(int $id, array $data): ?Provider
+  {
+    $model = $this->findById($id);
 
-        if (! $model) {
-            return null;
-        }
+    if (!$model) {
+      return null;
+    }
 
         $model->update($data);
 
@@ -78,226 +78,212 @@ class ProviderService
         return $provider->fresh(['user', 'profileMedia']);
     }
 
-    public function delete(int $id): bool
-    {
-        $model = $this->findById($id);
+  public function delete(int $id): bool
+  {
+    $model = $this->findById($id);
 
-        if (! $model) {
-            return false;
-        }
-
-        return $model->delete();
+    if (!$model) {
+      return false;
     }
 
-    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);
+    return $model->delete();
+  }
+
+  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
+  {
+    return DB::transaction(function () use ($id) {
+      $provider = Provider::findOrFail($id);
+      $provider->update(['approval_status' => ApprovalStatusEnum::ACCEPTED->value]);
+      return $provider->fresh(['user', 'profileMedia']);
+    });
+  }
+
+  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']);
+    });
+  }
+
+  public function register(array $data): ?array
+  {
+    try {
+      DB::beginTransaction();
+
+      $email = $data['email'] ?? null;
+      $phone = $data['phone'] ?? null;
+      $code = $data['code'] ?? null;
+
+      $user = User::query()
+        ->where('type', UserTypeEnum::PROVIDER->value)
+        ->where('code', $code)
+        ->where(function ($query) use ($email, $phone) {
+          if (!empty($email)) {
+            $query->orWhere('email', $email);
+          }
+
+          if (!empty($phone)) {
+            $query->orWhere('phone', $phone);
+          }
+        })
+        ->latest('id')
+        ->first();
+
+      if (!$user) {
+        throw new \Exception(__('messages.user_not_found_or_code_not_validated'));
+      }
+
+      $user->name = $data['name'];
+
+      if (empty($user->email) && !empty($email)) {
+        $user->email = $email;
+      }
+
+      if (empty($user->phone) && !empty($phone)) {
+        $user->phone = $phone;
+      }
+
+      $user->save();
+
+      $provider = new Provider();
+      $provider->user_id = $user->id;
+      $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->selfie_media_base64 = $data['selfie_base64'] ?? null;
+      $provider->document_front_media_base64 = $data['document_front_base64'] ?? null;
+      $provider->document_back_media_base64 = $data['document_back_base64'] ?? null;
+      $provider->save();
+      $provider->refresh();
+
+      $this->pagarmeRecipientService->createRecipientForProvider($provider, $data);
+      $this->createProviderAddress($provider->id, $data);
+      $this->createProviderServicesTypes($provider->id, $data);
+      $this->createProviderWorkingDays($provider->id, $data);
+
+      if (empty($user->email) || empty($user->code)) {
+        throw new \Exception(__('messages.user_not_found_or_code_not_validated'));
+      }
+
+      $result = $this->authService->loginWithEmail(
+        email: $user->email,
+        code: $user->code,
+      );
+
+      DB::commit();
+      return $result;
+    } catch (\Exception $e) {
+      DB::rollBack();
+      Log::error('Error registering provider: ' . $e->getMessage(), [
+        'data' => $data,
+      ]);
+      throw $e;
     }
+  }
 
-    public function approve(int $id): Provider
-    {
-        return DB::transaction(function () use ($id) {
-            $provider = Provider::findOrFail($id);
-
-            $provider->update(['approval_status' => ApprovalStatusEnum::ACCEPTED->value]);
+  private function createProviderAddress(int $providerId, array $data): void
+  {
+    $state = null;
+    $city = null;
 
-            return $provider->fresh(['user', 'profileMedia']);
-        });
+    if (!empty($data['state'])) {
+      $state = State::query()
+        ->whereRaw('LOWER(code) = ?', [mb_strtolower($data['state'])])
+        ->first();
     }
 
-    public function reject(int $id): Provider
-    {
-        return DB::transaction(function () use ($id) {
-            $provider = Provider::findOrFail($id);
+    if (!empty($data['city'])) {
+      $cityQuery = City::query()
+        ->whereRaw('LOWER(name) = ?', [mb_strtolower($data['city'])]);
 
-            $provider->update(['approval_status' => ApprovalStatusEnum::REJECTED->value]);
+      if ($state) {
+        $cityQuery->where('state_id', $state->id);
+      }
 
-            return $provider->fresh(['user', 'profileMedia']);
-        });
+      $city = $cityQuery->first();
     }
 
-    public function register(array $data): ?array
-    {
-        try {
-            DB::beginTransaction();
-
-            $email = $data['email'] ?? null;
-            $phone = $data['phone'] ?? null;
-            $code  = $data['code'] ?? null;
-
-            $user = User::query()
-                ->where('type', UserTypeEnum::PROVIDER->value)
-                ->where('code', $code)
-                ->where(function ($query) use ($email, $phone) {
-                    if (! empty($email)) {
-                        $query->orWhere('email', $email);
-                    }
-
-                    if (! empty($phone)) {
-                        $query->orWhere('phone', $phone);
-                    }
-                })
-                ->latest('id')
-                ->first();
-
-            if (! $user) {
-                throw new \Exception(__('messages.user_not_found_or_code_not_validated'));
-            }
-
-            $user->name = $data['name'];
-
-            if (empty($user->email) && ! empty($email)) {
-                $user->email = $email;
-            }
-
-            if (empty($user->phone) && ! empty($phone)) {
-                $user->phone = $phone;
-            }
-
-            $user->save();
-
-            $provider = new Provider;
-
-            $provider->user_id    = $user->id;
-            $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->selfie_media_base64         = $data['selfie_base64'] ?? null;
-            $provider->document_front_media_base64 = $data['document_front_base64'] ?? null;
-            $provider->document_back_media_base64  = $data['document_back_base64'] ?? null;
-
-            $provider->save();
-            $provider->refresh();
-
-            $this->pagarmeRecipientService->createRecipientForProvider($provider, $data);
-            $this->createProviderAddress($provider->id, $data);
-            $this->createProviderServicesTypes($provider->id, $data);
-            $this->createProviderWorkingDays($provider->id, $data);
-
-            if (empty($user->email) || empty($user->code)) {
-                throw new \Exception(__('messages.user_not_found_or_code_not_validated'));
-            }
-
-            $result = $this->authService->loginWithEmail(
-                email: $user->email,
-                code: $user->code,
-            );
-
-            DB::commit();
-
-            return $result;
-        } catch (\Exception $e) {
-            DB::rollBack();
-            Log::error('Error registering provider: '.$e->getMessage(), [
-                'data' => $data,
-            ]);
-            throw $e;
-        }
+    $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->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->save();
+  }
+
+  private function createProviderServicesTypes(int $providerId, array $data): void
+  {
+    $serviceTypeIds = $data['services_types_ids'] ?? $data['service_types_ids'] ?? [];
+
+    $uniqueIds = array_values(array_unique(array_map('intval', $serviceTypeIds)));
+
+    foreach ($uniqueIds as $serviceTypeId) {
+      ProviderServicesType::create([
+        'provider_id' => $providerId,
+        'service_type_id' => $serviceTypeId,
+      ]);
     }
+  }
 
-    private function createProviderAddress(int $providerId, array $data): void
-    {
-        $state = null;
-        $city  = null;
+  private function createProviderWorkingDays(int $providerId, array $data): void
+  {
+    $workingDays = $data['working_days'] ?? [];
+    $seen = [];
 
-        if (! empty($data['state'])) {
-            $state = State::query()
-                ->whereRaw('LOWER(code) = ?', [mb_strtolower($data['state'])])
-                ->first();
-        }
+    foreach ($workingDays as $workingDay) {
+      $day = (int) ($workingDay['day'] ?? -1);
+      $period = $workingDay['period'] ?? null;
 
-        if (! empty($data['city'])) {
-            $cityQuery = City::query()
-                ->whereRaw('LOWER(name) = ?', [mb_strtolower($data['city'])]);
+      if ($day < 0 || $day > 6 || !in_array($period, ['morning', 'afternoon'], true)) {
+        continue;
+      }
 
-            if ($state) {
-                $cityQuery->where('state_id', $state->id);
-            }
+      $uniqueKey = $day . '-' . $period;
+      if (isset($seen[$uniqueKey])) {
+        continue;
+      }
 
-            $city = $cityQuery->first();
-        }
+      $seen[$uniqueKey] = true;
 
-        $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->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->save();
+      ProviderWorkingDay::create([
+        'provider_id' => $providerId,
+        'day' => $day,
+        'period' => $period,
+      ]);
     }
+  }
 
-    private function createProviderServicesTypes(int $providerId, array $data): void
-    {
-        $serviceTypeIds = $data['services_types_ids'] ?? $data['service_types_ids'] ?? [];
-
-        $uniqueIds = array_values(array_unique(array_map('intval', $serviceTypeIds)));
-
-        foreach ($uniqueIds as $serviceTypeId) {
-            ProviderServicesType::create([
-                'provider_id'     => $providerId,
-                'service_type_id' => $serviceTypeId,
-            ]);
-        }
+  private function sanitizeDigits(?string $value): ?string
+  {
+    if ($value === null) {
+      return null;
     }
 
-    private function createProviderWorkingDays(int $providerId, array $data): void
-    {
-        $workingDays = $data['working_days'] ?? [];
-
-        $seen = [];
+    $digits = preg_replace('/\D+/', '', $value);
 
-        foreach ($workingDays as $workingDay) {
-            $day    = (int) ($workingDay['day'] ?? -1);
-            $period = $workingDay['period'] ?? null;
-
-            if ($day < 0 || $day > 6 || ! in_array($period, ['morning', 'afternoon'], true)) {
-                continue;
-            }
-
-            $uniqueKey = $day.'-'.$period;
-
-            if (isset($seen[$uniqueKey])) {
-                continue;
-            }
-
-            $seen[$uniqueKey] = true;
-
-            ProviderWorkingDay::create([
-                'provider_id' => $providerId,
-                'day'         => $day,
-                'period'      => $period,
-            ]);
-        }
-    }
-
-    private function sanitizeDigits(?string $value): ?string
-    {
-        if ($value === null) {
-            return null;
-        }
-
-        $digits = preg_replace('/\D+/', '', $value);
-
-        return $digits === '' ? null : $digits;
-    }
+    return $digits === '' ? null : $digits;
+  }
 }

+ 2 - 22
app/Services/SearchService.php

@@ -6,6 +6,7 @@ use App\Models\Address;
 use App\Models\Client;
 use App\Models\Provider;
 use App\Rules\ScheduleBusinessRules;
+use App\Services\DistanceService;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\DB;
 
@@ -103,27 +104,6 @@ class SearchService
 
     private function distanceSelect(?float $clientLatitude, ?float $clientLongitude): \Illuminate\Contracts\Database\Query\Expression
     {
-        if ($clientLatitude === null || $clientLongitude === null) {
-            return DB::raw('NULL as distance_km');
-        }
-
-        return DB::raw("
-            CASE
-                WHEN provider_address.latitude IS NOT NULL
-                AND provider_address.longitude IS NOT NULL
-                THEN ROUND((
-                    6371 * acos(
-                        least(1, greatest(-1,
-                            cos(radians({$clientLatitude}))
-                            * cos(radians(provider_address.latitude))
-                            * cos(radians(provider_address.longitude) - radians({$clientLongitude}))
-                            + sin(radians({$clientLatitude}))
-                            * sin(radians(provider_address.latitude))
-                        ))
-                    )
-                )::numeric, 1)
-                ELSE NULL
-            END as distance_km
-        ");
+        return DistanceService::sqlExpression($clientLatitude, $clientLongitude);
     }
 }

+ 10 - 10
lang/en/auth.php

@@ -13,17 +13,17 @@ return [
     |
     */
 
-    'failed'             => 'Invalid credentials',
-    'password'           => 'The provided password is incorrect.',
-    'throttle'           => 'Too many login attempts. Please try again in :seconds seconds.',
-    'logout'             => 'Logged out successfully',
-    'logged_in'          => 'Logged in successfully',
+    'failed' => 'Invalid credentials',
+    'password' => 'The provided password is incorrect.',
+    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
+    'logout' => 'Logged out successfully',
+    'logged_in' => 'Logged in successfully',
     'already_logged_out' => 'User already logged out',
-    'unauthorized'       => 'Unauthorized',
-    'session_expired'    => 'Session expired',
-    'invalid_code'       => 'Invalid code',
-    'valid_code'         => 'The provided code is valid.',
-    'wrong_user_type'    => 'This email cannot access this application. Please contact support for more information.',
+    'unauthorized' => 'Unauthorized',
+    'session_expired' => 'Session expired',
+    'invalid_code' => 'Invalid code',
+    'valid_code' => 'The provided code is valid.',
+    'wrong_user_type' => 'This email cannot access this application. Please contact support for more information.',
     // 'provider_pending' => 'Your registration is awaiting approval. We will contact you soon.',
     // 'provider_rejected' => 'Your registration was rejected. Please contact support.',
     'provider_not_accepted' => 'Your registration has not been approved yet. Please wait or contact support for more information.',

+ 10 - 10
lang/en/messages.php

@@ -1,16 +1,16 @@
 <?php
 
 return [
-    'welcome'                              => 'Welcome to our application',
-    'created'                              => 'Created successfully',
-    'updated'                              => 'Updated successfully',
-    'deleted'                              => 'Deleted successfully',
-    'email_sent'                           => 'Email sent successfully',
-    'email_not_sent'                       => 'Email not sent',
-    'imported'                             => 'Imported successfully',
-    'import_error'                         => 'Error importing',
-    'buyer_not_allowed'                    => 'Buyer not allowed',
-    'code_sent'                            => 'Verification code sent successfully',
+    'welcome' => 'Welcome to our application',
+    'created' => 'Created successfully',
+    'updated' => 'Updated successfully',
+    'deleted' => 'Deleted successfully',
+    'email_sent' => 'Email sent successfully',
+    'email_not_sent' => 'Email not sent',
+    'imported' => 'Imported successfully',
+    'import_error' => 'Error importing',
+    'buyer_not_allowed' => 'Buyer not allowed',
+    'code_sent' => 'Verification code sent successfully',
     'user_not_found_or_code_not_validated' => 'User not found or invalid code.',
     'provider_approved'                    => 'Provider approved successfully.',
     'provider_rejected'                    => 'Provider rejected.',

+ 10 - 10
lang/es/auth.php

@@ -13,17 +13,17 @@ return [
     |
     */
 
-    'failed'             => 'Estas credenciales no coinciden con nuestros registros.',
-    'password'           => 'La contraseña proporcionada es incorrecta.',
-    'throttle'           => 'Demasiados intentos de acceso. Por favor, inténtelo de nuevo en :seconds segundos.',
-    'logout'             => 'Cerró sesión correctamente',
-    'logged_in'          => 'Inició sesión correctamente',
+    'failed' => 'Estas credenciales no coinciden con nuestros registros.',
+    'password' => 'La contraseña proporcionada es incorrecta.',
+    'throttle' => 'Demasiados intentos de acceso. Por favor, inténtelo de nuevo en :seconds segundos.',
+    'logout' => 'Cerró sesión correctamente',
+    'logged_in' => 'Inició sesión correctamente',
     'already_logged_out' => 'El usuario ya ha cerrado sesión',
-    'unauthorized'       => 'No autorizado',
-    'session_expired'    => 'Sesión caducada',
-    'invalid_code'       => 'Código inválido',
-    'valid_code'         => 'El código proporcionado es válido.',
-    'wrong_user_type'    => 'Este correo electrónico no puede acceder a esta aplicación. Contacte al soporte para más información.',
+    'unauthorized' => 'No autorizado',
+    'session_expired' => 'Sesión caducada',
+    'invalid_code' => 'Código inválido',
+    'valid_code' => 'El código proporcionado es válido.',
+    'wrong_user_type' => 'Este correo electrónico no puede acceder a esta aplicación. Contacte al soporte para más información.',
     // 'provider_pending' => 'Su registro está pendiente de aprobación. Nos pondremos en contacto pronto.',
     // 'provider_rejected' => 'Su registro fue rechazado. Póngase en contacto con el soporte.',
     'provider_not_accepted' => 'Su registro aún no ha sido aprobado. Por favor, espere o póngase en contacto con el soporte para más información.',

+ 10 - 10
lang/es/messages.php

@@ -1,16 +1,16 @@
 <?php
 
 return [
-    'welcome'                              => 'Bienvenido a nuestra aplicación',
-    'created'                              => 'Creado exitosamente',
-    'updated'                              => 'Actualizado exitosamente',
-    'deleted'                              => 'Eliminado exitosamente',
-    'email_sent'                           => 'Email enviado exitosamente',
-    'email_not_sent'                       => 'Email no enviado',
-    'imported'                             => 'Importado exitosamente',
-    'import_error'                         => 'Error al importar',
-    'buyer_not_allowed'                    => 'Comprador no permitido',
-    'code_sent'                            => 'Código de verificación enviado exitosamente',
+    'welcome' => 'Bienvenido a nuestra aplicación',
+    'created' => 'Creado exitosamente',
+    'updated' => 'Actualizado exitosamente',
+    'deleted' => 'Eliminado exitosamente',
+    'email_sent' => 'Email enviado exitosamente',
+    'email_not_sent' => 'Email no enviado',
+    'imported' => 'Importado exitosamente',
+    'import_error' => 'Error al importar',
+    'buyer_not_allowed' => 'Comprador no permitido',
+    'code_sent' => 'Código de verificación enviado exitosamente',
     'user_not_found_or_code_not_validated' => 'Usuario no encontrado o código inválido.',
     'provider_approved'                    => 'Prestador aprobado exitosamente.',
     'provider_rejected'                    => 'Prestador rechazado.',

+ 10 - 10
lang/pt/auth.php

@@ -13,17 +13,17 @@ return [
     |
     */
 
-    'failed'             => 'Essas credenciais não correspondem aos nossos registros.',
-    'password'           => 'A senha fornecida está incorreta.',
-    'throttle'           => 'Muitas tentativas de login. Por favor, tente novamente em :seconds segundos.',
-    'logout'             => 'Desconectado com sucesso',
-    'logged_in'          => 'Conectado com sucesso',
+    'failed' => 'Essas credenciais não correspondem aos nossos registros.',
+    'password' => 'A senha fornecida está incorreta.',
+    'throttle' => 'Muitas tentativas de login. Por favor, tente novamente em :seconds segundos.',
+    'logout' => 'Desconectado com sucesso',
+    'logged_in' => 'Conectado com sucesso',
     'already_logged_out' => 'Usuário já desconectado',
-    'unauthorized'       => 'Não autorizado',
-    'session_expired'    => 'Sessão expirada',
-    'invalid_code'       => 'Código inválido',
-    'valid_code'         => 'O código fornecido é válido.',
-    'wrong_user_type'    => 'Este e-mail não pode acessar esse aplicativo. Entre em contato com o suporte para mais informações.',
+    'unauthorized' => 'Não autorizado',
+    'session_expired' => 'Sessão expirada',
+    'invalid_code' => 'Código inválido',
+    'valid_code' => 'O código fornecido é válido.',
+    'wrong_user_type' => 'Este e-mail não pode acessar esse aplicativo. Entre em contato com o suporte para mais informações.',
     // 'provider_pending' => 'Seu cadastro está aguardando aprovação. Em breve entraremos em contato.',
     // 'provider_rejected' => 'Seu cadastro foi recusado. Entre em contato com o suporte.',
     'provider_not_accepted' => 'Seu cadastro ainda não foi aprovado. Por favor, aguarde ou entre em contato com o suporte para mais informações.',

+ 10 - 10
lang/pt/messages.php

@@ -1,16 +1,16 @@
 <?php
 
 return [
-    'welcome'                              => 'Bem-vindo à nossa aplicação',
-    'created'                              => 'Criado com sucesso',
-    'updated'                              => 'Atualizado com sucesso',
-    'deleted'                              => 'Excluído com sucesso',
-    'email_sent'                           => 'Email enviado com sucesso',
-    'email_not_sent'                       => 'Email não enviado',
-    'imported'                             => 'Importado com sucesso',
-    'import_error'                         => 'Erro ao importar',
-    'buyer_not_allowed'                    => 'Compra não permitida, tente outro ingresso ou comprador',
-    'code_sent'                            => 'Código de verificação enviado com sucesso',
+    'welcome' => 'Bem-vindo à nossa aplicação',
+    'created' => 'Criado com sucesso',
+    'updated' => 'Atualizado com sucesso',
+    'deleted' => 'Excluído com sucesso',
+    'email_sent' => 'Email enviado com sucesso',
+    'email_not_sent' => 'Email não enviado',
+    'imported' => 'Importado com sucesso',
+    'import_error' => 'Erro ao importar',
+    'buyer_not_allowed' => 'Compra não permitida, tente outro ingresso ou comprador',
+    'code_sent' => 'Código de verificação enviado com sucesso',
     'user_not_found_or_code_not_validated' => 'Usuário não encontrado ou código inválido.',
     'provider_approved'                    => 'Prestador aprovado com sucesso.',
     'provider_rejected'                    => 'Prestador recusado.',

+ 2 - 2
routes/authRoutes/provider.php

@@ -1,9 +1,9 @@
 <?php
 
-use App\Http\Controllers\ProviderController;
 use Illuminate\Support\Facades\Route;
+use App\Http\Controllers\ProviderController;
 
-Route::get('/provider/pending',        [ProviderController::class, 'pending'])->middleware('permission:config.provider,view');
+Route::get('/provider/pending', [ProviderController::class, 'pending'])->middleware('permission:config.provider,view');
 Route::patch('/provider/{id}/approve', [ProviderController::class, 'approve'])->middleware('permission:config.provider,edit');
 Route::patch('/provider/{id}/reject',  [ProviderController::class, 'reject'])->middleware('permission:config.provider,edit');
 Route::get('/provider',                [ProviderController::class, 'index'])->middleware('permission:config.provider,view');

+ 10 - 10
routes/noAuthRoutes/auth.php

@@ -1,21 +1,21 @@
 <?php
 
+use Illuminate\Support\Facades\Route;
 use App\Http\Controllers\AuthController;
 use App\Http\Controllers\ClientController;
 use App\Http\Controllers\ProviderController;
-use Illuminate\Support\Facades\Route;
 
 // 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']);