coolify/resources/views/livewire/project/service/resource-card.blade.php
Andras Bacsai fc9e61b7b4 feat(services): open resource settings in a modal with restart limits
Move service application and database settings into an embedded modal with a footer, subtitle helper, and Docker restart-count control. Accept max_restart_count on the service applications API, cap compose YAML collection aliases, and tighten status, backup, and database sidebar layouts.
2026-09-10 17:39:56 +02:00

142 lines
7.7 KiB
PHP

@php
[$statusType, $statusLabel] = match (true) {
str($resource->status)->contains('running') => ['success', formatContainerStatus($resource->status)],
str($resource->status)->contains(['starting', 'restarting', 'degraded']) => ['warning', formatContainerStatus($resource->status)],
default => ['error', formatContainerStatus($resource->status)],
};
$resourceName = $resource->human_name
? Str::headline($resource->human_name)
: Str::headline($resource->name);
@endphp
<x-modal-input title="{{ $resourceName }}"
subtitle="{{ $isApplication ? 'Identity, image, and public access for this compose application.' : 'Identity, image, and public access for this compose database.' }}"
:contentClicks="false" :wireIgnore="false" isLarge>
<x-slot:content>
<div x-data="{
openSettings(event) {
if (event.target.closest('a, button')) {
return;
}
modalOpen = true;
}
}">
<div x-cloak x-show="viewMode === 'grid'"
class="group flex min-w-0 flex-col overflow-hidden rounded-[10px] border border-neutral-200 bg-white transition-[border-color,background-color,box-shadow] hover:border-neutral-300 hover:shadow-sm dark:border-white/[0.07] dark:bg-surface dark:hover:border-white/[0.12] dark:hover:bg-white/[0.035]">
<div class="flex min-w-0 flex-1 items-start gap-3 p-4">
<div
class="flex size-9 shrink-0 items-center justify-center rounded-lg bg-neutral-100 text-neutral-500 dark:bg-white/[0.06] dark:text-fg-dim">
<x-reicon :name="$isDatabase ? 'database' : 'grid'" class="size-4" />
</div>
<div class="min-w-0 flex-1">
<div class="flex min-w-0 items-start justify-between gap-3">
<div class="min-w-0">
<div class="truncate text-sm font-semibold text-black dark:text-fg">{{ $resourceName }}</div>
<div class="mt-0.5 truncate font-mono text-xs text-neutral-500 dark:text-fg-faint">
{{ $resource->image }}
</div>
</div>
<x-status-badge :status="$statusLabel" :type="$statusType" />
<x-application.restart-limit-warning :application="$resource" />
</div>
@if ($resource->configuration_required)
<div class="mt-2">
<x-status-badge status="Configuration required" type="warning" />
</div>
@elseif ($resource->description)
<p class="mt-2 line-clamp-2 text-xs leading-5 text-neutral-500 dark:text-fg-dim">
{{ $resource->description }}
</p>
@endif
</div>
</div>
<div
class="flex items-center justify-end gap-1 border-t border-neutral-200 bg-neutral-50 px-3 py-2 dark:border-white/[0.06] dark:bg-white/[0.02]">
@if ($isDatabase && ($resource->isBackupSolutionAvailable() || $resource->is_migrated))
<a class="icon-button" title="Service backups" aria-label="Service backups" {{ wireNavigate() }}
href="{{ route('project.service.volume-backups.index', $parameters) }}">
<x-reicon name="database" class="size-4" />
</a>
@endif
@if ($isApplication && $resource->fqdn)
@can('update', $service)
<a class="icon-button" title="Manage domains" aria-label="Manage domains" {{ wireNavigate() }}
href="{{ route('project.service.domains', $parameters) }}">
<x-reicon name="globe" class="size-4" />
</a>
@endcan
@endif
<button type="button" class="icon-button" title="Resource settings" aria-label="Resource settings"
@click="modalOpen = true">
<x-reicon name="settings" class="size-4" />
</button>
@if (str($resource->status)->contains('running'))
@can('update', $service)
<x-modal-confirmation
:title="$isApplication ? 'Confirm Service Application Restart?' : 'Confirm Service Database Restart?'"
buttonTitle="Restart" submitAction="restart" :actions="$isApplication
? ['The selected service application will be unavailable during the restart.']
: ['This service database will be unavailable during the restart.']"
:confirmWithText="false" :confirmWithPassword="false"
:step2ButtonText="$isApplication ? 'Restart Service Container' : 'Restart Database'" />
@endcan
@endif
</div>
</div>
<div x-cloak x-show="viewMode === 'table'"
x-on:click="openSettings($event)" x-on:keydown.enter="openSettings($event)"
role="link" tabindex="0" aria-label="Open {{ $resourceName }} settings"
class="grid min-h-14 min-w-[48rem] cursor-pointer grid-cols-[minmax(14rem,1fr)_minmax(12rem,1fr)_12rem_5rem] items-center gap-3 border-b border-neutral-200 px-4 py-2.5 last:border-b-0 hover:bg-neutral-50 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-primary dark:border-white/[0.07] dark:hover:bg-white/[0.025]">
<div class="flex min-w-0 items-center gap-3">
<div
class="flex size-8 shrink-0 items-center justify-center rounded-lg bg-neutral-100 text-neutral-500 dark:bg-white/[0.06] dark:text-fg-dim">
<x-reicon :name="$isDatabase ? 'database' : 'grid'" class="size-4" />
</div>
<div class="min-w-0">
<div class="truncate text-[13px] font-semibold text-black dark:text-fg">{{ $resourceName }}</div>
</div>
</div>
<div class="truncate font-mono text-xs text-neutral-500 dark:text-fg-faint">
{{ $resource->image }}
</div>
<div class="flex flex-wrap items-center gap-1">
<x-status-badge :status="$statusLabel" :type="$statusType" />
<x-application.restart-limit-warning :application="$resource" />
</div>
<div class="flex items-center justify-end gap-1">
@if ($isDatabase && ($resource->isBackupSolutionAvailable() || $resource->is_migrated))
<a class="icon-button" title="Service backups" aria-label="Service backups" {{ wireNavigate() }}
href="{{ route('project.service.volume-backups.index', $parameters) }}">
<x-reicon name="database" class="size-4" />
</a>
@endif
@if ($isApplication && $resource->fqdn)
@can('update', $service)
<a class="icon-button" title="Manage domains" aria-label="Manage domains" {{ wireNavigate() }}
href="{{ route('project.service.domains', $parameters) }}">
<x-reicon name="globe" class="size-4" />
</a>
@endcan
@endif
<button type="button" class="icon-button" title="Resource settings" aria-label="Resource settings"
@click="modalOpen = true">
<x-reicon name="settings" class="size-4" />
</button>
</div>
</div>
</div>
</x-slot:content>
@if ($isApplication)
<livewire:project.service.index :serviceApplication="$resource" :embedded="true"
wire:key="service-application-settings-{{ $resource->id }}" lazy />
@else
<livewire:project.service.index :serviceApplication="$resource" :embedded="true"
wire:key="service-database-settings-{{ $resource->id }}" lazy />
@endif
</x-modal-input>