TenantAtlas/apps/platform/resources/views/filament/pages/operations/tenantless-operation-run-viewer.blade.php
ahmido bd06b479e1
Some checks failed
Main Confidence / confidence (push) Failing after 43s
feat: add governance run summaries (#257)
## Summary
- add the Spec 220 governance run diagnostic summary seam and wire it through the canonical operation run detail presenter
- render summary-first decision guidance for covered governance run families while keeping technical diagnostics secondary
- add focused Pest coverage, spec artifacts, and complete the integrated-browser smoke validation for canonical run detail

## Testing
- cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent
- cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Monitoring/GovernanceOperationRunSummariesTest.php tests/Feature/Filament/OperationRunBaselineTruthSurfaceTest.php tests/Feature/Monitoring/ArtifactTruthRunDetailTest.php tests/Feature/Authorization/OperatorExplanationSurfaceAuthorizationTest.php tests/Feature/RunAuthorizationTenantIsolationTest.php tests/Unit/Support/OpsUx/GovernanceRunDiagnosticSummaryBuilderTest.php tests/Unit/Support/OperatorExplanation/OperatorExplanationBuilderTest.php
- integrated browser smoke pass on localhost:8081 covering summary-first hierarchy, zero-output runs, multi-cause runs, cross-family parity, workspace-wide visibility, and deny-as-not-found tenant safety

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #257
2026-04-20 20:46:09 +00:00

122 lines
7.0 KiB
PHP

@php
$contextBanner = $this->canonicalContextBanner();
$blockedBanner = $this->blockedExecutionBanner();
$lifecycleBanner = $this->lifecycleBanner();
$restoreContinuationBanner = $this->restoreContinuationBanner();
$monitoringDetail = $this->monitoringDetailSummary();
$pollInterval = $this->pollInterval();
@endphp
<x-filament-panels::page>
<div
@if ($pollInterval !== null) wire:poll.{{ $pollInterval }} @endif
>
@if ($contextBanner !== null)
@php
$bannerClasses = match ($contextBanner['tone']) {
'amber' => 'border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-100',
'slate' => 'border-slate-200 bg-slate-50 text-slate-900 dark:border-slate-500/30 dark:bg-slate-500/10 dark:text-slate-100',
default => 'border-sky-200 bg-sky-50 text-sky-900 dark:border-sky-500/30 dark:bg-sky-500/10 dark:text-sky-100',
};
@endphp
<div class="mb-6 rounded-lg border px-4 py-3 text-sm {{ $bannerClasses }}">
<p class="font-semibold">{{ $contextBanner['title'] }}</p>
<p class="mt-1">{{ $contextBanner['body'] }}</p>
</div>
@endif
@if ($blockedBanner !== null)
@php
$blockedBannerClasses = 'border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-100';
@endphp
<div class="mb-6 rounded-lg border px-4 py-3 text-sm {{ $blockedBannerClasses }}">
<p class="font-semibold">{{ $blockedBanner['title'] }}</p>
<p class="mt-1">{{ $blockedBanner['body'] }}</p>
</div>
@endif
@if ($lifecycleBanner !== null)
@php
$lifecycleBannerClasses = match ($lifecycleBanner['tone']) {
'rose' => 'border-rose-200 bg-rose-50 text-rose-900 dark:border-rose-500/30 dark:bg-rose-500/10 dark:text-rose-100',
default => 'border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-100',
};
@endphp
<div class="mb-6 rounded-lg border px-4 py-3 text-sm {{ $lifecycleBannerClasses }}">
<p class="font-semibold">{{ $lifecycleBanner['title'] }}</p>
<p class="mt-1">{{ $lifecycleBanner['body'] }}</p>
</div>
@endif
@if ($restoreContinuationBanner !== null)
@php
$restoreContinuationClasses = match ($restoreContinuationBanner['tone']) {
'amber' => 'border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-100',
default => 'border-sky-200 bg-sky-50 text-sky-900 dark:border-sky-500/30 dark:bg-sky-500/10 dark:text-sky-100',
};
@endphp
<div class="mb-6 rounded-lg border px-4 py-3 text-sm {{ $restoreContinuationClasses }}">
<p class="font-semibold">{{ $restoreContinuationBanner['title'] }}</p>
<p class="mt-1">{{ $restoreContinuationBanner['body'] }}</p>
@if ($restoreContinuationBanner['url'] !== null && $restoreContinuationBanner['link_label'] !== null)
<p class="mt-3">
<a href="{{ $restoreContinuationBanner['url'] }}" class="font-semibold underline underline-offset-2">
{{ $restoreContinuationBanner['link_label'] }}
</a>
</p>
@endif
</div>
@endif
@if ($this->redactionIntegrityNote())
<div class="mb-6 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-100">
{{ $this->redactionIntegrityNote() }}
</div>
@endif
{{ $this->infolist }}
<x-filament::section heading="Monitoring detail" class="mt-6">
<p class="text-sm text-gray-600 dark:text-gray-400">
Scope context, return navigation, utility, related drilldowns, and run-specific follow-up stay in separate lanes on this viewer.
</p>
<div class="mt-4 grid gap-4 md:grid-cols-2 xl:grid-cols-5">
<div class="rounded-xl border border-gray-200 bg-white/80 p-4 dark:border-white/10 dark:bg-white/5">
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-gray-500 dark:text-gray-400">Scope context</p>
<p class="mt-2 text-sm font-semibold text-gray-900 dark:text-white">{{ $monitoringDetail['scope_label'] }}</p>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">{{ $monitoringDetail['scope_body'] }}</p>
</div>
<div class="rounded-xl border border-gray-200 bg-white/80 p-4 dark:border-white/10 dark:bg-white/5">
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-gray-500 dark:text-gray-400">Navigation lane</p>
<p class="mt-2 text-sm font-semibold text-gray-900 dark:text-white">{{ $monitoringDetail['navigation_label'] }}</p>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">{{ $monitoringDetail['navigation_body'] }}</p>
</div>
<div class="rounded-xl border border-gray-200 bg-white/80 p-4 dark:border-white/10 dark:bg-white/5">
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-gray-500 dark:text-gray-400">Utility lane</p>
<p class="mt-2 text-sm font-semibold text-gray-900 dark:text-white">Refresh</p>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">{{ $monitoringDetail['utility_body'] }}</p>
</div>
<div class="rounded-xl border border-gray-200 bg-white/80 p-4 dark:border-white/10 dark:bg-white/5">
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-gray-500 dark:text-gray-400">Related drilldown</p>
<p class="mt-2 text-sm font-semibold text-gray-900 dark:text-white">Open</p>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">{{ $monitoringDetail['related_body'] }}</p>
</div>
<div class="rounded-xl border border-gray-200 bg-white/80 p-4 dark:border-white/10 dark:bg-white/5">
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-gray-500 dark:text-gray-400">Follow-up lane</p>
<p class="mt-2 text-sm font-semibold text-gray-900 dark:text-white">{{ $monitoringDetail['follow_up_label'] ?? 'No follow-up action' }}</p>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">{{ $monitoringDetail['follow_up_body'] }}</p>
</div>
</div>
</x-filament::section>
</div>
</x-filament-panels::page>