Backup
This commit is contained in:
parent
261cc92b19
commit
c567194b1c
@ -9,6 +9,7 @@ use PHPNative\Ui\Widget\Label;
|
||||
use PHPNative\Ui\Widget\StatusBar;
|
||||
use PHPNative\Ui\Widget\TabContainer;
|
||||
use PHPNative\Ui\Window;
|
||||
use ServerManager\UI\KanbanTab;
|
||||
use ServerManager\UI\MenuBarBuilder;
|
||||
use ServerManager\UI\ServerListTab;
|
||||
use ServerManager\UI\SettingsModal;
|
||||
@ -59,17 +60,27 @@ class App
|
||||
$tabContainer = new TabContainer('flex-1');
|
||||
|
||||
// Create tabs
|
||||
$kanbanTab = new KanbanTab($this->settings);
|
||||
$serverListTab = new ServerListTab(
|
||||
$currentApiKey,
|
||||
$currentPrivateKeyPath,
|
||||
$tabContainer,
|
||||
$statusLabel,
|
||||
$this->settings,
|
||||
$kanbanTab,
|
||||
);
|
||||
$sftpManagerTab = new SftpManagerTab(
|
||||
$currentApiKey,
|
||||
$currentPrivateKeyPath,
|
||||
$currentRemoteStartDir,
|
||||
$serverListTab,
|
||||
$tabContainer,
|
||||
$statusLabel,
|
||||
);
|
||||
$sftpManagerTab = new SftpManagerTab($currentApiKey, $currentPrivateKeyPath, $currentRemoteStartDir, $serverListTab, $tabContainer, $statusLabel);
|
||||
|
||||
// Add tabs
|
||||
$tabContainer->addTab('Server', $serverListTab->getContainer());
|
||||
$tabContainer->addTab('Kanban', $kanbanTab->getContainer());
|
||||
$tabContainer->addTab('SFTP Manager', $sftpManagerTab->getContainer());
|
||||
|
||||
$mainContainer->addComponent($tabContainer);
|
||||
|
||||
145
examples/ServerManager/UI/KanbanTab.php
Normal file
145
examples/ServerManager/UI/KanbanTab.php
Normal file
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace ServerManager\UI;
|
||||
|
||||
use PHPNative\Framework\Settings;
|
||||
use PHPNative\Ui\Widget\Button;
|
||||
use PHPNative\Ui\Widget\Container;
|
||||
use PHPNative\Ui\Widget\Label;
|
||||
use PHPNative\Ui\Widget\TextInput;
|
||||
|
||||
class KanbanTab
|
||||
{
|
||||
private Container $tab;
|
||||
private Settings $settings;
|
||||
private Container $boardsContainer;
|
||||
private TextInput $newBoardInput;
|
||||
|
||||
public function __construct(Settings $settings)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
|
||||
$this->ensureDefaultBoards();
|
||||
|
||||
$this->tab = new Container('flex flex-col p-4 gap-4 bg-gray-50');
|
||||
$headerRow = new Container('flex flex-row items-center gap-2');
|
||||
|
||||
$headerRow->addComponent(new Label('Kanban Tasks', 'text-xl font-bold text-black flex-1'));
|
||||
|
||||
$this->newBoardInput = new TextInput(
|
||||
'Neues Board...',
|
||||
'w-60 border border-gray-300 rounded px-3 py-2 bg-white text-black text-sm',
|
||||
);
|
||||
$addBoardButton = new Button(
|
||||
'Board hinzufügen',
|
||||
'px-3 py-2 bg-blue-600 rounded hover:bg-blue-700',
|
||||
null,
|
||||
'text-white text-sm',
|
||||
);
|
||||
|
||||
$headerRow->addComponent($this->newBoardInput);
|
||||
$headerRow->addComponent($addBoardButton);
|
||||
|
||||
$this->tab->addComponent($headerRow);
|
||||
|
||||
$this->boardsContainer = new Container(
|
||||
'flex flex-row gap-4 flex-1 overflow-auto bg-gray-100 rounded border border-gray-300 p-3',
|
||||
);
|
||||
$this->tab->addComponent($this->boardsContainer);
|
||||
|
||||
$kanbanTab = $this;
|
||||
$addBoardButton->setOnClick(function () use ($kanbanTab) {
|
||||
$name = trim($kanbanTab->newBoardInput->getValue());
|
||||
if ($name === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$boards = $kanbanTab->settings->get('kanban.boards', []);
|
||||
if (!is_array($boards)) {
|
||||
$boards = [];
|
||||
}
|
||||
|
||||
if (!in_array($name, $boards, true)) {
|
||||
$boards[] = $name;
|
||||
$kanbanTab->settings->set('kanban.boards', $boards);
|
||||
$kanbanTab->settings->save();
|
||||
}
|
||||
|
||||
$kanbanTab->newBoardInput->setValue('');
|
||||
$kanbanTab->renderBoards();
|
||||
});
|
||||
|
||||
$this->renderBoards();
|
||||
}
|
||||
|
||||
public function getContainer(): Container
|
||||
{
|
||||
return $this->tab;
|
||||
}
|
||||
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->renderBoards();
|
||||
}
|
||||
|
||||
private function ensureDefaultBoards(): void
|
||||
{
|
||||
$boards = $this->settings->get('kanban.boards', null);
|
||||
if (!is_array($boards) || empty($boards)) {
|
||||
$boards = ['neu', 'in arbeit', 'fertig'];
|
||||
$this->settings->set('kanban.boards', $boards);
|
||||
$this->settings->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function renderBoards(): void
|
||||
{
|
||||
$this->boardsContainer->clearChildren();
|
||||
|
||||
$boards = $this->settings->get('kanban.boards', []);
|
||||
if (!is_array($boards)) {
|
||||
$boards = [];
|
||||
}
|
||||
|
||||
$tasks = $this->settings->get('kanban.tasks', []);
|
||||
if (!is_array($tasks)) {
|
||||
$tasks = [];
|
||||
}
|
||||
|
||||
foreach ($boards as $boardName) {
|
||||
$column = new Container('flex flex-col bg-white rounded shadow-md w-64 max-h-full');
|
||||
|
||||
$columnHeader = new Container('px-3 py-2 border-b border-gray-300 bg-gray-100');
|
||||
$columnHeader->addComponent(new Label($boardName, 'text-sm font-semibold text-gray-800'));
|
||||
$column->addComponent($columnHeader);
|
||||
|
||||
$columnBody = new Container('flex flex-col gap-2 p-2 overflow-auto');
|
||||
|
||||
$boardTasks = array_values(array_filter(
|
||||
$tasks,
|
||||
static fn($task) => ($task['board'] ?? 'neu') === $boardName,
|
||||
));
|
||||
|
||||
if (empty($boardTasks)) {
|
||||
$columnBody->addComponent(new Label('Keine Tasks', 'text-xs text-gray-400 italic'));
|
||||
} else {
|
||||
foreach ($boardTasks as $task) {
|
||||
$title = (string) ($task['title'] ?? '');
|
||||
$serverId = $task['server_id'] ?? null;
|
||||
$serverLabel = $serverId !== null ? ('Server #' . $serverId) : 'Kein Server';
|
||||
|
||||
$card = new Container(
|
||||
'flex flex-col gap-1 px-3 py-2 bg-white border border-gray-200 rounded shadow-sm',
|
||||
);
|
||||
$card->addComponent(new Label($title, 'text-xs text-gray-900'));
|
||||
$card->addComponent(new Label($serverLabel, 'text-[10px] text-gray-500'));
|
||||
|
||||
$columnBody->addComponent($card);
|
||||
}
|
||||
}
|
||||
|
||||
$column->addComponent($columnBody);
|
||||
$this->boardsContainer->addComponent($column);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,11 +14,13 @@ use PHPNative\Ui\Widget\TabContainer;
|
||||
use PHPNative\Ui\Widget\Table;
|
||||
use PHPNative\Ui\Widget\TextInput;
|
||||
use ServerManager\Services\HetznerService;
|
||||
use ServerManager\UI\KanbanTab;
|
||||
use ServerManager\UI\LoadingIndicator;
|
||||
|
||||
class ServerListTab
|
||||
{
|
||||
private Settings $settings;
|
||||
private null|KanbanTab $kanbanTab;
|
||||
private Container $tab;
|
||||
private Table $table;
|
||||
private TextInput $searchInput;
|
||||
@ -49,8 +51,10 @@ class ServerListTab
|
||||
TabContainer $tabContainer,
|
||||
Label $statusLabel,
|
||||
Settings $settings,
|
||||
null|KanbanTab $kanbanTab = null,
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->kanbanTab = $kanbanTab;
|
||||
$this->statusLabel = $statusLabel;
|
||||
$currentApiKey = &$apiKey;
|
||||
$currentPrivateKeyPath = &$privateKeyPath;
|
||||
@ -247,13 +251,13 @@ class ServerListTab
|
||||
);
|
||||
$detailPanel->addComponent($saveServerSettingsButton);
|
||||
|
||||
$detailPanel->addComponent(new Label('TODOs für diesen Server:', 'text-xs text-gray-500 mt-3'));
|
||||
$detailPanel->addComponent(new Label('Tasks für diesen Server:', 'text-xs text-gray-500 mt-3'));
|
||||
$this->todoListContainer = new Container('flex flex-col gap-1');
|
||||
$detailPanel->addComponent($this->todoListContainer);
|
||||
|
||||
$todoInputRow = new Container('flex flex-row gap-2 mt-1');
|
||||
$this->todoInput = new TextInput(
|
||||
'Neue TODO...',
|
||||
'Neue Task...',
|
||||
'flex-1 border border-gray-300 rounded px-3 py-2 bg-white text-black',
|
||||
);
|
||||
$addTodoButton = new Button(
|
||||
@ -307,11 +311,9 @@ class ServerListTab
|
||||
if ($serverListTab->currentServerId !== null) {
|
||||
$settingsKeyBase = 'servers.' . $serverListTab->currentServerId;
|
||||
$apiKey = $serverListTab->settings->get($settingsKeyBase . '.api_key', '');
|
||||
$todos = $serverListTab->settings->get($settingsKeyBase . '.todos', []);
|
||||
|
||||
$serverListTab->serverApiKeyInput->setValue($apiKey);
|
||||
$serverListTab->currentServerTodos = is_array($todos) ? $todos : [];
|
||||
$serverListTab->renderTodoList();
|
||||
$serverListTab->loadServerTasks();
|
||||
} else {
|
||||
$serverListTab->serverApiKeyInput->setValue('');
|
||||
$serverListTab->currentServerTodos = [];
|
||||
@ -784,7 +786,6 @@ class ServerListTab
|
||||
$apiKey = trim($serverListTab->serverApiKeyInput->getValue());
|
||||
|
||||
$serverListTab->settings->set($settingsKeyBase . '.api_key', $apiKey);
|
||||
$serverListTab->settings->set($settingsKeyBase . '.todos', $serverListTab->currentServerTodos);
|
||||
$serverListTab->settings->save();
|
||||
|
||||
$serverListTab->statusLabel->setText(
|
||||
@ -804,9 +805,30 @@ class ServerListTab
|
||||
return;
|
||||
}
|
||||
|
||||
$serverListTab->currentServerTodos[] = $text;
|
||||
// Add as Kanban task in board "neu"
|
||||
$tasks = $serverListTab->settings->get('kanban.tasks', []);
|
||||
if (!is_array($tasks)) {
|
||||
$tasks = [];
|
||||
}
|
||||
|
||||
$newTask = [
|
||||
'id' => uniqid('task_', true),
|
||||
'server_id' => $serverListTab->currentServerId,
|
||||
'title' => $text,
|
||||
'board' => 'neu',
|
||||
];
|
||||
|
||||
$tasks[] = $newTask;
|
||||
|
||||
$serverListTab->settings->set('kanban.tasks', $tasks);
|
||||
$serverListTab->settings->save();
|
||||
|
||||
$serverListTab->todoInput->setValue('');
|
||||
$serverListTab->renderTodoList();
|
||||
$serverListTab->loadServerTasks();
|
||||
|
||||
if ($serverListTab->kanbanTab !== null) {
|
||||
$serverListTab->kanbanTab->refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -897,14 +919,20 @@ class ServerListTab
|
||||
|
||||
if (empty($this->currentServerTodos)) {
|
||||
$this->todoListContainer->addComponent(
|
||||
new Label('Keine TODOs', 'text-xs text-gray-500 italic'),
|
||||
new Label('Keine Tasks', 'text-xs text-gray-500 italic'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->currentServerTodos as $index => $todo) {
|
||||
foreach ($this->currentServerTodos as $index => $task) {
|
||||
$title = (string) ($task['title'] ?? '');
|
||||
$board = (string) ($task['board'] ?? 'neu');
|
||||
|
||||
$row = new Container('flex flex-row items-center gap-2');
|
||||
$row->addComponent(new Label('- ' . $todo, 'text-xs text-gray-800 flex-1'));
|
||||
$row->addComponent(new Label(
|
||||
'- ' . $title . ' [' . $board . ']',
|
||||
'text-xs text-gray-800 flex-1',
|
||||
));
|
||||
|
||||
$removeButton = new Button(
|
||||
'x',
|
||||
@ -914,14 +942,29 @@ class ServerListTab
|
||||
);
|
||||
|
||||
$serverListTab = $this;
|
||||
$removeButton->setOnClick(function () use ($serverListTab, $index) {
|
||||
if (!isset($serverListTab->currentServerTodos[$index])) {
|
||||
$removeButton->setOnClick(function () use ($serverListTab, $task) {
|
||||
$taskId = $task['id'] ?? null;
|
||||
if ($taskId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset($serverListTab->currentServerTodos[$index]);
|
||||
$serverListTab->currentServerTodos = array_values($serverListTab->currentServerTodos);
|
||||
$serverListTab->renderTodoList();
|
||||
$tasks = $serverListTab->settings->get('kanban.tasks', []);
|
||||
if (!is_array($tasks)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tasks = array_values(array_filter(
|
||||
$tasks,
|
||||
static fn($t) => ($t['id'] ?? null) !== $taskId,
|
||||
));
|
||||
|
||||
$serverListTab->settings->set('kanban.tasks', $tasks);
|
||||
$serverListTab->settings->save();
|
||||
$serverListTab->loadServerTasks();
|
||||
|
||||
if ($serverListTab->kanbanTab !== null) {
|
||||
$serverListTab->kanbanTab->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
$row->addComponent($removeButton);
|
||||
@ -952,4 +995,28 @@ class ServerListTab
|
||||
|
||||
return $checkbox;
|
||||
}
|
||||
|
||||
private function loadServerTasks(): void
|
||||
{
|
||||
$this->currentServerTodos = [];
|
||||
|
||||
if ($this->currentServerId === null) {
|
||||
$this->renderTodoList();
|
||||
return;
|
||||
}
|
||||
|
||||
$tasks = $this->settings->get('kanban.tasks', []);
|
||||
if (!is_array($tasks)) {
|
||||
$tasks = [];
|
||||
}
|
||||
|
||||
$this->currentServerTodos = array_values(array_filter(
|
||||
$tasks,
|
||||
function ($task) {
|
||||
return (int) ($task['server_id'] ?? 0) === (int) $this->currentServerId;
|
||||
},
|
||||
));
|
||||
|
||||
$this->renderTodoList();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user