HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/be.naniguide.com/app/Http/Controllers/Refactor/BaseNotificationController.php
<?php

namespace App\Http\Controllers\Refactor;

use App\Http\Controllers\Controller;
use App\Model\AppNotification;
use App\Notifications\AppNotification as Event;
use App\Services\Notifications\Notifier;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

/**
 * Shared CRUD/inbox endpoints for both admin and customer notification
 * surfaces. Subclasses provide:
 *   - inboxQuery(): the role-scoped Eloquent builder (direct rows for that
 *     user + any shared rows visible to that audience)
 *   - actor(): the Admin or Customer model used for read_by_* / dismissed_by_*
 *     audit columns
 *   - viewPath(): namespace prefix for the index/_list/dropdown blade files
 *
 * Authorization is enforced via NotificationPolicy at every per-row endpoint.
 */
abstract class BaseNotificationController extends Controller
{
    use AuthorizesRequests;

    public function __construct(protected Notifier $notifier) {}

    abstract protected function inboxQuery(): Builder;
    abstract protected function actor(): Model;
    abstract protected function viewPath(): string;
    abstract protected function routePrefix(): string;

    public function index(Request $request)
    {
        $base   = $this->inboxQuery();
        $stats  = [
            'unread'    => (clone $base)->whereNull('dismissed_at')->whereNull('read_at')->count(),
            'dismissed' => (clone $base)->whereNotNull('dismissed_at')->count(),
            'total'     => $base->count(),
        ];

        return view($this->viewPath() . '.index', [
            'stats'        => $stats,
            'route_prefix' => $this->routePrefix(),
        ]);
    }

    public function listing(Request $request)
    {
        $q = $this->inboxQuery();

        // Default tab = active (not dismissed). 'all' shows dismissed too.
        $tab = $request->input('tab', 'active');
        match ($tab) {
            'active'    => $q->whereNull('dismissed_at'),
            'unread'    => $q->whereNull('dismissed_at')->whereNull('read_at'),
            'action'    => $q->whereNull('dismissed_at')->where('category', Event::CATEGORY_ACTION),
            'alert'     => $q->whereNull('dismissed_at')->where('category', Event::CATEGORY_ALERT),
            'dismissed' => $q->whereNotNull('dismissed_at'),
            'all'       => null,
            default     => throw new \LogicException("Unknown notifications tab: {$tab}"),
        };

        if ($kw = trim((string) $request->input('keyword', ''))) {
            $like = '%' . $kw . '%';
            $q->where(function ($qq) use ($kw, $like) {
                $qq->whereJsonContains('data->title', $kw)
                   ->orWhere('data', 'like', $like);
            });
        }

        if ($sev = $request->input('severity')) {
            $q->where('severity', $sev);
        }

        $items = $q->paginate($request->input('per_page', 20));

        return view($this->viewPath() . '._list', [
            'items'        => $items,
            'route_prefix' => $this->routePrefix(),
        ]);
    }

    public function dropdown(Request $request)
    {
        $items = $this->inboxQuery()
            ->whereNull('dismissed_at')
            ->whereNull('read_at')
            ->limit(8)
            ->get();

        return view('refactor.components.notifications._dropdown_panel', [
            'items'        => $items,
            'route_prefix' => $this->routePrefix(),
        ]);
    }

    public function badge(): JsonResponse
    {
        return response()->json([
            'unread' => $this->inboxQuery()
                ->whereNull('dismissed_at')
                ->whereNull('read_at')
                ->count(),
        ]);
    }

    public function markRead(string $id): JsonResponse
    {
        $n = $this->findOrFail($id);
        $this->authorize('act', $n);
        $n->markAsRead($this->actor());
        return response()->json(['ok' => true]);
    }

    public function markUnread(string $id): JsonResponse
    {
        $n = $this->findOrFail($id);
        $this->authorize('act', $n);
        $n->markAsUnread();
        return response()->json(['ok' => true]);
    }

    public function dismiss(string $id): JsonResponse
    {
        $n = $this->findOrFail($id);
        $this->authorize('act', $n);
        $n->dismiss($this->actor());
        return response()->json(['ok' => true]);
    }

    public function markAllRead(): JsonResponse
    {
        $now    = now();
        $actor  = $this->actor();
        $marked = $this->inboxQuery()
            ->whereNull('dismissed_at')
            ->whereNull('read_at')
            ->update([
                'read_at'      => $now,
                'read_by_type' => $actor->getMorphClass(),
                'read_by_id'   => $actor->getKey(),
            ]);

        return response()->json(['ok' => true, 'marked' => $marked]);
    }

    /**
     * Resolve a notification by id and assert it's in this actor's inbox
     * (defense-in-depth on top of the policy check, so we never touch a row
     * the user can't see).
     */
    protected function findOrFail(string $id): AppNotification
    {
        return $this->inboxQuery()->whereKey($id)->firstOrFail();
    }
}