Procházet zdrojové kódy

cadastro + login prestador, dashboard funcional

Gustavo Zanatta před 1 měsícem
rodič
revize
2833f415c5

+ 16 - 2
app/Http/Controllers/DashboardController.php

@@ -3,7 +3,8 @@
 namespace App\Http\Controllers;
 
 use App\Services\DashboardService;
-use App\Http\Resources\DashboardResource;
+use App\Http\Resources\DashboardClienteResource;
+use App\Http\Resources\DashboardPrestadorResource;
 use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Support\Facades\Log;
@@ -17,11 +18,24 @@ class DashboardController extends Controller
     try {
       $dados = $this->service->dadosDashboardCliente();
       return $this->successResponse(
-        payload: new DashboardResource($dados),
+        payload: new DashboardClienteResource($dados),
       );
     } catch (\Exception $e) {
       Log::error("Erro ao obter dados do dashboard do cliente: " . $e->getMessage());
       return $this->errorResponse(message: __("messages.error_fetching_data"), code: 500);
     }
   }
+
+  public function dadosDashboardPrestador(): JsonResponse
+  {
+    try {
+      $dados = $this->service->dadosDashboardPrestador();
+      return $this->successResponse(
+        payload: new DashboardPrestadorResource($dados),
+      );
+    } catch (\Exception $e) {
+      Log::error("Erro ao obter dados do dashboard do prestador: " . $e->getMessage());
+      return $this->errorResponse(message: __("messages.error_fetching_data"), code: 500);
+    }
+  }
 }

+ 2 - 3
app/Http/Controllers/ProviderController.php

@@ -4,10 +4,9 @@ namespace App\Http\Controllers;
 
 use App\Services\ProviderService;
 use App\Http\Requests\ProviderRequest;
-use App\Http\Requests\RegisterClientRequest;
+use App\Http\Requests\RegisterProviderRequest;
 use App\Http\Resources\ProviderResource;
 use Illuminate\Http\JsonResponse;
-use Illuminate\Support\Facades\Log;
 use App\Http\Resources\AuthResource;
 
 class ProviderController extends Controller
@@ -56,7 +55,7 @@ class ProviderController extends Controller
         );
     }
 
-    public function register(RegisterClientRequest $request): JsonResponse
+    public function register(RegisterProviderRequest $request): JsonResponse
     {
       $result = $this->service->register($request->validated());
       if (!$result) {

+ 75 - 0
app/Http/Requests/RegisterProviderRequest.php

@@ -0,0 +1,75 @@
+<?php
+
+namespace App\Http\Requests;
+
+use App\Enums\WorkingPeriodEnum;
+use Illuminate\Foundation\Http\FormRequest;
+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',
+
+      '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',
+
+      'is_approved' => 'sometimes|boolean',
+    ];
+
+    if (!$this->has('email')) {
+      $rules['phone'] = 'required|string|max:20';
+      $rules['email'] = 'nullable';
+    }
+
+    if (!$this->has('phone')) {
+      $rules['email'] = 'required|email';
+      $rules['phone'] = 'nullable';
+    }
+
+    return $rules;
+  }
+}

+ 1 - 1
app/Http/Resources/DashboardResource.php → app/Http/Resources/DashboardClienteResource.php

@@ -5,7 +5,7 @@ namespace App\Http\Resources;
 use Illuminate\Http\Request;
 use Illuminate\Http\Resources\Json\JsonResource;
 
-class DashboardResource extends JsonResource
+class DashboardClienteResource extends JsonResource
 {
   /**
    * Transform the resource into an array.

+ 26 - 0
app/Http/Resources/DashboardPrestadorResource.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Http\Resources;
+
+use Illuminate\Http\Request;
+use Illuminate\Http\Resources\Json\JsonResource;
+
+class DashboardPrestadorResource extends JsonResource
+{
+  /**
+   * Transform the resource into an array.
+   *
+   * @return array<string, mixed>
+   */
+  public function toArray(Request $request): array
+  {
+    return [
+      'headerBar' => $this['headerBar'],
+      'summaryInfos' => $this['summaryInfos'],
+      'priceSuggested' => $this['priceSuggested'],
+      'solicitations' => $this['solicitations'],
+      'nextSchedules' => $this['nextSchedules'],
+      'opportunities' => $this['opportunities'],
+    ];
+  }
+}

+ 116 - 0
app/Services/DashboardService.php

@@ -110,4 +110,120 @@ class DashboardService
       'providersClose' => $providersClose,
     ];
   }
