pollSeconds = max(1, min(10, (int) config('tenantpilot.bulk_operations.poll_interval_seconds', 3))); $this->recentFinishedSeconds = max(3, min(60, (int) config('tenantpilot.bulk_operations.recent_finished_seconds', 12))); $this->loadRuns(); } #[Computed] public function activeRuns() { return $this->runs; } public function loadRuns() { try { $tenant = Tenant::current(); } catch (\RuntimeException $e) { $this->runs = collect(); return; } $recentThreshold = now()->subSeconds($this->recentFinishedSeconds); $this->runs = BulkOperationRun::query() ->where('tenant_id', $tenant->id) ->where('user_id', auth()->id()) ->where(function ($query) use ($recentThreshold): void { $query->whereIn('status', ['pending', 'running']) ->orWhere(function ($query) use ($recentThreshold): void { $query->whereIn('status', ['completed', 'completed_with_errors', 'failed', 'aborted']) ->where('updated_at', '>=', $recentThreshold); }); }) ->orderByDesc('created_at') ->get(); $this->reconcileBackupScheduleRuns($tenant->id); } private function reconcileBackupScheduleRuns(int $tenantId): void { $userId = auth()->id(); if (! $userId) { return; } $staleThreshold = now()->subSeconds(60); foreach ($this->runs as $bulkRun) { if ($bulkRun->resource !== 'backup_schedule') { continue; } if (! in_array($bulkRun->status, ['pending', 'running'], true)) { continue; } if (! $bulkRun->created_at || $bulkRun->created_at->gt($staleThreshold)) { continue; } $scheduleId = (int) Arr::first($bulkRun->item_ids ?? []); if ($scheduleId <= 0) { continue; } $scheduleRun = BackupScheduleRun::query() ->where('tenant_id', $tenantId) ->where('user_id', $userId) ->where('backup_schedule_id', $scheduleId) ->where('created_at', '>=', $bulkRun->created_at) ->orderByDesc('id') ->first(); if (! $scheduleRun) { continue; } if ($scheduleRun->finished_at) { $processed = 1; $succeeded = 0; $failed = 0; $skipped = 0; $status = 'completed'; switch ($scheduleRun->status) { case BackupScheduleRun::STATUS_SUCCESS: $succeeded = 1; break; case BackupScheduleRun::STATUS_SKIPPED: $skipped = 1; break; default: $failed = 1; $status = 'completed_with_errors'; break; } $bulkRun->forceFill([ 'status' => $status, 'processed_items' => $processed, 'succeeded' => $succeeded, 'failed' => $failed, 'skipped' => $skipped, ])->save(); continue; } if ($scheduleRun->started_at && $bulkRun->status === 'pending') { $bulkRun->forceFill(['status' => 'running'])->save(); } } } public function render(): \Illuminate\Contracts\View\View { return view('livewire.bulk-operation-progress'); } }