Merge claude/1

This commit is contained in:
librelad 2026-05-23 18:36:06 +01:00
commit a511ed9e8d
8 changed files with 59 additions and 48 deletions

View File

@ -46,7 +46,7 @@
</div>
</div>
<a href="/config?=overview" class="dashboard-admin-link">
<a href="/admin" class="dashboard-admin-link">
Admin overview
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
</a>

View File

@ -31,7 +31,7 @@
</svg>
App Center
</a>
<a href="config.html" class="nav-item" id="nav-config">
<a href="/admin" class="nav-item" id="nav-config">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path>
</svg>

View File

@ -58,7 +58,7 @@ class AdminOverview {
window.librePortalSPA?.navigate('/backup', true);
} else if (where === 'ssh' || where === 'security') {
const target = where === 'ssh' ? 'ssh-access' : 'security';
window.history.pushState({}, '', `/config?=${target}`);
window.history.pushState({}, '', window.adminPath(target));
window.configCategory = target;
window.configManager?.renderConfig?.(target);
}

View File

@ -26,7 +26,7 @@ class ConfigSidebar {
overviewItem.setAttribute('data-category', 'overview');
overviewItem.innerHTML = '<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" style="margin-right:8px;vertical-align:middle"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg> Overview';
overviewItem.addEventListener('click', function () {
window.history.pushState({}, '', '/config?=overview');
window.history.pushState({}, '', window.adminPath('overview'));
document.querySelectorAll('.category').forEach(function (item) { item.classList.remove('active'); });
this.classList.add('active');
window.configCategory = 'overview';
@ -76,8 +76,7 @@ class ConfigSidebar {
categoryItem.addEventListener('click', function() {
// Update URL without full page reload
const url = '/config?=' + category.id;
window.history.pushState({}, '', url);
window.history.pushState({}, '', window.adminPath(category.id));
// Update active state
document.querySelectorAll('.category').forEach(function(item) {
@ -109,7 +108,7 @@ class ConfigSidebar {
sshItem.setAttribute('data-category', 'ssh-access');
sshItem.innerHTML = '<img src="/icons/config/security.svg" alt="SSH Access" style="width: 20px; height: 20px; margin-right: 8px;"/> SSH Access';
sshItem.addEventListener('click', function () {
window.history.pushState({}, '', '/config?=ssh-access');
window.history.pushState({}, '', window.adminPath('ssh-access'));
document.querySelectorAll('.category').forEach(function (item) { item.classList.remove('active'); });
this.classList.add('active');
window.configCategory = 'ssh-access';

View File

@ -17,7 +17,7 @@ class TopbarComponent {
if (path.startsWith('/apps') || path === '/apps') {
return 'apps';
}
if (path.startsWith('/config') || path === '/config') {
if (path.startsWith('/admin') || path.startsWith('/config') || path.startsWith('/ssh')) {
return 'config';
}
if (path.startsWith('/tasks') || path === '/tasks') {
@ -266,14 +266,12 @@ class TopbarComponent {
// PRIMARY: Use path-based detection only (most reliable)
if (path.startsWith('/app') || path.startsWith('/apps')) {
activeNavId = 'nav-app-center';
} else if (path.startsWith('/config')) {
activeNavId = 'nav-config';
} else if (path.startsWith('/admin') || path.startsWith('/config') || path.startsWith('/ssh')) {
activeNavId = 'nav-config'; // Admin area (config + SSH live here)
} else if (path.startsWith('/tasks')) {
activeNavId = 'nav-tasks';
} else if (path.startsWith('/backup')) {
activeNavId = 'nav-backup';
} else if (path.startsWith('/ssh')) {
activeNavId = 'nav-config'; // SSH Access lives under the Admin (config) area
} else if (path === '/' || path === '/dashboard') {
activeNavId = 'nav-dashboard';
} else {
@ -349,7 +347,7 @@ class TopbarComponent {
if (path.startsWith('/app') || path.startsWith('/apps')) {
activeNavId = 'nav-app-center';
} else if (path.startsWith('/config')) {
} else if (path.startsWith('/admin') || path.startsWith('/config') || path.startsWith('/ssh')) {
activeNavId = 'nav-config';
} else if (path.startsWith('/tasks')) {
activeNavId = 'nav-tasks';

View File

@ -68,13 +68,15 @@ class LibrePortalSPAClean {
this.routes.set('/apps', () => this.handleApps());
this.routes.set('/app', () => this.handleAppDetail()); // Handle /app without query
this.routes.set('/app*', () => this.handleAppDetail()); // Handle /app with query
this.routes.set('/config', () => this.handleConfig()); // Handle /config without query
this.routes.set('/config*', () => this.handleConfig()); // Handle /config with query
this.routes.set('/admin', () => this.handleAdmin()); // Admin area (path-based: /admin/config/<x>, /admin/tools/<x>)
this.routes.set('/admin*', () => this.handleAdmin());
this.routes.set('/config', () => this.handleConfigRedirect()); // legacy → /admin
this.routes.set('/config*', () => this.handleConfigRedirect());
this.routes.set('/tasks', () => this.handleTasks()); // Handle /tasks without query
this.routes.set('/tasks*', () => this.handleTasks()); // Handle /tasks with query
this.routes.set('/backup', () => this.handleBackup());
this.routes.set('/backup*', () => this.handleBackup());
this.routes.set('/ssh', () => this.handleSsh());
this.routes.set('/ssh', () => this.handleSsh()); // legacy → /admin/tools/ssh-access
this.routes.set('/ssh*', () => this.handleSsh());
//console.log('📍 Routes registered:', Array.from(this.routes.keys()));
@ -270,9 +272,8 @@ class LibrePortalSPAClean {
}
async handleSsh() {
// SSH Access now lives inside the Admin (config) area as a sidebar item.
// Redirect old /ssh links to it.
this.navigate('/config?=ssh-access', true);
// Legacy /ssh → SSH Access under the Admin area.
this.navigate('/admin/tools/ssh-access', true);
}
async handleApps() {
@ -363,43 +364,41 @@ class LibrePortalSPAClean {
}
}
async handleConfig() {
//console.log('⚙️ Loading config...');
// Handle query parameters for config
const path = window.location.pathname + window.location.search;
if (path.includes('?=')) {
const [basePath, query] = path.split('?=');
window.configCategory = query || 'overview';
} else if (path.includes('?')) {
const url = new URL(path, window.location.origin);
const searchParams = url.searchParams;
window.configCategory = searchParams.get('config') || 'overview';
} else {
window.configCategory = 'overview';
}
// Admin area. Path-based: /admin (overview), /admin/config/<category>,
// /admin/tools/ssh-access. Reuses the config page shell + ConfigManager.
async handleAdmin() {
window.configCategory = window.adminCategoryFromPath(window.location.pathname);
try {
const html = await this.fetchContent('/html/config-content.html');
this.loadContent(html, 'Configuration');
this.loadContent(html, 'Admin');
// Config manager should already be initialized by SystemLoader
if (window.configManager) {
// Render the actual configuration
if (typeof window.configManager.renderConfig === 'function') {
await window.configManager.renderConfig(window.configCategory || 'overview');
}
//console.log('✅ Config loaded');
} else {
console.error('ConfigManager not available - SystemLoader should have initialized it');
throw new Error('ConfigManager not initialized by SystemLoader');
}
} catch (error) {
console.error('❌ Config load error:', error);
this.showError('Failed to load configuration');
console.error('❌ Admin load error:', error);
this.showError('Failed to load the Admin area');
}
}
// Legacy /config and /config?=<x> → the path-based /admin equivalent.
async handleConfigRedirect() {
const search = window.location.search || '';
let cat = 'overview';
if (search.includes('?=')) {
cat = (window.location.pathname + search).split('?=')[1] || 'overview';
} else {
cat = new URLSearchParams(search).get('config') || 'overview';
}
this.navigate(window.adminPath(cat), true);
}
async handleTasks() {
//console.log('📋 Loading tasks...');
@ -476,7 +475,7 @@ class LibrePortalSPAClean {
if (path.startsWith('/app') || path.startsWith('/apps')) {
activeId = 'nav-app-center';
} else if (path.startsWith('/config')) {
} else if (path.startsWith('/admin') || path.startsWith('/config') || path.startsWith('/ssh')) {
activeId = 'nav-config';
} else if (path.startsWith('/tasks')) {
activeId = 'nav-tasks';
@ -507,6 +506,21 @@ class LibrePortalSPAClean {
}
}
// Admin area path helpers (shared by the SPA, sidebar, overview, ssh page).
// Map a category to its path-based URL, and parse a path back to a category.
window.adminPath = function (category) {
if (!category || category === 'overview') return '/admin';
if (category === 'ssh-access') return '/admin/tools/ssh-access';
return '/admin/config/' + category;
};
window.adminCategoryFromPath = function (pathname) {
const segs = String(pathname || '').replace(/^\/admin\/?/, '').split('/').filter(Boolean);
if (!segs.length || segs[0] === 'overview') return 'overview';
if (segs[0] === 'config') return segs[1] || 'general';
if (segs[0] === 'tools') return segs[1] || 'overview';
return segs[0];
};
// Global navigation function for click handlers
window.navigateToRoute = function(href) {
if (window.spaClean) {
@ -522,8 +536,8 @@ window.navigateToRoute = function(href) {
route = '/dashboard';
} else if (route === 'apps') {
route = '/apps';
} else if (route === 'config') {
route = '/config?=general';
} else if (route === 'config' || route === 'admin') {
route = '/admin';
} else if (route === 'tasks') {
route = '/tasks';
} else if (!route.startsWith('/')) {

View File

@ -150,8 +150,8 @@ async function initializeData() {
} else if (currentPath === '/apps' || currentPath === '/app' || searchParams.has('apps') || searchParams.has('app')) {
// Apps page: All apps + categories (no system configs needed)
await loadAppsPageData();
} else if (currentPath.startsWith('/config') || searchParams.has('config')) {
// Config pages: System configs + apps + categories
} else if (currentPath.startsWith('/admin') || currentPath.startsWith('/config') || currentPath.startsWith('/ssh') || searchParams.has('config')) {
// Admin area (config + SSH): system configs + apps + categories
await loadConfigDetailData();
} else {
// Default: Load all data for SPA

View File

@ -264,7 +264,7 @@ class Router {
// PRIMARY: Use path-based detection only (most reliable)
if (pathname.startsWith('/app') || pathname.startsWith('/apps')) {
activeNavId = 'nav-app-center';
} else if (pathname.startsWith('/config')) {
} else if (pathname.startsWith('/admin') || pathname.startsWith('/config') || pathname.startsWith('/ssh')) {
activeNavId = 'nav-config';
} else if (pathname.startsWith('/tasks')) {
activeNavId = 'nav-tasks';