CluPilotCloud/resources/views/livewire/admin/maintenance.blade.php

180 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<div class="space-y-6">
<div class="animate-rise">
<h1 class="text-2xl font-bold tracking-tight text-ink">{{ __('maintenance.title') }}</h1>
<p class="mt-1 text-sm text-muted">{{ __('maintenance.subtitle') }}</p>
</div>
<div class="grid grid-cols-1 gap-6 lg:grid-cols-[1fr_420px] lg:items-start">
{{-- Windows list --}}
<div class="overflow-hidden rounded-lg border border-line bg-surface shadow-xs animate-rise [animation-delay:60ms]">
@if ($windows->isEmpty())
<p class="p-8 text-center text-sm text-muted">{{ __('maintenance.empty') }}</p>
@else
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-line bg-surface-2 text-left text-xs font-semibold text-muted">
<th class="px-4 py-3 font-semibold">{{ __('maintenance.col_window') }}</th>
<th class="px-4 py-3 font-semibold">{{ __('maintenance.col_when') }}</th>
<th class="px-4 py-3 font-semibold">{{ __('maintenance.col_impact') }}</th>
<th class="px-4 py-3 font-semibold">{{ __('maintenance.col_state') }}</th>
<th class="px-4 py-3 text-right font-semibold">{{ __('maintenance.col_actions') }}</th>
</tr>
</thead>
<tbody>
@foreach ($windows as $w)
@php $tone = ['draft' => 'info', 'upcoming' => 'provisioning', 'active' => 'warning', 'completed' => 'active', 'cancelled' => 'suspended'][$w['state']] ?? 'info'; @endphp
<tr wire:key="mw-{{ $w['uuid'] }}" class="border-b border-line last:border-0 hover:bg-surface-hover">
<td class="px-4 py-3 font-medium text-ink">{{ $w['title'] }}</td>
<td class="px-4 py-3 text-xs text-body">{{ $w['starts_at']->local()->isoFormat('DD.MM. HH:mm') }} {{ $w['ends_at']->local()->isoFormat('HH:mm') }}</td>
<td class="px-4 py-3 text-xs text-muted whitespace-nowrap">
{{ trans_choice('maintenance.impact_hosts', $w['hosts'], ['count' => $w['hosts']]) }}
<span class="text-muted">·</span>
{{ trans_choice('maintenance.impact_customers', $w['affected'], ['count' => $w['affected']]) }}
</td>
<td class="px-4 py-3"><x-ui.badge :status="$tone">{{ __('maintenance.state_'.$w['state']) }}</x-ui.badge></td>
<td class="px-4 py-3 text-right">
{{-- Icon buttons: the labels wrapped to two lines and made the
column unreadable. Same pattern as the other admin tables. --}}
<div class="inline-flex gap-1">
@if ($w['is_draft'])
<button type="button" wire:click="publishExisting('{{ $w['uuid'] }}')"
title="{{ __('maintenance.publish') }}" aria-label="{{ __('maintenance.publish') }}"
class="grid size-8 place-items-center rounded-md border border-line text-muted transition hover:border-accent-border hover:text-accent-text">
<x-ui.icon name="check" class="size-4" />
</button>
@endif
@if ($w['notifiable'])
<button type="button" wire:click="resend('{{ $w['uuid'] }}')"
title="{{ __('maintenance.notify_again') }}" aria-label="{{ __('maintenance.notify_again') }}"
class="grid size-8 place-items-center rounded-md border border-line text-muted transition hover:border-accent-border hover:text-accent-text">
<x-ui.icon name="bell" class="size-4" />
</button>
@endif
@if ($w['cancellable'])
<button type="button" wire:click="cancel('{{ $w['uuid'] }}')"
title="{{ __('maintenance.cancel') }}" aria-label="{{ __('maintenance.cancel') }}"
class="grid size-8 place-items-center rounded-md border border-line text-muted transition hover:border-danger hover:text-danger">
<x-ui.icon name="x" class="size-4" />
</button>
@endif
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
{{-- Create form --}}
<form wire:submit="publish" class="overflow-hidden rounded-lg border border-line bg-surface shadow-xs animate-rise [animation-delay:120ms]">
<div class="border-b border-line bg-surface-2 px-5 py-3.5">
<h2 class="font-semibold text-ink">{{ __('maintenance.new_title') }}</h2>
<p class="mt-0.5 text-xs text-muted">{{ __('maintenance.new_sub') }}</p>
</div>
<div class="space-y-5 p-5">
<x-ui.input name="title" wire:model="title" :label="__('maintenance.field_title')" placeholder="Netzwerk-Wartung Falkenstein" />
<div>
<label class="text-sm font-medium text-body" for="publicDescription">{{ __('maintenance.field_public') }}</label>
<textarea id="publicDescription" wire:model="publicDescription" rows="2" placeholder="{{ __('maintenance.field_public_ph') }}"
class="mt-1.5 w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-ink placeholder:text-muted focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/30"></textarea>
<p class="mt-1 text-xs text-muted">{{ __('maintenance.field_public_hint') }}</p>
</div>
<div>
<label class="text-sm font-medium text-body" for="internalNotes">{{ __('maintenance.field_internal') }}</label>
<textarea id="internalNotes" wire:model="internalNotes" rows="2" placeholder="{{ __('maintenance.field_internal_ph') }}"
class="mt-1.5 w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-ink placeholder:text-muted focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/30"></textarea>
</div>
<div class="space-y-3">
<div class="grid grid-cols-1 gap-3">
@foreach ([['startsAt', 'field_start'], ['endsAt', 'field_end']] as [$field, $key])
<div>
<label class="text-sm font-medium text-body" for="{{ $field }}">{{ __('maintenance.'.$key) }}</label>
<div class="relative mt-1.5">
<x-ui.icon name="calendar" class="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted" />
<input id="{{ $field }}" type="datetime-local" wire:model.live="{{ $field }}"
class="w-full rounded-md border border-line-strong bg-surface py-2 pl-9 pr-3 text-sm text-ink focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/30 [color-scheme:dark]" />
</div>
@error($field)<p class="mt-1 text-xs text-danger">{{ $message }}</p>@enderror
</div>
@endforeach
</div>
{{-- Typing an end time by hand is the fiddliest part of this form;
these set it from the start time. --}}
<div class="flex flex-wrap items-center gap-1.5">
<span class="text-xs text-muted">{{ __('maintenance.duration') }}</span>
@foreach ([30, 60, 120, 240] as $minutes)
<button type="button" wire:click="setDuration({{ $minutes }})"
class="rounded-pill border px-2.5 py-0.5 text-xs font-medium transition
{{ $durationMinutes === $minutes ? 'border-accent-border bg-accent-bg text-accent-text' : 'border-line text-muted hover:border-accent-border hover:text-accent-text' }}">
{{ $minutes < 60 ? $minutes.' min' : ($minutes / 60).' h' }}
</button>
@endforeach
@if ($durationMinutes !== null && ! in_array($durationMinutes, [30, 60, 120, 240], true))
<span class="rounded-pill border border-line-strong px-2.5 py-0.5 text-xs text-muted">{{ __('maintenance.duration_value', ['n' => $durationMinutes]) }}</span>
@endif
</div>
</div>
{{-- Host picker, grouped by datacenter --}}
<div>
<div class="flex items-baseline justify-between">
<label class="text-sm font-medium text-body">{{ __('maintenance.field_hosts') }}</label>
<span class="font-mono text-xs {{ count($hostIds) > 0 ? 'text-accent-text' : 'text-muted' }}">{{ __('maintenance.selected', ['n' => count($hostIds)]) }}</span>
</div>
@error('hostIds')<p class="mt-1 text-xs text-danger">{{ $message }}</p>@enderror
<div class="mt-1.5 max-h-64 divide-y divide-line overflow-y-auto rounded-lg border border-line-strong bg-surface-2">
@foreach ($datacenters as $dc)
@php
$dcHosts = $hosts->where('datacenter', $dc->code);
$dcSelected = $dcHosts->whereIn('id', $hostIds)->count();
@endphp
@if ($dcHosts->isNotEmpty())
<div class="p-2.5">
<div class="flex items-center justify-between px-1.5 pb-1.5">
<p class="text-xs font-semibold text-muted">
{{ $dc->name }}
@if ($dcSelected)
<span class="ml-1 font-mono text-accent-text">{{ $dcSelected }}/{{ $dcHosts->count() }}</span>
@endif
</p>
<button type="button" wire:click="selectDatacenter('{{ $dc->code }}')"
class="rounded-pill border border-line px-2 py-0.5 text-[11px] font-semibold text-muted transition hover:border-accent-border hover:text-accent-text">
{{ $dcSelected === $dcHosts->count() ? __('maintenance.select_none') : __('maintenance.select_all') }}
</button>
</div>
<div class="space-y-1">
@foreach ($dcHosts as $host)
@php $checked = in_array($host->id, $hostIds); @endphp
<label class="flex cursor-pointer items-center gap-2.5 rounded-md border px-2.5 py-2 text-sm transition
{{ $checked ? 'border-accent-border bg-accent-bg text-ink' : 'border-transparent text-body hover:border-line hover:bg-surface-hover' }}">
<input type="checkbox" wire:model.live="hostIds" value="{{ $host->id }}"
class="size-4 rounded border-line-strong bg-surface text-accent-active focus:ring-2 focus:ring-accent/30" />
<span class="font-mono">{{ $host->name }}</span>
<span class="ml-auto font-mono text-xs {{ $checked ? 'text-accent-text' : 'text-muted' }}">{{ $host->public_ip }}</span>
</label>
@endforeach
</div>
</div>
@endif
@endforeach
</div>
</div>
</div>
<div class="flex flex-wrap items-center justify-end gap-2 border-t border-line bg-surface-2 px-5 py-3.5">
<p class="mr-auto max-w-[14rem] text-xs text-muted">{{ __('maintenance.mail_note') }}</p>
<x-ui.button variant="secondary" type="button" size="sm" wire:click="saveDraft" wire:loading.attr="disabled" wire:target="saveDraft">{{ __('maintenance.save_draft') }}</x-ui.button>
<x-ui.button variant="primary" type="submit" size="sm" wire:loading.attr="disabled" wire:target="publish">{{ __('maintenance.publish_notify') }}</x-ui.button>
</div>
</form>
</div>
</div>