Selaa lähdekoodia

feat: :sparkles: feat (modulo-de-notificação) Foi criado o modulo completo de notificação

Foi criado o backend completo de notificação contendo a nova tabela do banco de dados. Tambem adicionado os modulos de envio de notificação

Fase:dev | origin:escopo
kayo henrique 2 viikkoa sitten
vanhempi
commit
1f7ef05020

+ 36 - 0
app/Models/Notification.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class Notification extends Model
+{
+    use HasFactory, SoftDeletes;
+
+    protected $fillable = [
+        'title',
+        'description',
+        'origin',
+        'origin_id',
+        'type',
+        'read',
+        'read_at',
+        'user_id',
+    ];
+
+    protected $casts = [
+        'read' => 'boolean',
+        'read_at' => 'datetime',
+        'created_at' => 'datetime',
+        'updated_at' => 'datetime',
+        'deleted_at' => 'datetime',
+    ];
+
+    public function user()
+    {
+        return $this->belongsTo(User::class);
+    }
+}

+ 37 - 20
app/Services/DashboardService.php

@@ -12,6 +12,7 @@ use App\Models\Review;
 use App\Models\Schedule;
 use App\Models\ScheduleProposal;
 use App\Models\Speciality;
+use App\Models\Notification;
 use App\Rules\ScheduleBusinessRules;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\DB;
@@ -239,8 +240,26 @@ class DashboardService
             ->orderBy('schedules.start_time', 'asc')
             ->get();
 
+        $notifications = Notification::where('user_id', $user->id)
+            ->orderBy('read', 'asc')
+            ->orderBy('created_at', 'desc')
+            ->limit(10)
+            ->get()
+            ->map(function ($notification) {
+                return [
+                    'id' => $notification->id,
+                    'title' => $notification->title,
+                    'description' => $notification->description,
+                    'time' => $notification->created_at->diffForHumans(),
+                    'read' => $notification->read,
+                    'avatar' => '/icons/avatar.svg',
+                ];
+            });
+
         $hasPaymentMethods = ClientPaymentMethod::where('client_id', $cliente->id)->exists();
 
+
+
         return [
             'headerBar'           => $headerBar,
             'summaryInfos'        => $summaryInfos,
@@ -251,16 +270,7 @@ class DashboardService
             'providersClose'      => $providersClose,
             'todaySchedules'      => $todaySchedules,
             'schedulesProposals'  => $schedulesProposals,
-            'notifications' => [
-                [
-                    'id' => 1,
-                    'title' => 'Serviço agendado!',
-                    'description' => 'Diária confirmada com Marina, 04/11 das 09h30 às 17h30 esse e o cliente',
-                    'time' => 'Há 2 minutos',
-                    'read' => false,
-                    'avatar' => '/icons/avatar.svg'
-                ]
-            ],
+            'notifications' => $notifications,
             'has_payment_methods' => $hasPaymentMethods,
         ];
     }
@@ -453,6 +463,22 @@ class DashboardService
         //   ->orderBy('schedules.date', 'asc')
         //   ->get();
 
+        $notifications = Notification::where('user_id', $user->id)
+            ->orderBy('read', 'asc')
+            ->orderBy('created_at', 'desc')
+            ->limit(10)
+            ->get()
+            ->map(function ($notification) {
+                return [
+                    'id' => $notification->id,
+                    'title' => $notification->title,
+                    'description' => $notification->description,
+                    'time' => $notification->created_at->diffForHumans(),
+                    'read' => $notification->read,
+                    'avatar' => '/icons/avatar.svg',
+                ];
+            });
+
         $customScheduleService = new CustomScheduleService;
 
         $opportunities = $customScheduleService->getAvailableOpportunities($provider->id);
@@ -465,16 +491,7 @@ class DashboardService
             'todayServices'  => $todayServices,
             'nextSchedules'  => $nextSchedules,
             'opportunities'  => $opportunities,
-            'notifications' => [
-                [
-                    'id' => 1,
-                    'title' => 'Serviço agendado!',
-                    'description' => 'Diária confirmada com Marina, 04/11 das 09h30 às 17h30, esse e o prestador',
-                    'time' => 'Há 2 minutos',
-                    'read' => false,
-                    'avatar' => '/icons/avatar.svg'
-                ]
-            ],
+            'notifications' => $notifications,
         ];
     }
 }

