Merge claude/1
This commit is contained in:
commit
86abfadb52
@ -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(window.taskPath('all', 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(window.taskPath('all', task.id)), 400);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('ConfigForm: Error saving configuration:', error);
|
console.error('ConfigForm: Error saving configuration:', error);
|
||||||
|
|||||||
@ -213,7 +213,7 @@ Object.assign(TasksManager.prototype, {
|
|||||||
<!-- 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="${window.taskPath ? window.taskPath('all', task.id) : '/tasks/all'}" 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'}
|
||||||
|
|||||||
@ -142,18 +142,13 @@ class TasksManager {
|
|||||||
const isMainTasksPage = currentUrl.pathname === '/tasks' || currentUrl.pathname.startsWith('/tasks/') || currentUrl.pathname === '/tasks.html';
|
const isMainTasksPage = currentUrl.pathname === '/tasks' || currentUrl.pathname.startsWith('/tasks/') || currentUrl.pathname === '/tasks.html';
|
||||||
|
|
||||||
if (isMainTasksPage) {
|
if (isMainTasksPage) {
|
||||||
// Category from the path (/tasks/<category>), else legacy ?=<category>.
|
// Category + single-task deep link from the path (/tasks/<category>/<id>),
|
||||||
const seg = currentUrl.pathname.replace(/^\/tasks\/?/, '').split('/')[0];
|
// with legacy ?=<category> and ?task=<id> queries still honoured.
|
||||||
this.currentCategory = seg || searchParams.get('') || 'all';
|
const parts = (typeof window.taskPartsFromPath === 'function')
|
||||||
|
? window.taskPartsFromPath(currentUrl.pathname, currentUrl.search)
|
||||||
// Only check for specific task parameter if we're not coming from an app page
|
: { category: '', taskId: searchParams.get('task') || '' };
|
||||||
const taskParam = searchParams.get('task');
|
this.currentCategory = parts.category || searchParams.get('') || 'all';
|
||||||
if (taskParam) {
|
this.highlightedTaskId = parts.taskId || null;
|
||||||
this.highlightedTaskId = taskParam;
|
|
||||||
} else {
|
|
||||||
// Clear any existing highlighted task when on main tasks page without task param
|
|
||||||
this.highlightedTaskId = null;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Not on main tasks page, get default filter from localStorage
|
// Not on main tasks page, get default filter from localStorage
|
||||||
this.currentCategory = localStorage.getItem('tasksDefaultFilter') || 'all';
|
this.currentCategory = localStorage.getItem('tasksDefaultFilter') || 'all';
|
||||||
@ -163,11 +158,11 @@ 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. The task deep link is a
|
||||||
let newURL = `/tasks/${category || 'all'}`;
|
// path segment (/tasks/<category>/<id>) — see window.taskPath.
|
||||||
if (taskId) {
|
const newURL = (typeof window.taskPath === 'function')
|
||||||
newURL += `?task=${taskId}`;
|
? window.taskPath(category, taskId)
|
||||||
}
|
: `/tasks/${category || 'all'}${taskId ? '/' + String(taskId).replace(/^task_/, '') : ''}`;
|
||||||
|
|
||||||
// Prevent the SPA from interfering
|
// Prevent the SPA from interfering
|
||||||
if (window.librePortalSPA) {
|
if (window.librePortalSPA) {
|
||||||
@ -184,7 +179,7 @@ class TasksManager {
|
|||||||
//// // console.log('🔧 Initializing TasksManager...');
|
//// // console.log('🔧 Initializing TasksManager...');
|
||||||
|
|
||||||
// Re-read the URL on every (re)mount. The SPA reuses this singleton, so a
|
// Re-read the URL on every (re)mount. The SPA reuses this singleton, so a
|
||||||
// navigation to /tasks/<cat>?task=X must refresh the category + deep-link
|
// navigation to /tasks/<cat>/<id> must refresh the category + deep-link
|
||||||
// state — a constructor-only read goes stale after the first visit.
|
// state — a constructor-only read goes stale after the first visit.
|
||||||
this.initializeFromURL();
|
this.initializeFromURL();
|
||||||
|
|
||||||
@ -415,7 +410,7 @@ class TasksManager {
|
|||||||
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)
|
||||||
? window.appPath(appName, 'tasks', null, taskId)
|
? window.appPath(appName, 'tasks', null, taskId)
|
||||||
: `/tasks/all?task=${taskId}`;
|
: window.taskPath('all', taskId);
|
||||||
// Per-action emoji (install ✅, backup 💾, restore 📦, …) in the
|
// Per-action emoji (install ✅, backup 💾, restore 📦, …) in the
|
||||||
// notification's leftmost icon slot, mirroring task-list rows.
|
// notification's leftmost icon slot, mirroring task-list rows.
|
||||||
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;
|
||||||
|
|||||||
@ -657,6 +657,27 @@ window.appPartsFromPath = function (pathname) {
|
|||||||
return { app, tab, sub };
|
return { app, tab, sub };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Global tasks page path helpers — bring the single-task deep link in line with
|
||||||
|
// /app/<name> and /admin/<…>: it's a path segment (/tasks/<category>/<id>), not
|
||||||
|
// a ?task= query. Task ids are guaranteed `task_<digits>_<base36>`, so the
|
||||||
|
// `task_` prefix is dropped in the URL for brevity and restored on read. A
|
||||||
|
// legacy ?task=<id> query is still parsed so older links and bookmarks resolve.
|
||||||
|
window.taskPath = function (category, taskId) {
|
||||||
|
let p = '/tasks/' + encodeURIComponent(category || 'all');
|
||||||
|
if (taskId) p += '/' + encodeURIComponent(String(taskId).replace(/^task_/, ''));
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
window.taskPartsFromPath = function (pathname, search) {
|
||||||
|
const segs = String(pathname || '').replace(/^\/tasks\/?/, '').split('/').filter(Boolean);
|
||||||
|
const category = segs[0] || '';
|
||||||
|
let taskId = segs[1] ? decodeURIComponent(segs[1]) : '';
|
||||||
|
if (!taskId && search) {
|
||||||
|
try { taskId = new URLSearchParams(search).get('task') || ''; } catch (_) {}
|
||||||
|
}
|
||||||
|
if (taskId && !/^task_/.test(taskId)) taskId = 'task_' + taskId;
|
||||||
|
return { category, taskId };
|
||||||
|
};
|
||||||
|
|
||||||
// Global navigation function for click handlers
|
// Global navigation function for click handlers
|
||||||
window.navigateToRoute = function(href) {
|
window.navigateToRoute = function(href) {
|
||||||
if (window.spaClean) {
|
if (window.spaClean) {
|
||||||
|
|||||||
@ -114,7 +114,7 @@ class NotificationSystem {
|
|||||||
// Action button (context-aware)
|
// Action button (context-aware)
|
||||||
if (appName && appUrl) {
|
if (appName && appUrl) {
|
||||||
let buttonText = 'Manage';
|
let buttonText = 'Manage';
|
||||||
if (appUrl.includes('task=')) {
|
if (appUrl.includes('task=') || /\/tasks\/[^/]+\/[^/?]+/.test(appUrl)) {
|
||||||
buttonText = 'View Task';
|
buttonText = 'View Task';
|
||||||
} else if (type === 'success' && message.includes('install')) {
|
} else if (type === 'success' && message.includes('install')) {
|
||||||
buttonText = 'Configure';
|
buttonText = 'Configure';
|
||||||
@ -399,14 +399,17 @@ window.handleNotificationNavigation = (url) => {
|
|||||||
console.warn('⚠️ appTabbedManager not available');
|
console.warn('⚠️ appTabbedManager not available');
|
||||||
}
|
}
|
||||||
} else if (currentPath.includes('/tasks')) {
|
} else if (currentPath.includes('/tasks')) {
|
||||||
// We're on the tasks page, navigate to the specified task
|
// We're on the tasks page, navigate to the specified task. The target may
|
||||||
if (taskId) {
|
// carry the id as a /tasks/<cat>/<id> path segment or a legacy ?task=.
|
||||||
console.log('🔗 On tasks page, opening task:', taskId);
|
const tasksTaskId = taskId
|
||||||
window.history.pushState({}, '', `/tasks/all?task=${taskId}`);
|
|| (window.taskPartsFromPath ? window.taskPartsFromPath(urlObj.pathname, urlObj.search).taskId : '');
|
||||||
|
if (tasksTaskId) {
|
||||||
|
console.log('🔗 On tasks page, opening task:', tasksTaskId);
|
||||||
|
window.history.pushState({}, '', window.taskPath ? window.taskPath('all', tasksTaskId) : url);
|
||||||
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:', tasksTaskId);
|
||||||
window.toggleTaskDetails(taskId);
|
window.toggleTaskDetails(tasksTaskId);
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -784,10 +784,11 @@ class SetupWizard {
|
|||||||
|
|
||||||
this.container.classList.add('setup-launched');
|
this.container.classList.add('setup-launched');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Path-based route (the app uses /… URLs); the specific task is still
|
// Path-based route (the app uses /… URLs); the specific task is a path
|
||||||
// selected via ?task=. Navigate via the SPA helper, with an absolute-path
|
// segment (/tasks/all/<id>) and ?from=setup flags the install handoff so
|
||||||
// full-load fallback.
|
// the page follows the running queue. Navigate via the SPA helper, with
|
||||||
const target = `/tasks/all?task=${encodeURIComponent(firstTaskId)}&from=setup`;
|
// an absolute-path full-load fallback.
|
||||||
|
const target = `${window.taskPath('all', firstTaskId)}?from=setup`;
|
||||||
if (typeof window.navigateToRoute === 'function' && window.spaClean) {
|
if (typeof window.navigateToRoute === 'function' && window.spaClean) {
|
||||||
window.navigateToRoute(target);
|
window.navigateToRoute(target);
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|||||||
@ -406,7 +406,7 @@ async configUpdate(changes) {
|
|||||||
if (currentUrl.includes('/app/') && appName) {
|
if (currentUrl.includes('/app/') && appName) {
|
||||||
taskUrl = window.appPath(appName, 'tasks', null, task.id);
|
taskUrl = window.appPath(appName, 'tasks', null, task.id);
|
||||||
} else {
|
} else {
|
||||||
taskUrl = `/tasks/all?task=${task.id}`;
|
taskUrl = window.taskPath('all', task.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user