Skip to content

Commit 946a2c2

Browse files
committed
feat(start_over): increase visibility of the button
Signed-off-by: Thomas Bétrancourt <thomas@betrancourt.net>
1 parent b4bab29 commit 946a2c2

File tree

3 files changed

+96
-73
lines changed

3 files changed

+96
-73
lines changed

ui/dashboard/projects/utask-lib/src/lib/@models/template.model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export default class Template {
55
blocked: boolean;
66
auto_runnable: boolean;
77
allow_all_resolver_usernames: boolean;
8+
allow_task_start_over: boolean;
89
allowed_resolver_usernames: string[];
910
allowed_resolver_groups: string[];
1011
doc_link: string;

ui/dashboard/projects/utask-lib/src/lib/@routes/task/task.component.ts

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { Component, OnInit, OnDestroy, ViewContainerRef, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
1+
import { Component, OnInit, OnDestroy, ViewContainerRef } from '@angular/core';
22
import { ActivatedRoute, Router } from '@angular/router';
33
import get from 'lodash-es/get';
44
import { FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
55
import { NzModalService } from 'ng-zorro-antd/modal';
6-
import { interval, Subscription } from 'rxjs';
7-
import { concatMap, filter } from 'rxjs/operators';
6+
import { BehaviorSubject, combineLatest, interval, Subscription } from 'rxjs';
7+
import { concatMap, filter, map, switchMap } from 'rxjs/operators';
88
import { NzNotificationService } from 'ng-zorro-antd/notification';
99
import { ApiService, UTaskLibOptions } from '../../@services/api.service';
1010
import { ResolutionService } from '../../@services/resolution.service';
@@ -16,13 +16,34 @@ import Task, { Comment, ResolverInput } from '../../@models/task.model';
1616
import { ModalApiYamlComponent } from '../../@modals/modal-api-yaml/modal-api-yaml.component';
1717
import { InputsFormComponent } from '../../@components/inputs-form/inputs-form.component';
1818
import { TasksListComponentOptions } from '../../@components/tasks-list/tasks-list.component';
19+
import Resolution from '../../@models/resolution.model';
1920

2021
@Component({
2122
selector: 'lib-utask-task',
2223
templateUrl: './task.html',
2324
styleUrls: ['./task.sass']
2425
})
2526
export class TaskComponent implements OnInit, OnDestroy {
27+
private _meta$ = new BehaviorSubject<Meta | null>(null);
28+
readonly meta$ = this._meta$.asObservable();
29+
30+
private _task$ = new BehaviorSubject<Task | null>(null);
31+
readonly task$ = this._task$.asObservable();
32+
33+
readonly template$ = this.task$.pipe(switchMap(task => this.api.template.get(task.template_name)));
34+
35+
private _resolution$ = new BehaviorSubject<Resolution | null>(null);
36+
readonly resolution$ = this._resolution$.asObservable();
37+
38+
readonly startOver$ = combineLatest([this.meta$, this.template$, this.resolution$]).pipe(map(([meta, template, resolution]) => {
39+
if (['PAUSED', 'CANCELLED', 'BLOCKED_BADREQUEST'].indexOf(resolution?.state) === -1) {
40+
return false;
41+
}
42+
43+
return !!meta?.user_is_admin || !!template?.allow_task_start_over;
44+
}));
45+
46+
2647
validateResolveForm!: FormGroup;
2748
validateRejectForm!: FormGroup;
2849
inputControls: Array<string> = [];
@@ -39,13 +60,10 @@ export class TaskComponent implements OnInit, OnDestroy {
3960
resolver_inputs: {},
4061
task_id: null
4162
};
42-
task: Task = null;
4363
taskJson: string;
4464
taskIsResolvable = false;
4565
taskId = '';
46-
resolution: any = null;
4766
selectedStep = '';
48-
meta: Meta = null;
4967
resolverInputs: Array<ResolverInput> = [];
5068

5169
JSON = JSON;
@@ -90,20 +108,20 @@ export class TaskComponent implements OnInit, OnDestroy {
90108
agree: [false, [Validators.requiredTrue]]
91109
});
92110

93-
this.meta = this.route.parent.snapshot.data.meta;
111+
this._meta$.next(this.route.parent.snapshot.data.meta)
94112
this.route.params.subscribe(params => {
95113
this.errors.main = null;
96114
this.taskId = params.id;
97115
this.loadTask().then(() => {
98-
this.display.request = (!this.task.result && !this.resolution) || (!this.resolution && this.taskIsResolvable);
99-
this.display.result = this.task.state === 'DONE';
100-
this.display.execution = !!this.resolution;
101-
this.display.reject = !this.resolution && this.taskIsResolvable;
102-
this.display.resolution = !this.resolution && this.taskIsResolvable;
103-
this.display.comments = this.task.comments && this.task.comments.length > 0;
116+
this.display.request = (!this._task$.value.result && !this._resolution$.value) || (!this._resolution$.value && this.taskIsResolvable);
117+
this.display.result = this._task$.value.state === 'DONE';
118+
this.display.execution = !!this._resolution$.value;
119+
this.display.reject = !this._resolution$.value && this.taskIsResolvable;
120+
this.display.resolution = !this._resolution$.value && this.taskIsResolvable;
121+
this.display.comments = this._task$.value.comments && this._task$.value.comments.length > 0;
104122
}).catch((err) => {
105123
console.log(err);
106-
if (!this.task || this.task.id !== params.id) {
124+
if (!this._task$.value || this._task$.value.id !== params.id) {
107125
this.errors.main = err;
108126
}
109127
});
@@ -119,9 +137,9 @@ export class TaskComponent implements OnInit, OnDestroy {
119137

120138
addComment() {
121139
this.loaders.addComment = true;
122-
this.api.task.comment.add(this.task.id, this.comment.content).toPromise().then((comment: Comment) => {
123-
this.task.comments = get(this.task, 'comments', []);
124-
this.task.comments.push(comment);
140+
this.api.task.comment.add(this._task$.value.id, this.comment.content).toPromise().then((comment: Comment) => {
141+
this._task$.value.comments = get(this._task$.value, 'comments', []);
142+
this._task$.value.comments.push(comment);
125143
this.errors.addComment = null;
126144
this.comment.content = '';
127145
}).catch((err) => {
@@ -150,13 +168,13 @@ export class TaskComponent implements OnInit, OnDestroy {
150168
nzWidth: '80%',
151169
nzViewContainerRef: this.viewContainerRef,
152170
nzComponentParams: {
153-
apiCall: () => this.api.resolution.getAsYaml(this.resolution.id).toPromise()
171+
apiCall: () => this.api.resolution.getAsYaml(this._resolution$.value.id).toPromise()
154172
},
155173
});
156174
}
157175

158-
editRequest(task: Task) {
159-
this.requestService.edit(task).then((data: any) => {
176+
editRequest() {
177+
this.requestService.edit(this._task$.value).then((data: any) => {
160178
this.loadTask(true);
161179
this._notif.info('', 'The request has been edited.');
162180
}).catch((err) => {
@@ -221,8 +239,8 @@ export class TaskComponent implements OnInit, OnDestroy {
221239
});
222240
}
223241

224-
deleteTask(taskId: string) {
225-
this.taskService.delete(taskId).then((data: any) => {
242+
deleteTask() {
243+
this.taskService.delete(this._task$.value.id).then((data: any) => {
226244
this.router.navigate([this._options.uiBaseUrl + '/']);
227245
this._notif.info('', 'The task has been deleted.');
228246
}).catch((err) => {
@@ -244,7 +262,7 @@ export class TaskComponent implements OnInit, OnDestroy {
244262
}
245263

246264
this.loaders.rejectTask = true;
247-
this.api.task.reject(this.task.id).toPromise().then((res: any) => {
265+
this.api.task.reject(this._task$.value.id).toPromise().then((res: any) => {
248266
this.errors.rejectTask = null;
249267
this.loadTask(true);
250268
}).catch((err) => {
@@ -316,7 +334,7 @@ export class TaskComponent implements OnInit, OnDestroy {
316334
});
317335
this.inputControls = this.resolverInputs.map(input => 'input_' + input.name);
318336
}
319-
this.item.task_id = this.task.id;
337+
this.item.task_id = this._task$.value.id;
320338
}
321339

322340
loadTask(refresh: boolean = false) {
@@ -327,18 +345,18 @@ export class TaskComponent implements OnInit, OnDestroy {
327345
this.api.task.get(this.taskId).toPromise(),
328346
this.api.task.list({
329347
page_size: 10,
330-
type: this.meta.user_is_admin ? 'all' : 'own',
348+
type: this._meta$.value.user_is_admin ? 'all' : 'own',
331349
tag: '_utask_parent_task_id=' + this.taskId
332350
} as any).toPromise(),
333351
]).then(async (data) => {
334-
this.task = data[0];
352+
this._task$.next(data[0]);
335353
this.taskJson = JSON.stringify(data[0].result, null, 2);
336354
this.haveAtLeastOneChilTask = data[1].body.length > 0;
337-
this.task.comments = get(this.task, 'comments', []).sort((a, b) => a.created < b.created ? -1 : 1);
355+
this._task$.value.comments = get(this._task$.value, 'comments', []).sort((a, b) => a.created < b.created ? -1 : 1);
338356

339-
if (this.template?.name !== this.task.template_name) {
357+
if (this.template?.name !== this._task$.value.template_name) {
340358
try {
341-
this.template = await this.getTemplate(this.task.template_name);
359+
this.template = await this.getTemplate(this._task$.value.template_name);
342360
this.resolverInputs = this.template.resolver_inputs;
343361
} catch (err) {
344362
reject(err);
@@ -349,26 +367,26 @@ export class TaskComponent implements OnInit, OnDestroy {
349367
this.taskChanged();
350368
}
351369

352-
this.taskIsResolvable = this.requestService.isResolvable(this.task, this.meta, this.template);
353-
if (['DONE', 'WONTFIX', 'CANCELLED'].indexOf(this.task.state) > -1) {
370+
this.taskIsResolvable = this.requestService.isResolvable(this._task$.value, this._meta$.value, this.template);
371+
if (['DONE', 'WONTFIX', 'CANCELLED'].indexOf(this._task$.value.state) > -1) {
354372
this.autorefresh.enable = false;
355373
this.autorefresh.actif = false;
356374
} else {
357375
this.autorefresh.enable = true;
358376
if (!this.autorefresh.hasChanged) {
359-
this.autorefresh.actif = ['TODO', 'RUNNING', 'TO_AUTORUN', 'WAITING'].indexOf(this.task.state) > -1;
377+
this.autorefresh.actif = ['TODO', 'RUNNING', 'TO_AUTORUN', 'WAITING'].indexOf(this._task$.value.state) > -1;
360378
}
361379
}
362380

363-
if (this.task.resolution) {
381+
if (this._task$.value.resolution) {
364382
this.loaders.resolution = !refresh;
365383
this.loaders.refreshResolution = !refresh;
366-
this.loadResolution(this.task.resolution).then(rData => {
367-
if (!this.resolution && rData) {
384+
this.loadResolution(this._task$.value.resolution).then(rData => {
385+
if (!this._resolution$.value && rData) {
368386
this.display.execution = true;
369387
this.display.request = false;
370388
}
371-
this.resolution = rData;
389+
this._resolution$.next(rData);
372390
resolve();
373391
}).catch((err) => {
374392
reject(err);
@@ -377,7 +395,7 @@ export class TaskComponent implements OnInit, OnDestroy {
377395
this.loaders.refreshResolution = false;
378396
});
379397
} else {
380-
this.resolution = null;
398+
this._resolution$.next(null);
381399
resolve();
382400
}
383401
}).catch((err: any) => {

0 commit comments

Comments
 (0)