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>