feat: add notification center with bell dropdown, full page, and workspace scoping (Story 3.3)
Enhance NotificationDropdown with type-specific icons, French description builder, click-to-navigate with mark-as-read, and "Voir toutes les notifications" link. Add full notifications page at /notifications with pagination (25/page), individual mark-as-read, and empty state. Includes code review fixes: workspace-scoped unread count and dropdown items, race condition fix (mark-as-read before navigate), efficient markAllAsRead via direct update, deleted declaration URL handling, and per-workspace cache keys. 7 new feature tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,12 +2,61 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Concerns\HasWorkspaceScope;
|
||||
use App\Models\Declaration;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class NotificationController extends Controller
|
||||
{
|
||||
use HasWorkspaceScope;
|
||||
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$workspace = $this->currentWorkspace();
|
||||
|
||||
$notifications = $request->user()
|
||||
->notifications()
|
||||
->whereJsonContains('data->workspace_id', $workspace->id)
|
||||
->latest()
|
||||
->paginate(25);
|
||||
|
||||
$declarationIds = collect($notifications->items())->pluck('data.declaration_id')->filter()->unique()->values();
|
||||
$senderIds = collect($notifications->items())->pluck('data.sender_id')->filter()->unique()->values();
|
||||
|
||||
$declarations = Declaration::whereIn('id', $declarationIds)->pluck('title', 'id');
|
||||
$senders = User::whereIn('id', $senderIds)->pluck('name', 'id');
|
||||
|
||||
$notifications->through(function ($n) use ($declarations, $senders) {
|
||||
$data = $n->data;
|
||||
$declarationId = $data['declaration_id'] ?? null;
|
||||
$senderId = $data['sender_id'] ?? null;
|
||||
|
||||
$declarationExists = $declarationId && isset($declarations[$declarationId]);
|
||||
$data['declaration_title'] = $declarationExists ? $declarations[$declarationId] : null;
|
||||
$data['sender_name'] = $senderId ? ($senders[$senderId] ?? null) : null;
|
||||
$data['url'] = $declarationExists ? route('declarations.show', $declarationId) : null;
|
||||
|
||||
return [
|
||||
'id' => $n->id,
|
||||
'type' => class_basename($n->type),
|
||||
'data' => $data,
|
||||
'read_at' => $n->read_at?->toISOString(),
|
||||
'created_at' => $n->created_at->diffForHumans(),
|
||||
];
|
||||
});
|
||||
|
||||
return Inertia::render('notifications/Index', [
|
||||
'notifications' => $notifications,
|
||||
'markAllReadUrl' => route('notifications.readAll'),
|
||||
'readUrl' => route('notifications.read', ['id' => '__ID__']),
|
||||
]);
|
||||
}
|
||||
|
||||
public function markAsRead(Request $request, string $id): RedirectResponse
|
||||
{
|
||||
$request->user()
|
||||
@@ -16,16 +65,22 @@ class NotificationController extends Controller
|
||||
->firstOrFail()
|
||||
->markAsRead();
|
||||
|
||||
Cache::forget("user:{$request->user()->id}:unread_notifications");
|
||||
$workspaceId = $request->session()->get('current_workspace_id');
|
||||
Cache::forget("user:{$request->user()->id}:workspace:{$workspaceId}:unread_notifications");
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
public function markAllAsRead(Request $request): RedirectResponse
|
||||
{
|
||||
$request->user()->unreadNotifications->markAsRead();
|
||||
$workspace = $this->currentWorkspace();
|
||||
|
||||
Cache::forget("user:{$request->user()->id}:unread_notifications");
|
||||
$request->user()
|
||||
->unreadNotifications()
|
||||
->whereJsonContains('data->workspace_id', $workspace->id)
|
||||
->update(['read_at' => now()]);
|
||||
|
||||
Cache::forget("user:{$request->user()->id}:workspace:{$workspace->id}:unread_notifications");
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user