Step 1: Update Policy to Check Permissions and App Type
Your QuotePolicy should check both the user's permission and the application type. However, to keep the button state logic separate from the policy, we'll include the business logic in the service
namespace App\Policies;
use App\Models\User;
use App\Models\Quote;
use App\Enums\AppTypeEnum;
use Illuminate\Auth\Access\Response;
class QuotePolicy
{
public function reject(User $user, Quote $quote, AppTypeEnum $app): Response
{
if (!$user->can('reject quotes')) {
return Response::deny('You do not have permission to reject quotes.');
}
if ($app === AppTypeEnum::CONSUMER) {
return Response::allow();
}
return Response::deny('This action is not allowed for the current application type.');
}
}
Step 2: Controller to Handle Reject Request
Your QuoteController should handle the rejection request, using the policy to check access and the service to perform the rejection.
namespace App\Http\Controllers;
use App\Models\Quote;
use App\Services\QuoteService;
use App\Enums\AppTypeEnum;
use App\Exceptions\QuoteAlreadyRejectedException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class QuoteController extends Controller
{
protected $quoteService;
public function __construct(QuoteService $quoteService)
{
$this->quoteService = $quoteService;
}
public function reject(Quote $quote): JsonResponse
{
try {
$this->authorize('reject', [$quote, AppTypeEnum::CONSUMER]);
$success = $this->quoteService->reject($quote);
} catch (QuoteAlreadyRejectedException $e) {
return $this->errorResponse($e->getMessage(), $e->getCode());
}
return $this->successResponse($success);
}
public function permissions(Quote $quote): JsonResponse
{
$canReject = auth()->user()->can('reject', [$quote, AppTypeEnum::CONSUMER]);
return response()->json(['canReject' => $canReject]);
}
}
Step 3: Define the Service Logic
Your QuoteService should handle the logic for rejecting a quote.
namespace App\Services;
use App\Models\Quote;
use Carbon\Carbon;
use App\Exceptions\QuoteAlreadyRejectedException;
class QuoteService
{
public function reject(Quote $quote): bool
{
if ($quote->rejected_at) {
throw new QuoteAlreadyRejectedException('Quote has already been rejected.');
}
return $quote->update(['rejected_at' => Carbon::now()]);
}
}
Step 4: Define Routes
Define routes for handling the reject request and checking permissions.
use App\Http\Controllers\QuoteController;
Route::middleware('auth:api')->group(function () {
Route::post('quotes/{quote}/reject', [QuoteController::class, 'reject']);
Route::get('quotes/{quote}/permissions', [QuoteController::class, 'permissions']);
});
Step 5: Client-side Logic
Fetch the user's permissions to manage the UI button state.
1- fetch permissions
async function checkRejectPermission(quoteId) {
const response = await fetch(`/api/quotes/${quoteId}/permissions`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
const data = await response.json();
return data.canReject;
}
manage the button state
document.addEventListener('DOMContentLoaded', async () => {
const quoteId = 123; // Example quote ID
const canReject = await checkRejectPermission(quoteId);
const rejectButton = document.getElementById('reject-button');
if (canReject) {
rejectButton.style.display = 'block';
} else {
rejectButton.style.display = 'none';
}
});