feat(tasks): Enhance task management with identity handling and initial data loading
This commit is contained in:
		| @@ -44,6 +44,105 @@ export class CloudlyDashboard extends DeesElement { | ||||
|     clusters: [], | ||||
|   }; | ||||
|  | ||||
|   // Keep view tabs stable across renders to preserve active selection | ||||
|   private readonly viewTabs: plugins.deesCatalog.IView[] = [ | ||||
|     { | ||||
|       name: 'Overview', | ||||
|       iconName: 'lucide:LayoutDashboard', | ||||
|       element: CloudlyViewOverview, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Settings', | ||||
|       iconName: 'lucide:Settings', | ||||
|       element: CloudlyViewSettings, | ||||
|     }, | ||||
|     { | ||||
|       name: 'SecretGroups', | ||||
|       iconName: 'lucide:ShieldCheck', | ||||
|       element: CloudlyViewSecretGroups, | ||||
|     }, | ||||
|     { | ||||
|       name: 'SecretBundles', | ||||
|       iconName: 'lucide:LockKeyhole', | ||||
|       element: CloudlyViewSecretBundles, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Clusters', | ||||
|       iconName: 'lucide:Network', | ||||
|       element: CloudlyViewClusters, | ||||
|     }, | ||||
|     { | ||||
|       name: 'ExternalRegistries', | ||||
|       iconName: 'lucide:Package', | ||||
|       element: CloudlyViewExternalRegistries, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Images', | ||||
|       iconName: 'lucide:Image', | ||||
|       element: CloudlyViewImages, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Services', | ||||
|       iconName: 'lucide:Layers', | ||||
|       element: CloudlyViewServices, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Testing & Building', | ||||
|       iconName: 'lucide:HardHat', | ||||
|       element: CloudlyViewServices, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Deployments', | ||||
|       iconName: 'lucide:Rocket', | ||||
|       element: CloudlyViewDeployments, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Tasks', | ||||
|       iconName: 'lucide:ListChecks', | ||||
|       element: CloudlyViewTasks, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Domains', | ||||
|       iconName: 'lucide:Globe2', | ||||
|       element: CloudlyViewDomains, | ||||
|     }, | ||||
|     { | ||||
|       name: 'DNS', | ||||
|       iconName: 'lucide:Globe', | ||||
|       element: CloudlyViewDns, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Mails', | ||||
|       iconName: 'lucide:Mail', | ||||
|       element: CloudlyViewMails, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Logs', | ||||
|       iconName: 'lucide:FileText', | ||||
|       element: CloudlyViewLogs, | ||||
|     }, | ||||
|     { | ||||
|       name: 's3', | ||||
|       iconName: 'lucide:Cloud', | ||||
|       element: CloudlyViewS3, | ||||
|     }, | ||||
|     { | ||||
|       name: 'DBs', | ||||
|       iconName: 'lucide:Database', | ||||
|       element: CloudlyViewDbs, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Backups', | ||||
|       iconName: 'lucide:Save', | ||||
|       element: CloudlyViewBackups, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Fleet', | ||||
|       iconName: 'lucide:Truck', | ||||
|       element: CloudlyViewBackups, | ||||
|     }, | ||||
|   ]; | ||||
|  | ||||
|   constructor() { | ||||
|     super(); | ||||
|     document.title = `cloudly v${commitinfo.version}`; | ||||
| @@ -76,103 +175,7 @@ export class CloudlyDashboard extends DeesElement { | ||||
|       <div class="maincontainer"> | ||||
|         <dees-simple-login name="cloudly v${commitinfo.version}"> | ||||
|           <dees-simple-appdash name="cloudly v${commitinfo.version}" | ||||
|             .viewTabs=${[ | ||||
|               { | ||||
|                 name: 'Overview', | ||||
|                 iconName: 'lucide:LayoutDashboard', | ||||
|                 element: CloudlyViewOverview, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Settings', | ||||
|                 iconName: 'lucide:Settings', | ||||
|                 element: CloudlyViewSettings, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'SecretGroups', | ||||
|                 iconName: 'lucide:ShieldCheck', | ||||
|                 element: CloudlyViewSecretGroups, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'SecretBundles', | ||||
|                 iconName: 'lucide:LockKeyhole', | ||||
|                 element: CloudlyViewSecretBundles, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Clusters', | ||||
|                 iconName: 'lucide:Network', | ||||
|                 element: CloudlyViewClusters, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'ExternalRegistries', | ||||
|                 iconName: 'lucide:Package', | ||||
|                 element: CloudlyViewExternalRegistries, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Images', | ||||
|                 iconName: 'lucide:Image', | ||||
|                 element: CloudlyViewImages, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Services', | ||||
|                 iconName: 'lucide:Layers', | ||||
|                 element: CloudlyViewServices, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Testing & Building', | ||||
|                 iconName: 'lucide:HardHat', | ||||
|                 element: CloudlyViewServices, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Deployments', | ||||
|                 iconName: 'lucide:Rocket', | ||||
|                 element: CloudlyViewDeployments, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Tasks', | ||||
|                 iconName: 'lucide:ListChecks', | ||||
|                 element: CloudlyViewTasks, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Domains', | ||||
|                 iconName: 'lucide:Globe2', | ||||
|                 element: CloudlyViewDomains, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'DNS', | ||||
|                 iconName: 'lucide:Globe', | ||||
|                 element: CloudlyViewDns, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Mails', | ||||
|                 iconName: 'lucide:Mail', | ||||
|                 element: CloudlyViewMails, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Logs', | ||||
|                 iconName: 'lucide:FileText', | ||||
|                 element: CloudlyViewLogs, | ||||
|               }, | ||||
|               { | ||||
|                 name: 's3', | ||||
|                 iconName: 'lucide:Cloud', | ||||
|                 element: CloudlyViewS3, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'DBs', | ||||
|                 iconName: 'lucide:Database', | ||||
|                 element: CloudlyViewDbs, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Backups', | ||||
|                 iconName: 'lucide:Save', | ||||
|                 element: CloudlyViewBackups, | ||||
|               }, | ||||
|               { | ||||
|                 name: 'Fleet', | ||||
|                 iconName: 'lucide:Truck', | ||||
|                 element: CloudlyViewBackups, | ||||
|               } | ||||
|             ] as plugins.deesCatalog.IView[]} | ||||
|             .viewTabs=${this.viewTabs} | ||||
|           ></dees-simple-appdash> | ||||
|         </dees-simple-login> | ||||
|       </div> | ||||
|   | ||||
| @@ -18,12 +18,6 @@ export class CloudlyViewTasks extends DeesElement { | ||||
|   @state() | ||||
|   private data: appstate.IDataState = {}; | ||||
|  | ||||
|   @state() | ||||
|   private tasks: any[] = []; | ||||
|  | ||||
|   @state() | ||||
|   private executions: plugins.interfaces.data.ITaskExecution[] = []; | ||||
|  | ||||
|   @state() | ||||
|   private selectedExecution: plugins.interfaces.data.ITaskExecution | null = null; | ||||
|  | ||||
| @@ -41,6 +35,18 @@ export class CloudlyViewTasks extends DeesElement { | ||||
|         this.data = dataArg; | ||||
|       }); | ||||
|     this.rxSubscriptions.push(subscription); | ||||
|      | ||||
|     // Load initial data (non-blocking) | ||||
|     this.loadInitialData(); | ||||
|   } | ||||
|  | ||||
|   private async loadInitialData() { | ||||
|     try { | ||||
|       await appstate.dataState.dispatchAction(appstate.taskActions.getTasks, {}); | ||||
|       await appstate.dataState.dispatchAction(appstate.taskActions.getTaskExecutions, {}); | ||||
|     } catch (error) { | ||||
|       console.error('Failed to load initial task data:', error); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public static styles = [ | ||||
| @@ -276,41 +282,6 @@ export class CloudlyViewTasks extends DeesElement { | ||||
|     `, | ||||
|   ]; | ||||
|  | ||||
|   async connectedCallback() { | ||||
|     super.connectedCallback(); | ||||
|     await this.loadTasks(); | ||||
|     await this.loadExecutions(); | ||||
|   } | ||||
|  | ||||
|   private async loadTasks() { | ||||
|     this.loading = true; | ||||
|     try { | ||||
|       const response: any = await appstate.dataState.dispatchAction( | ||||
|         appstate.taskActions.getTasks, {} | ||||
|       ); | ||||
|       this.tasks = response.tasks || []; | ||||
|     } catch (error) { | ||||
|       console.error('Failed to load tasks:', error); | ||||
|     } finally { | ||||
|       this.loading = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async loadExecutions() { | ||||
|     try { | ||||
|       const filter: any = {}; | ||||
|       if (this.filterStatus !== 'all') { | ||||
|         filter.status = this.filterStatus; | ||||
|       } | ||||
|        | ||||
|       const response: any = await appstate.dataState.dispatchAction( | ||||
|         appstate.taskActions.getTaskExecutions, { filter } | ||||
|       ); | ||||
|       this.executions = response.executions || []; | ||||
|     } catch (error) { | ||||
|       console.error('Failed to load executions:', error); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async triggerTask(taskName: string) { | ||||
|     try { | ||||
| @@ -319,13 +290,28 @@ export class CloudlyViewTasks extends DeesElement { | ||||
|       ); | ||||
|        | ||||
|       // Reload tasks and executions to show the new execution | ||||
|       await this.loadTasks(); | ||||
|       await this.loadExecutions(); | ||||
|       await appstate.dataState.dispatchAction(appstate.taskActions.getTasks, {}); | ||||
|       await this.loadExecutionsWithFilter(); | ||||
|     } catch (error) { | ||||
|       console.error('Failed to trigger task:', error); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async loadExecutionsWithFilter() { | ||||
|     try { | ||||
|       const filter: any = {}; | ||||
|       if (this.filterStatus !== 'all') { | ||||
|         filter.status = this.filterStatus; | ||||
|       } | ||||
|        | ||||
|       await appstate.dataState.dispatchAction( | ||||
|         appstate.taskActions.getTaskExecutions, { filter } | ||||
|       ); | ||||
|     } catch (error) { | ||||
|       console.error('Failed to load executions:', error); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private formatDate(timestamp: number): string { | ||||
|     return new Date(timestamp).toLocaleString(); | ||||
|   } | ||||
| @@ -359,7 +345,8 @@ export class CloudlyViewTasks extends DeesElement { | ||||
|   } | ||||
|  | ||||
|   private renderTaskCard(task: any) { | ||||
|     const lastExecution = this.executions | ||||
|     const executions = this.data.taskExecutions || []; | ||||
|     const lastExecution = executions | ||||
|       .filter(e => e.data.taskName === task.name) | ||||
|       .sort((a, b) => (b.data.startedAt || 0) - (a.data.startedAt || 0))[0]; | ||||
|      | ||||
| @@ -495,32 +482,32 @@ export class CloudlyViewTasks extends DeesElement { | ||||
|       <div class="filter-bar"> | ||||
|         <button | ||||
|           class="filter-button ${this.filterStatus === 'all' ? 'active' : ''}" | ||||
|           @click=${() => { this.filterStatus = 'all'; this.loadExecutions(); }} | ||||
|           @click=${() => { this.filterStatus = 'all'; this.loadExecutionsWithFilter(); }} | ||||
|         > | ||||
|           All | ||||
|         </button> | ||||
|         <button | ||||
|           class="filter-button ${this.filterStatus === 'running' ? 'active' : ''}" | ||||
|           @click=${() => { this.filterStatus = 'running'; this.loadExecutions(); }} | ||||
|           @click=${() => { this.filterStatus = 'running'; this.loadExecutionsWithFilter(); }} | ||||
|         > | ||||
|           Running | ||||
|         </button> | ||||
|         <button | ||||
|           class="filter-button ${this.filterStatus === 'completed' ? 'active' : ''}" | ||||
|           @click=${() => { this.filterStatus = 'completed'; this.loadExecutions(); }} | ||||
|           @click=${() => { this.filterStatus = 'completed'; this.loadExecutionsWithFilter(); }} | ||||
|         > | ||||
|           Completed | ||||
|         </button> | ||||
|         <button | ||||
|           class="filter-button ${this.filterStatus === 'failed' ? 'active' : ''}" | ||||
|           @click=${() => { this.filterStatus = 'failed'; this.loadExecutions(); }} | ||||
|           @click=${() => { this.filterStatus = 'failed'; this.loadExecutionsWithFilter(); }} | ||||
|         > | ||||
|           Failed | ||||
|         </button> | ||||
|       </div> | ||||
|        | ||||
|       <div class="task-grid"> | ||||
|         ${this.tasks.map(task => this.renderTaskCard(task))} | ||||
|         ${(this.data.tasks || []).map(task => this.renderTaskCard(task))} | ||||
|       </div> | ||||
|        | ||||
|       <cloudly-sectionheading>Execution History</cloudly-sectionheading> | ||||
| @@ -528,7 +515,7 @@ export class CloudlyViewTasks extends DeesElement { | ||||
|       <dees-table | ||||
|         .heading1=${'Task Executions'} | ||||
|         .heading2=${'History of task runs and their outcomes'} | ||||
|         .data=${this.executions} | ||||
|         .data=${this.data.taskExecutions || []} | ||||
|         .displayFunction=${(itemArg: plugins.interfaces.data.ITaskExecution) => { | ||||
|           return { | ||||
|             Task: itemArg.data.taskName, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user