feat(admin): compact redesign of /admin/provisioning current-run panel

Progress bar + X/total, highlighted current step, faint next step, failure
inline — replaces the long repetitive per-step 'done' list. Tactical-Terminal
tokens, DE+EN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/portal-design
nexxo 2026-07-25 13:31:49 +02:00
parent 7c1a153446
commit eabcb98f91
4 changed files with 76 additions and 7 deletions

View File

@ -53,8 +53,31 @@ class Provisioning extends Component
'attempt' => $r->attempt,
'state' => $this->runState($r),
])->all(),
'steps' => $active ? $this->buildRunSteps($active) : [],
'activeLabel' => $active ? $this->subjectLabel($active).' · '.$active->pipeline : '—',
'panel' => $active ? $this->panelFor($active) : null,
]);
}
/** Compact "current run" readout for the right column. */
private function panelFor(ProvisioningRun $run): array
{
$pipeline = config('provisioning.pipelines.'.$run->pipeline, []);
$total = max(count($pipeline), 1);
$current = min($run->current_step, $total - 1);
$completed = $run->status === ProvisioningRun::STATUS_COMPLETED;
$done = $completed ? $total : $current;
return [
'subject' => $this->subjectLabel($run),
'pipeline' => $run->pipeline,
'attempt' => $run->attempt,
'status' => $this->runState($run), // running|done|failed
'failed' => $run->status === ProvisioningRun::STATUS_FAILED,
'current' => $completed ? null : (isset($pipeline[$current]) ? __(app($pipeline[$current])->label()) : null),
'next' => isset($pipeline[$current + 1]) && ! $completed ? __(app($pipeline[$current + 1])->label()) : null,
'error' => $run->error,
'done' => $done,
'total' => $total,
'percent' => (int) round($done / $total * 100),
];
}
}

View File

@ -37,6 +37,10 @@ return [
'view_all' => 'Alle ansehen',
'alerts' => 'Hinweise',
'run_detail' => 'Aktueller Lauf',
'run_attempt' => 'Versuch :n',
'run_done' => ':done von :total abgeschlossen',
'run_current' => 'Aktueller Schritt',
'run_next' => 'Als Nächstes',
'run' => [
'deploy' => 'DeployApplicationStack',

View File

@ -37,6 +37,10 @@ return [
'view_all' => 'View all',
'alerts' => 'Alerts',
'run_detail' => 'Current run',
'run_attempt' => 'Attempt :n',
'run_done' => ':done of :total complete',
'run_current' => 'Current step',
'run_next' => 'Up next',
'run' => [
'deploy' => 'DeployApplicationStack',

View File

@ -38,12 +38,50 @@
</div>
<div class="rounded-xl border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:120ms]">
<h2 class="font-semibold text-ink">{{ __('admin.run_detail') }}</h2>
<p class="mb-3 font-mono text-xs text-muted">{{ $activeLabel }}</p>
@if (count($steps) > 0)
<x-ui.progress-stepper :steps="$steps" />
<div class="flex items-center justify-between">
<h2 class="text-xs font-semibold uppercase tracking-wider text-faint">{{ __('admin.run_detail') }}</h2>
@if ($panel)
<span class="font-mono text-xs {{ $panel['failed'] ? 'text-danger' : 'text-muted' }}">{{ $panel['percent'] }}%</span>
@endif
</div>
@if (! $panel)
<p class="mt-4 text-sm text-muted">{{ __('admin.no_runs') }}</p>
@else
<p class="text-sm text-muted">{{ __('admin.no_runs') }}</p>
<p class="mt-2 truncate font-semibold text-ink">{{ $panel['subject'] }}</p>
<p class="font-mono text-xs text-faint">{{ $panel['pipeline'] }} · {{ __('admin.run_attempt', ['n' => $panel['attempt']]) }}</p>
{{-- Progress bar (width is the one allowed inline style, R4) --}}
<div class="mt-4 h-1.5 overflow-hidden rounded-pill bg-surface-2">
<div class="h-full rounded-pill {{ $panel['failed'] ? 'bg-danger' : 'bg-accent' }} transition-[width] duration-500"
style="width: {{ $panel['percent'] }}%"></div>
</div>
<p class="mt-1.5 font-mono text-xs text-muted">{{ __('admin.run_done', ['done' => $panel['done'], 'total' => $panel['total']]) }}</p>
@if ($panel['failed'])
<div class="mt-4 rounded-lg border border-danger-border bg-danger-bg p-3">
<p class="text-xs font-semibold text-danger">{{ __('admin.state_failed') }}</p>
@if ($panel['error'])<p class="mt-0.5 break-words text-xs text-body">{{ $panel['error'] }}</p>@endif
</div>
@elseif ($panel['current'])
<div class="mt-4">
<p class="text-[0.65rem] font-semibold uppercase tracking-wider text-faint">{{ __('admin.run_current') }}</p>
<div class="mt-1.5 flex items-center gap-2.5">
<span class="size-2 shrink-0 rounded-pill bg-info motion-safe:animate-pulse" aria-hidden="true"></span>
<span class="text-sm font-medium text-ink">{{ $panel['current'] }}</span>
</div>
</div>
@endif
@if ($panel['next'] && ! $panel['failed'])
<div class="mt-4">
<p class="text-[0.65rem] font-semibold uppercase tracking-wider text-faint">{{ __('admin.run_next') }}</p>
<div class="mt-1.5 flex items-center gap-2.5">
<span class="size-2 shrink-0 rounded-pill border border-line-strong" aria-hidden="true"></span>
<span class="text-sm text-muted">{{ $panel['next'] }}</span>
</div>
</div>
@endif
@endif
</div>
</div>