86 lines
5.6 KiB
PHP
86 lines
5.6 KiB
PHP
@php($runs = $runs ?? collect())
|
|
@php($interval = $runs->isEmpty() ? max((int) $pollSeconds, 10) : (int) $pollSeconds)
|
|
|
|
<div wire:poll.{{ $interval }}s="loadRuns">
|
|
<!-- Bulk Operation Progress Component: {{ $runs->count() }} active runs -->
|
|
@if($runs->isNotEmpty())
|
|
<div class="fixed bottom-4 right-4 z-[999999] w-96 space-y-2" style="pointer-events: auto;">
|
|
@foreach ($runs as $run)
|
|
@php($effectiveTotal = max((int) $run->total_items, (int) $run->processed_items))
|
|
@php($percent = $effectiveTotal > 0 ? min(100, round(($run->processed_items / $effectiveTotal) * 100)) : 0)
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl border-2 border-primary-500 dark:border-primary-400 p-4 transition-all animate-in slide-in-from-right duration-300"
|
|
wire:key="run-{{ $run->id }}">
|
|
|
|
<div class="flex justify-between items-start mb-3">
|
|
<div class="flex-1">
|
|
<h4 class="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
|
{{ ucfirst($run->action) }} {{ ucfirst(str_replace('_', ' ', $run->resource)) }}
|
|
</h4>
|
|
<p class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
|
@if($run->status === 'pending')
|
|
@php($isStalePending = $run->created_at->lt(now()->subSeconds(30)))
|
|
<span class="inline-flex items-center">
|
|
<svg class="animate-spin -ml-1 mr-1.5 h-3 w-3 text-primary-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
</svg>
|
|
{{ $isStalePending ? 'Queued…' : 'Starting...' }}
|
|
</span>
|
|
@elseif($run->status === 'running')
|
|
<span class="inline-flex items-center">
|
|
<svg class="animate-spin -ml-1 mr-1.5 h-3 w-3 text-primary-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
</svg>
|
|
Processing...
|
|
</span>
|
|
@elseif(in_array($run->status, ['completed', 'completed_with_errors'], true))
|
|
<span class="text-success-600 dark:text-success-400">Done</span>
|
|
@elseif(in_array($run->status, ['failed', 'aborted'], true))
|
|
<span class="text-danger-600 dark:text-danger-400">Failed</span>
|
|
@endif
|
|
</p>
|
|
</div>
|
|
<div class="text-right">
|
|
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">
|
|
{{ $run->processed_items }} / {{ $effectiveTotal }}
|
|
</span>
|
|
<div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
|
{{ $percent }}%
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="w-full bg-gray-200 rounded-full h-3 dark:bg-gray-700 overflow-hidden">
|
|
<div class="bg-primary-600 dark:bg-primary-500 h-3 rounded-full transition-all duration-300 ease-out"
|
|
style="width: {{ $percent }}%"></div>
|
|
</div>
|
|
|
|
<div class="mt-2 flex items-center justify-between text-xs">
|
|
<div class="flex items-center gap-3">
|
|
@if ($run->succeeded > 0)
|
|
<span class="text-success-600 dark:text-success-400">
|
|
✓ {{ $run->succeeded }} succeeded
|
|
</span>
|
|
@endif
|
|
@if ($run->failed > 0)
|
|
<span class="text-danger-600 dark:text-danger-400">
|
|
✗ {{ $run->failed }} failed
|
|
</span>
|
|
@endif
|
|
@if ($run->skipped > 0)
|
|
<span class="text-gray-500 dark:text-gray-400">
|
|
⊘ {{ $run->skipped }} skipped
|
|
</span>
|
|
@endif
|
|
</div>
|
|
<span class="text-gray-400 dark:text-gray-500">
|
|
{{ $run->created_at->diffForHumans(null, true, true) }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</div>
|