83 lines
4.7 KiB
PHP
83 lines
4.7 KiB
PHP
@php
|
|
$field = 'h-9 w-full rounded-md border border-line bg-inset px-3 text-sm text-ink placeholder:text-ink-4 focus:border-accent/40 focus:outline-none';
|
|
$label = 'mb-1 block font-mono text-[11px] uppercase tracking-wider text-ink-3';
|
|
@endphp
|
|
|
|
<div class="space-y-5" @if (! $ready) wire:init="scan" @endif>
|
|
{{-- Header --}}
|
|
<div class="flex flex-wrap items-end justify-between gap-3">
|
|
<div>
|
|
<p class="font-mono text-[11px] uppercase tracking-[0.2em] text-accent-text">{{ __('health.eyebrow') }}</p>
|
|
<h2 class="mt-0.5 font-display text-xl font-semibold text-ink sm:text-2xl">{{ __('health.title') }}</h2>
|
|
<p class="mt-1 text-sm text-ink-3">{{ __('health.subtitle') }}</p>
|
|
</div>
|
|
@if ($ready && $total > 0)
|
|
<x-status-pill :status="$up === $total ? 'online' : 'offline'">{{ __('health.up_of', ['up' => $up, 'total' => $total]) }}</x-status-pill>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- Add check --}}
|
|
<x-panel :title="__('health.new_check')">
|
|
<form wire:submit="addCheck" class="grid gap-3 sm:grid-cols-[minmax(0,10rem)_7rem_minmax(0,1fr)_6rem_auto] sm:items-end">
|
|
<div>
|
|
<label class="{{ $label }}">{{ __('health.label_label') }}</label>
|
|
<input wire:model="label" type="text" maxlength="60" class="{{ $field }}" />
|
|
</div>
|
|
<div>
|
|
<label class="{{ $label }}">{{ __('health.type_label') }}</label>
|
|
<select wire:model.live="type" class="{{ $field }}">
|
|
<option value="http">{{ __('health.type_http') }}</option>
|
|
<option value="tcp">{{ __('health.type_tcp') }}</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="{{ $label }}">{{ __('health.target_label') }}</label>
|
|
<input wire:model="target" type="text" maxlength="2048" class="{{ $field }} font-mono"
|
|
placeholder="{{ $type === 'tcp' ? __('health.target_tcp_placeholder') : __('health.target_http_placeholder') }}" />
|
|
@error('target') <p class="mt-1 font-mono text-[11px] text-offline">{{ $message }}</p> @enderror
|
|
</div>
|
|
<div @class(['invisible' => $type !== 'tcp'])>
|
|
<label class="{{ $label }}">{{ __('health.port_label') }}</label>
|
|
<input wire:model="port" type="number" min="1" max="65535" class="{{ $field }} font-mono" />
|
|
@error('port') <p class="mt-1 font-mono text-[11px] text-offline">{{ $message }}</p> @enderror
|
|
</div>
|
|
<x-btn variant="primary" size="lg" type="submit">{{ __('health.add') }}</x-btn>
|
|
</form>
|
|
</x-panel>
|
|
|
|
{{-- Checks --}}
|
|
@if ($checks->isEmpty())
|
|
<x-panel>
|
|
<div class="py-8 text-center">
|
|
<p class="font-display text-sm font-semibold text-ink">{{ __('health.no_checks_title') }}</p>
|
|
<p class="mt-1 max-w-sm text-xs text-ink-3">{{ __('health.no_checks') }}</p>
|
|
</div>
|
|
</x-panel>
|
|
@else
|
|
<x-panel :padded="false">
|
|
<div class="divide-y divide-line">
|
|
@foreach ($checks as $check)
|
|
@php($r = $results[$check->uuid] ?? null)
|
|
<div wire:key="hc-{{ $check->uuid }}" class="flex flex-wrap items-center gap-3 px-4 py-3 sm:px-5">
|
|
<x-status-dot :status="$r ? ($r['ok'] ? 'online' : 'offline') : 'pending'" :ping="$r && $r['ok']" class="h-2.5 w-2.5" />
|
|
<div class="min-w-0 flex-1">
|
|
<p class="truncate text-sm text-ink">{{ $check->label ?: $check->target }}</p>
|
|
<p class="truncate font-mono text-[11px] text-ink-3">
|
|
<span class="uppercase">{{ $check->type }}</span> · {{ $check->target }}@if ($check->type === 'tcp'):{{ $check->port }}@endif
|
|
@if ($r) · {{ $r['detail'] }} @endif
|
|
</p>
|
|
</div>
|
|
@if ($r && ! is_null($r['latency']))
|
|
<span class="shrink-0 font-mono text-[11px] text-ink-4">{{ __('health.latency', ['ms' => $r['latency']]) }}</span>
|
|
@endif
|
|
@if ($r)
|
|
<x-status-pill :status="$r['ok'] ? 'online' : 'offline'">{{ $r['ok'] ? __('health.status_up') : __('health.status_down') }}</x-status-pill>
|
|
@endif
|
|
<x-modal-trigger variant="danger-soft" action="confirmDeleteCheck('{{ $check->uuid }}')">{{ __('health.delete') }}</x-modal-trigger>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</x-panel>
|
|
@endif
|
|
</div>
|