fix(ui): flatten process log dialogs and show the run status next to the title

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-10-24 22:50:30 +02:00
co-authored by Claude Opus 5.5
parent 0c9f89fafd
commit 9c8f38752a
11 changed files with 85 additions and 20 deletions
+20
View File
@@ -2979,6 +2979,26 @@ input[type="search"]::-webkit-search-results-decoration {
width: 100%;
}
/* The dialog body is already the well: the log sits in it directly, without
its own box. */
.process-dialog-body .activity-monitor-log {
padding: 0;
border: 0;
border-radius: 0;
background: transparent;
}
/* Show the status of the activity monitor next to the dialog title. */
.process-dialog-status {
display: none;
}
.process-dialog:has([data-activity-status="running"]) .process-dialog-status-running,
.process-dialog:has([data-activity-status="finished"]) .process-dialog-status-finished,
.process-dialog:has([data-activity-status="failed"]) .process-dialog-status-failed {
display: inline-flex;
}
.validation-installation-logs {
border: 1px solid var(--coollabs-fill);
}
@@ -66,9 +66,18 @@
])
style="box-shadow: 0 0 0 1px var(--coollabs-hairline), var(--shadow-modal)">
<header class="flex-nowrap!">
<h3 id="process-dialog-title" class="min-w-0 flex-1 truncate">
{{ $title }}
</h3>
<div class="flex min-w-0 flex-1 items-center gap-2">
<h3 id="process-dialog-title" class="min-w-0 truncate">
{{ $title }}
</h3>
<x-loading compact class="process-dialog-status process-dialog-status-running text-accent!" />
<span class="process-dialog-status process-dialog-status-finished items-center gap-1.5 text-xs text-neutral-500 dark:text-fg-dim" role="status">
<span class="size-2 rounded-full bg-success"></span>Finished
</span>
<span class="process-dialog-status process-dialog-status-failed items-center gap-1.5 text-xs text-neutral-500 dark:text-fg-dim" role="status">
<span class="size-2 rounded-full bg-error"></span>Failed
</span>
</div>
<button type="button" @click="processDialogOpen = false"
class="flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-500 outline-0 transition-colors hover:bg-neutral-100 hover:text-black focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg"
aria-label="Close">
@@ -1,5 +1,15 @@
@php use App\Actions\CoolifyTask\RunRemoteProcess; @endphp
<div @class([
@php
use App\Actions\CoolifyTask\RunRemoteProcess;
$exitCode = data_get($activity, 'properties.exitCode');
$activityStatus = match (true) {
! $activity => null,
$isPollingActive || $exitCode === null => 'running',
(int) $exitCode === 0 => 'finished',
default => 'failed',
};
@endphp
<div data-activity-status="{{ $activityStatus }}" @class([
'h-full flex flex-col overflow-hidden' => $fullHeight,
'overflow-hidden' => !$fullHeight,
])>
@@ -48,7 +58,7 @@
characterData: true
});" x-destroy="observer && observer.disconnect()"
@class([
'flex flex-col w-full min-w-0 max-w-full px-4 py-2 overflow-y-auto bg-white border border-solid rounded-sm dark:text-white dark:bg-coolgray-100 scrollbar border-neutral-300 dark:border-coolgray-300',
'activity-monitor-log flex flex-col w-full min-w-0 max-w-full px-4 py-2 overflow-y-auto bg-white border border-solid rounded-sm dark:text-white dark:bg-coolgray-100 scrollbar border-neutral-300 dark:border-coolgray-300',
'flex-1 min-h-0' => $fullHeight,
'max-h-96' => !$fullHeight,
])>
@@ -49,10 +49,10 @@
<livewire:project.shared.configuration-checker :resource="$database" />
<x-process-dialog @startdatabase.window="processDialogOpen = true" closeWithX>
<x-slot:title>Database Startup</x-slot:title>
<x-slot:title>Database Startup Logs</x-slot:title>
<x-slot:content>
<div wire:ignore class="flex h-full min-h-0 min-w-0 max-w-full flex-col">
<livewire:activity-monitor header="Logs" fullHeight />
<livewire:activity-monitor fullHeight />
</div>
</x-slot:content>
</x-process-dialog>
@@ -281,7 +281,7 @@
<x-slot:title>Database Restore Output</x-slot:title>
<x-slot:content>
<div class="flex h-full min-h-0 flex-col" wire:ignore>
<livewire:activity-monitor wire:key="database-restore-{{ $resourceUuid }}" header="Logs" fullHeight />
<livewire:activity-monitor wire:key="database-restore-{{ $resourceUuid }}" fullHeight />
</div>
</x-slot:content>
</x-process-dialog>
@@ -18,9 +18,9 @@
<livewire:project.shared.configuration-checker :resource="$service" />
<x-process-dialog @startservice.window="processDialogOpen = true" closeWithX>
<x-slot:title>Service Startup</x-slot:title>
<x-slot:title>Service Startup Logs</x-slot:title>
<x-slot:content>
<livewire:activity-monitor header="Logs" fullHeight />
<livewire:activity-monitor fullHeight />
</x-slot:content>
</x-process-dialog>
@@ -17,9 +17,9 @@
<x-forms.button wire:click="updateCloudflareTunnel">Update Cloudflare Tunnel</x-forms.button>
<x-process-dialog @cloudflare-tunnel-update-started.window="processDialogOpen = true"
closeWithX size="xl">
<x-slot:title>Cloudflare Tunnel Update</x-slot:title>
<x-slot:title>Cloudflare Tunnel Update Logs</x-slot:title>
<x-slot:content>
<livewire:activity-monitor header="Logs" fullHeight />
<livewire:activity-monitor fullHeight />
</x-slot:content>
</x-process-dialog>
@endif
@@ -91,9 +91,9 @@
</x-callout>
@else
<x-process-dialog @automated.window="processDialogOpen = true" closeWithX size="xl">
<x-slot:title>Cloudflare Tunnel Configuration</x-slot:title>
<x-slot:title>Cloudflare Tunnel Configuration Logs</x-slot:title>
<x-slot:content>
<livewire:activity-monitor header="Logs" fullHeight />
<livewire:activity-monitor fullHeight />
</x-slot:content>
</x-process-dialog>
@@ -217,9 +217,9 @@
</form>
@can('update', $server)
<x-process-dialog @sysbox-install-started.window="processDialogOpen = true" closeWithX size="xl">
<x-slot:title>Install Sysbox</x-slot:title>
<x-slot:title>Sysbox Installation Logs</x-slot:title>
<x-slot:content>
<livewire:activity-monitor header="Logs" fullHeight />
<livewire:activity-monitor fullHeight />
</x-slot:content>
</x-process-dialog>
@endcan
@@ -11,7 +11,7 @@
</div>
@endif
<div class="flex min-h-0 flex-1 flex-col">
<livewire:activity-monitor header="Logs" fullHeight />
<livewire:activity-monitor fullHeight />
</div>
</div>
</x-slot:content>
@@ -6,10 +6,10 @@
<livewire:server.navbar :server="$server" />
<x-process-dialog @startupdate.window="processDialogOpen = true" closeWithX size="xl">
<x-slot:title>Updating packages</x-slot:title>
<x-slot:title>Package Update Logs</x-slot:title>
<x-slot:content>
<div class="flex h-full min-h-0 flex-col">
<livewire:activity-monitor header="Logs" fullHeight />
<livewire:activity-monitor fullHeight />
</div>
</x-slot:content>
</x-process-dialog>
@@ -227,3 +227,29 @@ describe('activity output of a resource', function () {
expect($component->get('activity')?->id)->toBe($this->serviceActivity->id);
});
});
test('the process status shows running until the activity ends, then its result', function (?int $exitCode, string $status) {
$server = Server::factory()->create(['team_id' => $this->team->id]);
$activity = Activity::create([
'log_name' => 'default',
'description' => 'proxy restart activity',
'properties' => ['team_id' => $this->team->id, 'server_uuid' => $server->uuid],
]);
$this->actingAs($this->user);
session(['currentTeam' => ['id' => $this->team->id]]);
$component = Livewire::test(ActivityMonitor::class)
->call('newMonitorActivity', $activity->id)
->assertSeeHtml('data-activity-status="running"');
$activity->update(['properties' => $activity->properties->put('exitCode', $exitCode)]);
$component->call('polling')
->assertSet('isPollingActive', $exitCode === null)
->assertSeeHtml("data-activity-status=\"{$status}\"");
})->with([
'still running' => [null, 'running'],
'succeeded' => [0, 'finished'],
'failed' => [1, 'failed'],
]);