feat(database): reopen the live start/restart log after closing it

Extend the service reopen-deployment pattern to databases:
- Track the running activity id (refreshed on the 10s poll, on broadcasts, and
  instantly when start/restart runs)
- Add reopenDeployment() to re-attach the activity monitor and reopen the dialog
- Show the shared x-deploying-indicator in the mobile status row + desktop HUD
- Clicking Start/Restart while an operation runs reopens the log instead of
  launching a duplicate

Applications already use a persistent deployment-log page, so they need no
reopen affordance.
This commit is contained in:
Aditya Tripathi
2026-09-11 09:10:20 +00:00
parent af093eea6e
commit eec61e45b4
2 changed files with 75 additions and 0 deletions
+58
View File
@@ -6,9 +6,11 @@ use App\Actions\Database\RestartDatabase;
use App\Actions\Database\StartDatabase;
use App\Actions\Database\StopDatabase;
use App\Actions\Docker\GetContainersStatus;
use App\Enums\ProcessStatus;
use App\Events\ServiceStatusChanged;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Component;
use Spatie\Activitylog\Models\Activity;
class Heading extends Component
{
@@ -20,6 +22,10 @@ class Heading extends Component
public $docker_cleanup = true;
public $isDeploymentProgress = false;
public $runningActivityId = null;
public function getListeners()
{
$teamId = auth()->user()->currentTeam()->id;
@@ -61,6 +67,8 @@ class Heading extends Component
public function checkStatus()
{
$this->checkDeployments();
if ($this->database->destination->server->isFunctional()) {
GetContainersStatus::dispatch($this->database->destination->server);
} else {
@@ -68,6 +76,52 @@ class Heading extends Component
}
}
public function checkDeployments()
{
try {
$activity = Activity::where('properties->type_uuid', $this->database->uuid)->latest()->first();
$status = data_get($activity, 'properties.status');
if ($status === ProcessStatus::QUEUED->value || $status === ProcessStatus::IN_PROGRESS->value) {
$this->isDeploymentProgress = true;
$this->runningActivityId = $activity->id;
} else {
$this->isDeploymentProgress = false;
$this->runningActivityId = null;
}
} catch (\Throwable) {
$this->isDeploymentProgress = false;
$this->runningActivityId = null;
}
return $this->isDeploymentProgress;
}
/**
* Re-attach the live log dialog to a start/restart that is already running,
* so the log reappears after the dialog was closed.
*/
public function reopenDeployment()
{
$this->authorize('view', $this->database);
$this->checkDeployments();
if ($this->isDeploymentProgress && $this->runningActivityId) {
$this->dispatch('activityMonitor', $this->runningActivityId, ServiceStatusChanged::class);
$this->js("window.dispatchEvent(new CustomEvent('startdatabase'))");
} else {
$this->dispatch('info', 'No operation is currently running.');
}
}
private function markDeploymentRunning($activity): void
{
if (is_object($activity)) {
$this->isDeploymentProgress = true;
$this->runningActivityId = $activity->id;
}
}
public function manualCheckStatus()
{
$this->checkStatus();
@@ -80,6 +134,8 @@ class Heading extends Component
'environment_uuid' => $this->database->environment->uuid,
'database_uuid' => $this->database->uuid,
];
$this->checkDeployments();
}
public function stop()
@@ -102,6 +158,7 @@ class Heading extends Component
$activity = RestartDatabase::run($this->database);
$this->auditDatabaseAction('ui.database.restarted');
$this->markDeploymentRunning($activity);
$this->js("window.dispatchEvent(new CustomEvent('startdatabase'))");
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class);
} catch (\Throwable $e) {
@@ -116,6 +173,7 @@ class Heading extends Component
$activity = StartDatabase::run($this->database);
$this->auditDatabaseAction('ui.database.started');
$this->markDeploymentRunning($activity);
$this->js("window.dispatchEvent(new CustomEvent('startdatabase'))");
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class);
} catch (\Throwable $e) {
@@ -66,6 +66,9 @@
</h1>
<div class="relative flex w-full min-w-0 items-center gap-2">
<x-status-summary :status="$database->status" title="Database status" />
@if ($isDeploymentProgress)
<x-deploying-indicator label="Working" />
@endif
</div>
<div class="flex w-full flex-wrap gap-1">
<x-application.restart-limit-warning :application="$database" />
@@ -107,6 +110,9 @@
<div
class="resource-heading-navbar application-heading-actions flex w-auto min-w-0 items-center justify-end gap-1 overflow-visible">
<div class="resource-heading-actions flex shrink-0 items-center gap-0.5">
@if ($isDeploymentProgress)
<x-deploying-indicator label="Working" class="mr-1" />
@endif
@if ($database->destination->server->isFunctional())
@can('manage', $database)
<x-split-action id="database-desktop-actions">
@@ -170,6 +176,12 @@
@script
<script>
$wire.$on('startEvent', async () => {
if (await $wire.$call('checkDeployments')) {
// An operation is already running: reopen its live log.
$wire.$call('reopenDeployment');
window.dispatchEvent(new CustomEvent('database-action-finished'));
return;
}
window.dispatchEvent(new CustomEvent('startdatabase'));
try {
await $wire.$call('start');
@@ -178,6 +190,11 @@
}
});
$wire.$on('restartEvent', async () => {
if (await $wire.$call('checkDeployments')) {
// An operation is already running: reopen its live log.
$wire.$call('reopenDeployment');
return;
}
window.dispatchEvent(new CustomEvent('database-busy'));
$wire.$dispatch('info', 'Restarting database.');
window.dispatchEvent(new CustomEvent('startdatabase'));