style(logs): show deployment status dots like the service menu

Deployment options get the same status dot and Stopped caption as
services instead of colored badges, so the listbox no longer needs
labelClass.
This commit is contained in:
Andras Bacsai committed 2026-10-23 12:25:57 +02:00
1 parent a90be6aa93
commit 3bfeb14b7d
4 files changed
+40 -30

No files matched your search

+1 -1
View File
@@ -213,7 +213,7 @@ class Logs extends Component
/**
* @return array{
* deployments: list<array{value: string, label: string, labelClass: string}>,
* deployments: list<array{value: string, label: string, dot: string, state?: string}>,
* services: list<array{value: string, label: string, dot: string, description?: string}>,
* deployment: string,
* service: string,
+31 -21
View File
@@ -17,7 +17,7 @@ class RuntimeLogTargets
/**
* @param list<array{uuid: string, name: string, containers: list<array<string, mixed>>}> $servers
* @return array{
* deployments: list<array{value: string, label: string, labelClass: string}>,
* deployments: list<array{value: string, label: string, dot: string, state?: string}>,
* services: list<array{value: string, label: string, dot: string, description?: string, state?: string}>,
* deployment: string,
* service: string,
@@ -50,7 +50,7 @@ class RuntimeLogTargets
$sources = self::sourcesFor($groups[$chosenKey]);
return [
'deployments' => $groupDeployments ? self::deploymentOptions($deploymentIds) : [],
'deployments' => $groupDeployments ? self::deploymentOptions($deploymentIds, $containers) : [],
'services' => self::serviceOptions($groups),
'deployment' => $chosenDeployment,
'service' => count($groups) > 1 ? $chosenKey : '',
@@ -204,18 +204,37 @@ class RuntimeLogTargets
}
/**
* Deployments show the same status dot and caption as services, for all their containers.
*
* @param list<string> $ids
* @return list<array{value: string, label: string, labelClass: string}>
* @param list<array{deployment: string, state: string}> $containers
* @return list<array{value: string, label: string, dot: string, state?: string}>
*/
private static function deploymentOptions(array $ids): array
private static function deploymentOptions(array $ids, array $containers): array
{
return array_map(fn (string $id): array => [
'value' => $id,
'label' => $id === '0' ? 'Production' : 'PR #'.$id,
'labelClass' => $id === '0'
? 'deployment-badge deployment-badge-production'
: 'deployment-badge deployment-badge-preview',
], $ids);
return array_map(function (string $id) use ($containers): array {
$states = array_column(array_filter(
$containers,
fn (array $container): bool => $container['deployment'] === $id,
), 'state');
return self::statusOption($id, $id === '0' ? 'Production' : 'PR #'.$id, $states);
}, $ids);
}
/**
* @param list<string> $states
* @return array{value: string, label: string, dot: string, state?: string}
*/
private static function statusOption(string $value, string $label, array $states): array
{
$option = ['value' => $value, 'label' => $label, 'dot' => self::serviceDot($states)];
$state = self::serviceStateLabel($states);
if ($state !== null) {
$option['state'] = $state;
}
return $option;
}
/**
@@ -261,16 +280,7 @@ class RuntimeLogTargets
{
$options = [];
foreach ($groups as $key => $containers) {
$states = array_column($containers, 'state');
$option = [
'value' => $key,
'label' => $key,
'dot' => self::serviceDot($states),
];
$state = self::serviceStateLabel($states);
if ($state !== null) {
$option['state'] = $state;
}
$option = self::statusOption($key, $key, array_column($containers, 'state'));
if (count($containers) > 1) {
$option['description'] = count($containers).' containers';
}
@@ -4,7 +4,7 @@
'label' => null,
'helper' => null,
'required' => false,
'options' => [], // list of ['value' => ..., 'label' => ..., 'description' => ?string, 'disabled' => bool, 'dot' => ?string, 'state' => ?string, 'labelClass' => ?string]
'options' => [], // list of ['value' => ..., 'label' => ..., 'description' => ?string, 'disabled' => bool, 'dot' => ?string, 'state' => ?string]
'placeholder' => 'Select…',
'emptyText' => 'No options available.',
'live' => false,
@@ -236,7 +236,7 @@
:aria-expanded="open" @if ($tooltip) :title="title" @endif>
<span class="listbox-trigger-label flex items-center gap-2">
<span x-show="selectedOption?.dot" class="size-1.5 shrink-0 rounded-full" :class="dotClass(selectedOption?.dot)"></span>
<span class="min-w-0 truncate" :class="selectedOption?.labelClass" x-text="current">{{ $multiple ? $multipleLabel : '' }}</span>
<span class="min-w-0 truncate" x-text="current">{{ $multiple ? $multipleLabel : '' }}</span>
<span x-show="selectedOption?.state" class="shrink-0 text-xs text-neutral-500 dark:text-fg-dim" x-text="selectedOption?.state"></span>
</span>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
@@ -292,7 +292,7 @@
<span x-show="option.dot" class="size-1.5 shrink-0 rounded-full" :class="dotClass(option.dot)"></span>
<span class="flex min-w-0 flex-col items-start">
<span class="flex min-w-0 items-baseline gap-2">
<span class="truncate" :class="option.labelClass" x-text="option.label"></span>
<span class="truncate" x-text="option.label"></span>
<span x-show="option.state" x-text="option.state"
class="shrink-0 text-xs text-neutral-500 dark:text-fg-dim"></span>
</span>
@@ -364,7 +364,7 @@
<span x-show="option.dot" class="size-1.5 shrink-0 rounded-full" :class="dotClass(option.dot)"></span>
<span class="flex min-w-0 flex-col items-start">
<span class="flex min-w-0 items-baseline gap-2">
<span class="truncate" :class="option.labelClass" x-text="option.label"></span>
<span class="truncate" x-text="option.label"></span>
<span x-show="option.state" x-text="option.state"
class="shrink-0 text-xs text-neutral-500 dark:text-fg-dim"></span>
</span>
+4 -4
View File
@@ -29,16 +29,16 @@ function runtimeLogTargets(array $servers, string $deployment = '0', string $ser
it('orders production ahead of newer previews and uses the top bar pills', function () {
$targets = runtimeLogTargets([
runtimeLogServer('primary', 'Primary', [
runtimeLogContainer('app-pr-7', 'coolify.pullRequestId=7,com.docker.compose.service=web'),
runtimeLogContainer('app-pr-7', 'coolify.pullRequestId=7,com.docker.compose.service=web', 'exited'),
runtimeLogContainer('app', 'coolify.pullRequestId=0,com.docker.compose.service=web'),
runtimeLogContainer('app-pr-42', 'coolify.pullRequestId=42,com.docker.compose.service=web'),
]),
]);
expect($targets['deployments'])->toBe([
['value' => '0', 'label' => 'Production', 'labelClass' => 'deployment-badge deployment-badge-production'],
['value' => '42', 'label' => 'PR #42', 'labelClass' => 'deployment-badge deployment-badge-preview'],
['value' => '7', 'label' => 'PR #7', 'labelClass' => 'deployment-badge deployment-badge-preview'],
['value' => '0', 'label' => 'Production', 'dot' => 'success'],
['value' => '42', 'label' => 'PR #42', 'dot' => 'success'],
['value' => '7', 'label' => 'PR #7', 'dot' => 'error', 'state' => 'Stopped'],
])
->and($targets['deployment'])->toBe('0')
->and($targets['service'])->toBe('')