feat(ui): unify status summaries, tooltips, and resource headings

Add shared status-summary and icon-tooltip components and wire them into
application, service, and database headings, breadcrumbs, and previews.
Badge production and PR domain links, prefer helper popups below the
trigger, keep full wire targets for button loading, and cover the UI
with feature tests.
This commit is contained in:
Andras Bacsai committed 2026-08-06 18:15:58 +02:00
1 parent 6b41899d5b
commit 262ad8981a
35 files changed
+749 -265

No files matched your search

@@ -0,0 +1,94 @@
@props(['status', 'title' => 'Application status', 'containerName' => 'Container'])
@php
$rawStatus = str((string) $status)->lower()->trim()->value();
$statusParts = explode(':', $rawStatus);
$containerStatus = $statusParts[0] ?? 'unknown';
$healthStatus = $statusParts[1] ?? null;
$monitoringExcluded = in_array('excluded', $statusParts, true);
if ($healthStatus === null && preg_match('/^([^()]+)\(([^)]+)\)$/', $rawStatus, $matches)) {
$containerStatus = trim($matches[1]);
$healthStatus = trim($matches[2]);
}
$containerLabel = str($containerStatus ?: 'unknown')->headline()->value();
$healthLabel = match (true) {
$monitoringExcluded => 'Monitoring disabled',
$healthStatus === 'healthy' => 'Healthy',
$healthStatus === 'unhealthy' => 'Unhealthy',
$healthStatus === 'starting' => 'Starting',
default => 'Not configured',
};
$containerType = match (true) {
str($containerStatus)->startsWith('running') => 'success',
str($containerStatus)->startsWith(['starting', 'restarting']) => 'warning',
default => 'error',
};
$healthType = match (true) {
$monitoringExcluded => 'warning',
$healthStatus === 'healthy' => 'success',
$healthStatus === 'unhealthy' => 'error',
default => 'warning',
};
[$summaryLabel, $summaryType] = match (true) {
$containerType === 'error' => [$containerLabel, 'error'],
$healthType === 'error' => ['Degraded', 'error'],
$containerType === 'warning' => [$containerLabel, 'warning'],
$monitoringExcluded => ["{$containerLabel} (monitoring disabled)", 'warning'],
$healthStatus === 'starting' => ["{$containerLabel} (health check starting)", 'warning'],
$healthType === 'warning' => ["{$containerLabel} (no health check)", 'warning'],
default => [$containerLabel, 'success'],
};
@endphp
<div class="relative shrink-0" x-data="{ open: false }" @click.outside="open = false"
@keydown.escape.window="open = false">
<x-status-badge as="button" dynamic @click="open = !open" x-bind:aria-expanded="open" aria-haspopup="menu"
class="cursor-pointer">
<span @class([
'size-1.5 shrink-0 rounded-full',
'bg-success' => $summaryType === 'success',
'bg-warning' => $summaryType === 'warning',
'bg-error' => $summaryType === 'error',
])></span>
<span>{{ $summaryLabel }}</span>
<span class="inline-flex transition-transform" :class="open && 'rotate-180'">
<x-reicon name="chevron-down" class="size-3 opacity-55" />
</span>
</x-status-badge>
<div x-cloak x-show="open" x-transition.origin.top.left
class="listbox-panel top-8! right-auto! left-0! z-[90]! w-64! min-w-64!" role="menu">
<div class="px-3 py-2 text-[11px] font-medium text-neutral-400 dark:text-fg-faint">{{ $title }}</div>
<div class="listbox-option cursor-default! gap-2.5!">
<span @class([
'size-1.5 shrink-0 rounded-full',
'bg-success' => $containerType === 'success',
'bg-warning' => $containerType === 'warning',
'bg-error' => $containerType === 'error',
])></span>
<span class="flex-1">{{ $containerName }}</span>
<span>{{ $containerLabel }}</span>
</div>
<div class="listbox-option cursor-default! gap-2.5!">
<span @class([
'size-1.5 shrink-0 rounded-full',
'bg-success' => $healthType === 'success',
'bg-warning' => $healthType === 'warning',
'bg-error' => $healthType === 'error',
])></span>
<span class="flex-1">Health check</span>
<span class="inline-flex items-center gap-1.5">
{{ $healthLabel }}
@if ($healthLabel === 'Not configured')
<x-helper label="About unconfigured health checks"
helper="No health check is configured, so Coolify can only report the container state. Traffic can still be routed to the container, but Coolify cannot verify that the application inside it is ready to receive requests." />
@endif
</span>
</div>
</div>
</div>