TenantAtlas/resources/views/filament/forms/components/restore-run-checks.blade.php
ahmido 3c3daae405 feat: normalize operator outcome taxonomy (#186)
## Summary
- introduce a shared operator outcome taxonomy with semantic axes, severity bands, and next-action policy
- apply the taxonomy to operations, evidence/review completeness, baseline semantics, and restore semantics
- harden badge rendering, tenant-safe filtering/search behavior, and operator-facing summary/notification wording
- add the spec kit artifacts, reference documentation, and regression coverage for diagnostic-vs-primary state handling

## Testing
- focused Pest coverage for taxonomy registry and badge guardrails
- operations presentation and notification tests
- evidence, baseline, restore, and tenant-scope regression tests

## Notes
- Livewire v4.0+ compliance is preserved in the existing Filament v5 stack
- panel provider registration remains unchanged in bootstrap/providers.php
- no new globally searchable resource was added; adopted resources remain tenant-safe and out of global search where required
- no new destructive action family was introduced; existing actions keep their current authorization and confirmation behavior
- no new frontend asset strategy was introduced; existing deploy flow with filament:assets remains unchanged

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #186
2026-03-22 12:13:34 +00:00

122 lines
5.3 KiB
PHP

@php
$fieldWrapperView = $getFieldWrapperView();
$results = $getState() ?? [];
$results = is_array($results) ? $results : [];
$summary = $summary ?? [];
$summary = is_array($summary) ? $summary : [];
$blocking = (int) ($summary['blocking'] ?? 0);
$warning = (int) ($summary['warning'] ?? 0);
$safe = (int) ($summary['safe'] ?? 0);
$ranAt = $ranAt ?? null;
$ranAtLabel = null;
if (is_string($ranAt) && $ranAt !== '') {
try {
$ranAtLabel = \Carbon\CarbonImmutable::parse($ranAt)->format('Y-m-d H:i');
} catch (\Throwable) {
$ranAtLabel = $ranAt;
}
}
$severitySpec = static function (?string $severity): \App\Support\Badges\BadgeSpec {
return \App\Support\Badges\BadgeRenderer::spec(\App\Support\Badges\BadgeDomain::RestoreCheckSeverity, $severity);
};
$limitedList = static function (array $items, int $limit = 5): array {
if (count($items) <= $limit) {
return $items;
}
return array_slice($items, 0, $limit);
};
@endphp
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
<div class="space-y-4">
<x-filament::section
heading="Safety checks"
:description="$ranAtLabel ? ('Last run: ' . $ranAtLabel) : 'Run checks to evaluate risk before previewing.'"
>
<div class="flex flex-wrap gap-2">
<x-filament::badge :color="$blocking > 0 ? $severitySpec('blocking')->color : 'gray'">
{{ $blocking }} {{ \Illuminate\Support\Str::lower($severitySpec('blocking')->label) }}
</x-filament::badge>
<x-filament::badge :color="$warning > 0 ? $severitySpec('warning')->color : 'gray'">
{{ $warning }} {{ \Illuminate\Support\Str::lower($severitySpec('warning')->label) }}
</x-filament::badge>
<x-filament::badge :color="$safe > 0 ? $severitySpec('safe')->color : 'gray'">
{{ $safe }} {{ \Illuminate\Support\Str::lower($severitySpec('safe')->label) }}
</x-filament::badge>
</div>
</x-filament::section>
@if ($results === [])
<x-filament::section>
<div class="text-sm text-gray-600 dark:text-gray-300">
No checks have been run yet.
</div>
</x-filament::section>
@else
<div class="space-y-3">
@foreach ($results as $result)
@php
$severity = is_array($result) ? ($result['severity'] ?? 'safe') : 'safe';
$title = is_array($result) ? ($result['title'] ?? $result['code'] ?? 'Check') : 'Check';
$message = is_array($result) ? ($result['message'] ?? null) : null;
$meta = is_array($result) ? ($result['meta'] ?? []) : [];
$meta = is_array($meta) ? $meta : [];
$unmappedGroups = $meta['unmapped'] ?? [];
$unmappedGroups = is_array($unmappedGroups) ? $limitedList($unmappedGroups) : [];
@endphp
<x-filament::section>
<div class="flex items-start justify-between gap-4">
<div class="space-y-1">
<div class="text-sm font-medium text-gray-900 dark:text-white">
{{ $title }}
</div>
@if (is_string($message) && $message !== '')
<div class="text-sm text-gray-600 dark:text-gray-300">
{{ $message }}
</div>
@endif
</div>
@php
$spec = $severitySpec($severity);
@endphp
<x-filament::badge :color="$spec->color" :icon="$spec->icon" size="sm">
{{ $spec->label }}
</x-filament::badge>
</div>
@if ($unmappedGroups !== [])
<div class="mt-3">
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
Unmapped groups
</div>
<ul class="mt-2 space-y-1 text-sm text-gray-700 dark:text-gray-200">
@foreach ($unmappedGroups as $group)
@php
$label = is_array($group) ? ($group['label'] ?? $group['id'] ?? null) : null;
@endphp
@if (is_string($label) && $label !== '')
<li>{{ $label }}</li>
@endif
@endforeach
</ul>
</div>
@endif
</x-filament::section>
@endforeach
</div>
@endif
</div>
</x-dynamic-component>