librelad 875a60f90f LibrePortal v0.1.0 — initial release
A free, open, self-hosted app platform (GNU AGPLv3): one-click app deploys,
Traefik reverse proxy with automatic SSL, rootless Docker support, gluetun
VPN routing, and a web dashboard to manage it all.

Free & open forever to self-host; optional paid hosted services fund it.
See PROMISE.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-21 20:37:54 +01:00

199 lines
9.0 KiB
HTML
Executable File

<!-- Tasks Content - Sidebar Layout -->
<div class="tasks-layout">
<!-- Mobile overlay -->
<div class="mobile-overlay" id="mobile-overlay"></div>
<!-- Sidebar Container -->
<div class="sidebar-container">
<div class="sidebar" id="sidebar">
<!-- Task Categories -->
<div class="sidebar-section">
<div class="sidebar-category">
<h3>📋 All Tasks</h3>
<div class="sidebar-items">
<a href="#" class="sidebar-item active" data-category="all" onclick="filterTasksByCategory('all')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<line x1="9" y1="9" x2="15" y2="9"></line>
<line x1="9" y1="15" x2="15" y2="15"></line>
</svg>
All Tasks
<span class="task-count" id="count-all">0</span>
</a>
</div>
</div>
<div class="sidebar-category">
<h3>📊 By Status</h3>
<div class="sidebar-items">
<a href="#" class="sidebar-item" data-category="queued" onclick="filterTasksByCategory('queued')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" style="stroke: var(--status-warning);" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
Queued
<span class="task-count" id="count-queued">0</span>
</a>
<a href="#" class="sidebar-item" data-category="running" onclick="filterTasksByCategory('running')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" style="stroke: var(--status-success);" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
Running
<span class="task-count" id="count-running">0</span>
</a>
<a href="#" class="sidebar-item" data-category="completed" onclick="filterTasksByCategory('completed')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" style="stroke: var(--status-success);" stroke-width="2">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
<polyline points="22 4 12 14.01 9 11.01"></polyline>
</svg>
Completed
<span class="task-count" id="count-completed">0</span>
</a>
<a href="#" class="sidebar-item" data-category="failed" onclick="filterTasksByCategory('failed')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" style="stroke: var(--status-danger);" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<line x1="15" y1="9" x2="9" y2="15"></line>
<line x1="9" y1="9" x2="15" y2="15"></line>
</svg>
Failed
<span class="task-count" id="count-failed">0</span>
</a>
</div>
</div>
<div class="sidebar-category">
<h3>🏷️ By Type</h3>
<div class="sidebar-items">
<a href="#" class="sidebar-item" data-category="install" onclick="filterTasksByCategory('install')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Install
<span class="task-count" id="count-install">0</span>
</a>
<a href="#" class="sidebar-item" data-category="uninstall" onclick="filterTasksByCategory('uninstall')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 6h18"></path>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
</svg>
Uninstall
<span class="task-count" id="count-uninstall">0</span>
</a>
<a href="#" class="sidebar-item" data-category="management" onclick="filterTasksByCategory('management')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"></circle>
<path d="M12 1v6m0 6v6m4.22-13.22l4.24 4.24M1.54 9.96l4.24 4.24M1.54 14.04l4.24-4.24M18.46 14.04l4.24-4.24"></path>
</svg>
Management
<span class="task-count" id="count-management">0</span>
</a>
<a href="#" class="sidebar-item" data-category="backup" onclick="filterTasksByCategory('backup')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
<polyline points="17 21 17 13 7 13 7 21"></polyline>
<polyline points="7 3 7 8 15 8"></polyline>
</svg>
Backups
<span class="task-count" id="count-backup">0</span>
</a>
<a href="#" class="sidebar-item" data-category="config" onclick="filterTasksByCategory('config')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"></circle>
<path d="M12 1v6m0 6v6m4.22-13.22l4.24 4.24M1.54 9.96l4.24 4.24M1.54 14.04l4.24-4.24M18.46 14.04l4.24-4.24"></path>
</svg>
Configuration
<span class="task-count" id="count-config">0</span>
</a>
</div>
</div>
<div class="sidebar-category">
<h3>📱 By App</h3>
<div class="sidebar-items" id="app-categories">
<!-- App categories will be dynamically generated here -->
<div class="loading-categories">
<div class="loading-spinner"></div>
<p style="color: var(--text-primary);">Loading apps...</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Main Content Area -->
<div class="main-content">
<!-- Status Bar -->
<div class="terminal-status-bar">
<div class="status-item">
<span class="status-indicator status-queued"></span>
Queued: <span id="queued-count">0</span>
</div>
<div class="status-item">
<span class="status-indicator status-running"></span>
Running: <span id="running-count">0</span>
</div>
<div class="status-item">
<span class="status-indicator status-completed"></span>
Completed: <span id="completed-count">0</span>
</div>
<div class="status-item">
<span class="status-indicator status-failed"></span>
Failed: <span id="failed-count">0</span>
</div>
<div class="status-item">
<button class="refresh-btn" onclick="refreshTasks()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"></path>
</svg>
Refresh
</button>
<button class="clear-btn" onclick="clearAllTasks()" title="Clear All Tasks">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 6h18"></path>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
</svg>
Clear All
</button>
</div>
</div>
<!-- Tasks List -->
<div class="tasks-terminal">
<div class="tasks-list" id="tasks-list">
<!-- Modern loading spinner -->
<div class="loading-content" style="
text-align: center;
padding: 60px 20px;
color: var(--text-primary);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 400px;
background: var(--surface-sunken);
border-radius: 8px;
border: 1px solid var(--border-subtle);
margin: 16px;
">
<div class="loading-spinner" style="
width: 32px;
height: 32px;
border: 4px solid rgba(var(--accent-rgb), 0.3);
border-top: 4px solid var(--accent);
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 20px;
"></div>
<p style="font-size: 16px; margin-bottom: 8px;">Loading tasks...</p>
<p style="font-size: 12px; opacity: 0.7;">Please wait while we fetch your tasks</p>
</div>
</div>
</div>
</div>
</div>