2026-03-11 23:33:10 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
|
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>
2026-03-26 11:26:40 +01:00
|
|
|
use App\Concerns\HasWorkspaceScope;
|
|
|
|
|
use App\Models\Declaration;
|
|
|
|
|
use App\Models\User;
|
2026-03-11 23:33:10 +00:00
|
|
|
use Illuminate\Http\RedirectResponse;
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
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>
2026-03-26 11:26:40 +01:00
|
|
|
use Inertia\Inertia;
|
|
|
|
|
use Inertia\Response;
|
2026-03-11 23:33:10 +00:00
|
|
|
|
|
|
|
|
class NotificationController extends Controller
|
|
|
|
|
{
|
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>
2026-03-26 11:26:40 +01:00
|
|
|
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__']),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 23:33:10 +00:00
|
|
|
public function markAsRead(Request $request, string $id): RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
$request->user()
|
|
|
|
|
->notifications()
|
|
|
|
|
->where('id', $id)
|
|
|
|
|
->firstOrFail()
|
|
|
|
|
->markAsRead();
|
|
|
|
|
|
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>
2026-03-26 11:26:40 +01:00
|
|
|
$workspaceId = $request->session()->get('current_workspace_id');
|
|
|
|
|
Cache::forget("user:{$request->user()->id}:workspace:{$workspaceId}:unread_notifications");
|
2026-03-11 23:33:10 +00:00
|
|
|
|
|
|
|
|
return back();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function markAllAsRead(Request $request): RedirectResponse
|
|
|
|
|
{
|
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>
2026-03-26 11:26:40 +01:00
|
|
|
$workspace = $this->currentWorkspace();
|
|
|
|
|
|
|
|
|
|
$request->user()
|
|
|
|
|
->unreadNotifications()
|
|
|
|
|
->whereJsonContains('data->workspace_id', $workspace->id)
|
|
|
|
|
->update(['read_at' => now()]);
|
2026-03-11 23:33:10 +00:00
|
|
|
|
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>
2026-03-26 11:26:40 +01:00
|
|
|
Cache::forget("user:{$request->user()->id}:workspace:{$workspace->id}:unread_notifications");
|
2026-03-11 23:33:10 +00:00
|
|
|
|
|
|
|
|
return back();
|
|
|
|
|
}
|
|
|
|
|
}
|