TenantAtlas/app/Livewire/BulkOperationProgress.php
ahmido 1142d283eb feat: Spec 178 — Operations Lifecycle Alignment & Cross-Surface Truth Consistency (#209)
## Spec 178 — Operations Lifecycle Alignment & Cross-Surface Truth Consistency

Härtet die Run-Lifecycle-Wahrheit und Cross-Surface-Konsistenz über alle zentralen Operator-Flächen hinweg.

### Kern-Änderungen

**Lifecycle Truth Alignment**
- Einheitliche stale/stuck-Semantik zwischen Tenant-, Workspace-, Admin- und System-Surfaces
- `OperationRunFreshnessState` wird konsistent über alle Widgets und Seiten propagiert
- Gemeinsame Problem-Klassen-Trennung: `terminal_follow_up` vs. `active_stale_attention`

**BulkOperationProgress Freshness**
- Overlay zeigt nur noch `healthyActive()` Runs statt alle aktiven Runs
- Likely-stale Runs halten das Polling nicht mehr künstlich aktiv
- Terminal Runs verschwinden zeitnah aus dem Progress-Overlay

**Decision Zone im Run Detail**
- Stale/reconciled Attention in der primären Decision-Hierarchie
- Klare Antworten: aktiv? stale? reconciled? nächster Schritt?
- Artifact-reiche Runs behalten Lifecycle-Truth vor Deep-Diagnostics

**Cross-Surface Link-Continuity**
- Dashboard → Operations Hub → Run Detail erzählen dieselbe Geschichte
- Notifications referenzieren korrekte Problem-Klasse
- Workspace/Tenant-Attention verlinken problemklassengerecht

**System-Plane Fixes**
- `/system/ops/failures` 500-Error behoben (panel-sichere Artifact-URLs)
- System-Stuck/Failures zeigen reconciled stale lineage

### Weitere Fixes
- Inventory auth guard bereinigt (Gate statt ad-hoc Facades)
- Browser-Smoke-Tests stabilisiert (DOM-Assertions statt fragile Klicks)
- Test-Assertion-Drift für Verification/Lifecycle-Texte korrigiert

### Test-Ergebnis
Full Suite: **3269 passed**, 8 skipped, 0 failed

### Spec-Artefakte
- `specs/178-ops-truth-alignment/spec.md`
- `specs/178-ops-truth-alignment/plan.md`
- `specs/178-ops-truth-alignment/tasks.md`
- `specs/178-ops-truth-alignment/research.md`
- `specs/178-ops-truth-alignment/data-model.md`
- `specs/178-ops-truth-alignment/quickstart.md`
- `specs/178-ops-truth-alignment/contracts/operations-truth-alignment.openapi.yaml`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #209
2026-04-05 22:42:24 +00:00

105 lines
2.6 KiB
PHP

<?php
namespace App\Livewire;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Support\OpsUx\ActiveRuns;
use App\Support\OpsUx\OpsUxBrowserEvents;
use Filament\Facades\Filament;
use Illuminate\Support\Collection;
use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Component;
class BulkOperationProgress extends Component
{
/**
* @var Collection<int, OperationRun>
*/
public Collection $runs;
public int $overflowCount = 0;
public bool $disabled = false;
public bool $hasActiveRuns = false;
public ?int $tenantId = null;
public function mount(): void
{
$this->runs = collect();
$tenant = Filament::getTenant();
$this->tenantId = $tenant instanceof Tenant ? (int) $tenant->id : null;
$this->refreshRuns();
}
#[On(OpsUxBrowserEvents::RunEnqueued)]
public function onRunEnqueued(?int $tenantId = null): void
{
if ($tenantId !== null) {
$this->tenantId = $tenantId;
}
$this->refreshRuns();
}
#[Computed]
public function activeRuns()
{
return $this->runs;
}
public function refreshRuns(): void
{
$tenantId = $this->tenantId;
// Best-effort: if we're mounted on a tenant page, capture it once.
if ($tenantId === null) {
$tenant = Filament::getTenant();
$tenantId = $tenant instanceof Tenant ? (int) $tenant->id : null;
$this->tenantId = $tenantId;
}
if ($tenantId === null) {
$this->disabled = true;
$this->runs = collect();
$this->overflowCount = 0;
$this->hasActiveRuns = false;
return;
}
if (! auth()->user()?->can('viewAny', OperationRun::class)) {
$this->disabled = true;
$this->runs = collect();
$this->overflowCount = 0;
$this->hasActiveRuns = false;
return;
}
$this->disabled = false;
$query = OperationRun::query()
->where('tenant_id', $tenantId)
->healthyActive()
->orderByDesc('created_at');
$activeCount = (clone $query)->count();
$this->runs = (clone $query)->limit(6)->get();
$this->overflowCount = max(0, $activeCount - 5);
$this->hasActiveRuns = ActiveRuns::existForTenantId($tenantId);
}
public function render(): \Illuminate\Contracts\View\View
{
return view('livewire.bulk-operation-progress', [
'tenant' => Filament::getTenant(),
]);
}
}