This commit is contained in:
Thomas Peterson 2025-11-17 10:20:51 +01:00
parent 29b14379e7
commit b07058f742
2 changed files with 78 additions and 14 deletions

View File

@ -0,0 +1,44 @@
<?php
namespace ServerManager\UI;
use PHPNative\Tailwind\Data\Icon as IconName;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Icon;
use PHPNative\Ui\Widget\Label;
class LoadingIndicator extends Container
{
private Icon $icon;
private Label $label;
private bool $loading = false;
public function __construct(string $style = '')
{
parent::__construct('flex flex-row items-center gap-1 px-2 ' . $style);
$this->icon = new Icon(IconName::sync, 14, 'text-blue-600');
$this->label = new Label('Laden...', 'text-xs text-gray-700');
$this->addComponent($this->icon);
$this->addComponent($this->label);
$this->setVisible(false);
}
public function setLoading(bool $loading): void
{
if ($this->loading === $loading) {
return;
}
$this->loading = $loading;
$this->setVisible($loading);
}
public function isLoading(): bool
{
return $this->loading;
}
}

View File

@ -12,6 +12,7 @@ use PHPNative\Ui\Widget\TabContainer;
use PHPNative\Ui\Widget\Table;
use PHPNative\Ui\Widget\TextInput;
use ServerManager\Services\HetznerService;
use ServerManager\UI\LoadingIndicator;
class ServerListTab
{
@ -32,6 +33,7 @@ class ServerListTab
private Label $detailType;
private Label $detailIpv4;
private Container $detailDomainsContainer;
private LoadingIndicator $loadingIndicator;
public function __construct(
string &$apiKey,
@ -49,6 +51,9 @@ class ServerListTab
// Left side: Table with search and refresh
$leftSide = new Container('flex flex-col gap-2 flex-1');
// Header row with refresh button and loading indicator on the right
$headerRow = new Container('flex flex-row items-center gap-2');
// Refresh button
$this->refreshButton = new Button(
'Server aktualisieren',
@ -58,7 +63,13 @@ class ServerListTab
);
$refreshIcon = new Icon(IconName::sync, 16, 'text-white');
$this->refreshButton->setIcon($refreshIcon);
$leftSide->addComponent($this->refreshButton);
$headerRow->addComponent($this->refreshButton);
// Loading indicator (top-right in the server tab header)
$this->loadingIndicator = new LoadingIndicator('ml-auto');
$headerRow->addComponent($this->loadingIndicator);
$leftSide->addComponent($headerRow);
// Search input
$this->searchInput = new TextInput(
@ -222,9 +233,11 @@ class ServerListTab
}
});
// Refresh button - use reference to apiKey & privateKey variable
$this->refreshButton->setOnClickAsync(
function () use (&$currentApiKey) {
// Refresh button - use TaskManager directly so we can control the loading indicator
$this->refreshButton->setOnClick(function () use (&$currentApiKey, $serverListTab, &$currentPrivateKeyPath) {
$serverListTab->loadingIndicator->setLoading(true);
$task = TaskManager::getInstance()->runAsync(function () use (&$currentApiKey) {
try {
if (empty($currentApiKey)) {
return ['error' => 'Kein API-Key konfiguriert'];
@ -253,8 +266,9 @@ class ServerListTab
} catch (\Exception $e) {
return ['error' => 'Exception: ' . $e->getMessage()];
}
},
function ($result) use (&$serverListTab, &$currentPrivateKeyPath) {
});
$task->onComplete(function ($result) use (&$serverListTab, &$currentPrivateKeyPath) {
if (is_array($result)) {
if (isset($result['error'])) {
$serverListTab->statusLabel->setText('Fehler: ' . $result['error']);
@ -275,12 +289,13 @@ class ServerListTab
$ip = $row['ipv4'] ?? '';
if (empty($ip) || empty($currentPrivateKeyPath) || !file_exists($currentPrivateKeyPath)) {
$serverListTab->currentServerData[$index]['docker_error'] = 'Kein gültiger Private-Key oder IP';
$serverListTab->currentServerData[$index]['docker_error'] =
'Kein gültiger Private-Key oder IP';
$serverListTab->currentServerData[$index]['docker_status'] = 'error';
continue;
}
$task = TaskManager::getInstance()->runAsync(function () use (
$dockerTask = TaskManager::getInstance()->runAsync(function () use (
$ip,
$currentPrivateKeyPath,
$index,
@ -337,7 +352,7 @@ class ServerListTab
}
});
$task->onComplete(function ($dockerResult) use (&$serverListTab) {
$dockerTask->onComplete(function ($dockerResult) use (&$serverListTab) {
if (!is_array($dockerResult) || !isset($dockerResult['index'])) {
return;
}
@ -380,7 +395,7 @@ class ServerListTab
}
});
$task->onError(function ($error) use ($serverListTab, $index) {
$dockerTask->onError(function ($error) use (&$serverListTab, $index) {
$errorMsg = is_object($error) && method_exists($error, 'getMessage')
? $error->getMessage()
: ((string) $error);
@ -392,15 +407,20 @@ class ServerListTab
}
}
}
},
function ($error) use (&$serverListTab) {
$serverListTab->loadingIndicator->setLoading(false);
});
$task->onError(function ($error) use (&$serverListTab) {
$errorMsg = is_object($error) && method_exists($error, 'getMessage')
? $error->getMessage()
: ((string) $error);
$serverListTab->statusLabel->setText('Async Fehler: ' . $errorMsg);
echo "Async error: {$errorMsg}\n";
},
);
$serverListTab->loadingIndicator->setLoading(false);
});
});
}
public function getContainer(): Container