+ 59 - 0
app/Services/NotificationService.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace App\Services;
+
+use App\Models\Notification;
+use Illuminate\Database\Eloquent\Collection;
+
+class NotificationService
+{
+    public function getByUser(int $userId): Collection
+    {
+        return Notification::where('user_id', $userId)
+            ->orderBy('read', 'asc')
+            ->orderBy('created_at', 'desc')
+            ->get();
+    }
+
+    public function findById(int $id): Notification
+    {
+        return Notification::findOrFail($id);
+    }
+
+    public function create(array $data): Notification
+    {
+        return Notification::create($data);
+    }
+
+    public function markAsRead(Notification $notification): Notification
+    {
+        $notification->update([
+            'read' => true,
+            'read_at' => now(),
+        ]);
+
+        return $notification;
+    }
+
+    public function markAllAsRead(int $userId): void
+    {
+        Notification::where('user_id', $userId)
+            ->where('read', false)
+            ->update([
+                'read' => true,
+                'read_at' => now(),
+            ]);
+    }
+
+    public function unreadCount(int $userId): int
+    {
+        return Notification::where('user_id', $userId)
+            ->where('read', false)
+            ->count();
+    }
+
+    public function delete(Notification $notification): void
+    {
+        $notification->delete();
+    }
+}

+ 22 - 3
app/Services/ScheduleService.php

@@ -6,6 +6,8 @@ use App\Jobs\StartScheduleJob;
 use App\Models\Provider;
 use App\Models\Schedule;
 use App\Rules\ScheduleBusinessRules;
+use App\Services\NotificationService;
+use App\Enums\NotificationTypeEnum;
 use Carbon\Carbon;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\DB;
@@ -266,12 +268,29 @@ class ScheduleService
                 case 'pending':
                     break;
                 case 'accepted':
+
+                    $notificationService = app(NotificationService::class);
+
+                    $notificationService->create([
+                        'title' => 'Agendamento aceito',
+
+                        'description' => 'Seu agendamento foi aceito pelo prestador.',
+
+                        'origin' => 'schedule',
+
+                        'origin_id' => $schedule->id,
+
+                        'type' => NotificationTypeEnum::SCHEDULE_ACCEPTED->value,
+
+                        'user_id' => $schedule->client->user_id,
+                    ]);
+
                     break;
                 case 'rejected':
                     break;
                 case 'paid':
                     $date_cleaned       = Carbon::parse($schedule->date)->format('Y-m-d');
-                    $date_time_dispatch = Carbon::parse($date_cleaned.' '.$schedule->start_time);
+                    $date_time_dispatch = Carbon::parse($date_cleaned . ' ' . $schedule->start_time);
 
                     // StartScheduleJob::dispatch($schedule->id)->delay($date_time_dispatch);
 
@@ -291,7 +310,7 @@ class ScheduleService
             return $schedule->fresh(['client.user', 'provider.user', 'address']);
         } catch (\Exception $e) {
             DB::rollBack();
-            Log::error('Erro ao atualizar status do agendamento: '.$e->getMessage());
+            Log::error('Erro ao atualizar status do agendamento: ' . $e->getMessage());
             throw new \Exception('Não foi possível atualizar o status do agendamento.');
         }
     }
@@ -320,7 +339,7 @@ class ScheduleService
             return $schedule->fresh(['client.user', 'provider.user', 'address']);
         } catch (\Exception $e) {
             DB::rollBack();
-            Log::error('Erro ao cancelar agendamento: '.$e->getMessage());
+            Log::error('Erro ao cancelar agendamento: ' . $e->getMessage());
             throw new \Exception('Não foi possível cancelar o agendamento.');
         }
     }

+ 41 - 0
database/migrations/2026_05_25_102711_create_notifications_table.php

@@ -0,0 +1,41 @@
+<?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::create('notifications', function (Blueprint $table) {
+
+            $table->id();
+
+            $table->string('title');
+
+            $table->text('description');
+
+            $table->string('origin');
+
+            $table->unsignedBigInteger('origin_id');
+
+            $table->string('type');
+
+            $table->boolean('read')->default(false);
+
+            $table->dateTime('read_at')->nullable();
+
+           $table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete();
+
+            $table->softDeletes();
+
+            $table->timestamps();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('notifications');
+    }
+};