+
+  public function dadosDashboardPrestador(): array
+  {
+    $user = Auth::user();
+    $provider = Provider::where('user_id', $user->id)->first();
+
+    $headerBar = [
+      'rating' => $provider->average_rating,
+      'total_ratings' => Review::where('reviews.origin', 'client')->leftJoin('schedules', 'schedules.id', '=', 'reviews.schedule_id')->where('schedules.provider_id', $provider->id)->count(),
+      'total_services' => $provider->total_services,
+    ];
+
+    $summaryInfos = [
+      'name' => $user->name,
+      'address' => Address::where('source', 'provider')->where('source_id', $provider->id)->first()->address ?? null,
+      'pending_services' => Schedule::where('provider_id', $provider->id)->where('status', 'pending')->count(),
+    ];
+
+    $priceSuggestedAvg = Provider::where('user_id', '!=', $user->id)
+      ->whereNotNull('daily_price_8h')
+      ->pluck('daily_price_8h')
+      ->avg();
+
+    $priceActual = $provider->daily_price_8h;
+
+    $priceSuggested = [
+      'average_price' => $priceSuggestedAvg,
+      'your_price' => $priceActual,
+    ];
+
+    $solicitations = Schedule::where('schedules.provider_id', $provider->id)
+      ->where('schedules.status', 'pending')
+      ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
+      ->leftJoin('users as client_user', 'client_user.id', '=', 'clients.user_id')
+      ->select(
+        // bairro
+        'schedules.id',
+        'client_user.name as client_name',
+        'clients.average_rating',
+        'schedules.date',
+        DB::raw("TO_CHAR(schedules.date, 'DD/MM/YYYY') as formatted_date"),
+        'schedules.start_time',
+        'schedules.end_time',
+        'schedules.total_amount',
+        'schedules.period_type',
+        'schedules.schedule_type',
+      )
+      ->orderBy('schedules.date', 'asc')
+      ->get();
+
+    $nextSchedules = Schedule::where('schedules.provider_id', $provider->id)
+      ->whereIn('schedules.status', ['accepted', 'paid'])
+      ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
+      ->leftJoin('users as client_user', 'client_user.id', '=', 'clients.user_id')
+      ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
+      ->select(
+        'schedules.id',
+        'client_user.name as client_name',
+        'schedules.date',
+        'schedules.start_time',
+        'schedules.end_time',
+        'schedules.total_amount',
+        'schedules.period_type',
+        'schedules.schedule_type',
+        'custom_schedules.offers_meal'
+        // bairro
+      )
+      ->orderBy('schedules.date', 'asc')
+      ->get();
+
+    $opportunities = Schedule::where('schedules.schedule_type', 'custom')
+      ->where('schedules.status', 'pending')
+      ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
+      ->leftJoin('users as client_user', 'client_user.id', '=', 'clients.user_id')
+      ->select(
+        'schedules.id',
+        'client_user.name as client_name',
+        'clients.average_rating',
+        'schedules.date',
+        'schedules.start_time',
+        'schedules.end_time',
+        'schedules.period_type',
+        'schedules.schedule_type',
+      )
+      ->orderBy('schedules.date', 'asc')
+      ->get();
+
+    $opportunities = $opportunities->map(function ($item) use ($provider) {
+      $price = 0;
+      switch ($item->period_type) {
+        case 2:
+          $price = $provider->daily_price_2h;
+          break;
+        case 4:
+          $price = $provider->daily_price_4h;
+          break;
+        case 6:
+          $price = $provider->daily_price_6h;
+          break;
+        case 8:
+          $price = $provider->daily_price_8h;
+          break;
+      }
+      $item->total_amount = $price;
+      return $item;
+    });
+
+    return [
+      'headerBar' => $headerBar,
+      'summaryInfos' => $summaryInfos,
+      'priceSuggested' => $priceSuggested,
+      'solicitations' => $solicitations,
+      'nextSchedules' => $nextSchedules,
+      'opportunities' => $opportunities,
+    ];
+  }
 }

+ 144 - 30
app/Services/ProviderService.php

@@ -2,9 +2,12 @@
 
 namespace App\Services;
 
+use App\Enums\UserTypeEnum;
 use App\Models\Address;
 use App\Models\City;
 use App\Models\Provider;
+use App\Models\ProviderServicesType;
+use App\Models\ProviderWorkingDay;
 use App\Models\State;
 use App\Models\User;
 use Illuminate\Database\Eloquent\Collection;
