Files

225 lines
7.5 KiB
PHP

<?php
namespace App\Http\Controllers\SystemSettings;
use App\Events\NotificationSent;
use App\Models\Notification;
use App\Repositories\NotificationRepository;
use App\Services\SystemConfig\SystemConfigService;
use App\Support\DataTable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Spatie\Permission\Models\Role;
class NotificationCenterController extends Controller
{
public function __construct(
protected SystemConfigService $systemConfig,
protected NotificationRepository $notificationRepo
) {
// Middleware handled in web.php
}
protected function ensureFeatureEnabled(): void
{
$featureEnabled = $this->systemConfig->get('feature_notification_center', true);
if (! $featureEnabled) {
// Instead of 404, we change the authorization requirement.
// If feature is disabled, only users who can 'manage global settings' can access.
abort_unless(
Auth::user()?->can('manage global settings'),
403,
__('The Notification Center feature is currently disabled by the system administrator.')
);
}
}
/**
* =====================================================
* INDEX
* =====================================================
*/
public function index(Request $request)
{
$this->ensureFeatureEnabled();
if (DataTable::isDataTableRequest($request)) {
return $this->dataTable($request);
}
$roles = Role::pluck('name')->toArray();
return view('pages.system_settings.notification-center', compact('roles'));
}
/**
* DATATABLE (Supports personal status)
*/
protected function dataTable(Request $request)
{
try {
$user = Auth::user();
$authorizedRecipients = $this->notificationRepo->getAuthorizedRecipients($user);
$start = DataTable::start($request);
$length = DataTable::length($request);
// Efficient query using LEFT JOIN (no N+1)
$baseQuery = Notification::whereIn('recipient', $authorizedRecipients)
->leftJoin('system_notification_user', function ($join) use ($user) {
$join->on('system_notifications.id', '=', 'system_notification_user.notification_id')
->where('system_notification_user.user_id', '=', $user->id);
})
->whereNull('system_notification_user.deleted_at')
->select('system_notifications.*', 'system_notification_user.read_at as personal_read_at');
$recordsTotal = $baseQuery->count();
$recordsFiltered = $recordsTotal;
$notifications = (clone $baseQuery)
->latest('system_notifications.created_at')
->skip($start)
->take($length)
->get();
$rows = $notifications->map(function ($n) {
return [
'id' => $n->id,
'is_unread' => is_null($n->personal_read_at),
'title' => e($n->title),
'message' => e(strip_tags($n->message)),
'type' => $n->type,
'recipient' => $n->recipient,
'time_ago' => $n->created_at ? $n->created_at->diffForHumans() : '',
'read_url' => route('notification-center.read', $n->id),
'delete_url' => route('notification-center.destroy', $n->id),
'created_at' => $n->created_at ? $n->created_at->format('Y-m-d H:i:s') : '',
];
})->values();
return DataTable::response($request, $recordsTotal, $recordsFiltered, $rows->toArray());
} catch (\Exception $e) {
\Log::error('NotificationCenter DataTable Error: '.$e->getMessage());
return response()->json(['error' => $e->getMessage(), 'data' => []], 500);
}
}
/**
* STORE (Send Broadcast)
*/
public function store(Request $request)
{
$this->ensureFeatureEnabled();
$roles = Role::pluck('name')->toArray();
$roles[] = 'all';
$validated = $request->validate([
'title' => 'required|string|max:255',
'message' => 'required|string',
'recipient' => 'required|in:'.implode(',', $roles),
'type' => 'required|in:info,warning,system',
]);
$notification = $this->notificationRepo->create([
...$validated,
'created_by' => Auth::id(),
'read_at' => null,
]);
event(new NotificationSent($notification));
return response()->json(['success' => true, 'message' => __('Notification broadcasted.')]);
}
/**
* MARK AS READ (Personal)
*/
public function markAsRead(Notification $notification)
{
$this->ensureFeatureEnabled();
$this->notificationRepo->markAsRead($notification->id, Auth::id());
return response()->json(['success' => true, 'message' => __('Marked as read.')]);
}
/**
* DELETE (Personal Hide)
*/
public function destroy(Notification $notification)
{
$this->ensureFeatureEnabled();
$this->notificationRepo->personalDelete($notification->id, Auth::id());
return response()->json(['success' => true, 'message' => __('Notification hidden.')]);
}
/**
* MARK ALL AS READ (Personal)
*/
public function markAllAsRead()
{
$this->ensureFeatureEnabled();
$this->notificationRepo->markAllAsReadForUser(Auth::user());
return response()->json(['success' => true, 'message' => __('All marked as read.')]);
}
/**
* CLEAR READ (Personal Hide all read)
*/
public function clearRead()
{
$this->ensureFeatureEnabled();
$user = Auth::user();
$readIds = \DB::table('system_notification_user')
->where('user_id', $user->id)
->whereNotNull('read_at')
->pluck('notification_id');
foreach ($readIds as $id) {
$this->notificationRepo->personalDelete($id, $user->id);
}
return response()->json(['success' => true, 'message' => __('Read notifications cleared from your view.')]);
}
/**
* RECENT NOTIFICATIONS API (Personal status)
*/
public function recentNotifications(Request $request)
{
$this->ensureFeatureEnabled();
$user = Auth::user();
$offset = $request->get('offset', 0);
$limit = $request->get('limit', 10);
$notifications = $this->notificationRepo->getActiveNotificationsForUser($user, $offset, $limit);
$mapped = $notifications->map(function ($n) {
return [
'id' => $n->id,
'title' => e($n->title),
'message' => e(strip_tags($n->message)),
'type' => $n->type,
'time_ago' => $n->created_at->diffForHumans(),
'read_url' => route('notification-center.read', $n->id),
'delete_url' => route('notification-center.destroy', $n->id),
'is_unread' => is_null($n->personal_read_at),
'recipient' => $n->recipient,
];
})->values()->all();
return response()->json([
'success' => true,
'unread_count' => $this->notificationRepo->getUnreadCount($user),
'notifications' => $mapped,
'has_more' => count($mapped) === (int) $limit,
]);
}
}