127 lines
7.2 KiB
PHP
127 lines
7.2 KiB
PHP
@php
|
|
$field = 'w-full rounded-md border border-line bg-inset px-3 py-2 text-sm text-ink 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">
|
|
{{-- Header --}}
|
|
<div>
|
|
<p class="font-mono text-[11px] uppercase tracking-[0.2em] text-accent-text">{{ __('commands.eyebrow') }}</p>
|
|
<h2 class="mt-0.5 font-display text-xl font-semibold text-ink sm:text-2xl">{{ __('commands.title') }}</h2>
|
|
<p class="mt-1 text-sm text-ink-3">{{ __('commands.subtitle') }}</p>
|
|
</div>
|
|
|
|
{{-- Ad-hoc --}}
|
|
<x-panel :title="__('commands.adhoc_title')">
|
|
<form wire:submit="run" class="space-y-4">
|
|
<div>
|
|
<label class="{{ $label }}">{{ __('commands.command_label') }}</label>
|
|
<textarea wire:model="command" rows="2" class="{{ $field }} font-mono placeholder:text-ink-4" placeholder="{{ __('commands.command_placeholder') }}"></textarea>
|
|
@error('command') <p class="mt-1 flex items-center gap-1.5 font-mono text-[11px] text-offline"><x-icon name="alert" class="h-3.5 w-3.5 shrink-0" />{{ $message }}</p> @enderror
|
|
</div>
|
|
|
|
<div>
|
|
<label class="{{ $label }}">{{ __('commands.scope_label') }}</label>
|
|
<div class="flex flex-wrap gap-1.5">
|
|
@foreach (['all', 'group', 'servers'] as $s)
|
|
<button type="button" wire:click="$set('scopeType', '{{ $s }}')"
|
|
@class([
|
|
'inline-flex min-h-9 items-center rounded-md border px-3 text-xs transition-colors',
|
|
'border-accent/25 bg-accent/10 text-ink' => $scopeType === $s,
|
|
'border-line bg-inset text-ink-2 hover:bg-raised hover:text-ink' => $scopeType !== $s,
|
|
])>{{ __('commands.scope_'.$s) }}</button>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
@if ($scopeType === 'group')
|
|
<div>
|
|
<label class="{{ $label }}">{{ __('commands.target_group_label') }}</label>
|
|
<select wire:model="scopeGroupUuid" class="{{ $field }}">
|
|
<option value="">—</option>
|
|
@foreach ($groups as $g)
|
|
<option value="{{ $g->uuid }}">{{ $g->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
@elseif ($scopeType === 'servers')
|
|
<div>
|
|
<label class="{{ $label }}">{{ __('commands.target_servers_label') }}</label>
|
|
<div class="grid gap-1.5 sm:grid-cols-2 lg:grid-cols-3">
|
|
@foreach ($servers as $srv)
|
|
<label class="flex min-h-11 items-center gap-2.5 rounded-md border border-line bg-inset px-3 py-2 text-sm text-ink-2 transition-colors hover:bg-raised sm:min-h-0">
|
|
<input type="checkbox" value="{{ $srv->uuid }}" wire:model="selectedServers" class="h-4 w-4 shrink-0 rounded border-line bg-inset text-accent focus:ring-accent/40" />
|
|
<span class="min-w-0 truncate">{{ $srv->name }}</span>
|
|
</label>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="flex justify-end">
|
|
<x-btn variant="primary" size="lg" type="submit">
|
|
<x-icon name="command" class="h-3.5 w-3.5" /> {{ __('commands.run') }}
|
|
</x-btn>
|
|
</div>
|
|
</form>
|
|
</x-panel>
|
|
|
|
{{-- Results --}}
|
|
@if (! empty($results))
|
|
<x-panel :title="__('commands.results_title')" :padded="false">
|
|
<div class="divide-y divide-line">
|
|
@foreach ($results as $r)
|
|
<div wire:key="res-{{ $loop->index }}" class="px-4 py-3 sm:px-5">
|
|
<div class="flex items-center gap-2.5">
|
|
<x-status-dot :status="$r['ok'] ? 'online' : 'offline'" class="h-2.5 w-2.5" />
|
|
<span class="min-w-0 flex-1 truncate font-mono text-sm text-ink">{{ $r['server'] }}</span>
|
|
<x-status-pill :status="$r['ok'] ? 'online' : 'offline'">{{ $r['ok'] ? __('commands.exit_ok') : __('commands.exit_fail') }}</x-status-pill>
|
|
</div>
|
|
@if ($r['output'] !== '')
|
|
<pre class="mt-2 max-h-64 overflow-auto rounded-md border border-line bg-void p-3 font-mono text-[11px] leading-relaxed text-ink-2">{{ $r['output'] }}</pre>
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</x-panel>
|
|
@endif
|
|
|
|
{{-- Runbooks --}}
|
|
<x-panel :title="__('commands.runbooks_title')" :padded="false">
|
|
@if ($runbooks->isEmpty())
|
|
<div class="px-4 py-6 text-center sm:px-5">
|
|
<p class="font-display text-sm font-semibold text-ink">{{ __('commands.no_runbooks_title') }}</p>
|
|
<p class="mt-1 text-xs text-ink-3">{{ __('commands.no_runbooks') }}</p>
|
|
</div>
|
|
@else
|
|
<div class="divide-y divide-line">
|
|
@foreach ($runbooks as $rb)
|
|
<div wire:key="rb-{{ $rb->uuid }}" class="flex flex-wrap items-center gap-3 px-4 py-3 sm:px-5">
|
|
<div class="min-w-0 flex-1">
|
|
<p class="truncate text-sm font-medium text-ink">{{ $rb->name }}</p>
|
|
<p class="truncate font-mono text-[11px] text-ink-3">{{ $rb->command }}</p>
|
|
</div>
|
|
<x-btn variant="secondary" wire:click="runRunbook('{{ $rb->uuid }}')">{{ __('commands.run') }}</x-btn>
|
|
<x-modal-trigger variant="danger-soft" action="confirmDeleteRunbook('{{ $rb->uuid }}')">{{ __('common.delete') }}</x-modal-trigger>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
<div class="border-t border-line p-4 sm:p-5">
|
|
<p class="{{ $label }}">{{ __('commands.new_runbook') }}</p>
|
|
<form wire:submit="createRunbook" class="grid gap-3 sm:grid-cols-[minmax(0,14rem)_minmax(0,1fr)_auto] sm:items-start">
|
|
<div>
|
|
<input wire:model="runbookName" type="text" maxlength="80" placeholder="{{ __('commands.runbook_name_label') }}" class="{{ $field }} placeholder:text-ink-4" />
|
|
@error('runbookName') <p class="mt-1 font-mono text-[11px] text-offline">{{ $message }}</p> @enderror
|
|
</div>
|
|
<div>
|
|
<input wire:model="runbookCommand" type="text" maxlength="4000" placeholder="{{ __('commands.runbook_command_label') }}" class="{{ $field }} font-mono placeholder:text-ink-4" />
|
|
@error('runbookCommand') <p class="mt-1 font-mono text-[11px] text-offline">{{ $message }}</p> @enderror
|
|
</div>
|
|
<x-btn variant="primary" size="lg" type="submit">{{ __('commands.create') }}</x-btn>
|
|
</form>
|
|
</div>
|
|
</x-panel>
|
|
</div>
|