feat(tasks): Enhance task management with identity handling and initial data loading

This commit is contained in:
2025-09-10 17:04:18 +00:00
parent 5b37bb5b11
commit 5d281d9b6c
4 changed files with 158 additions and 151 deletions

View File

@@ -53,6 +53,8 @@ export interface IDataState {
deployments?: plugins.interfaces.data.IDeployment[];
domains?: plugins.interfaces.data.IDomain[];
dnsEntries?: plugins.interfaces.data.IDnsEntry[];
tasks?: any[];
taskExecutions?: plugins.interfaces.data.ITaskExecution[];
mails?: any[];
logs?: any[];
s3?: any[];
@@ -71,6 +73,8 @@ export const dataState = await appstate.getStatePart<IDataState>(
deployments: [],
domains: [],
dnsEntries: [],
tasks: [],
taskExecutions: [],
mails: [],
logs: [],
s3: [],
@@ -676,7 +680,10 @@ export const taskActions = {
const response = await trGetTasks.fire({
identity: loginStatePart.getState().identity,
});
return response as any;
return {
...currentState,
tasks: response.tasks,
};
}
),
@@ -692,7 +699,10 @@ export const taskActions = {
identity: loginStatePart.getState().identity,
filter: payloadArg.filter,
});
return response as any;
return {
...currentState,
taskExecutions: response.executions,
};
}
),
@@ -708,7 +718,7 @@ export const taskActions = {
identity: loginStatePart.getState().identity,
executionId: payloadArg.executionId,
});
return response as any;
return currentState;
}
),

View File

@@ -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>

View File

@@ -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,