Backup
This commit is contained in:
parent
7ea3d6cf11
commit
77c9c733ae
@ -48,7 +48,7 @@ class HetznerService
|
||||
/**
|
||||
* Generate test server data for development
|
||||
*/
|
||||
public static function generateTestData(int $count = 63): array
|
||||
public static function generateTestData(int $count = 5): array
|
||||
{
|
||||
$testData = [];
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
|
||||
@ -17,8 +17,8 @@ class LoadingIndicator extends Container
|
||||
{
|
||||
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->icon = new Icon(IconName::sync, 16, 'py-2 text-blue-600');
|
||||
$this->label = new Label('Laden...', 'py-2 text-gray-700');
|
||||
|
||||
$this->addComponent($this->icon);
|
||||
$this->addComponent($this->label);
|
||||
@ -41,4 +41,3 @@ class LoadingIndicator extends Container
|
||||
return $this->loading;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,8 +5,8 @@ namespace ServerManager\UI;
|
||||
use PHPNative\Async\TaskManager;
|
||||
use PHPNative\Tailwind\Data\Icon as IconName;
|
||||
use PHPNative\Ui\Widget\Button;
|
||||
use PHPNative\Ui\Widget\Container;
|
||||
use PHPNative\Ui\Widget\Checkbox;
|
||||
use PHPNative\Ui\Widget\Container;
|
||||
use PHPNative\Ui\Widget\Icon;
|
||||
use PHPNative\Ui\Widget\Label;
|
||||
use PHPNative\Ui\Widget\TabContainer;
|
||||
@ -104,7 +104,7 @@ class ServerListTab
|
||||
'width' => 40,
|
||||
'render' => [$this, 'renderSelectCell'],
|
||||
],
|
||||
['key' => 'id', 'title' => 'ID', 'width' => 80],
|
||||
['key' => 'id', 'title' => 'ID', 'width' => 100],
|
||||
['key' => 'name', 'title' => 'Name'],
|
||||
['key' => 'status', 'title' => 'Status', 'width' => 90],
|
||||
['key' => 'type', 'title' => 'Typ', 'width' => 80],
|
||||
@ -234,8 +234,7 @@ class ServerListTab
|
||||
string &$currentPrivateKeyPath,
|
||||
Button $rebootButton,
|
||||
Button $updateButton,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
// Table row selection
|
||||
$serverListTab = $this;
|
||||
$this->table->setOnRowSelect(function ($index, $row) use ($serverListTab) {
|
||||
@ -260,7 +259,7 @@ class ServerListTab
|
||||
$searchTerm = strtolower(trim($value));
|
||||
|
||||
if (empty($searchTerm)) {
|
||||
$serverListTab->table->setData($serverListTab->currentServerData);
|
||||
$serverListTab->table->setData($serverListTab->currentServerData, true);
|
||||
} else {
|
||||
$filteredData = array_filter($serverListTab->currentServerData, function ($row) use ($searchTerm) {
|
||||
return (
|
||||
@ -270,7 +269,7 @@ class ServerListTab
|
||||
}))
|
||||
);
|
||||
});
|
||||
$serverListTab->table->setData(array_values($filteredData));
|
||||
$serverListTab->table->setData(array_values($filteredData), false);
|
||||
}
|
||||
});
|
||||
|
||||
@ -325,7 +324,7 @@ class ServerListTab
|
||||
'os_version' => 'unbekannt',
|
||||
], $row), $result['servers']);
|
||||
|
||||
$serverListTab->table->setData($serverListTab->currentServerData);
|
||||
$serverListTab->table->setData($serverListTab->currentServerData, false);
|
||||
$serverListTab->statusLabel->setText('Server geladen: ' . $result['count'] . ' gefunden');
|
||||
|
||||
// Danach: pro Server asynchron Docker-Infos und Systemstatus nachladen
|
||||
@ -455,7 +454,7 @@ class ServerListTab
|
||||
$serverListTab->currentServerData[$i]['domains'] = $domains;
|
||||
}
|
||||
$serverListTab->currentServerData[$i]['docker'] = $dockerResult['docker'];
|
||||
$serverListTab->table->setData($serverListTab->currentServerData);
|
||||
$serverListTab->table->setData($serverListTab->currentServerData, true);
|
||||
}
|
||||
|
||||
// Map system status into human-readable table fields
|
||||
@ -639,22 +638,23 @@ class ServerListTab
|
||||
];
|
||||
}
|
||||
|
||||
// Run update & upgrade non-interactively
|
||||
$ssh->exec(
|
||||
'apt-get update >/dev/null 2>&1 && ' .
|
||||
'DEBIAN_FRONTEND=noninteractive apt-get -y upgrade >/dev/null 2>&1',
|
||||
);
|
||||
// Run update & upgrade non-interactively and then output reboot + update status in one go
|
||||
$statusOutput = trim($ssh->exec('apt-get update >/dev/null 2>&1 && ' .
|
||||
'DEBIAN_FRONTEND=noninteractive apt-get -y upgrade >/dev/null 2>&1; ' .
|
||||
'reboot_flag=$([ -f /var/run/reboot-required ] && echo yes || echo no); ' .
|
||||
'updates=$(apt-get -s upgrade 2>/dev/null | grep -c "^Inst " || echo 0); ' .
|
||||
'echo "$reboot_flag $updates"'));
|
||||
|
||||
// Re-check number of available updates
|
||||
$updatesOutput = trim($ssh->exec(
|
||||
'apt-get -s upgrade 2>/dev/null | grep -c "^Inst " || echo 0',
|
||||
));
|
||||
$updatesCount = is_numeric($updatesOutput) ? (int) $updatesOutput : 0;
|
||||
$parts = preg_split('/\s+/', $statusOutput);
|
||||
$rebootFlag = $parts[0] ?? 'no';
|
||||
$updatesCount = isset($parts[1]) && is_numeric($parts[1]) ? ((int) $parts[1]) : 0;
|
||||
$needsReboot = $rebootFlag === 'yes';
|
||||
|
||||
return [
|
||||
'index' => $index,
|
||||
'success' => true,
|
||||
'updates' => $updatesCount,
|
||||
'needs_reboot' => $needsReboot,
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
return [
|
||||
@ -674,11 +674,17 @@ class ServerListTab
|
||||
if ($index !== null && isset($serverListTab->currentServerData[$index])) {
|
||||
if (isset($result['updates'])) {
|
||||
$updates = (int) $result['updates'];
|
||||
$serverListTab->currentServerData[$index]['updates_available'] =
|
||||
$updates > 0 ? ('ja (' . $updates . ')') : 'nein';
|
||||
$serverListTab->currentServerData[$index]['updates_available'] = $updates > 0
|
||||
? ('ja (' . $updates . ')')
|
||||
: 'nein';
|
||||
}
|
||||
|
||||
$serverListTab->table->setData($serverListTab->currentServerData);
|
||||
if (array_key_exists('needs_reboot', $result)) {
|
||||
$needsReboot = (bool) $result['needs_reboot'];
|
||||
$serverListTab->currentServerData[$index]['needs_reboot'] = $needsReboot ? 'ja' : 'nein';
|
||||
}
|
||||
|
||||
$serverListTab->table->setData($serverListTab->currentServerData, true);
|
||||
}
|
||||
|
||||
if ($result['success']) {
|
||||
@ -778,12 +784,19 @@ class ServerListTab
|
||||
$checkbox = new Checkbox('', $isChecked);
|
||||
|
||||
$serverListTab = $this;
|
||||
$checkbox->setOnChange(function (bool $checked) use ($serverListTab, $rowIndex) {
|
||||
if (!isset($serverListTab->currentServerData[$rowIndex])) {
|
||||
$rowId = $rowData['id'] ?? null;
|
||||
$checkbox->setOnChange(function (bool $checked) use (&$serverListTab, &$rowData, $rowId) {
|
||||
if ($rowId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$serverListTab->currentServerData[$rowIndex]['selected'] = $checked;
|
||||
foreach ($serverListTab->currentServerData as $index => $row) {
|
||||
if (($row['id'] ?? null) === $rowId) {
|
||||
$serverListTab->currentServerData[$index]['selected'] = $checked;
|
||||
$serverListTab->table->setData($serverListTab->currentServerData, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return $checkbox;
|
||||
|
||||
@ -12,12 +12,8 @@ class Checkbox extends Component
|
||||
private $onChange = null;
|
||||
private string $labelText = '';
|
||||
|
||||
public function __construct(
|
||||
string $label = '',
|
||||
bool $checked = false,
|
||||
string $style = '',
|
||||
$onChange = null,
|
||||
) {
|
||||
public function __construct(string $label = '', bool $checked = false, string $style = '', $onChange = null)
|
||||
{
|
||||
parent::__construct($style);
|
||||
|
||||
$this->checked = $checked;
|
||||
@ -59,12 +55,12 @@ class Checkbox extends Component
|
||||
public function handleMouseClick(float $mouseX, float $mouseY, int $button): bool
|
||||
{
|
||||
// Check if click is within checkbox bounds (not label)
|
||||
$checkboxSize = 20;
|
||||
$checkboxSize = 20 * $this->viewport->uiScale;
|
||||
if (
|
||||
$mouseX >= $this->viewport->x &&
|
||||
$mouseX <= ($this->viewport->x + $checkboxSize) &&
|
||||
$mouseY >= $this->viewport->y &&
|
||||
$mouseY <= ($this->viewport->y + $checkboxSize)
|
||||
$mouseX <= ($this->viewport->x + $checkboxSize) &&
|
||||
$mouseY >= $this->viewport->y &&
|
||||
$mouseY <= ($this->viewport->y + $checkboxSize)
|
||||
) {
|
||||
$this->checked = !$this->checked;
|
||||
|
||||
@ -80,7 +76,7 @@ class Checkbox extends Component
|
||||
|
||||
public function render(&$renderer, null|TextRenderer $textRenderer = null): void
|
||||
{
|
||||
$checkboxSize = 20;
|
||||
$checkboxSize = 20 * $this->viewport->uiScale;
|
||||
|
||||
// Draw checkbox border
|
||||
sdl_set_render_draw_color($renderer, 156, 163, 175, 255); // Gray-400
|
||||
|
||||
@ -68,15 +68,26 @@ class Table extends Container
|
||||
* Set table data
|
||||
*
|
||||
* @param array $data Array of row data (associative arrays)
|
||||
* @param bool $preserveScroll Whether to preserve scroll position
|
||||
*/
|
||||
public function setData(array $data): void
|
||||
public function setData(array $data, bool $preserveScroll = false): void
|
||||
{
|
||||
$this->rows = $data;
|
||||
|
||||
$scrollPosition = null;
|
||||
if ($preserveScroll) {
|
||||
$scrollPosition = $this->bodyContainer->getScrollPosition();
|
||||
}
|
||||
|
||||
$this->bodyContainer->clearChildren();
|
||||
|
||||
foreach ($data as $rowIndex => $row) {
|
||||
$this->addRow($row, $rowIndex);
|
||||
}
|
||||
|
||||
if ($preserveScroll && $scrollPosition !== null) {
|
||||
$this->bodyContainer->setScrollPosition($scrollPosition['x'], $scrollPosition['y']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -197,8 +208,8 @@ class Table extends Container
|
||||
return $this->sortAscending ? $result : -$result;
|
||||
});
|
||||
|
||||
// Re-render with sorted data
|
||||
$this->setData($sortedRows);
|
||||
// Re-render with sorted data (reset scroll)
|
||||
$this->setData($sortedRows, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -217,8 +228,8 @@ class Table extends Container
|
||||
($this->onRowSelect)($rowIndex, $this->rows[$rowIndex] ?? null);
|
||||
}
|
||||
|
||||
// Re-render rows to update selection
|
||||
$this->setData($this->rows);
|
||||
// Re-render rows to update selection but keep scroll
|
||||
$this->setData($this->rows, true);
|
||||
|
||||
// Restore scroll position
|
||||
$this->bodyContainer->setScrollPosition($scrollPosition['x'], $scrollPosition['y']);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user