Skip to main content

AI & workflows

Ai Criteria Editor code

Copy the implementation or inspect every file that belongs to this component unit.

resources/views/components/ui/ai-criteria-editor.blade.php

@props([
    'criteria' => [],
    'name' => 'criteria[]',
    'label' => 'Success criteria',
    'placeholder' => 'Add a criterion...',
    'editable' => true,
])

@php
    /*
    | Editable checklist of loop success criteria.
    |
    | Usage:
    |   <x-ui.ai-criteria-editor :criteria="['Tests pass', 'Showcase updated']" />
    */
    $normalizedCriteria = collect($criteria)
        ->map(function ($item, $key) {
            if (is_array($item)) {
                return [
                    'label' => $item['label'] ?? (is_string($key) ? $key : ''),
                    'complete' => (bool) ($item['complete'] ?? false),
                ];
            }

            return [
                'label' => is_string($key) ? $key : (string) $item,
                'complete' => is_string($key) ? (bool) $item : false,
            ];
        })
        ->filter(fn (array $item) => $item['label'] !== '')
        ->values()
        ->all();
    $labelId = 'criteria-editor-label-'.substr(md5($name.$label), 0, 10);
    $inputId = 'criteria-editor-input-'.substr(md5($name.$placeholder), 0, 10);
@endphp

<div
    {{ $attributes->merge(['class' => 'rounded-2xl border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-gray-900 sm:p-5']) }}
    @if ($editable)
        x-data="{
            items: @js(collect($normalizedCriteria)->pluck('label')->values()->all()),
            draft: '',
            add() {
                const value = this.draft.trim();
                if (! value || this.items.includes(value)) {
                    return;
                }
                this.items.push(value);
                this.draft = '';
            },
            remove(index) {
                this.items.splice(index, 1);
            },
        }"
    @endif
>
    <div class="mb-3 flex items-center justify-between gap-3">
        <p id="{{ $labelId }}" class="text-sm font-medium text-gray-700 dark:text-gray-300">{{ $label }}</p>
        <span class="text-xs text-gray-500 dark:text-gray-400">{{ count($normalizedCriteria) }} items</span>
    </div>

    <ul class="space-y-2" role="list">
        @if ($editable)
            <template x-for="(item, index) in items" :key="`${item}-${index}`">
                <li class="flex items-center gap-3 rounded-xl border border-gray-200 bg-gray-50 px-3 py-2.5 dark:border-gray-800 dark:bg-gray-950">
                    <span class="h-2 w-2 shrink-0 rounded-full bg-gray-300 dark:bg-gray-600" aria-hidden="true"></span>
                    <span class="min-w-0 flex-1 break-words text-sm text-gray-800 dark:text-gray-200" x-text="item"></span>
                    <input type="hidden" :name="@js($name)" :value="item">
                    <button type="button" class="shrink-0 self-start text-xs font-medium text-gray-500 transition hover:text-red-600 dark:text-gray-400 dark:hover:text-red-400" x-on:click="remove(index)">
                        Remove
                    </button>
                </li>
            </template>
        @else
            @foreach ($normalizedCriteria as $item)
                <li class="flex items-center gap-3 rounded-xl border border-gray-200 bg-gray-50 px-3 py-2.5 dark:border-gray-800 dark:bg-gray-950">
                    <span @class([
                        'h-2 w-2 shrink-0 rounded-full',
                        'bg-brand-600' => $item['complete'],
                        'bg-gray-300 dark:bg-gray-600' => ! $item['complete'],
                    ]) aria-hidden="true"></span>
                    <span class="min-w-0 flex-1 break-words text-sm text-gray-800 dark:text-gray-200">{{ $item['label'] }}</span>
                </li>
            @endforeach
        @endif
    </ul>

    @if ($editable)
        <div class="mt-3 flex flex-col gap-2 sm:flex-row sm:items-center">
            <input
                id="{{ $inputId }}"
                type="text"
                x-model="draft"
                placeholder="{{ $placeholder }}"
                aria-labelledby="{{ $labelId }}"
                class="block min-w-0 w-full rounded-lg border border-gray-300 bg-white px-3.5 py-2 text-sm text-gray-900 shadow-sm focus:border-brand-500 focus:ring-1 focus:ring-brand-500 dark:border-gray-700 dark:bg-gray-950 dark:text-gray-100"
                x-on:keydown.enter.prevent="add()"
            >
            <x-ui.button type="button" variant="outline" size="sm" class="w-full shrink-0 sm:w-auto" x-on:click="add()">
                Add
            </x-ui.button>
        </div>
    @endif
</div>