147 lines
8.6 KiB
PHP
147 lines
8.6 KiB
PHP
@php
|
|
use App\Support\Badges\BadgeDomain;
|
|
use App\Support\Badges\BadgeRenderer;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
$groups = isset($groups) ? $groups : (isset($getState) ? $getState() : []);
|
|
$groups = is_array($groups) ? $groups : [];
|
|
|
|
$formatTimestamp = static function (?string $value): string {
|
|
if (! is_string($value) || trim($value) === '') {
|
|
return '—';
|
|
}
|
|
|
|
try {
|
|
return Carbon::parse($value)->toDayDateTimeString();
|
|
} catch (\Throwable) {
|
|
return $value;
|
|
}
|
|
};
|
|
@endphp
|
|
|
|
<div class="space-y-4">
|
|
@if ($groups === [])
|
|
<div class="rounded-lg border border-dashed border-gray-300 px-4 py-6 text-sm text-gray-600 dark:border-gray-700 dark:text-gray-300">
|
|
No snapshot items were captured for this baseline snapshot.
|
|
</div>
|
|
@else
|
|
@foreach ($groups as $group)
|
|
@php
|
|
$fidelitySpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotFidelity, $group['fidelity'] ?? null);
|
|
$gapSpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotGapStatus, data_get($group, 'gapSummary.badge_state'));
|
|
$messages = data_get($group, 'gapSummary.messages', []);
|
|
@endphp
|
|
|
|
<x-filament::section
|
|
:heading="$group['label'] ?? ($group['policyType'] ?? 'Policy type')"
|
|
:description="$group['coverageHint'] ?? null"
|
|
collapsible
|
|
:collapsed="(bool) ($group['initiallyCollapsed'] ?? true)"
|
|
>
|
|
<x-slot name="headerEnd">
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
{{ (int) ($group['itemCount'] ?? 0) }} {{ \Illuminate\Support\Str::plural('item', (int) ($group['itemCount'] ?? 0)) }}
|
|
</span>
|
|
<x-filament::badge :color="$fidelitySpec->color" :icon="$fidelitySpec->icon" size="sm">
|
|
{{ $fidelitySpec->label }}
|
|
</x-filament::badge>
|
|
<x-filament::badge :color="$gapSpec->color" :icon="$gapSpec->icon" size="sm">
|
|
{{ $gapSpec->label }}
|
|
</x-filament::badge>
|
|
</div>
|
|
</x-slot>
|
|
|
|
<div class="space-y-4">
|
|
@if (! empty($group['renderingError']))
|
|
<div class="rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-900 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-200">
|
|
{{ $group['renderingError'] }}
|
|
</div>
|
|
@endif
|
|
|
|
@if (is_array($messages) && $messages !== [])
|
|
<div class="rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 text-sm text-gray-700 dark:border-gray-700 dark:bg-gray-900/40 dark:text-gray-200">
|
|
<div class="font-medium">Gap details</div>
|
|
<ul class="mt-2 list-disc space-y-1 pl-5">
|
|
@foreach ($messages as $message)
|
|
<li>{{ $message }}</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="space-y-3">
|
|
@foreach (($group['items'] ?? []) as $item)
|
|
@php
|
|
$itemFidelitySpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotFidelity, $item['fidelity'] ?? null);
|
|
$itemGapSpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotGapStatus, data_get($item, 'gapSummary.badge_state'));
|
|
@endphp
|
|
|
|
<div class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-900">
|
|
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
|
<div class="space-y-1">
|
|
<div class="text-sm font-semibold text-gray-900 dark:text-white">
|
|
{{ $item['label'] ?? 'Snapshot item' }}
|
|
</div>
|
|
<div class="text-xs text-gray-500 dark:text-gray-400">
|
|
{{ $item['typeLabel'] ?? 'Policy type' }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<span class="rounded-full bg-gray-100 px-2.5 py-1 text-xs font-medium text-gray-700 dark:bg-gray-800 dark:text-gray-200">
|
|
{{ $item['referenceStatus'] ?? 'Captured metadata' }}
|
|
</span>
|
|
<x-filament::badge :color="$itemFidelitySpec->color" :icon="$itemFidelitySpec->icon" size="sm">
|
|
{{ $itemFidelitySpec->label }}
|
|
</x-filament::badge>
|
|
<x-filament::badge :color="$itemGapSpec->color" :icon="$itemGapSpec->icon" size="sm">
|
|
{{ $itemGapSpec->label }}
|
|
</x-filament::badge>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-3 grid gap-2 text-sm text-gray-700 dark:text-gray-200 md:grid-cols-3">
|
|
<div>
|
|
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Identity</div>
|
|
<div class="mt-1 break-all">{{ $item['identityHint'] ?? '—' }}</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Observed</div>
|
|
<div class="mt-1">{{ $formatTimestamp($item['observedAt'] ?? null) }}</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Source reference</div>
|
|
<div class="mt-1">{{ $item['sourceReference'] ?? '—' }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if (is_array(data_get($item, 'gapSummary.messages')) && data_get($item, 'gapSummary.messages') !== [])
|
|
<div class="mt-3 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-900 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-200">
|
|
{{ implode(' ', data_get($item, 'gapSummary.messages', [])) }}
|
|
</div>
|
|
@endif
|
|
|
|
@if (($item['structuredAttributes'] ?? []) !== [])
|
|
<dl class="mt-4 grid gap-3 md:grid-cols-2">
|
|
@foreach (($item['structuredAttributes'] ?? []) as $attribute)
|
|
<div class="rounded-lg border border-gray-100 bg-gray-50 px-3 py-2 dark:border-gray-800 dark:bg-gray-950/40">
|
|
<dt class="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
|
{{ $attribute['label'] ?? 'Attribute' }}
|
|
</dt>
|
|
<dd class="mt-1 text-sm text-gray-800 dark:text-gray-100">
|
|
{{ $attribute['value'] ?? '—' }}
|
|
</dd>
|
|
</div>
|
|
@endforeach
|
|
</dl>
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</x-filament::section>
|
|
@endforeach
|
|
@endif
|
|
</div>
|