fix: resolve uncloseable database restore modal on MariaDB import (#7335)
Fixes the "Snapshot missing on Livewire component" error that occurs when toggling the "Backup includes all databases" checkbox during MariaDB database import operations. Root Cause: - ActivityMonitor component was initialized without proper lifecycle hooks - When parent Import component re-rendered (via checkbox toggle), the ActivityMonitor's Livewire snapshot became stale - Missing null checks caused errors when querying with undefined activityId - No state cleanup when slide-over closed, causing issues on subsequent opens Changes: - Add updatedActivityId() lifecycle hook to ActivityMonitor for proper hydration - Add defensive null check in hydrateActivity() to prevent query errors - Track activityId in Import component for state management - Add slideOverClosed event dispatch in slide-over component - Add event listener in Import component to reset activityId on close Testing: - Manually verify checkbox toggle doesn't trigger popup - Verify actual restore operations work correctly - Test both file-based and S3-based restore methods - Ensure X button properly closes the modal - Verify no console errors or Livewire warnings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
68c5ebf221
commit
aa18c48823
3 changed files with 37 additions and 2 deletions
|
|
@ -10,7 +10,7 @@ class ActivityMonitor extends Component
|
||||||
{
|
{
|
||||||
public ?string $header = null;
|
public ?string $header = null;
|
||||||
|
|
||||||
public $activityId;
|
public $activityId = null;
|
||||||
|
|
||||||
public $eventToDispatch = 'activityFinished';
|
public $eventToDispatch = 'activityFinished';
|
||||||
|
|
||||||
|
|
@ -49,9 +49,24 @@ public function newMonitorActivity($activityId, $eventToDispatch = 'activityFini
|
||||||
|
|
||||||
public function hydrateActivity()
|
public function hydrateActivity()
|
||||||
{
|
{
|
||||||
|
if ($this->activityId === null) {
|
||||||
|
$this->activity = null;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->activity = Activity::find($this->activityId);
|
$this->activity = Activity::find($this->activityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updatedActivityId($value)
|
||||||
|
{
|
||||||
|
if ($value) {
|
||||||
|
$this->hydrateActivity();
|
||||||
|
$this->isPollingActive = true;
|
||||||
|
self::$eventDispatched = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function polling()
|
public function polling()
|
||||||
{
|
{
|
||||||
$this->hydrateActivity();
|
$this->hydrateActivity();
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,8 @@ private function validateServerPath(string $path): bool
|
||||||
|
|
||||||
public string $customLocation = '';
|
public string $customLocation = '';
|
||||||
|
|
||||||
|
public ?int $activityId = null;
|
||||||
|
|
||||||
public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB';
|
public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB';
|
||||||
|
|
||||||
public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE';
|
public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE';
|
||||||
|
|
@ -156,9 +158,15 @@ public function getListeners()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
|
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
|
||||||
|
'slideOverClosed' => 'resetActivityId',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function resetActivityId()
|
||||||
|
{
|
||||||
|
$this->activityId = null;
|
||||||
|
}
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
|
|
@ -327,6 +335,9 @@ public function runImport()
|
||||||
'serverId' => $this->server->id,
|
'serverId' => $this->server->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Track the activity ID
|
||||||
|
$this->activityId = $activity->id;
|
||||||
|
|
||||||
// Dispatch activity to the monitor and open slide-over
|
// Dispatch activity to the monitor and open slide-over
|
||||||
$this->dispatch('activityMonitor', $activity->id);
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
$this->dispatch('databaserestore');
|
$this->dispatch('databaserestore');
|
||||||
|
|
@ -548,6 +559,9 @@ public function restoreFromS3()
|
||||||
'serverId' => $this->server->id,
|
'serverId' => $this->server->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Track the activity ID
|
||||||
|
$this->activityId = $activity->id;
|
||||||
|
|
||||||
// Dispatch activity to the monitor and open slide-over
|
// Dispatch activity to the monitor and open slide-over
|
||||||
$this->dispatch('activityMonitor', $activity->id);
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
$this->dispatch('databaserestore');
|
$this->dispatch('databaserestore');
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
@props(['closeWithX' => false, 'fullScreen' => false])
|
@props(['closeWithX' => false, 'fullScreen' => false])
|
||||||
<div x-data="{
|
<div x-data="{
|
||||||
slideOverOpen: false
|
slideOverOpen: false
|
||||||
}" {{ $attributes->merge(['class' => 'relative w-auto h-auto']) }}>
|
}"
|
||||||
|
x-init="$watch('slideOverOpen', value => {
|
||||||
|
if (!value) {
|
||||||
|
$dispatch('slideOverClosed')
|
||||||
|
}
|
||||||
|
})"
|
||||||
|
{{ $attributes->merge(['class' => 'relative w-auto h-auto']) }}>
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
<template x-teleport="body">
|
<template x-teleport="body">
|
||||||
<div x-show="slideOverOpen" @if (!$closeWithX) @keydown.window.escape="slideOverOpen=false" @endif
|
<div x-show="slideOverOpen" @if (!$closeWithX) @keydown.window.escape="slideOverOpen=false" @endif
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue