137 lines
7.9 KiB
PHP
137 lines
7.9 KiB
PHP
<div class="space-y-5">
|
||
<div class="flex flex-wrap items-center gap-x-4 gap-y-1 animate-rise">
|
||
<h1 class="text-2xl font-bold tracking-tight text-ink">{{ __('admin.overview_title') }}</h1>
|
||
{{-- Derived from the notice list below, never asserted: a green badge
|
||
that is not computed is the most expensive kind of decoration. --}}
|
||
@if ($noticeCount === 0)
|
||
<span class="ml-auto inline-flex items-center gap-2 rounded-pill bg-success-bg px-3.5 py-1.5 text-xs font-semibold text-success">
|
||
<span class="size-2 rounded-pill bg-success-bright" aria-hidden="true"></span>{{ __('admin.systems_ok') }}
|
||
</span>
|
||
@else
|
||
<span class="ml-auto inline-flex items-center gap-2 rounded-pill bg-warning-bg px-3.5 py-1.5 text-xs font-semibold text-warning">
|
||
<x-ui.icon name="alert-triangle" class="size-3.5" />{{ __('admin.systems_notices', ['n' => $noticeCount]) }}
|
||
</span>
|
||
@endif
|
||
<p class="w-full text-sm text-muted">{{ __('admin.overview_sub') }}</p>
|
||
</div>
|
||
|
||
{{-- KPI row --}}
|
||
@php $kd = ['[animation-delay:40ms]', '[animation-delay:80ms]', '[animation-delay:120ms]', '[animation-delay:160ms]']; @endphp
|
||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||
@foreach ($kpis as $i => $kpi)
|
||
<div class="rounded-lg border border-line bg-surface p-5 shadow-xs animate-rise {{ $kd[$i] ?? '' }}">
|
||
<p class="text-xs font-semibold text-muted">{{ $kpi['label'] }}</p>
|
||
<p class="mt-2 font-mono text-2xl font-semibold text-ink">{{ $kpi['value'] }}</p>
|
||
@if (($kpi['sub'] ?? null) !== null)
|
||
<p class="mt-1 text-xs text-muted">{{ $kpi['sub'] }}</p>
|
||
@endif
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
|
||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-3">
|
||
{{-- New instances per month --}}
|
||
<div class="rounded-lg border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:60ms] lg:col-span-2">
|
||
<h2 class="font-semibold text-ink">{{ __('admin.new_instances') }}</h2>
|
||
<p class="text-sm text-muted">{{ __('admin.new_instances_sub') }}</p>
|
||
@if ($newInstances['empty'])
|
||
<p class="mt-8 text-center text-sm text-muted">{{ __('admin.no_data_yet') }}</p>
|
||
@else
|
||
<div class="mt-4 h-56" wire:ignore><x-ui.chart :config="$newInstances['config']" class="h-56" :label="__('admin.new_instances')" /></div>
|
||
@endif
|
||
</div>
|
||
|
||
{{-- Committed storage per host --}}
|
||
<div class="flex flex-col rounded-lg border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:120ms]">
|
||
<h2 class="font-semibold text-ink">{{ __('admin.host_load') }}</h2>
|
||
<p class="text-sm text-muted">{{ __('admin.host_load_sub') }}</p>
|
||
<ul class="mt-4 flex-1 space-y-3.5">
|
||
@forelse ($hostLoad as $h)
|
||
@php $bar = ['ok' => 'bg-success-bright', 'warn' => 'bg-accent-active', 'high' => 'bg-danger', 'unknown' => 'bg-line-strong'][$h['level']] ?? 'bg-accent-active'; @endphp
|
||
<li>
|
||
<div class="flex items-baseline justify-between gap-3 text-sm">
|
||
<span class="font-mono text-ink">{{ $h['name'] }}</span>
|
||
<span class="font-mono text-xs text-muted">{{ $h['detail'] }}</span>
|
||
</div>
|
||
<div class="mt-1.5 h-2 overflow-hidden rounded-pill bg-surface-2">
|
||
<div class="h-full rounded-pill {{ $bar }}" style="width: {{ $h['pct'] }}%"></div>
|
||
</div>
|
||
</li>
|
||
@empty
|
||
<li class="text-sm text-muted">{{ __('admin.no_hosts_yet') }}</li>
|
||
@endforelse
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- What we can promise per package, and who is waiting for a machine.
|
||
|
||
The same answer the price sheet gives a visitor, from the same source,
|
||
so the operator can see on the front page what is being promised out
|
||
there. "Bereitstellung in 2–3 Werktagen" is not a failure state — the
|
||
order is taken and parked, and the queue is the shopping list. --}}
|
||
@if ($delivery['plans'] !== [])
|
||
<div class="rounded-lg border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:60ms]">
|
||
<div class="flex flex-wrap items-center gap-x-4 gap-y-1">
|
||
<h2 class="font-semibold text-ink">{{ __('admin.delivery_title') }}</h2>
|
||
@if ($delivery['queued'] > 0)
|
||
<x-ui.badge status="warning">{{ trans_choice('capacity.queue_count', $delivery['queued'], ['count' => $delivery['queued']]) }}</x-ui.badge>
|
||
@endif
|
||
<a href="{{ route('admin.capacity') }}" class="ml-auto text-xs font-semibold text-accent-text hover:underline">{{ __('admin.delivery_link') }} ›</a>
|
||
</div>
|
||
<p class="text-sm text-muted">{{ __('admin.delivery_sub') }}</p>
|
||
|
||
<div class="mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
||
@foreach ($delivery['plans'] as $plan)
|
||
<div class="rounded-md border border-line bg-surface-2 px-4 py-3">
|
||
<div class="flex items-baseline justify-between gap-3">
|
||
<span class="text-sm font-medium text-ink">{{ $plan['name'] }}</span>
|
||
<span class="font-mono text-xs tabular-nums text-muted">{{ $plan['needs'] }} GB</span>
|
||
</div>
|
||
<x-ui.delivery :state="$plan['state']" class="mt-2" />
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
</div>
|
||
@endif
|
||
|
||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||
{{-- Open provisioning runs --}}
|
||
<div class="rounded-lg border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:60ms]">
|
||
<div class="flex items-center">
|
||
<h2 class="font-semibold text-ink">{{ __('admin.active_runs') }}</h2>
|
||
<a href="{{ route('admin.provisioning') }}" class="ml-auto text-xs font-semibold text-accent-text hover:underline">{{ __('admin.view_all') }} ›</a>
|
||
</div>
|
||
<ul class="mt-2 divide-y divide-line">
|
||
@forelse ($runs as $r)
|
||
<li class="flex items-center gap-3 py-2.5 text-sm">
|
||
<span class="size-2 shrink-0 rounded-pill bg-info" aria-hidden="true"></span>
|
||
<div class="min-w-0 flex-1">
|
||
<p class="truncate font-medium text-ink">{{ $r['subject'] }}</p>
|
||
<p class="font-mono text-xs text-muted">{{ $r['step'] }}</p>
|
||
</div>
|
||
<x-ui.badge :status="$r['status'] === 'failed' ? 'error' : 'provisioning'">{{ __('admin.run_status.'.$r['status']) }}</x-ui.badge>
|
||
</li>
|
||
@empty
|
||
<li class="py-3 text-sm text-muted">{{ __('admin.no_open_runs') }}</li>
|
||
@endforelse
|
||
</ul>
|
||
</div>
|
||
|
||
{{-- Notices --}}
|
||
<div class="rounded-lg border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:120ms]">
|
||
<h2 class="font-semibold text-ink">{{ __('admin.alerts') }}</h2>
|
||
<ul class="mt-2 space-y-2">
|
||
@forelse ($notices as $a)
|
||
<li class="flex items-start gap-2.5 text-sm">
|
||
<x-ui.icon name="alert-triangle" class="mt-0.5 size-4 shrink-0 text-warning" />
|
||
<span class="text-body">{{ $a['text'] }}</span>
|
||
</li>
|
||
@empty
|
||
<li class="py-1 text-sm text-muted">{{ __('admin.no_notices') }}</li>
|
||
@endforelse
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|