diff --git a/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js b/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js
index a630c98..8cff938 100755
--- a/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js
+++ b/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js
@@ -774,7 +774,7 @@ class AppTabbedManager {
if (this.runningTasks.has(key)) {
const existing = this.runningTasks.get(key);
- // Same task firing twice — `createAndExecuteTask` dispatches taskCreated
+ // Same task firing twice — executeTask dispatches taskCreated
// synchronously, and the SSE bus also dispatches it when the new task
// file shows up. Both events carry the same taskId; treat as a no-op.
if (existing && existing.taskId === taskId) return;
diff --git a/containers/libreportal/frontend/js/components/task/task-actions.js b/containers/libreportal/frontend/js/components/task/task-actions.js
index 31ebe53..c7f03d0 100755
--- a/containers/libreportal/frontend/js/components/task/task-actions.js
+++ b/containers/libreportal/frontend/js/components/task/task-actions.js
@@ -302,7 +302,15 @@ async configUpdate(changes) {
}
if (window.notificationSystem) {
- const displayName = window.getAppDisplayName ? window.getAppDisplayName(appName) : appName;
+ // `appName === 'system'` is a category sentinel (config_update,
+ // system_update — no real app). Resolve it to the LibrePortal
+ // logo + name so the toast doesn't read "App: System" with a
+ // broken /icons/apps/system.svg.
+ const isSystem = appName === 'system';
+ const displayName = isSystem
+ ? 'LibrePortal'
+ : (window.getAppDisplayName ? window.getAppDisplayName(appName) : appName);
+ const notifIcon = isSystem ? '/icons/libreportal.svg' : appIcon;
const typeIcon = (window.tasksManager && window.tasksManager.getTaskTypeIcon
? window.tasksManager.getTaskTypeIcon({ type: action })
: null)?.icon || '';
@@ -311,11 +319,11 @@ async configUpdate(changes) {
? `${displayLabel} task started!`
: `${action.charAt(0).toUpperCase() + action.slice(1)} task started!`;
window.notificationSystem.show(
- `App: ${displayName}
${headline}`,
+ `${displayName}
${headline}`,
'success',
appName,
taskUrl,
- appIcon,
+ notifIcon,
customIcon
);
}
@@ -330,64 +338,7 @@ async configUpdate(changes) {
}
}
- /**
- * Execute task monitoring (separated to avoid duplicate task creation)
- */
- async executeTaskMonitoring(task, appName, action) {
- // Emit task creation event for AppTabbedManager
- window.dispatchEvent(new CustomEvent('taskCreated', {
- detail: {
- taskId: task.id,
- appName: appName,
- action: action,
- timestamp: Date.now()
- }
- }));
-
- // console.log(`✅ Task created: ${task.id} for ${action} on ${appName}`);
-
- // Set up monitoring for this specific task
- this.tasksManager.monitorTask(task.id, appName, action);
-
- // Try to get app data for better notifications
- let appData = null;
- try {
- appData = this.commands.getAppData ? this.commands.getAppData(appName) : null;
- } catch (error) {
- console.warn('Could not get app data:', error);
- }
- const appIcon = appData?.icon || `/icons/apps/${task.app}.svg`;
-
- // Smart URL generation - always include app name
- let taskUrl;
- const currentUrl = window.location.href;
- // console.log('🔍 TaskActions: Current URL:', currentUrl);
-
- // Always generate URL with app name for proper navigation
- if (currentUrl.includes('/app/') && appName) {
- // We're on an app page, maintain app context
- taskUrl = `/app/${appName}?tab=tasks&task=${task.id}`;
- } else {
- // We're on main tasks page, use normal URL
- taskUrl = `/tasks/all?task=${task.id}`;
- }
-
- // Show success notification with app icon and direct link
- if (window.notificationSystem) {
- const displayName = window.getAppDisplayName ? window.getAppDisplayName(appName) : appName;
- window.notificationSystem.show(
- `App: ${displayName}
- ${action.charAt(0).toUpperCase() + action.slice(1)} task started!`,
- 'success',
- appName,
- taskUrl,
- appIcon
- );
- }
-
- return task;
- }
-
+
/**
* Update task in the manager
*/
diff --git a/containers/libreportal/frontend/js/components/tasks/tasks-manager.js b/containers/libreportal/frontend/js/components/tasks/tasks-manager.js
index ee66658..12e7503 100755
--- a/containers/libreportal/frontend/js/components/tasks/tasks-manager.js
+++ b/containers/libreportal/frontend/js/components/tasks/tasks-manager.js
@@ -1320,48 +1320,6 @@ class TasksManager {
}
};
- window.createAndExecuteTask = async (action, appName, config = '') => {
- try {
- // Create task using NEW signature
- const task = await this.taskManager.createTask(`libreportal app ${action} ${appName}`, action, appName, config);
-
- // Emit task creation event for AppTabbedManager
- window.dispatchEvent(new CustomEvent('taskCreated', {
- detail: {
- taskId: task.id,
- appName: appName,
- action: action,
- timestamp: Date.now()
- }
- }));
-
- // Set up monitoring for this specific task
- this.monitorTask(task.id, appName, action);
-
- // Show success notification with app icon and direct link
- if (window.notificationSystem) {
- const taskUrl = `/tasks/all?task=${task.id}`;
- const typeIcon = this.getTaskTypeIcon ? this.getTaskTypeIcon({ type: action })?.icon : '';
- const customIcon = typeIcon ? `${typeIcon}` : null;
- window.notificationSystem.show(
- `Task created: ${action} ${appName}`,
- 'info',
- appName,
- taskUrl,
- `/icons/apps/${appName}.svg`,
- customIcon
- );
- }
-
- return task;
- } catch (error) {
- console.error(`❌ Failed to create ${action} task for ${appName}:`, error);
- if (window.notificationSystem) {
- window.notificationSystem.error(`Failed to start ${action} task for ${appName}: ${error.message}`);
- }
- throw error;
- }
- };
}
// Single page-wide subscription to the TaskEventBus. This complements
@@ -1452,18 +1410,22 @@ class TasksManager {
}
actionTitle = toolLabel;
}
- const isSystemTask = action.startsWith('setup-');
+ // System-level tasks resolve to LibrePortal as the subject + use the
+ // LibrePortal logo as the app-icon. Covers `app: 'system'` (config
+ // update, system update) and `setup-*` types (setup wizard phases).
+ const isSystemTask = action.startsWith('setup-') || appName === 'system';
const displayName = isSystemTask
? 'LibrePortal'
: ((appName && window.getAppDisplayName)
? window.getAppDisplayName(appName)
: (appName || (task.command || `Task ${taskId}`)));
- const subjectLabel = isSystemTask ? 'System' : 'App';
const onAppPage = window.location.pathname.startsWith('/app') && !window.location.pathname.startsWith('/apps');
const url = (onAppPage && appName)
? `/app/${appName}?tab=tasks&task=${taskId}`
: `/tasks/all?task=${taskId}`;
- const icon = appName ? `/icons/apps/${appName}.svg` : null;
+ const icon = isSystemTask
+ ? '/icons/libreportal.svg'
+ : (appName ? `/icons/apps/${appName}.svg` : null);
// Match the per-action emoji used in the task list rows (see
// `getTaskTypeIcon`). Passed as the 6th `customIcon` arg so the
@@ -1475,13 +1437,13 @@ class TasksManager {
let body;
let level;
if (task.status === 'completed') {
- body = `${subjectLabel}: ${displayName}
${actionTitle} task completed!`;
+ body = `${displayName}
${actionTitle} task completed!`;
level = 'success';
} else if (task.status === 'failed') {
- body = `${subjectLabel}: ${displayName}
${actionTitle} task failed.`;
+ body = `${displayName}
${actionTitle} task failed.`;
level = 'error';
} else if (task.status === 'cancelled') {
- body = `${subjectLabel}: ${displayName}
${actionTitle} task cancelled.`;
+ body = `${displayName}
${actionTitle} task cancelled.`;
level = 'warning';
}
if (body) window.notificationSystem.show(body, level, appName, url, icon, customIcon);