feat: header con mensajes y notificaciones en tiempo real

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 14:14:46 -06:00
parent 9dd5ac6cbf
commit eaeedf2029
8 changed files with 298 additions and 149 deletions
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class NewNotification implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public int $userId,
public string $title,
public string $body,
public string $icon = 'fa-bell',
public string $color = 'primary',
public ?string $url = null,
) {}
public function broadcastOn(): array
{
return [new Channel('notifications.' . $this->userId)];
}
public function broadcastWith(): array
{
return [
'title' => $this->title,
'body' => $this->body,
'icon' => $this->icon,
'color' => $this->color,
'url' => $this->url,
'time' => now()->format('H:i'),
];
}
}
+18 -24
View File
@@ -1,36 +1,30 @@
<?php
namespace App\Providers;
use App\View\Composers\HeaderComposer;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Illuminate\Mail\Events\MessageSending;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
public function register(): void {}
public function boot(): void
{
//
View::composer('layouts._partials.header', HeaderComposer::class);
Event::listen(MessageSending::class, function ($event) {
$event->message->getHeaders()->addTextHeader(
'List-Unsubscribe',
'<mailto:bajas@tudominio.com>, <https://tudominio.com/unsubscribe>'
);
$event->message->getHeaders()->addTextHeader(
'List-Unsubscribe-Post',
'List-Unsubscribe=One-Click'
);
});
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::listen(MessageSending::class, function ($event) {
$event->message->getHeaders()->addTextHeader(
'List-Unsubscribe',
'<mailto:bajas@tudominio.com>, <https://tudominio.com/unsubscribe>'
);
$event->message->getHeaders()->addTextHeader(
'List-Unsubscribe-Post',
'List-Unsubscribe=One-Click'
);
});
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace App\View\Composers;
use App\Models\Message;
use Illuminate\View\View;
class HeaderComposer
{
public function compose(View $view): void
{
if (!auth()->check()) {
return;
}
$userId = auth()->id();
// Últimas conversaciones con mensajes no leídos
$unreadConversations = Message::where('receiver_id', $userId)
->whereNull('read_at')
->with('sender')
->orderBy('created_at', 'desc')
->get()
->unique('user_id')
->take(5);
$unreadMessagesCount = Message::where('receiver_id', $userId)
->whereNull('read_at')
->count();
// Notificaciones no leídas
$unreadNotifications = auth()->user()
->unreadNotifications()
->latest()
->take(5)
->get();
$unreadNotificationsCount = auth()->user()->unreadNotifications()->count();
$view->with([
'headerUnreadConversations' => $unreadConversations,
'headerUnreadMessagesCount' => $unreadMessagesCount,
'headerUnreadNotifications' => $unreadNotifications,
'headerUnreadNotificationsCount' => $unreadNotificationsCount,
]);
}
}