feat: add app and database modules
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
<?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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user