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, ]); } }