Explorar o código

✨ feat(avaliacao): implementar fluxo de avaliação pós-agendamento

- Adicionados flags client_reviewed e provider_reviewed no DashboardService
- ReviewService: lógica de bloqueio e favorito em requisição única
- ReviewService: checagem de duplicidade antes de criar bloqueios
- ImprovementTypeService: incluir tipos 'both' na filtragem por origin
- Adicionado ImprovementTypeSeeder com tipos client, provider e both
- UserTypePermissionSeeder: corrigido para updateOrCreate por user_type+permission_id
- Adicionadas permissões de review e improvement_type para CLIENT e PROVIDER

Fase: dev | Origin: ajuste-cliente
Gustavo Zanatta hai 4 días
pai
achega
d1a7cd27d3

+ 3 - 2
app/Http/Requests/ReviewRequest.php

@@ -24,8 +24,9 @@ class ReviewRequest extends FormRequest
             'comment'     => ['nullable', 'string'],
             'improvements_ids' => ['nullable', 'array'],
             'improvements_ids.*' => ['integer', 'exists:improvement_types,id'],
-            'block_provider' => ['sometimes', 'boolean'],
-            'block_client'   => ['sometimes', 'boolean'],
+            'block_provider'    => ['sometimes', 'boolean'],
+            'block_client'      => ['sometimes', 'boolean'],
+            'favorite_provider' => ['sometimes', 'boolean'],
         ];
 
         if ($this->isMethod('POST')) {

+ 15 - 0
app/Services/DashboardService.php

@@ -204,6 +204,13 @@ class DashboardService
         'schedules.code_verified',
         'schedules.code',
         'media.url as provider_photo',
+        DB::raw("EXISTS(
+          SELECT 1 FROM reviews
+          WHERE reviews.schedule_id = schedules.id
+            AND reviews.origin = 'client'
+            AND reviews.origin_id = {$cliente->id}
+            AND reviews.deleted_at IS NULL
+        ) as client_reviewed"),
       )
       ->orderBy('schedules.start_time', 'asc')
       ->get();
@@ -331,6 +338,7 @@ class DashboardService
       ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
       ->select(
         'schedules.id',
+        'schedules.client_id',
         'client_user.name as client_name',
         'schedules.date',
         'schedules.start_time',
@@ -343,6 +351,13 @@ class DashboardService
         'schedules.code_verified',
         'schedules.code',
         'custom_schedules.offers_meal',
+        DB::raw("EXISTS(
+          SELECT 1 FROM reviews
+          WHERE reviews.schedule_id = schedules.id
+            AND reviews.origin = 'provider'
+            AND reviews.origin_id = {$provider->id}
+            AND reviews.deleted_at IS NULL
+        ) as provider_reviewed"),
       )
       ->orderBy('schedules.start_time', 'asc')
       ->get();

+ 1 - 1
app/Services/ImprovementTypeService.php

@@ -16,7 +16,7 @@ class ImprovementTypeService
                     $query->whereIn('improvement_type', ['client', 'provider', 'both']);
                 })
                     ->when($origin !== 'both', function ($query) use ($origin) {
-                        $query->where('improvement_type', $origin);
+                        $query->whereIn('improvement_type', [$origin, 'both']);
                     });
             })
             ->get();

+ 43 - 14
app/Services/ReviewService.php

@@ -3,6 +3,7 @@
 namespace App\Services;
 
 use App\Models\Client;
+use App\Models\ClientFavoriteProvider;
 use App\Models\ClientProviderBlock;
 use App\Models\Provider;
 use App\Models\ProviderClientBlock;
@@ -52,12 +53,12 @@ class ReviewService
           case 'client':
             $schedule = Schedule::find($data['schedule_id']);
             $provider = Provider::find($schedule->provider_id);
-            $provider->updateAverageRating();
+            $provider->updateAverageRating((float) $data['stars']);
             break;
           case 'provider':
             $schedule = Schedule::find($data['schedule_id']);
             $client = Client::find($schedule->client_id);
-            $client->updateAverageRating();
+            $client->updateAverageRating((float) $data['stars']);
             break;
         }
       }
@@ -66,23 +67,51 @@ class ReviewService
         $review->improvements()->sync($data['improvements_ids']);
       }
   
