AI & workflows
Ai Approval Queue code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/ai-approval-queue.blade.php
@props([
'action' => '#',
'method' => 'POST',
'requests' => [],
'title' => 'Pending approvals',
'requestIdName' => 'approval_id',
'decisionName' => 'decision',
'approveLabel' => 'Approve',
'rejectLabel' => 'Reject',
'returnLoopId' => null,
'returnLoopIdName' => 'loop_id',
'embedded' => false,
])
@php
/*
| Queue of pending approval requests for ask-first agent workflows.
|
| Usage:
| <x-ui.ai-approval-queue
| action="/ai/approvals"
| :requests="[
| ['id' => 'apr_1', 'title' => 'Run tests?', 'preview' => 'php artisan test'],
| ]"
| />
*/
$httpMethod = strtoupper($method);
$formMethod = in_array($httpMethod, ['GET', 'POST'], true) ? $httpMethod : 'POST';
$usesMethodSpoofing = ! in_array($httpMethod, ['GET', 'POST'], true);
$normalizedRequests = collect($requests)
->map(function ($request) {
return [
'id' => $request['id'] ?? '',
'title' => $request['title'] ?? 'Approval required',
'description' => $request['description'] ?? null,
'preview' => $request['preview'] ?? null,
];
})
->filter(fn (array $request) => $request['id'] !== '')
->values()
->all();
@endphp
<div {{ $attributes->merge(['class' => $embedded ? 'min-w-0' : 'min-w-0 overflow-hidden rounded-2xl border border-amber-200 bg-white shadow-sm dark:border-amber-900/60 dark:bg-gray-900']) }}>
@unless ($embedded)
<div class="flex items-start justify-between gap-3 border-b border-amber-100 px-4 py-4 dark:border-amber-900/40 sm:items-center sm:px-5">
<div>
<p class="text-sm font-semibold text-gray-900 dark:text-white">{{ $title }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400">{{ count($normalizedRequests) }} waiting</p>
</div>
@if (count($normalizedRequests) > 0)
<x-ui.badge variant="amber">{{ count($normalizedRequests) }}</x-ui.badge>
@endif
</div>
@endunless
@if (count($normalizedRequests) === 0)
<p class="px-4 py-6 text-sm text-gray-500 dark:text-gray-400 sm:px-5">No approvals waiting.</p>
@else
<ul class="divide-y divide-amber-100 dark:divide-amber-900/40" role="list">
@foreach ($normalizedRequests as $request)
<li class="px-4 py-4 sm:px-5">
<form method="{{ $formMethod }}" action="{{ $action }}" class="space-y-3">
@if ($usesMethodSpoofing)
@method($httpMethod)
@endif
@csrf
<input type="hidden" name="{{ $requestIdName }}" value="{{ $request['id'] }}">
@if ($returnLoopId)
<input type="hidden" name="{{ $returnLoopIdName }}" value="{{ $returnLoopId }}">
@endif
<div class="min-w-0">
<p class="break-words text-sm font-medium text-gray-900 dark:text-white">{{ $request['title'] }}</p>
@if ($request['description'])
<p class="mt-1 break-words text-sm text-gray-600 dark:text-gray-400">{{ $request['description'] }}</p>
@endif
@if ($request['preview'])
<pre class="mt-2 max-w-full overflow-x-auto whitespace-pre-wrap break-all rounded-lg bg-gray-50 px-3 py-2 text-xs text-gray-700 dark:bg-gray-950 dark:text-gray-300">{{ $request['preview'] }}</pre>
@endif
</div>
<div class="flex flex-col-reverse gap-2 sm:flex-row sm:flex-wrap">
<x-ui.button type="submit" name="{{ $decisionName }}" value="reject" variant="outline" size="sm" class="w-full sm:w-auto">
{{ $rejectLabel }}
</x-ui.button>
<x-ui.button type="submit" name="{{ $decisionName }}" value="approve" size="sm" class="w-full sm:w-auto">
{{ $approveLabel }}
</x-ui.button>
</div>
</form>
</li>
@endforeach
</ul>
@endif
@if (trim($slot) !== '')
<div class="border-t border-amber-100 px-4 py-3 dark:border-amber-900/40 sm:px-5">{{ $slot }}</div>
@endif
</div>