The per-app Backups tab was the odd one out: snapshots and the "Backup now" / "Open backup center" buttons all sat inside a single flat .backup-app-card with no styling parity to Services or Tasks. The Services tab uses .services-title (20px header + bottom border) on top of a recessed .services-rows panel; Tasks uses the same recipe with .tasks-title + .tasks-container. Backups now matches. .backup-title is the header row — h3 + subtitle on the left, Backup-now (primary) and Open-backup-center (secondary) buttons pinned to the right so they stay reachable regardless of how long the snapshot list grows. No pagination needed: the renderer already soft-caps the displayed list at 50 with an "Open backup center" overflow link, and per-app snapshot counts almost never exceed that. .backup-snapshots-container is the dark panel (rgba bg 0.2, radius 8, padding/margin 16) wrapping the existing status line + snapshot rows. JS untouched — it still writes to #backup-app-card-status and #backup-app-card-snapshots; only the outer shell changed. Signed-off-by: librelad <librelad@digitalangels.vip>
243 lines
9.6 KiB
HTML
Executable File
243 lines
9.6 KiB
HTML
Executable File
<!-- Unified Apps Layout with Persistent Sidebar -->
|
|
<div class="apps-layout">
|
|
<!-- Mobile overlay -->
|
|
<div class="mobile-overlay" id="mobile-overlay"></div>
|
|
|
|
<!-- Sidebar Container -->
|
|
<div class="sidebar-container">
|
|
<div class="sidebar" id="sidebar">
|
|
<div class="apps-search">
|
|
<svg class="apps-search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<circle cx="11" cy="11" r="8"></circle>
|
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
</svg>
|
|
<input
|
|
type="search"
|
|
id="apps-search-input"
|
|
class="apps-search-input"
|
|
placeholder="Search apps…"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
oninput="if(window.appsManager) window.appsManager.filterAppsByQuery(this.value)"
|
|
>
|
|
<button type="button" class="apps-search-clear" aria-label="Clear search" onclick="if(window.appsManager) window.appsManager.clearAppsSearch()">×</button>
|
|
</div>
|
|
<!-- Categories will be dynamically generated here -->
|
|
<div id="dynamic-categories">
|
|
<!-- Loading spinner for categories -->
|
|
<div class="loading-categories">
|
|
<div class="loading-spinner"></div>
|
|
<p style="color: #ffffff;">Loading categories...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content Area -->
|
|
<div class="main-content">
|
|
<!-- Apps List View -->
|
|
<div id="apps-view" class="content-view">
|
|
<div class="apps-section" id="apps-section">
|
|
<!-- Loading spinner for apps -->
|
|
<div class="loading-content" style="
|
|
text-align: center;
|
|
padding: 22px;
|
|
background: var(--input-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 6px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
margin: 22px;
|
|
">
|
|
<div class="loading-spinner" style="
|
|
width: 24px;
|
|
height: 24px;
|
|
border: 3px solid rgba(52, 152, 219, 0.3);
|
|
border-top: 3px solid #3498db;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin: 0 auto 16px auto;
|
|
display: inline-block;
|
|
"></div>
|
|
<div class="loading-message" style="
|
|
font-size: 16px;
|
|
color: #ffffff;
|
|
font-weight: 500;
|
|
margin-bottom: 8px;
|
|
">
|
|
Loading applications...
|
|
</div>
|
|
<div class="loading-subtitle" style="
|
|
font-size: 14px;
|
|
color: rgba(255, 255, 255, 0.8);
|
|
font-style: italic;
|
|
">
|
|
Discovering the perfect applications for you...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- App Detail View -->
|
|
<div id="app-detail-view" class="content-view">
|
|
<div class="app-header" id="app-header">
|
|
<!-- App info will be dynamically inserted -->
|
|
</div>
|
|
|
|
<!-- Tabbed Interface -->
|
|
<div class="tabbed-interface">
|
|
<div class="tab-navigation">
|
|
<button class="main-tab-button active" data-tab="config" onclick="if(window.appTabbedManager) window.appTabbedManager.switchTab('config')">
|
|
<span class="tab-emoji">🛠️</span>
|
|
<span class="tab-name">Config</span>
|
|
</button>
|
|
<button class="main-tab-button" data-tab="services" onclick="if(window.appTabbedManager) window.appTabbedManager.switchTab('services')">
|
|
<span class="tab-emoji">⚡</span>
|
|
<span class="tab-name">Services</span>
|
|
</button>
|
|
<button class="main-tab-button" data-tab="tools" style="display: none;" onclick="if(window.appTabbedManager) window.appTabbedManager.switchTab('tools')">
|
|
<span class="tab-emoji">🧰</span>
|
|
<span class="tab-name">Tools</span>
|
|
</button>
|
|
<button class="main-tab-button" data-tab="routing" style="display: none;" onclick="if(window.appTabbedManager) window.appTabbedManager.switchTab('routing')">
|
|
<span class="tab-emoji">🛡️</span>
|
|
<span class="tab-name">Routing</span>
|
|
</button>
|
|
<button class="main-tab-button" data-tab="backups" onclick="if(window.appTabbedManager) window.appTabbedManager.switchTab('backups')">
|
|
<span class="tab-emoji">💾</span>
|
|
<span class="tab-name">Backups</span>
|
|
</button>
|
|
<button class="main-tab-button" data-tab="tasks" onclick="if(window.appTabbedManager) window.appTabbedManager.switchTab('tasks')">
|
|
<span class="tab-emoji">📋</span>
|
|
<span class="tab-name">Tasks</span>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Tab Content -->
|
|
<div class="tab-content">
|
|
<!-- Config Tab -->
|
|
<div class="tab-pane active" id="config-tab">
|
|
<div class="config-section" id="config-section">
|
|
<!-- Loading spinner for config -->
|
|
<div class="loading-content" style="
|
|
text-align: center;
|
|
padding: 40px 20px;
|
|
color: #ffffff;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 200px;
|
|
background: rgba(0, 0, 0, 0.2);
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
margin: 16px;
|
|
">
|
|
<div class="loading-spinner" style="
|
|
width: 24px;
|
|
height: 24px;
|
|
border: 3px solid rgba(52, 152, 219, 0.3);
|
|
border-top: 3px solid #3498db;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin-bottom: 16px;
|
|
"></div>
|
|
<p>Loading configuration...</p>
|
|
</div>
|
|
<!-- Config form will be dynamically inserted -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Services Tab -->
|
|
<div class="tab-pane" id="services-tab">
|
|
<div class="services-section">
|
|
<div id="services-list" class="services-list">
|
|
<!-- Service rows are rendered here by services-manager.js -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tools Tab -->
|
|
<div class="tab-pane" id="tools-tab">
|
|
<div class="tools-section">
|
|
<div id="tools-list" class="tools-list">
|
|
<!-- Tool buttons are rendered here by tools-manager.js -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Routing Tab (Traefik only) -->
|
|
<div class="tab-pane" id="routing-tab">
|
|
<div class="routing-section-wrap">
|
|
<div id="routing-list" class="routing-list">
|
|
<!-- Routing rows rendered by routing-manager.js -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Backups Tab -->
|
|
<div class="tab-pane" id="backups-tab">
|
|
<div class="backup-section" id="backup-section">
|
|
<div class="backup-title">
|
|
<div class="backup-title-main">
|
|
<h3>💾 Backups</h3>
|
|
<p>Backups for <span id="backup-app-name">this app</span> across all configured locations.</p>
|
|
</div>
|
|
<div class="backup-title-actions">
|
|
<button type="button" class="backup-primary-btn" id="backup-app-card-backup-btn">Backup now</button>
|
|
<a class="backup-secondary-btn" href="/backup">Open backup center →</a>
|
|
</div>
|
|
</div>
|
|
<div class="backup-snapshots-container">
|
|
<div class="backup-app-card-status" id="backup-app-card-status">Loading…</div>
|
|
<div class="backup-app-card-snapshots" id="backup-app-card-snapshots"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tasks Tab -->
|
|
<div class="tab-pane" id="tasks-tab">
|
|
<div class="tasks-section">
|
|
<div class="tasks-title">
|
|
<h3>📋 Task Management</h3>
|
|
<p>Tasks for <span id="tasks-app-name">this application</span> - Monitor and manage application tasks</p>
|
|
</div>
|
|
<div class="tasks-container" id="app-tasks">
|
|
<!-- Loading spinner for tasks -->
|
|
<div class="loading-content" style="
|
|
text-align: center;
|
|
padding: 40px 20px;
|
|
color: #ffffff;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 200px;
|
|
background: rgba(0, 0, 0, 0.2);
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
margin: 16px;
|
|
">
|
|
<div class="loading-spinner" style="
|
|
width: 24px;
|
|
height: 24px;
|
|
border: 3px solid rgba(52, 152, 219, 0.3);
|
|
border-top: 3px solid #3498db;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin-bottom: 16px;
|
|
"></div>
|
|
<p>Loading tasks...</p>
|
|
</div>
|
|
<!-- App-specific tasks will be loaded here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|