## Summary - add the shared operator explanation layer with explanation families, trustworthiness semantics, count descriptors, and centralized badge mappings - adopt explanation-first rendering across baseline compare, governance operation run detail, baseline snapshot presentation, tenant review detail, and review register rows - extend reason translation, artifact-truth presentation, fallback ops UX messaging, and focused regression coverage for operator explanation semantics ## Testing - vendor/bin/sail bin pint --dirty --format agent - vendor/bin/sail artisan test --compact tests/Feature/Monitoring/OperationsTenantScopeTest.php tests/Feature/Operations/OperationRunBlockedExecutionPresentationTest.php - vendor/bin/sail artisan test --compact ## Notes - Livewire v4 compatible - panel provider registration remains in bootstrap/providers.php - no destructive Filament actions were added or changed in this PR - no new global-search behavior was introduced in this slice Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #191
197 lines
10 KiB
PHP
197 lines
10 KiB
PHP
@php
|
|
use App\Support\Badges\BadgeCatalog;
|
|
use App\Support\Badges\BadgeDomain;
|
|
|
|
$resolvedState = isset($getState) ? $getState() : ($artifactTruthState ?? ($state ?? null));
|
|
$state = is_array($resolvedState) ? $resolvedState : [];
|
|
$dimensions = collect(is_array($state['dimensions'] ?? null) ? $state['dimensions'] : []);
|
|
|
|
$primary = $dimensions->first(fn (mixed $dimension): bool => is_array($dimension) && ($dimension['classification'] ?? null) === 'primary');
|
|
$existence = $dimensions->first(fn (mixed $dimension): bool => is_array($dimension) && ($dimension['axis'] ?? null) === 'artifact_existence');
|
|
$freshness = $dimensions->first(fn (mixed $dimension): bool => is_array($dimension) && ($dimension['axis'] ?? null) === 'data_freshness');
|
|
$publication = $dimensions->first(fn (mixed $dimension): bool => is_array($dimension) && ($dimension['axis'] ?? null) === 'publication_readiness');
|
|
$actionability = $dimensions->first(fn (mixed $dimension): bool => is_array($dimension) && ($dimension['axis'] ?? null) === 'operator_actionability');
|
|
|
|
$specFor = static function (mixed $dimension): ?\App\Support\Badges\BadgeSpec {
|
|
if (! is_array($dimension)) {
|
|
return null;
|
|
}
|
|
|
|
if (! is_string($dimension['badgeDomain'] ?? null) || ! is_string($dimension['badgeState'] ?? null)) {
|
|
return null;
|
|
}
|
|
|
|
return BadgeCatalog::spec(BadgeDomain::from($dimension['badgeDomain']), $dimension['badgeState']);
|
|
};
|
|
|
|
$primarySpec = $specFor($primary);
|
|
$existenceSpec = $specFor($existence);
|
|
$freshnessSpec = $specFor($freshness);
|
|
$publicationSpec = $specFor($publication);
|
|
$actionabilitySpec = $specFor($actionability);
|
|
$reason = is_array($state['reason'] ?? null) ? $state['reason'] : [];
|
|
$nextSteps = is_array($reason['nextSteps'] ?? null) ? $reason['nextSteps'] : [];
|
|
$operatorExplanation = is_array($state['operatorExplanation'] ?? null) ? $state['operatorExplanation'] : [];
|
|
$evaluationSpec = is_string($operatorExplanation['evaluationResult'] ?? null)
|
|
? BadgeCatalog::spec(BadgeDomain::OperatorExplanationEvaluationResult, $operatorExplanation['evaluationResult'])
|
|
: null;
|
|
$trustSpec = is_string($operatorExplanation['trustworthinessLevel'] ?? null)
|
|
? BadgeCatalog::spec(BadgeDomain::OperatorExplanationTrustworthiness, $operatorExplanation['trustworthinessLevel'])
|
|
: null;
|
|
$operatorCounts = collect(is_array($operatorExplanation['countDescriptors'] ?? null) ? $operatorExplanation['countDescriptors'] : []);
|
|
@endphp
|
|
|
|
<div class="space-y-4">
|
|
<div class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-800 dark:bg-gray-900">
|
|
<div class="flex flex-wrap items-start gap-2">
|
|
@if ($evaluationSpec && $evaluationSpec->label !== 'Unknown')
|
|
<x-filament::badge :color="$evaluationSpec->color" :icon="$evaluationSpec->icon" size="sm">
|
|
{{ $evaluationSpec->label }}
|
|
</x-filament::badge>
|
|
@endif
|
|
|
|
@if ($trustSpec && $trustSpec->label !== 'Unknown')
|
|
<x-filament::badge :color="$trustSpec->color" :icon="$trustSpec->icon" size="sm">
|
|
{{ $trustSpec->label }}
|
|
</x-filament::badge>
|
|
@endif
|
|
|
|
@if ($primarySpec)
|
|
<x-filament::badge :color="$primarySpec->color" :icon="$primarySpec->icon" size="sm">
|
|
{{ $primarySpec->label }}
|
|
</x-filament::badge>
|
|
@endif
|
|
|
|
@if ($actionabilitySpec)
|
|
<x-filament::badge :color="$actionabilitySpec->color" :icon="$actionabilitySpec->icon" size="sm">
|
|
{{ $actionabilitySpec->label }}
|
|
</x-filament::badge>
|
|
@endif
|
|
</div>
|
|
|
|
<div class="mt-3 space-y-2">
|
|
<div class="text-sm font-medium text-gray-950 dark:text-gray-100">
|
|
{{ $operatorExplanation['headline'] ?? ($state['primaryLabel'] ?? 'Artifact truth') }}
|
|
</div>
|
|
|
|
@if (is_string($operatorExplanation['reliabilityStatement'] ?? null) && trim($operatorExplanation['reliabilityStatement']) !== '')
|
|
<p class="text-sm text-gray-600 dark:text-gray-300">
|
|
{{ $operatorExplanation['reliabilityStatement'] }}
|
|
</p>
|
|
@elseif (is_string($state['primaryExplanation'] ?? null) && trim($state['primaryExplanation']) !== '')
|
|
<p class="text-sm text-gray-600 dark:text-gray-300">
|
|
{{ $state['primaryExplanation'] }}
|
|
</p>
|
|
@endif
|
|
|
|
@if (is_string(data_get($operatorExplanation, 'dominantCause.explanation')) && trim(data_get($operatorExplanation, 'dominantCause.explanation')) !== '')
|
|
<p class="text-sm text-gray-600 dark:text-gray-300">
|
|
{{ data_get($operatorExplanation, 'dominantCause.explanation') }}
|
|
</p>
|
|
@endif
|
|
|
|
@if (is_string($operatorExplanation['coverageStatement'] ?? null) && trim($operatorExplanation['coverageStatement']) !== '')
|
|
<p class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
|
Coverage: {{ $operatorExplanation['coverageStatement'] }}
|
|
</p>
|
|
@endif
|
|
|
|
@if (is_string($state['diagnosticLabel'] ?? null) && trim($state['diagnosticLabel']) !== '')
|
|
<p class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
|
Diagnostic: {{ $state['diagnosticLabel'] }}
|
|
</p>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<dl class="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
|
@if ($existenceSpec)
|
|
<div class="rounded-md border border-gray-100 bg-gray-50 px-3 py-2 dark:border-gray-800 dark:bg-gray-950/60">
|
|
<dt class="text-[11px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Artifact exists</dt>
|
|
<dd class="mt-1">
|
|
<x-filament::badge :color="$existenceSpec->color" :icon="$existenceSpec->icon" size="sm">
|
|
{{ $existenceSpec->label }}
|
|
</x-filament::badge>
|
|
</dd>
|
|
</div>
|
|
@endif
|
|
|
|
@if ($freshnessSpec)
|
|
<div class="rounded-md border border-gray-100 bg-gray-50 px-3 py-2 dark:border-gray-800 dark:bg-gray-950/60">
|
|
<dt class="text-[11px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Freshness</dt>
|
|
<dd class="mt-1">
|
|
<x-filament::badge :color="$freshnessSpec->color" :icon="$freshnessSpec->icon" size="sm">
|
|
{{ $freshnessSpec->label }}
|
|
</x-filament::badge>
|
|
</dd>
|
|
</div>
|
|
@endif
|
|
|
|
@if ($publicationSpec)
|
|
<div class="rounded-md border border-gray-100 bg-gray-50 px-3 py-2 dark:border-gray-800 dark:bg-gray-950/60">
|
|
<dt class="text-[11px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Publication</dt>
|
|
<dd class="mt-1">
|
|
<x-filament::badge :color="$publicationSpec->color" :icon="$publicationSpec->icon" size="sm">
|
|
{{ $publicationSpec->label }}
|
|
</x-filament::badge>
|
|
</dd>
|
|
</div>
|
|
@endif
|
|
|
|
@if ($trustSpec && $trustSpec->label !== 'Unknown')
|
|
<div class="rounded-md border border-gray-100 bg-gray-50 px-3 py-2 dark:border-gray-800 dark:bg-gray-950/60">
|
|
<dt class="text-[11px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Result trust</dt>
|
|
<dd class="mt-1">
|
|
<x-filament::badge :color="$trustSpec->color" :icon="$trustSpec->icon" size="sm">
|
|
{{ $trustSpec->label }}
|
|
</x-filament::badge>
|
|
</dd>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="rounded-md border border-gray-100 bg-gray-50 px-3 py-2 dark:border-gray-800 dark:bg-gray-950/60">
|
|
<dt class="text-[11px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Next step</dt>
|
|
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
|
|
{{ data_get($operatorExplanation, 'nextAction.text') ?? ($state['nextActionLabel'] ?? 'No action needed') }}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
|
|
@if ($operatorCounts->isNotEmpty())
|
|
<div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
|
@foreach ($operatorCounts as $count)
|
|
@continue(! is_array($count))
|
|
|
|
<div class="rounded-md border border-gray-100 bg-gray-50 px-3 py-2 dark:border-gray-800 dark:bg-gray-950/60">
|
|
<div class="text-[11px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
|
{{ $count['label'] ?? 'Count' }}
|
|
</div>
|
|
<div class="mt-1 text-lg font-semibold text-gray-900 dark:text-gray-100">
|
|
{{ (int) ($count['value'] ?? 0) }}
|
|
</div>
|
|
@if (filled($count['qualifier'] ?? null))
|
|
<div class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
{{ $count['qualifier'] }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
@if ($nextSteps !== [])
|
|
<div class="space-y-2">
|
|
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Guidance</div>
|
|
<ul class="space-y-1 text-sm text-gray-700 dark:text-gray-300">
|
|
@foreach ($nextSteps as $step)
|
|
@continue(! is_string($step) || trim($step) === '')
|
|
|
|
<li class="rounded-md border border-primary-100 bg-primary-50 px-3 py-2 dark:border-primary-900/40 dark:bg-primary-950/30">
|
|
{{ $step }}
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
@endif
|
|
</div>
|