Merge claude/1
This commit is contained in:
commit
7e7a7f524c
@ -97,15 +97,15 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
// Get app name from URL parameter
|
// Get app name from URL parameter
|
||||||
getAppFromURL() {
|
getAppFromURL() {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
// Path-based /app/<name> first, then legacy ?app= / ?=name.
|
||||||
let appName = urlParams.get('app');
|
let appName = window.location.pathname.replace(/^\/app\/?/, '').split('/')[0];
|
||||||
|
appName = appName ? decodeURIComponent(appName) : '';
|
||||||
// Fallback to old format if app param not found
|
|
||||||
if (!appName) {
|
if (!appName) {
|
||||||
const fullPath = window.location.search;
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
if (fullPath.includes('?=')) {
|
appName = urlParams.get('app');
|
||||||
const [basePath, query] = fullPath.split('?=');
|
if (!appName && window.location.search.includes('?=')) {
|
||||||
appName = query.split('&')[0]; // Get only the app name, ignore other params
|
appName = window.location.search.split('?=')[1].split('&')[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,88 +163,34 @@ class AppTabbedManager {
|
|||||||
search.includes('?=')); // Old format app pages
|
search.includes('?=')); // Old format app pages
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update URL with app and tab
|
// Update URL with app and tab — path-based: /app/<app>?tab=<tab>&task=<task>.
|
||||||
updateURL(app = null, tab = null) {
|
updateURL(app = null, tab = null) {
|
||||||
// console.log('🔍 updateURL called with:', { app, tab });
|
// Only update URLs on app pages - prevent interference with other pages.
|
||||||
// console.log('🔍 Current URL before update:', window.location.href);
|
if (!this.isAppPage()) return;
|
||||||
|
|
||||||
// Only update URLs on app pages - prevent interference with other pages
|
const params = new URLSearchParams(window.location.search);
|
||||||
if (!this.isAppPage()) {
|
const fromPath = window.location.pathname.replace(/^\/app\/?/, '').split('/')[0];
|
||||||
// console.log('🚫 Not on app page, skipping URL update');
|
const currentApp = app || (fromPath ? decodeURIComponent(fromPath) : '') || this.currentApp;
|
||||||
return;
|
if (!currentApp) return;
|
||||||
|
|
||||||
|
const q = new URLSearchParams();
|
||||||
|
const finalTab = tab || params.get('tab');
|
||||||
|
if (finalTab) q.set('tab', finalTab);
|
||||||
|
// Keep a deep-linked task only when staying on the same app (tab-only update).
|
||||||
|
if (!app) {
|
||||||
|
const task = params.get('task');
|
||||||
|
if (task) q.set('task', task);
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(window.location);
|
const qs = q.toString();
|
||||||
const params = new URLSearchParams(url.search);
|
window.history.replaceState({}, '', `/app/${encodeURIComponent(currentApp)}${qs ? '?' + qs : ''}`);
|
||||||
const fullPath = window.location.search; // Define here for both blocks
|
|
||||||
|
|
||||||
// Handle both old format (?=appname) and new format (?app=appname)
|
|
||||||
if (app) {
|
|
||||||
// Check if we're using the old format
|
|
||||||
if (fullPath.includes('?=')) {
|
|
||||||
// Update old format: /app?=appname&tab=tabname
|
|
||||||
const newURL = `/app?=${app}`;
|
|
||||||
if (tab) {
|
|
||||||
// console.log('🔄 Updating URL to:', `${newURL}&tab=${tab}`);
|
|
||||||
window.history.replaceState({}, '', `${newURL}&tab=${tab}`);
|
|
||||||
} else {
|
|
||||||
// console.log('🔄 Updating URL to:', newURL);
|
|
||||||
window.history.replaceState({}, '', newURL);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Update new format: /app?app=appname&tab=tabname
|
|
||||||
if (tab) {
|
|
||||||
params.set('app', app);
|
|
||||||
params.set('tab', tab);
|
|
||||||
} else {
|
|
||||||
params.set('app', app);
|
|
||||||
}
|
|
||||||
const newSearch = params.toString();
|
|
||||||
// console.log('🔄 Updating URL to:', `${window.location.pathname}?${newSearch}`);
|
|
||||||
window.history.replaceState({}, '', `${window.location.pathname}?${newSearch}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Only updating tab, preserve existing app and task parameters
|
|
||||||
if (fullPath.includes('?=')) {
|
|
||||||
// Old format: preserve app and task, update tab
|
|
||||||
const currentApp = params.get('=') || this.currentApp;
|
|
||||||
const currentTask = params.get('task');
|
|
||||||
let newURL = `/app?=${currentApp}&tab=${tab}`;
|
|
||||||
if (currentTask) {
|
|
||||||
newURL += `&task=${currentTask}`;
|
|
||||||
}
|
|
||||||
// console.log('🔄 Updating URL (old format) to:', newURL);
|
|
||||||
window.history.replaceState({}, '', newURL);
|
|
||||||
} else {
|
|
||||||
// New format: preserve app and task, update tab
|
|
||||||
const currentApp = params.get('app') || this.currentApp;
|
|
||||||
const currentTask = params.get('task');
|
|
||||||
params.set('app', currentApp);
|
|
||||||
params.set('tab', tab);
|
|
||||||
if (currentTask) {
|
|
||||||
params.set('task', currentTask);
|
|
||||||
}
|
|
||||||
const newSearch = params.toString();
|
|
||||||
// console.log('🔄 Updating URL (new format) to:', `${window.location.pathname}?${newSearch}`);
|
|
||||||
window.history.replaceState({}, '', `${window.location.pathname}?${newSearch}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update current app and refresh content
|
// Update current app and refresh content
|
||||||
updateApp(newAppName) {
|
updateApp(newAppName) {
|
||||||
this.setCurrentApp(newAppName);
|
this.setCurrentApp(newAppName);
|
||||||
|
// Reset to the config tab on the path-based app URL.
|
||||||
// Reset URL to config tab
|
history.replaceState({}, '', `/app/${encodeURIComponent(newAppName)}?tab=config`);
|
||||||
const currentUrl = window.location.href;
|
|
||||||
let newUrl;
|
|
||||||
if (currentUrl.includes('tab=')) {
|
|
||||||
newUrl = currentUrl.replace(/tab=[^&]*/, 'tab=config');
|
|
||||||
} else {
|
|
||||||
newUrl = `${currentUrl}&tab=config`;
|
|
||||||
}
|
|
||||||
history.replaceState({}, '', newUrl);
|
|
||||||
|
|
||||||
this.switchTab('config');
|
this.switchTab('config');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,7 +539,7 @@ class AppTabbedManager {
|
|||||||
const currentApp = this.currentApp || '';
|
const currentApp = this.currentApp || '';
|
||||||
|
|
||||||
// Construct proper URL with correct parameter order
|
// Construct proper URL with correct parameter order
|
||||||
const newUrl = `/app?=${currentApp}&tab=tasks&task=${taskId}`;
|
const newUrl = `/app/${currentApp}?tab=tasks&task=${taskId}`;
|
||||||
// console.log('🔍 Updating URL with task parameter:', newUrl);
|
// console.log('🔍 Updating URL with task parameter:', newUrl);
|
||||||
history.pushState({}, '', newUrl);
|
history.pushState({}, '', newUrl);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,7 @@ class AppsManager {
|
|||||||
// reset password) show without needing a page refresh. Don't
|
// reset password) show without needing a page refresh. Don't
|
||||||
// switch tabs — they may be reading the tool's task log.
|
// switch tabs — they may be reading the tool's task log.
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
const currentAppFromUrl = url.searchParams.get('app') || url.searchParams.get('');
|
const currentAppFromUrl = decodeURIComponent((window.location.pathname.match(/^\/app\/([^/?]+)/) || [])[1] || '') || url.searchParams.get('app') || url.searchParams.get('');
|
||||||
const onAppDetail = window.location.pathname === '/app' || window.location.pathname.startsWith('/app/');
|
const onAppDetail = window.location.pathname === '/app' || window.location.pathname.startsWith('/app/');
|
||||||
if (onAppDetail && appName && currentAppFromUrl === appName) {
|
if (onAppDetail && appName && currentAppFromUrl === appName) {
|
||||||
this.displayConfigForm?.((window.apps || []).find(a =>
|
this.displayConfigForm?.((window.apps || []).find(a =>
|
||||||
@ -115,7 +115,7 @@ class AppsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const currentUrl = new URL(window.location.href);
|
const currentUrl = new URL(window.location.href);
|
||||||
const currentAppFromUrl = currentUrl.searchParams.get('app') || currentUrl.searchParams.get('');
|
const currentAppFromUrl = decodeURIComponent((window.location.pathname.match(/^\/app\/([^/?]+)/) || [])[1] || '') || currentUrl.searchParams.get('app') || currentUrl.searchParams.get('');
|
||||||
const pathname = window.location.pathname;
|
const pathname = window.location.pathname;
|
||||||
const isAppsPage = pathname === '/apps' || pathname.startsWith('/apps/');
|
const isAppsPage = pathname === '/apps' || pathname.startsWith('/apps/');
|
||||||
const isAppDetailPage = pathname === '/app' || pathname.startsWith('/app/');
|
const isAppDetailPage = pathname === '/app' || pathname.startsWith('/app/');
|
||||||
@ -251,7 +251,7 @@ class AppsManager {
|
|||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
if (path === '/app' || path.startsWith('/app/') || searchParams.has('app')) {
|
if (path === '/app' || path.startsWith('/app/') || searchParams.has('app')) {
|
||||||
const appName = searchParams.get('app') || window.appName || '';
|
const appName = decodeURIComponent((path.match(/^\/app\/([^/?]+)/) || [])[1] || '') || searchParams.get('app') || window.appName || '';
|
||||||
this.showAppDetail(appName);
|
this.showAppDetail(appName);
|
||||||
} else {
|
} else {
|
||||||
// Use the category parsed by SPA
|
// Use the category parsed by SPA
|
||||||
@ -266,7 +266,7 @@ class AppsManager {
|
|||||||
|
|
||||||
// Update URL only for specific categories, not for 'all'
|
// Update URL only for specific categories, not for 'all'
|
||||||
if (category && category !== 'all') {
|
if (category && category !== 'all') {
|
||||||
history.pushState({}, '', `/apps?=${category}`);
|
history.pushState({}, '', `/apps/${category}`);
|
||||||
}
|
}
|
||||||
// For 'all' category, keep URL as /apps to avoid redirect loops
|
// For 'all' category, keep URL as /apps to avoid redirect loops
|
||||||
|
|
||||||
@ -311,7 +311,7 @@ class AppsManager {
|
|||||||
// console.log('🔍 Preserving existing tab:', targetTab);
|
// console.log('🔍 Preserving existing tab:', targetTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newUrl = `/app?=${appName}&tab=${targetTab}`;
|
const newUrl = `/app/${appName}?tab=${targetTab}`;
|
||||||
// console.log('🔍 Setting URL to:', newUrl);
|
// console.log('🔍 Setting URL to:', newUrl);
|
||||||
history.pushState({}, '', newUrl);
|
history.pushState({}, '', newUrl);
|
||||||
|
|
||||||
@ -369,7 +369,7 @@ class AppsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set URL to target tab (config or tasks)
|
// Set URL to target tab (config or tasks)
|
||||||
const newUrl = `/app?=${appName}&tab=${targetTab}`;
|
const newUrl = `/app/${appName}?tab=${targetTab}`;
|
||||||
history.pushState({}, '', newUrl);
|
history.pushState({}, '', newUrl);
|
||||||
|
|
||||||
// Update app-tabbed-manager. setCurrentApp clears stale disable state from
|
// Update app-tabbed-manager. setCurrentApp clears stale disable state from
|
||||||
@ -509,7 +509,7 @@ class AppsManager {
|
|||||||
if (categoryId === 'all') {
|
if (categoryId === 'all') {
|
||||||
history.pushState({}, '', '/apps');
|
history.pushState({}, '', '/apps');
|
||||||
} else {
|
} else {
|
||||||
history.pushState({}, '', `/apps?=${categoryId}`);
|
history.pushState({}, '', `/apps/${categoryId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Direct view update without URL change to avoid conflicts
|
// Direct view update without URL change to avoid conflicts
|
||||||
@ -2086,9 +2086,9 @@ class AppsManager {
|
|||||||
|
|
||||||
navigateToServiceApp(slug) {
|
navigateToServiceApp(slug) {
|
||||||
if (typeof window.navigateToApp === 'function') return window.navigateToApp(slug);
|
if (typeof window.navigateToApp === 'function') return window.navigateToApp(slug);
|
||||||
if (window.librePortalSPA?.navigate) return window.librePortalSPA.navigate(`/app?=${slug}`);
|
if (window.librePortalSPA?.navigate) return window.librePortalSPA.navigate(`/app/${slug}`);
|
||||||
if (typeof window.navigateToRoute === 'function') return window.navigateToRoute(`app?=${slug}`);
|
if (typeof window.navigateToRoute === 'function') return window.navigateToRoute(`app/${slug}`);
|
||||||
window.location.href = `/app?=${slug}`;
|
window.location.href = `/app/${slug}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find matching CFG_ key for a field (working method from app-config-original.js)
|
// Find matching CFG_ key for a field (working method from app-config-original.js)
|
||||||
@ -3221,10 +3221,10 @@ class AppsManager {
|
|||||||
}, 500);
|
}, 500);
|
||||||
} else if (window.librePortalSPA) {
|
} else if (window.librePortalSPA) {
|
||||||
// Fallback: navigate to app with tasks tab
|
// Fallback: navigate to app with tasks tab
|
||||||
const taskUrl = task ? `/app?=${appName}&tab=tasks&task=${task.id}` : `/app?=${appName}&tab=tasks`;
|
const taskUrl = task ? `/app/${appName}?tab=tasks&task=${task.id}` : `/app/${appName}?tab=tasks`;
|
||||||
window.librePortalSPA.navigateTo(taskUrl);
|
window.librePortalSPA.navigateTo(taskUrl);
|
||||||
} else if (window.navigateToRoute) {
|
} else if (window.navigateToRoute) {
|
||||||
window.navigateToRoute(`app?=${appName}&tab=tasks${task ? `&task=${task.id}` : ''}`);
|
window.navigateToRoute(`app/${appName}?tab=tasks${task ? `&task=${task.id}` : ''}`);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
@ -3466,11 +3466,11 @@ class AppsManager {
|
|||||||
}, 500);
|
}, 500);
|
||||||
} else if (window.librePortalSPA) {
|
} else if (window.librePortalSPA) {
|
||||||
// Fallback: navigate to app with tasks tab
|
// Fallback: navigate to app with tasks tab
|
||||||
const taskUrl = task ? `/app?=${appName}&tab=tasks&task=${task.id}` : `/app?=${appName}&tab=tasks`;
|
const taskUrl = task ? `/app/${appName}?tab=tasks&task=${task.id}` : `/app/${appName}?tab=tasks`;
|
||||||
// console.log(`🔄 Navigating to app tasks with uninstall task: ${task?.id}`);
|
// console.log(`🔄 Navigating to app tasks with uninstall task: ${task?.id}`);
|
||||||
window.librePortalSPA.navigateTo(taskUrl);
|
window.librePortalSPA.navigateTo(taskUrl);
|
||||||
} else if (window.navigateToRoute) {
|
} else if (window.navigateToRoute) {
|
||||||
window.navigateToRoute(`app?=${appName}&tab=tasks${task ? `&task=${task.id}` : ''}`);
|
window.navigateToRoute(`app/${appName}?tab=tasks${task ? `&task=${task.id}` : ''}`);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
|||||||
@ -117,6 +117,10 @@ class BackupPage {
|
|||||||
either source resolve correctly. */
|
either source resolve correctly. */
|
||||||
parseTabFromUrl() {
|
parseTabFromUrl() {
|
||||||
const allowed = new Set(['dashboard', 'backups', 'locations', 'configuration']);
|
const allowed = new Set(['dashboard', 'backups', 'locations', 'configuration']);
|
||||||
|
// Path-based: /backup/<tab> (bare /backup → default tab).
|
||||||
|
const seg = window.location.pathname.replace(/^\/backup\/?/, '').split('/')[0];
|
||||||
|
if (seg && allowed.has(seg)) return seg;
|
||||||
|
// Legacy ?=<tab> / ?backup=<tab> / ?tab=<tab> for old links.
|
||||||
const search = window.location.search || '';
|
const search = window.location.search || '';
|
||||||
const legacy = search.match(/\?=([^&]+)/);
|
const legacy = search.match(/\?=([^&]+)/);
|
||||||
if (legacy && allowed.has(legacy[1])) return legacy[1];
|
if (legacy && allowed.has(legacy[1])) return legacy[1];
|
||||||
@ -376,7 +380,7 @@ class BackupPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pushTabToUrl(tab) {
|
pushTabToUrl(tab) {
|
||||||
const url = `/backup?=${tab}`;
|
const url = `/backup/${tab}`;
|
||||||
// Use replaceState for the *first* push (initial tab inferred from
|
// Use replaceState for the *first* push (initial tab inferred from
|
||||||
// URL); otherwise pushState so back/forward navigates between tabs.
|
// URL); otherwise pushState so back/forward navigates between tabs.
|
||||||
if (!this._pushedAnyTab) {
|
if (!this._pushedAnyTab) {
|
||||||
|
|||||||
@ -82,9 +82,9 @@ class ConfigForm {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (task && window.librePortalSPA && typeof window.librePortalSPA.navigate === 'function') {
|
if (task && window.librePortalSPA && typeof window.librePortalSPA.navigate === 'function') {
|
||||||
setTimeout(() => window.librePortalSPA.navigate(`/tasks?=all&task=${task.id}`), 400);
|
setTimeout(() => window.librePortalSPA.navigate(`/tasks/all?task=${task.id}`), 400);
|
||||||
} else if (task && window.navigateToRoute) {
|
} else if (task && window.navigateToRoute) {
|
||||||
setTimeout(() => window.navigateToRoute(`tasks?=all&task=${task.id}`), 400);
|
setTimeout(() => window.navigateToRoute(`tasks/all?task=${task.id}`), 400);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('ConfigForm: Error saving configuration:', error);
|
console.error('ConfigForm: Error saving configuration:', error);
|
||||||
|
|||||||
@ -39,7 +39,7 @@ function createInstalledAppCard(app) {
|
|||||||
const shortName = app.name.split(' - ')[0].trim();
|
const shortName = app.name.split(' - ')[0].trim();
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="frontpage-app-tile" onclick="window.location.href='/app?=${appName}'">
|
<div class="frontpage-app-tile" onclick="window.location.href='/app/${appName}'">
|
||||||
<div class="frontpage-app-icon-wrap">
|
<div class="frontpage-app-icon-wrap">
|
||||||
<img src="${icon}" alt="${shortName}" onerror="this.src='/icons/apps/default.svg'">
|
<img src="${icon}" alt="${shortName}" onerror="this.src='/icons/apps/default.svg'">
|
||||||
<div class="frontpage-app-overlay" id="frontpage-overlay-${appName}" onclick="event.stopPropagation()"></div>
|
<div class="frontpage-app-overlay" id="frontpage-overlay-${appName}" onclick="event.stopPropagation()"></div>
|
||||||
@ -103,12 +103,12 @@ function setupEventListeners() {
|
|||||||
function navigateToApp(appName) {
|
function navigateToApp(appName) {
|
||||||
// Use proper SPA navigation to the app page
|
// Use proper SPA navigation to the app page
|
||||||
if (window.librePortalSPA && typeof window.librePortalSPA.navigate === 'function') {
|
if (window.librePortalSPA && typeof window.librePortalSPA.navigate === 'function') {
|
||||||
window.librePortalSPA.navigate(`/app?=${appName}`);
|
window.librePortalSPA.navigate(`/app/${appName}`);
|
||||||
} else if (window.navigateToRoute && typeof window.navigateToRoute === 'function') {
|
} else if (window.navigateToRoute && typeof window.navigateToRoute === 'function') {
|
||||||
window.navigateToRoute(`app?=${appName}`);
|
window.navigateToRoute(`app/${appName}`);
|
||||||
} else {
|
} else {
|
||||||
// Fallback to direct navigation
|
// Fallback to direct navigation
|
||||||
window.location.href = `/app?=${appName}`;
|
window.location.href = `/app/${appName}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -346,7 +346,7 @@ window.handleNotificationNavigation = (url) => {
|
|||||||
// We're on an app page - navigate to the specified app and tab
|
// We're on an app page - navigate to the specified app and tab
|
||||||
if (window.appTabbedManager) {
|
if (window.appTabbedManager) {
|
||||||
// Update the URL to the target app/tab/task
|
// Update the URL to the target app/tab/task
|
||||||
const newUrl = `/app?=${appName}&tab=${tab}&task=${taskId}`;
|
const newUrl = `/app/${appName}?tab=${tab}&task=${taskId}`;
|
||||||
console.log('🔗 Pushing state to URL:', newUrl);
|
console.log('🔗 Pushing state to URL:', newUrl);
|
||||||
window.history.pushState({}, '', newUrl);
|
window.history.pushState({}, '', newUrl);
|
||||||
|
|
||||||
@ -402,7 +402,7 @@ window.handleNotificationNavigation = (url) => {
|
|||||||
// We're on the tasks page, navigate to the specified task
|
// We're on the tasks page, navigate to the specified task
|
||||||
if (taskId) {
|
if (taskId) {
|
||||||
console.log('🔗 On tasks page, opening task:', taskId);
|
console.log('🔗 On tasks page, opening task:', taskId);
|
||||||
window.history.pushState({}, '', `/tasks?=all&task=${taskId}`);
|
window.history.pushState({}, '', `/tasks/all?task=${taskId}`);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (typeof window.toggleTaskDetails === 'function') {
|
if (typeof window.toggleTaskDetails === 'function') {
|
||||||
console.log('🔗 Opening task details for:', taskId);
|
console.log('🔗 Opening task details for:', taskId);
|
||||||
@ -414,7 +414,7 @@ window.handleNotificationNavigation = (url) => {
|
|||||||
} else {
|
} else {
|
||||||
// Not on app or tasks page - navigate to the app's tasks tab
|
// Not on app or tasks page - navigate to the app's tasks tab
|
||||||
if (appName && tab) {
|
if (appName && tab) {
|
||||||
window.history.pushState({}, '', `/app?=${appName}&tab=${tab}&task=${taskId}`);
|
window.history.pushState({}, '', `/app/${appName}?tab=${tab}&task=${taskId}`);
|
||||||
// Let the SPA handle the navigation
|
// Let the SPA handle the navigation
|
||||||
if (window.appTabbedManager) {
|
if (window.appTabbedManager) {
|
||||||
window.appTabbedManager.showAppDetail(appName);
|
window.appTabbedManager.showAppDetail(appName);
|
||||||
|
|||||||
@ -295,10 +295,10 @@ async configUpdate(changes) {
|
|||||||
let taskUrl;
|
let taskUrl;
|
||||||
const currentUrl = window.location.href;
|
const currentUrl = window.location.href;
|
||||||
|
|
||||||
if (currentUrl.includes('/app?=') && appName) {
|
if (currentUrl.includes('/app/') && appName) {
|
||||||
taskUrl = `/app?=${appName}&tab=tasks&task=${task.id}`;
|
taskUrl = `/app/${appName}?tab=tasks&task=${task.id}`;
|
||||||
} else {
|
} else {
|
||||||
taskUrl = `/tasks?=all&task=${task.id}`;
|
taskUrl = `/tasks/all?task=${task.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
@ -364,12 +364,12 @@ async configUpdate(changes) {
|
|||||||
// console.log('🔍 TaskActions: Current URL:', currentUrl);
|
// console.log('🔍 TaskActions: Current URL:', currentUrl);
|
||||||
|
|
||||||
// Always generate URL with app name for proper navigation
|
// Always generate URL with app name for proper navigation
|
||||||
if (currentUrl.includes('/app?=') && appName) {
|
if (currentUrl.includes('/app/') && appName) {
|
||||||
// We're on an app page, maintain app context
|
// We're on an app page, maintain app context
|
||||||
taskUrl = `/app?=${appName}&tab=tasks&task=${task.id}`;
|
taskUrl = `/app/${appName}?tab=tasks&task=${task.id}`;
|
||||||
} else {
|
} else {
|
||||||
// We're on main tasks page, use normal URL
|
// We're on main tasks page, use normal URL
|
||||||
taskUrl = `/tasks?=all&task=${task.id}`;
|
taskUrl = `/tasks/all?task=${task.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show success notification with app icon and direct link
|
// Show success notification with app icon and direct link
|
||||||
|
|||||||
@ -122,11 +122,12 @@ class TasksManager {
|
|||||||
const searchParams = currentUrl.searchParams;
|
const searchParams = currentUrl.searchParams;
|
||||||
|
|
||||||
// Check if we're on the main tasks page (not app page)
|
// Check if we're on the main tasks page (not app page)
|
||||||
const isMainTasksPage = currentUrl.pathname === '/tasks' || currentUrl.pathname === '/tasks.html';
|
const isMainTasksPage = currentUrl.pathname === '/tasks' || currentUrl.pathname.startsWith('/tasks/') || currentUrl.pathname === '/tasks.html';
|
||||||
|
|
||||||
if (isMainTasksPage) {
|
if (isMainTasksPage) {
|
||||||
// On main tasks page, get category from URL
|
// Category from the path (/tasks/<category>), else legacy ?=<category>.
|
||||||
this.currentCategory = searchParams.get('') || 'all';
|
const seg = currentUrl.pathname.replace(/^\/tasks\/?/, '').split('/')[0];
|
||||||
|
this.currentCategory = seg || searchParams.get('') || 'all';
|
||||||
|
|
||||||
// Only check for specific task parameter if we're not coming from an app page
|
// Only check for specific task parameter if we're not coming from an app page
|
||||||
const taskParam = searchParams.get('task');
|
const taskParam = searchParams.get('task');
|
||||||
@ -149,9 +150,9 @@ class TasksManager {
|
|||||||
|
|
||||||
updateURL(category, taskId = null) {
|
updateURL(category, taskId = null) {
|
||||||
// Update URL without page reload and without hash
|
// Update URL without page reload and without hash
|
||||||
let newURL = `/tasks?=${category}`;
|
let newURL = `/tasks/${category || 'all'}`;
|
||||||
if (taskId) {
|
if (taskId) {
|
||||||
newURL += `&task=${taskId}`;
|
newURL += `?task=${taskId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent the SPA from interfering
|
// Prevent the SPA from interfering
|
||||||
@ -619,13 +620,13 @@ class TasksManager {
|
|||||||
<!-- Task metadata -->
|
<!-- Task metadata -->
|
||||||
<div class="task-meta">
|
<div class="task-meta">
|
||||||
<div class="meta-item">
|
<div class="meta-item">
|
||||||
<strong>Task ID:</strong> <a href="/tasks?=all&task=${task.id}" class="task-id-link" data-task-id="${task.id}">${task.id}</a>
|
<strong>Task ID:</strong> <a href="/tasks/all?task=${task.id}" class="task-id-link" data-task-id="${task.id}">${task.id}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta-item">
|
<div class="meta-item">
|
||||||
<strong>Type:</strong> ${task.type || 'unknown'}
|
<strong>Type:</strong> ${task.type || 'unknown'}
|
||||||
</div>
|
</div>
|
||||||
<div class="meta-item">
|
<div class="meta-item">
|
||||||
<strong>App:</strong> ${task.app ? `<a href="/app?=${task.app}" class="task-app-link" data-app-name="${task.app}">${task.app}</a>` : 'system'}
|
<strong>App:</strong> ${task.app ? `<a href="/app/${task.app}" class="task-app-link" data-app-name="${task.app}">${task.app}</a>` : 'system'}
|
||||||
</div>
|
</div>
|
||||||
<div class="meta-item">
|
<div class="meta-item">
|
||||||
<strong>Created:</strong> ${new Date(task.createdAt).toLocaleString()}
|
<strong>Created:</strong> ${new Date(task.createdAt).toLocaleString()}
|
||||||
@ -1334,7 +1335,7 @@ class TasksManager {
|
|||||||
|
|
||||||
// Show success notification with app icon and direct link
|
// Show success notification with app icon and direct link
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
const taskUrl = `/tasks?=all&task=${task.id}`;
|
const taskUrl = `/tasks/all?task=${task.id}`;
|
||||||
const typeIcon = this.getTaskTypeIcon ? this.getTaskTypeIcon({ type: action })?.icon : '';
|
const typeIcon = this.getTaskTypeIcon ? this.getTaskTypeIcon({ type: action })?.icon : '';
|
||||||
const customIcon = typeIcon ? `<span style="font-size:18px;line-height:1;">${typeIcon}</span>` : null;
|
const customIcon = typeIcon ? `<span style="font-size:18px;line-height:1;">${typeIcon}</span>` : null;
|
||||||
window.notificationSystem.show(
|
window.notificationSystem.show(
|
||||||
@ -1455,8 +1456,8 @@ class TasksManager {
|
|||||||
const subjectLabel = isSystemTask ? 'System' : 'App';
|
const subjectLabel = isSystemTask ? 'System' : 'App';
|
||||||
const onAppPage = window.location.pathname.startsWith('/app') && !window.location.pathname.startsWith('/apps');
|
const onAppPage = window.location.pathname.startsWith('/app') && !window.location.pathname.startsWith('/apps');
|
||||||
const url = (onAppPage && appName)
|
const url = (onAppPage && appName)
|
||||||
? `/app?=${appName}&tab=tasks&task=${taskId}`
|
? `/app/${appName}?tab=tasks&task=${taskId}`
|
||||||
: `/tasks?=all&task=${taskId}`;
|
: `/tasks/all?task=${taskId}`;
|
||||||
const icon = appName ? `icons/apps/${appName}.svg` : null;
|
const icon = appName ? `icons/apps/${appName}.svg` : null;
|
||||||
|
|
||||||
// Match the per-action emoji used in the task list rows (see
|
// Match the per-action emoji used in the task list rows (see
|
||||||
|
|||||||
@ -145,7 +145,7 @@ class TopbarComponent {
|
|||||||
window.tasksManager.tasks = []; // Clear the task list
|
window.tasksManager.tasks = []; // Clear the task list
|
||||||
|
|
||||||
// Clear URL parameters
|
// Clear URL parameters
|
||||||
window.history.pushState({ category: 'all', taskId: null }, '', '/tasks?=all');
|
window.history.pushState({ category: 'all', taskId: null }, '', '/tasks/all');
|
||||||
|
|
||||||
// Clear any localStorage filters
|
// Clear any localStorage filters
|
||||||
localStorage.removeItem('tasksDefaultFilter');
|
localStorage.removeItem('tasksDefaultFilter');
|
||||||
|
|||||||
@ -66,8 +66,9 @@ class LibrePortalSPAClean {
|
|||||||
this.routes.set('/', () => this.handleDashboard());
|
this.routes.set('/', () => this.handleDashboard());
|
||||||
this.routes.set('/dashboard', () => this.handleDashboard());
|
this.routes.set('/dashboard', () => this.handleDashboard());
|
||||||
this.routes.set('/apps', () => this.handleApps());
|
this.routes.set('/apps', () => this.handleApps());
|
||||||
this.routes.set('/app', () => this.handleAppDetail()); // Handle /app without query
|
this.routes.set('/apps*', () => this.handleApps()); // /apps/<category> — MUST precede /app* (/apps startsWith /app)
|
||||||
this.routes.set('/app*', () => this.handleAppDetail()); // Handle /app with query
|
this.routes.set('/app', () => this.handleAppDetail()); // /app/<name>
|
||||||
|
this.routes.set('/app*', () => this.handleAppDetail());
|
||||||
this.routes.set('/admin', () => this.handleAdmin()); // Admin area (path-based: /admin/config/<x>, /admin/tools/<x>)
|
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('/admin*', () => this.handleAdmin());
|
||||||
this.routes.set('/config', () => this.handleConfigRedirect()); // legacy → /admin
|
this.routes.set('/config', () => this.handleConfigRedirect()); // legacy → /admin
|
||||||
@ -279,17 +280,17 @@ class LibrePortalSPAClean {
|
|||||||
async handleApps() {
|
async handleApps() {
|
||||||
//console.log('📱 Loading apps...');
|
//console.log('📱 Loading apps...');
|
||||||
|
|
||||||
// Handle query parameters for apps
|
// Category from the path (/apps/<category>), else legacy ?=<cat> / ?apps=.
|
||||||
const path = window.location.pathname + window.location.search;
|
const seg = window.location.pathname.replace(/^\/apps\/?/, '').split('/')[0];
|
||||||
if (path.includes('?=')) {
|
if (seg) {
|
||||||
const [basePath, query] = path.split('?=');
|
window.appsCategory = decodeURIComponent(seg);
|
||||||
window.appsCategory = query || 'all';
|
|
||||||
} else if (path.includes('?')) {
|
|
||||||
const url = new URL(path, window.location.origin);
|
|
||||||
const searchParams = url.searchParams;
|
|
||||||
window.appsCategory = searchParams.get('apps') || 'all';
|
|
||||||
} else {
|
} else {
|
||||||
window.appsCategory = 'all';
|
const search = window.location.search || '';
|
||||||
|
if (search.includes('?=')) {
|
||||||
|
window.appsCategory = (window.location.pathname + search).split('?=')[1] || 'all';
|
||||||
|
} else {
|
||||||
|
window.appsCategory = new URLSearchParams(search).get('apps') || 'all';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -321,10 +322,13 @@ class LibrePortalSPAClean {
|
|||||||
async handleAppDetail() {
|
async handleAppDetail() {
|
||||||
//console.log('🔍 Loading app detail...');
|
//console.log('🔍 Loading app detail...');
|
||||||
|
|
||||||
// Extract app name from URL
|
// Extract app name. Path-based /app/<name> first, then legacy ?app= / ?=name.
|
||||||
const url = new URL(window.location);
|
const url = new URL(window.location);
|
||||||
let appName = url.searchParams.get('app');
|
let appName = url.pathname.replace(/^\/app\/?/, '').split('/')[0];
|
||||||
|
appName = appName ? decodeURIComponent(appName) : '';
|
||||||
|
|
||||||
|
if (!appName) appName = url.searchParams.get('app');
|
||||||
|
|
||||||
// Handle old format ?=appname&tab=tabname
|
// Handle old format ?=appname&tab=tabname
|
||||||
if (!appName && url.search.includes('?=')) {
|
if (!appName && url.search.includes('?=')) {
|
||||||
const queryPart = url.search.replace('?', '');
|
const queryPart = url.search.replace('?', '');
|
||||||
|
|||||||
@ -142,13 +142,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function navigateToRecommendedApps() {
|
function navigateToRecommendedApps() {
|
||||||
const route = '/apps?=recommended';
|
const route = '/apps/recommended';
|
||||||
if (window.spaClean && typeof window.spaClean.navigate === 'function') {
|
if (window.spaClean && typeof window.spaClean.navigate === 'function') {
|
||||||
window.spaClean.navigate(route);
|
window.spaClean.navigate(route);
|
||||||
} else if (typeof window.navigateToRoute === 'function') {
|
} else if (typeof window.navigateToRoute === 'function') {
|
||||||
window.navigateToRoute(route);
|
window.navigateToRoute(route);
|
||||||
} else {
|
} else {
|
||||||
window.location.href = 'apps.html?=recommended';
|
window.location.href = '/apps/recommended';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -147,7 +147,7 @@ async function initializeData() {
|
|||||||
if (currentPath === '/dashboard' || currentPath === '/') {
|
if (currentPath === '/dashboard' || currentPath === '/') {
|
||||||
// Dashboard: Only installed apps + system info (no categories needed)
|
// Dashboard: Only installed apps + system info (no categories needed)
|
||||||
await loadDashboardData();
|
await loadDashboardData();
|
||||||
} else if (currentPath === '/apps' || currentPath === '/app' || searchParams.has('apps') || searchParams.has('app')) {
|
} else if (currentPath.startsWith('/apps') || currentPath.startsWith('/app') || searchParams.has('apps') || searchParams.has('app')) {
|
||||||
// Apps page: All apps + categories (no system configs needed)
|
// Apps page: All apps + categories (no system configs needed)
|
||||||
await loadAppsPageData();
|
await loadAppsPageData();
|
||||||
} else if (currentPath.startsWith('/admin') || currentPath.startsWith('/config') || currentPath.startsWith('/ssh') || searchParams.has('config')) {
|
} else if (currentPath.startsWith('/admin') || currentPath.startsWith('/config') || currentPath.startsWith('/ssh') || searchParams.has('config')) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user