feature/desktop-view #5

Merged
master merged 12 commits from feature/desktop-view into main 2025-09-19 21:06:23 +02:00
2 changed files with 73 additions and 0 deletions
Showing only changes of commit d870219136 - Show all commits

View File

@@ -2,6 +2,7 @@
class NodeDetailsComponent extends Component {
constructor(container, viewModel, eventBus) {
super(container, viewModel, eventBus);
this.suppressLoadingUI = false;
}
setupViewModelListeners() {
@@ -31,6 +32,7 @@ class NodeDetailsComponent extends Component {
// Handle loading state update
handleLoadingUpdate(isLoading) {
if (isLoading) {
if (this.suppressLoadingUI) return;
this.renderLoading('<div class="loading-details">Loading detailed information...</div>');
}
}
@@ -103,6 +105,13 @@ class NodeDetailsComponent extends Component {
<button class="tab-button ${activeTab === 'endpoints' ? 'active' : ''}" data-tab="endpoints">Endpoints</button>
<button class="tab-button ${activeTab === 'tasks' ? 'active' : ''}" data-tab="tasks">Tasks</button>
<button class="tab-button ${activeTab === 'firmware' ? 'active' : ''}" data-tab="firmware">Firmware</button>
<button class="tab-refresh-btn" title="Refresh current tab" aria-label="Refresh">
<svg class="refresh-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
<path d="M1 4v6h6" />
<path d="M23 20v-6h-6" />
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15" />
</svg>
</button>
</div>
<div class="tab-content ${activeTab === 'status' ? 'active' : ''}" id="status-tab">
@@ -145,6 +154,7 @@ class NodeDetailsComponent extends Component {
this.setHTML('', html);
this.setupTabs();
this.setupTabRefreshButton();
// Restore last active tab from view model if available
const restored = this.viewModel && typeof this.viewModel.get === 'function' ? this.viewModel.get('activeTab') : null;
if (restored) {
@@ -153,6 +163,46 @@ class NodeDetailsComponent extends Component {
this.setupFirmwareUpload();
}
setupTabRefreshButton() {
const btn = this.findElement('.tab-refresh-btn');
if (!btn) return;
this.addEventListener(btn, 'click', async (e) => {
e.stopPropagation();
const original = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = `
<svg class="refresh-icon spinning" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
<path d="M1 4v6h6" />
<path d="M23 20v-6h-6" />
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15" />
</svg>
`;
try {
const activeTab = (this.viewModel && typeof this.viewModel.get === 'function') ? (this.viewModel.get('activeTab') || 'status') : 'status';
const nodeIp = (this.viewModel && typeof this.viewModel.get === 'function') ? this.viewModel.get('nodeIp') : null;
this.suppressLoadingUI = true;
if (activeTab === 'endpoints' && typeof this.viewModel.loadEndpointsData === 'function') {
await this.viewModel.loadEndpointsData();
} else if (activeTab === 'tasks' && typeof this.viewModel.loadTasksData === 'function') {
await this.viewModel.loadTasksData();
} else {
// status or firmware: refresh core node details
if (nodeIp && typeof this.viewModel.loadNodeDetails === 'function') {
await this.viewModel.loadNodeDetails(nodeIp);
}
}
} catch (err) {
logger.error('Tab refresh failed:', err);
} finally {
this.suppressLoadingUI = false;
btn.disabled = false;
btn.innerHTML = original;
}
});
}
renderEndpointsTab(endpoints) {
if (!endpoints || !Array.isArray(endpoints.endpoints) || endpoints.endpoints.length === 0) {
return `

View File

@@ -2184,6 +2184,29 @@ p {
border-bottom: none;
}
/* Refresh button aligned to the right of tabs, blends with tab header */
.tabs-header .tab-refresh-btn {
margin-left: auto;
background: transparent;
border: 1px solid transparent;
color: var(--text-secondary);
padding: 0.4rem;
border-radius: 8px;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
}
.tabs-header .tab-refresh-btn:hover {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.12);
color: var(--text-primary);
}
.tabs-header .tab-refresh-btn:disabled {
opacity: 0.6;
cursor: default;
}
.tab-button {
background: transparent;
border: 1px solid transparent;