Skip to main content

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>