-      if($data['block_provider'] == true) {
+      if (!empty($data['block_provider'])) {
         $schedule = Schedule::find($data['schedule_id']);
+        $alreadyBlocked = ClientProviderBlock::where('client_id', $schedule->client_id)
+          ->where('provider_id', $schedule->provider_id)
+          ->whereNull('deleted_at')
+          ->exists();
 
-        $client_provider_block = new ClientProviderBlock();
-        $client_provider_block->client_id = $schedule->client_id;
-        $client_provider_block->provider_id = $schedule->provider_id;
-        $client_provider_block->save();
+        if (!$alreadyBlocked) {
+          ClientProviderBlock::create([
+            'client_id'   => $schedule->client_id,
+            'provider_id' => $schedule->provider_id,
+          ]);
+        }
       }
-  
-      if($data['block_client'] == true) {
+
+      if (!empty($data['block_client'])) {
         $schedule = Schedule::find($data['schedule_id']);
-        $provider_client_block = new ProviderClientBlock();
-        $provider_client_block->provider_id = $schedule->provider_id;
-        $provider_client_block->client_id = $schedule->client_id;
-        $provider_client_block->save();
+        $alreadyBlocked = ProviderClientBlock::where('provider_id', $schedule->provider_id)
+          ->where('client_id', $schedule->client_id)
+          ->whereNull('deleted_at')
+          ->exists();
+
+        if (!$alreadyBlocked) {
+          ProviderClientBlock::create([
+            'provider_id' => $schedule->provider_id,
+            'client_id'   => $schedule->client_id,
+          ]);
+        }
       }
-  
+
+      if (!empty($data['favorite_provider'])) {
+        $schedule = Schedule::find($data['schedule_id']);
+        $alreadyFavorited = ClientFavoriteProvider::where('client_id', $schedule->client_id)
+          ->where('provider_id', $schedule->provider_id)
+          ->whereNull('deleted_at')
+          ->exists();
+
+        if (!$alreadyFavorited) {
+          ClientFavoriteProvider::create([
+            'client_id'   => $schedule->client_id,
+            'provider_id' => $schedule->provider_id,
+          ]);
+        }
+      }
+
       DB::commit();
       return $review;
     } catch (Exception $e) {

+ 1 - 0
database/seeders/DatabaseSeeder.php

@@ -16,6 +16,7 @@ class DatabaseSeeder extends Seeder
             PermissionSeeder::class,
             UserTypePermissionSeeder::class,
             BrazilCitiesSeeder::class,
+            ImprovementTypeSeeder::class,
         ]);
     }
 }

+ 42 - 0
database/seeders/ImprovementTypeSeeder.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace Database\Seeders;
+
+use App\Models\ImprovementType;
+use Illuminate\Database\Seeder;
+
+class ImprovementTypeSeeder extends Seeder
+{
+    public function run(): void
+    {
+        $types = [
+            // Exibidos quando o cliente avalia o prestador
+            ['description' => 'Agilidade',                 'improvement_type' => 'client'],
+            ['description' => 'Qualidade',                 'improvement_type' => 'client'],
+            ['description' => 'Dedicação',                 'improvement_type' => 'client'],
+            ['description' => 'Pontualidade',              'improvement_type' => 'client'],
+            ['description' => 'Cuidado com itens pessoais','improvement_type' => 'client'],
+
+            // Exibidos quando o prestador avalia o cliente
+            ['description' => 'Ambiente',                  'improvement_type' => 'provider'],
+            ['description' => 'Respeito',                  'improvement_type' => 'provider'],
+            ['description' => 'Materiais de limpeza',      'improvement_type' => 'provider'],
+            ['description' => 'Comunicação',               'improvement_type' => 'provider'],
+            ['description' => 'Comportamento adequado',    'improvement_type' => 'provider'],
+
+            // Exibidos nos dois contextos
+            ['description' => 'Organização',               'improvement_type' => 'both'],
+            ['description' => 'Simpatia',                  'improvement_type' => 'both'],
+        ];
+
+        foreach ($types as $type) {
+            ImprovementType::firstOrCreate(
+                [
+                    'description'      => $type['description'],
+                    'improvement_type' => $type['improvement_type'],
+                ],
+                ['is_active' => true]
+            );
+        }
+    }
+}

+ 14 - 6
database/seeders/UserTypePermissionSeeder.php

@@ -72,6 +72,9 @@ class UserTypePermissionSeeder extends Seeder
             ['scope' => 'config.service_type', 'bits' => 1],
             ['scope' => 'config.schedule', 'bits' => 271],
             ['scope' => 'config.custom_schedule', 'bits' => 271],
+            ['scope' => 'config.improvement_type', 'bits' => 1],
+            ['scope' => 'config.review', 'bits' => 271],
+            ['scope' => 'config.provider_client_block', 'bits' => 9],
           ];
           $this->seedUserTypePermissions($providerPermissions, UserTypeEnum::PROVIDER->value);
           break;
@@ -87,7 +90,9 @@ class UserTypePermissionSeeder extends Seeder
             ['scope' => 'config.client_payment_method', 'bits' => 271],
             ['scope' => 'config.provider_working_day', 'bits' => 271],
             ['scope' => 'config.provider_blocked_day', 'bits' => 271],
+            ['scope' => 'config.improvement_type', 'bits' => 1],
             ['scope' => 'config.review', 'bits' => 271],
+            ['scope' => 'config.client_provider_block', 'bits' => 9],
             ['scope' => 'config.schedule', 'bits' => 271],
             ['scope' => 'config.custom_schedule', 'bits' => 271],
             ['scope' => 'config.speciality', 'bits' => 271],
@@ -104,12 +109,15 @@ class UserTypePermissionSeeder extends Seeder
     foreach ($permissions as $permissionData) {
       $permission = Permission::where('scope', $permissionData['scope'])->first();
       if ($permission) {
-        $userTypePermission = UserTypePermission::firstOrNew([
-          'user_type' => $userType,
-          'permission_id' => $permission->id,
-          'bits' => $permissionData['bits'],
-        ]);
-        $userTypePermission->save();
+        UserTypePermission::updateOrCreate(
+          [
+            'user_type' => $userType,
+            'permission_id' => $permission->id,
+          ],
+          [
+            'bits' => $permissionData['bits'],
+          ]
+        );
       }
     }
   }