@@ -21,8 +24,9 @@ class ProviderService
   {
     $providers = Provider::query()
       ->with(['user', 'profileMedia'])
-      ->orderBy("created_at", "desc")
+      ->orderBy('created_at', 'desc')
       ->get();
+
     return $providers;
   }
 
@@ -59,51 +63,69 @@ class ProviderService
     return $model->delete();
   }
 
-    public function register(array $data): ?array
+  public function register(array $data): ?array
   {
     try {
       DB::beginTransaction();
-      $user = User::when($data['email'], function ($q) use ($data) {
-          $q->where('email', $data['email']);
-        })
-        ->when($data['phone'], function ($q) use ($data) {
-          $q->where('phone', $data['phone']);
+
+      $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);
+          }
         })
-        ->where('type', 'PROVIDER')
-        ->where('code', $data['code'])
-        ->where('validated_code', false)
+        ->latest('id')
         ->first();
 
-      if(!$user) {
+      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['document'];
-      $provider->document = $data['document'];
+      $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->is_approved = false;
+      $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();
 
-      $address = new Address();
-      $address->source = 'client';
-      $address->source_id = $provider->id;
-      $address->zip_code = $data['zip_code'] ?? null;
-      $address->address = $data['address'] ?? null;
-      $address->has_complement = $data['has_complement'] ?? null;
-      $address->nickname = $data['nickname'] ?? null;
-      $address->instructions = $data['instructions'] ?? null;
-      $address->address_type = $data['address_type'] ?? null;
-
+      $this->createProviderAddress($provider->id, $data);
+      $this->createProviderServicesTypes($provider->id, $data);
+      $this->createProviderWorkingDays($provider->id, $data);
 
-      $state = State::where('code', $data['state'])->first();
-      $city = City::where('name', $data['city'])->where('state_id', $state->id)->first();
-
-      $address->state_id = $state->id;
-      $address->city_id = $city->id;
-      $address->save();
+      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,
@@ -114,8 +136,100 @@ class ProviderService
       return $result;
     } catch (\Exception $e) {
       DB::rollBack();
-      Log::error("Error registering client: " . $e->getMessage());
+      Log::error('Error registering provider: ' . $e->getMessage(), [
+        'data' => $data,
+      ]);
       throw $e;
     }
   }
+
+  private function createProviderAddress(int $providerId, array $data): void
+  {
+    $state = null;
+    $city = null;
+
+    if (!empty($data['state'])) {
+      $state = State::query()
+        ->whereRaw('LOWER(code) = ?', [mb_strtolower($data['state'])])
+        ->first();
+    }
+
+    if (!empty($data['city'])) {
+      $cityQuery = City::query()
+        ->whereRaw('LOWER(name) = ?', [mb_strtolower($data['city'])]);
+
+      if ($state) {
+        $cityQuery->where('state_id', $state->id);
+      }
+
+      $city = $cityQuery->first();
+    }
+
+    $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 createProviderWorkingDays(int $providerId, array $data): void
+  {
+    $workingDays = $data['working_days'] ?? [];
+    $seen = [];
+
+    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;
+  }
 }

+ 28 - 0
database/migrations/2026_03_23_160000_add_base64_media_fields_to_providers_table.php

@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::table('providers', function (Blueprint $table) {
+            $table->longText('selfie_media_base64')->nullable();
+            $table->longText('document_front_media_base64')->nullable();
+            $table->longText('document_back_media_base64')->nullable();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('providers', function (Blueprint $table) {
+            $table->dropColumn([
+                'selfie_media_base64',
+                'document_front_media_base64',
+                'document_back_media_base64',
+            ]);
+        });
+    }
+};

+ 2 - 1
routes/authRoutes/dashboard.php

@@ -3,4 +3,5 @@
 use App\Http\Controllers\DashboardController;
 use Illuminate\Support\Facades\Route;
 
-Route::get('/dados-dashboard-cliente', [DashboardController::class, 'dadosDashboardCliente'])->middleware('permission:dashboard,view');
+Route::get('/dados-dashboard-cliente', [DashboardController::class, 'dadosDashboardCliente'])->middleware('permission:dashboard,view');
+Route::get('/dados-dashboard-prestador', [DashboardController::class, 'dadosDashboardPrestador'])->middleware('permission:dashboard,view');

+ 6 - 0
routes/noAuthRoutes/service_type.php

@@ -0,0 +1,6 @@
+<?php
+
+use Illuminate\Support\Facades\Route;
+use App\Http\Controllers\ServiceTypeController;
+
+Route::get('/public-service-types', [ServiceTypeController::class, 'index']);