107 lines
6.6 KiB
PHP
107 lines
6.6 KiB
PHP
@php
|
|
// Colour token key -> literal @theme bg utility (literal strings so Tailwind's source scan
|
|
// generates them; a dynamic bg-{{ $var }} would never be emitted). R3: tokens only, no hex.
|
|
$dot = [
|
|
'accent' => 'bg-accent', 'cyan' => 'bg-cyan', 'online' => 'bg-online',
|
|
'warning' => 'bg-warning', 'offline' => 'bg-offline', 'ink' => 'bg-ink-3',
|
|
];
|
|
@endphp
|
|
|
|
<div class="space-y-5">
|
|
{{-- 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">{{ __('groups.eyebrow') }}</p>
|
|
<h2 class="mt-0.5 font-display text-xl font-semibold text-ink sm:text-2xl">{{ __('groups.title') }}</h2>
|
|
<p class="mt-1 text-sm text-ink-3">{{ __('groups.subtitle') }}</p>
|
|
</div>
|
|
<x-btn :href="route('servers.index')" wire:navigate>
|
|
<x-icon name="server" class="h-3.5 w-3.5" /> {{ __('groups.back_to_servers') }}
|
|
</x-btn>
|
|
</div>
|
|
|
|
{{-- New group --}}
|
|
<x-panel :title="__('groups.new_group')">
|
|
<form wire:submit="create" class="grid gap-4 sm:grid-cols-[minmax(0,1fr)_auto_auto] sm:items-end">
|
|
<div>
|
|
<label class="mb-1 block font-mono text-[11px] uppercase tracking-wider text-ink-3">{{ __('groups.name_label') }}</label>
|
|
<input wire:model="name" type="text" maxlength="60" placeholder="{{ __('groups.name_placeholder') }}"
|
|
class="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" />
|
|
@error('name') <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="mb-1 block font-mono text-[11px] uppercase tracking-wider text-ink-3">{{ __('groups.color_label') }}</label>
|
|
<div class="flex h-9 items-center gap-1.5">
|
|
@foreach ($colors as $c)
|
|
<button type="button" wire:click="$set('color', '{{ $c }}')"
|
|
aria-label="{{ $c }}" aria-pressed="{{ $color === $c ? 'true' : 'false' }}"
|
|
@class(['grid h-6 w-6 place-items-center rounded-full transition', 'ring-2 ring-offset-2 ring-offset-surface ring-ink' => $color === $c])>
|
|
<span class="h-3.5 w-3.5 rounded-full {{ $dot[$c] }}"></span>
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
<x-btn variant="primary" type="submit">{{ __('groups.create') }}</x-btn>
|
|
</form>
|
|
</x-panel>
|
|
|
|
{{-- Groups list --}}
|
|
@if ($groups->isEmpty())
|
|
<x-panel>
|
|
<div class="flex flex-col items-center gap-2 py-8 text-center">
|
|
<x-icon name="folder" class="h-6 w-6 text-ink-4" />
|
|
<p class="font-display text-sm font-semibold text-ink">{{ __('groups.empty_title') }}</p>
|
|
<p class="max-w-sm text-xs text-ink-3">{{ __('groups.empty') }}</p>
|
|
</div>
|
|
</x-panel>
|
|
@else
|
|
<div class="space-y-4">
|
|
@foreach ($groups as $g)
|
|
<x-panel wire:key="group-{{ $g->uuid }}">
|
|
<div class="flex flex-wrap items-center gap-3">
|
|
<span class="h-3 w-3 shrink-0 rounded-full {{ $dot[$g->color] ?? 'bg-accent' }}"></span>
|
|
<input type="text" value="{{ $g->name }}" maxlength="60"
|
|
wire:change="rename('{{ $g->uuid }}', $event.target.value)"
|
|
class="min-w-0 flex-1 rounded-md border border-transparent bg-transparent px-2 py-1 font-display text-sm font-semibold text-ink hover:border-line focus:border-accent/40 focus:bg-inset focus:outline-none" />
|
|
<span class="shrink-0 font-mono text-[11px] text-ink-3">{{ trans_choice('groups.members_count', $g->servers_count, ['count' => $g->servers_count]) }}</span>
|
|
|
|
{{-- Recolour --}}
|
|
<div class="flex shrink-0 items-center gap-1">
|
|
@foreach ($colors as $c)
|
|
<button type="button" wire:click="setColor('{{ $g->uuid }}', '{{ $c }}')"
|
|
aria-label="{{ $c }}" aria-pressed="{{ $g->color === $c ? 'true' : 'false' }}"
|
|
@class(['grid h-5 w-5 place-items-center rounded-full transition', 'ring-2 ring-offset-2 ring-offset-surface ring-ink' => $g->color === $c])>
|
|
<span class="h-3 w-3 rounded-full {{ $dot[$c] }}"></span>
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
|
|
<x-modal-trigger variant="danger-soft" action="confirmDelete('{{ $g->uuid }}')">{{ __('common.delete') }}</x-modal-trigger>
|
|
</div>
|
|
|
|
{{-- Membership grid --}}
|
|
<div class="mt-4 border-t border-line pt-4">
|
|
<p class="mb-2 font-mono text-[11px] uppercase tracking-wider text-ink-3">{{ __('groups.assign') }}</p>
|
|
@if ($servers->isEmpty())
|
|
<p class="font-mono text-[11px] text-ink-3">{{ __('groups.no_servers') }}</p>
|
|
@else
|
|
<div class="grid gap-1.5 sm:grid-cols-2 lg:grid-cols-3">
|
|
@php($memberIds = $g->servers->pluck('id'))
|
|
@foreach ($servers as $s)
|
|
<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" wire:key="member-{{ $g->uuid }}-{{ $s->uuid }}"
|
|
@checked($memberIds->contains($s->id))
|
|
wire:click="toggleMember('{{ $g->uuid }}', '{{ $s->uuid }}')"
|
|
class="h-4 w-4 shrink-0 rounded border-line bg-inset text-accent focus:ring-accent/40" />
|
|
<span class="min-w-0 truncate">{{ $s->name }}</span>
|
|
</label>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</x-panel>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</div>
|