diff --git a/web/src/app/administration/components/access-items-management/access-items-management.component.spec.ts b/web/src/app/administration/components/access-items-management/access-items-management.component.spec.ts index 518f170d8..a173e1393 100644 --- a/web/src/app/administration/components/access-items-management/access-items-management.component.spec.ts +++ b/web/src/app/administration/components/access-items-management/access-items-management.component.spec.ts @@ -60,11 +60,6 @@ describe('AccessItemsManagementComponent', () => { let store: Store; let actions$: Observable; - @Component({ selector: 'taskana-shared-spinner', template: '' }) - class TaskanaSharedSpinnerStub { - @Input() isRunning: boolean; - } - @Component({ selector: 'taskana-shared-sort', template: '' }) class TaskanaSharedSortStub { @Input() sortingFields: Map; @@ -95,12 +90,7 @@ describe('AccessItemsManagementComponent', () => { MatListModule, MatExpansionModule ], - declarations: [ - AccessItemsManagementComponent, - TypeAheadComponent, - TaskanaSharedSortStub, - TaskanaSharedSpinnerStub - ], + declarations: [AccessItemsManagementComponent, TypeAheadComponent, TaskanaSharedSortStub], providers: [ { provide: FormsValidatorService, useClass: formValidatorServiceSpy }, { provide: NotificationService, useClass: notificationServiceSpy }, diff --git a/web/src/app/administration/components/classification-details/classification-details.component.html b/web/src/app/administration/components/classification-details/classification-details.component.html index d9be6d7b1..720264688 100644 --- a/web/src/app/administration/components/classification-details/classification-details.component.html +++ b/web/src/app/administration/components/classification-details/classification-details.component.html @@ -1,7 +1,6 @@
- -
+
diff --git a/web/src/app/administration/components/classification-details/classification-details.component.spec.ts b/web/src/app/administration/components/classification-details/classification-details.component.spec.ts index 3ee6d8d2a..aa8be88dc 100644 --- a/web/src/app/administration/components/classification-details/classification-details.component.spec.ts +++ b/web/src/app/administration/components/classification-details/classification-details.component.spec.ts @@ -32,11 +32,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { By } from '@angular/platform-browser'; import { MatProgressBarModule } from '@angular/material/progress-bar'; -@Component({ selector: 'taskana-shared-spinner', template: '' }) -class SpinnerStub { - @Input() isRunning; -} - @Component({ selector: 'taskana-shared-field-error-display', template: '' }) class FieldErrorDisplayStub { @Input() displayError; @@ -87,7 +82,8 @@ const importExportServiceSpy = jest.fn().mockImplementation( const requestInProgressServiceSpy = jest.fn().mockImplementation( (): Partial => ({ - setRequestInProgress: jest.fn().mockReturnValue(of()) + setRequestInProgress: jest.fn().mockReturnValue(of()), + getRequestInProgress: jest.fn().mockReturnValue(of(false)) }) ); @@ -133,14 +129,7 @@ describe('ClassificationDetailsComponent', () => { MatMenuModule, BrowserAnimationsModule ], - declarations: [ - ClassificationDetailsComponent, - SpinnerStub, - InputStub, - FieldErrorDisplayStub, - SvgIconStub, - TextareaStub - ], + declarations: [ClassificationDetailsComponent, InputStub, FieldErrorDisplayStub, SvgIconStub, TextareaStub], providers: [ { provide: ClassificationsService, useClass: classificationServiceSpy }, { provide: ClassificationCategoriesService, useClass: classificationCategoriesServiceSpy }, @@ -254,7 +243,7 @@ describe('ClassificationDetailsComponent', () => { /* HTML */ it('should not show details when spinner is running', () => { - component.spinnerIsRunning = true; + component.requestInProgress = true; component.classification = {}; fixture.detectChanges(); expect(debugElement.nativeElement.querySelector('.classification-details__action-toolbar')).toBeFalsy(); @@ -262,7 +251,7 @@ describe('ClassificationDetailsComponent', () => { }); it('should not show details when classification does not exist', () => { - component.spinnerIsRunning = false; + component.requestInProgress = false; component.classification = null; fixture.detectChanges(); expect(debugElement.nativeElement.querySelector('.classification-details__action-toolbar')).toBeFalsy(); diff --git a/web/src/app/administration/components/classification-details/classification-details.component.ts b/web/src/app/administration/components/classification-details/classification-details.component.ts index 7c75bac35..4298aa808 100644 --- a/web/src/app/administration/components/classification-details/classification-details.component.ts +++ b/web/src/app/administration/components/classification-details/classification-details.component.ts @@ -20,7 +20,6 @@ import { NotificationService } from '../../../shared/services/notifications/noti import { ClassificationCategoryImages, CustomField, getCustomFields } from '../../../shared/models/customisation'; import { Classification } from '../../../shared/models/classification'; import { customFieldCount } from '../../../shared/models/classification-summary'; -import { CategoriesResponse } from '../../../shared/services/classification-categories/classification-categories.service'; import { SaveCreatedClassification, @@ -40,19 +39,18 @@ import { }) export class ClassificationDetailsComponent implements OnInit, OnDestroy { classification: Classification; - requestInProgress = false; @Select(ClassificationSelectors.selectCategories) categories$: Observable; @Select(EngineConfigurationSelectors.selectCategoryIcons) categoryIcons$: Observable; @Select(ClassificationSelectors.selectedClassificationType) selectedClassificationType$: Observable; @Select(ClassificationSelectors.selectedClassification) selectedClassification$: Observable; @Select(ClassificationSelectors.getBadgeMessage) badgeMessage$: Observable; - spinnerIsRunning = false; customFields$: Observable; isCreatingNewClassification: boolean = false; readonly lengthError = 'You have reached the maximum length for this field'; inputOverflowMap = new Map(); validateInputOverflow: Function; + requestInProgress: boolean; @ViewChild('ClassificationForm') classificationForm: NgForm; toggleValidationMap = new Map(); @@ -86,6 +84,13 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy { this.store.dispatch(new SelectClassification(this.classification.classificationId)); }); + this.requestInProgressService + .getRequestInProgress() + .pipe(takeUntil(this.destroy$)) + .subscribe((value) => { + this.requestInProgress = value; + }); + this.formsValidatorService.inputOverflowObservable.pipe(takeUntil(this.destroy$)).subscribe((inputOverflowMap) => { this.inputOverflowMap = inputOverflowMap; }); @@ -141,10 +146,6 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy { ); } - spinnerRunning(value) { - this.spinnerIsRunning = value; - } - validChanged(): void { this.classification.isValidInDomain = !this.classification.isValidInDomain; } diff --git a/web/src/app/administration/components/classification-list/classification-list.component.html b/web/src/app/administration/components/classification-list/classification-list.component.html index 68215c0ce..a31da5050 100644 --- a/web/src/app/administration/components/classification-list/classification-list.component.html +++ b/web/src/app/administration/components/classification-list/classification-list.component.html @@ -61,11 +61,9 @@ - - + (switchTaskanaSpinnerEmit)="setRequestInProgress($event)">

There are no classifications

diff --git a/web/src/app/administration/components/classification-list/classification-list.component.spec.ts b/web/src/app/administration/components/classification-list/classification-list.component.spec.ts index 47e6bbb0e..e11adae45 100644 --- a/web/src/app/administration/components/classification-list/classification-list.component.spec.ts +++ b/web/src/app/administration/components/classification-list/classification-list.component.spec.ts @@ -19,6 +19,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MatInputModule } from '@angular/material/input'; import { By } from '@angular/platform-browser'; import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service'; @Component({ selector: 'taskana-administration-import-export', template: '' }) class ImportExportStub { @@ -29,11 +30,6 @@ class ImportExportStub { @Component({ selector: 'taskana-administration-classification-types-selector', template: '' }) class ClassificationTypesSelectorStub {} -@Component({ selector: 'taskana-shared-spinner', template: '' }) -class SpinnerStub { - @Input() isRunning; -} - @Component({ selector: 'taskana-administration-tree', template: '' }) class TreeStub { @Input() filterText; @@ -75,6 +71,13 @@ const importExportServiceSpy = jest.fn().mockImplementation( }) ); +const requestInProgressServiceSpy = jest.fn().mockImplementation( + (): Partial => ({ + setRequestInProgress: jest.fn().mockReturnValue(of()), + getRequestInProgress: jest.fn().mockReturnValue(of(false)) + }) +); + describe('ClassificationListComponent', () => { let fixture: ComponentFixture; let debugElement: DebugElement; @@ -96,7 +99,6 @@ describe('ClassificationListComponent', () => { declarations: [ ClassificationListComponent, ClassificationTypesSelectorStub, - SpinnerStub, TreeStub, SvgIconStub, ImportExportStub, @@ -106,7 +108,8 @@ describe('ClassificationListComponent', () => { { provide: ClassificationsService, useClass: classificationServiceSpy }, { provide: ClassificationCategoriesService, useClass: classificationCategoriesServiceSpy }, { provide: DomainService, useClass: domainServiceSpy }, - { provide: ImportExportService, useClass: importExportServiceSpy } + { provide: ImportExportService, useClass: importExportServiceSpy }, + { provide: RequestInProgressService, useClass: requestInProgressServiceSpy } ] }).compileComponents(); diff --git a/web/src/app/administration/components/classification-list/classification-list.component.ts b/web/src/app/administration/components/classification-list/classification-list.component.ts index 1cfa96dea..1bda0874a 100644 --- a/web/src/app/administration/components/classification-list/classification-list.component.ts +++ b/web/src/app/administration/components/classification-list/classification-list.component.ts @@ -18,6 +18,7 @@ import { } from '../../../shared/store/classification-store/classification.actions'; import { DomainService } from '../../../shared/services/domain/domain.service'; import { ClassificationSummary } from '../../../shared/models/classification-summary'; +import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service'; @Component({ selector: 'taskana-administration-classification-list', @@ -43,15 +44,16 @@ export class ClassificationListComponent implements OnInit, OnDestroy { constructor( private location: Location, private importExportService: ImportExportService, + private domainService: DomainService, + private requestInProgressService: RequestInProgressService, private store: Store, - private ngxsActions$: Actions, - private domainService: DomainService + private ngxsActions$: Actions ) { this.ngxsActions$.pipe(ofActionDispatched(GetClassifications), takeUntil(this.destroy$)).subscribe(() => { - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); }); this.ngxsActions$.pipe(ofActionCompleted(GetClassifications), takeUntil(this.destroy$)).subscribe(() => { - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); }); } @@ -79,6 +81,13 @@ export class ClassificationListComponent implements OnInit, OnDestroy { .subscribe((domain) => { this.store.dispatch(GetClassifications); }); + + this.requestInProgressService + .getRequestInProgress() + .pipe(takeUntil(this.destroy$)) + .subscribe((value) => { + this.requestInProgress = value; + }); } addClassification() { @@ -107,6 +116,10 @@ export class ClassificationListComponent implements OnInit, OnDestroy { this.showFilter = !this.showFilter; } + setRequestInProgress(value: boolean) { + this.requestInProgressService.setRequestInProgress(value); + } + ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); diff --git a/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.html b/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.html index fdc68ae2f..f7d054333 100644 --- a/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.html +++ b/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.html @@ -1,4 +1,3 @@ -
diff --git a/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.spec.ts b/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.spec.ts index 05dba3020..72f808343 100644 --- a/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.spec.ts +++ b/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { WorkbasketAccessItemsComponent } from './workbasket-access-items.component'; -import { Component, DebugElement, Input } from '@angular/core'; +import { DebugElement } from '@angular/core'; import { Actions, NgxsModule, ofActionDispatched, Store } from '@ngxs/store'; import { Observable, of } from 'rxjs'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -32,19 +32,12 @@ import { } from '../../../shared/store/workbasket-store/workbasket.actions'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ACTION } from '../../../shared/models/action'; -import { WorkbasketAccessItems } from '../../../shared/models/workbasket-access-items'; import { MatSelectModule } from '@angular/material/select'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatProgressBarModule } from '@angular/material/progress-bar'; -@Component({ selector: 'taskana-shared-spinner', template: '' }) -class SpinnerStub { - @Input() isRunning: boolean; - @Input() positionClass: string; -} - const savingWorkbasketServiceSpy = jest.fn().mockImplementation( (): Partial => ({ triggeredAccessItemsSaving: jest.fn().mockReturnValue(of(true)) @@ -100,7 +93,7 @@ describe('WorkbasketAccessItemsComponent', () => { MatAutocompleteModule, MatProgressBarModule ], - declarations: [WorkbasketAccessItemsComponent, TypeAheadComponent, SpinnerStub], + declarations: [WorkbasketAccessItemsComponent, TypeAheadComponent], providers: [ { provide: SavingWorkbasketService, useClass: savingWorkbasketServiceSpy }, { provide: RequestInProgressService, useClass: requestInProgressServiceSpy }, diff --git a/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.ts b/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.ts index fc76cc77e..00c3ce00c 100644 --- a/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.ts +++ b/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.ts @@ -61,7 +61,6 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest accessItemsRepresentation: WorkbasketAccessItemsRepresentation; accessItemsClone: Array; accessItemsResetClone: Array; - requestInProgress = false; AccessItemsForm = this.formBuilder.group({ accessItemsGroups: this.formBuilder.array([]) }); @@ -148,9 +147,9 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest if (!this.workbasket._links.accessItems) { return; } - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); this.store.dispatch(new GetWorkbasketAccessItems(this.workbasket._links.accessItems.href)).subscribe(() => { - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); }); this.savingWorkbaskets diff --git a/web/src/app/administration/components/workbasket-details/workbasket-details.component.html b/web/src/app/administration/components/workbasket-details/workbasket-details.component.html index 4a83c2244..ac2994146 100644 --- a/web/src/app/administration/components/workbasket-details/workbasket-details.component.html +++ b/web/src/app/administration/components/workbasket-details/workbasket-details.component.html @@ -48,6 +48,5 @@ -
diff --git a/web/src/app/administration/components/workbasket-details/workbasket-details.component.spec.ts b/web/src/app/administration/components/workbasket-details/workbasket-details.component.spec.ts index 351d2ebd8..fcf5e1f05 100644 --- a/web/src/app/administration/components/workbasket-details/workbasket-details.component.spec.ts +++ b/web/src/app/administration/components/workbasket-details/workbasket-details.component.spec.ts @@ -28,11 +28,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { CreateWorkbasket } from '../../../shared/store/workbasket-store/workbasket.actions'; import { take } from 'rxjs/operators'; -@Component({ selector: 'taskana-shared-spinner', template: '' }) -class SpinnerStub { - @Input() isRunning: boolean; -} - @Component({ selector: 'taskana-administration-workbasket-information', template: '
i
' }) class WorkbasketInformationStub { @Input() workbasket: Workbasket; @@ -91,7 +86,6 @@ describe('WorkbasketDetailsComponent', () => { ], declarations: [ WorkbasketDetailsComponent, - SpinnerStub, WorkbasketAccessItemsStub, WorkbasketDistributionTargetsStub, WorkbasketInformationStub @@ -126,7 +120,6 @@ describe('WorkbasketDetailsComponent', () => { it('should render information component when workbasket details is opened', () => { component.workbasket = { workbasketId: '1' }; - component.requestInProgress = false; fixture.detectChanges(); const information = debugElement.nativeElement.querySelector('taskana-administration-workbasket-information'); expect(information).toBeTruthy(); diff --git a/web/src/app/administration/components/workbasket-details/workbasket-details.component.ts b/web/src/app/administration/components/workbasket-details/workbasket-details.component.ts index 7b3623f56..b3a2b1b6e 100644 --- a/web/src/app/administration/components/workbasket-details/workbasket-details.component.ts +++ b/web/src/app/administration/components/workbasket-details/workbasket-details.component.ts @@ -17,6 +17,7 @@ import { SelectComponent } from '../../../shared/store/workbasket-store/workbasket.actions'; import { ButtonAction } from '../../models/button-action'; +import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service'; import { WorkbasketComponent } from '../../models/workbasket-component'; @Component({ @@ -28,7 +29,6 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges workbasket: Workbasket; workbasketCopy: Workbasket; selectedId: string; - requestInProgress = false; action: ACTION; badgeMessage = ''; @@ -52,6 +52,7 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges private router: Router, private domainService: DomainService, private importExportService: ImportExportService, + private requestInProgressService: RequestInProgressService, private store: Store ) {} @@ -108,7 +109,7 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges if (selectedWorkbasket) { workbasketIdSelected = selectedWorkbasket.workbasketId; } - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); if (!workbasketIdSelected && this.action === ACTION.CREATE) { // CREATE this.workbasket = {}; @@ -118,16 +119,16 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges .subscribe((domain) => { this.workbasket.domain = domain; }); - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); } else if (!workbasketIdSelected && this.action === ACTION.COPY) { // COPY this.workbasket = { ...this.workbasketCopy }; delete this.workbasket.workbasketId; - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); } if (workbasketIdSelected) { this.workbasket = selectedWorkbasket; - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); this.checkDomainAndRedirect(); } } diff --git a/web/src/app/administration/components/workbasket-information/workbasket-information.component.html b/web/src/app/administration/components/workbasket-information/workbasket-information.component.html index 9e6517943..4cd1eca87 100644 --- a/web/src/app/administration/components/workbasket-information/workbasket-information.component.html +++ b/web/src/app/administration/components/workbasket-information/workbasket-information.component.html @@ -1,4 +1,3 @@ -
@@ -43,7 +42,7 @@ -
+

There are no workbaskets

diff --git a/web/src/app/administration/components/workbasket-list/workbasket-list.component.spec.ts b/web/src/app/administration/components/workbasket-list/workbasket-list.component.spec.ts index af37b4339..3e4911ec9 100644 --- a/web/src/app/administration/components/workbasket-list/workbasket-list.component.spec.ts +++ b/web/src/app/administration/components/workbasket-list/workbasket-list.component.spec.ts @@ -17,12 +17,12 @@ import { Filter } from '../../../shared/models/filter'; import { ICONTYPES } from '../../../shared/models/icon-types'; import { Page } from '../../../shared/models/page'; import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { FormsModule } from '@angular/forms'; -import { MatListModule, MatSelectionList } from '@angular/material/list'; +import { MatListModule } from '@angular/material/list'; import { DomainService } from '../../../shared/services/domain/domain.service'; import { RouterTestingModule } from '@angular/router/testing'; +import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service'; const workbasketSavedTriggeredFn = jest.fn().mockReturnValue(of(1)); const workbasketSummaryFn = jest.fn().mockReturnValue(of({})); @@ -59,6 +59,13 @@ const domainServiceSpy = jest.fn().mockImplementation( }) ); +const requestInProgressServiceSpy = jest.fn().mockImplementation( + (): Partial => ({ + setRequestInProgress: jest.fn().mockReturnValue(of()), + getRequestInProgress: jest.fn().mockReturnValue(of(false)) + }) +); + @Component({ selector: 'taskana-administration-workbasket-list-toolbar', template: '' }) class WorkbasketListToolbarStub { @Input() workbaskets: Array; @@ -73,11 +80,6 @@ class IconTypeStub { @Input() selected = false; } -@Component({ selector: 'taskana-shared-spinner', template: '' }) -class SpinnerStub { - @Input() isRunning: boolean; -} - @Component({ selector: 'taskana-shared-pagination', template: '' }) class PaginationStub { @Input() page: Page; @@ -108,19 +110,13 @@ describe('WorkbasketListComponent', () => { MatSelectModule, MatListModule ], - declarations: [ - WorkbasketListComponent, - WorkbasketListToolbarStub, - IconTypeStub, - SpinnerStub, - PaginationStub, - SvgIconStub - ], + declarations: [WorkbasketListComponent, WorkbasketListToolbarStub, IconTypeStub, PaginationStub, SvgIconStub], providers: [ { provide: WorkbasketService, useClass: workbasketServiceMock }, { provide: OrientationService, useClass: orientationServiceMock }, { provide: ImportExportService, useClass: importExportServiceMock }, - { provide: DomainService, useClass: domainServiceSpy } + { provide: DomainService, useClass: domainServiceSpy }, + { provide: RequestInProgressService, useClass: requestInProgressServiceSpy } ] }).compileComponents(); diff --git a/web/src/app/administration/components/workbasket-list/workbasket-list.component.ts b/web/src/app/administration/components/workbasket-list/workbasket-list.component.ts index 363ff5f67..50a9c505f 100644 --- a/web/src/app/administration/components/workbasket-list/workbasket-list.component.ts +++ b/web/src/app/administration/components/workbasket-list/workbasket-list.component.ts @@ -22,6 +22,7 @@ import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/work import { Workbasket } from '../../../shared/models/workbasket'; import { MatSelectionList } from '@angular/material/list'; import { DomainService } from '../../../shared/services/domain/domain.service'; +import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service'; @Component({ selector: 'taskana-administration-workbasket-list', @@ -30,7 +31,6 @@ import { DomainService } from '../../../shared/services/domain/domain.service'; }) export class WorkbasketListComponent implements OnInit, OnDestroy { selectedId = ''; - requestInProgress = false; pageSelected = 1; pageSize = 9; type = 'workbaskets'; @@ -38,6 +38,8 @@ export class WorkbasketListComponent implements OnInit, OnDestroy { workbasketDefaultSortBy: string = 'name'; sort: Sorting = new Sorting(this.workbasketDefaultSortBy); filterBy: Filter = new Filter({ name: '', owner: '', type: '', description: '', key: '' }); + requestInProgress: boolean; + requestInProgressLocal = false; @ViewChild('wbToolbar', { static: true }) private toolbarElement: ElementRef; @@ -61,18 +63,21 @@ export class WorkbasketListComponent implements OnInit, OnDestroy { private orientationService: OrientationService, private importExportService: ImportExportService, private domainService: DomainService, + private requestInProgressService: RequestInProgressService, private ngxsActions$: Actions ) { this.ngxsActions$.pipe(ofActionDispatched(GetWorkbasketsSummary), takeUntil(this.destroy$)).subscribe(() => { - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); + this.requestInProgressLocal = true; }); this.ngxsActions$.pipe(ofActionCompleted(GetWorkbasketsSummary), takeUntil(this.destroy$)).subscribe(() => { - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); + this.requestInProgressLocal = false; }); } ngOnInit() { - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); this.selectedWorkbasket$.pipe(takeUntil(this.destroy$)).subscribe((selectedWorkbasket) => { if (typeof selectedWorkbasket !== 'undefined') { @@ -117,11 +122,18 @@ export class WorkbasketListComponent implements OnInit, OnDestroy { .getWorkbasketActionToolbarExpansion() .pipe(takeUntil(this.destroy$)) .subscribe((value) => { - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); setTimeout(() => { this.refreshWorkbasketList(); }, 1); }); + + this.requestInProgressService + .getRequestInProgress() + .pipe(takeUntil(this.destroy$)) + .subscribe((value) => { + this.requestInProgress = value; + }); } selectWorkbasket(id: string) { @@ -176,7 +188,7 @@ export class WorkbasketListComponent implements OnInit, OnDestroy { ) ) .subscribe(() => { - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); }); TaskanaQueryParameters.pageSize = this.cards; } diff --git a/web/src/app/app.component.html b/web/src/app/app.component.html index 8c9398564..3a63a9618 100644 --- a/web/src/app/app.component.html +++ b/web/src/app/app.component.html @@ -21,13 +21,16 @@ -
+
+ +
- - + +
- \ No newline at end of file + diff --git a/web/src/app/app.component.scss b/web/src/app/app.component.scss index 2f9fe605e..b41e01931 100644 --- a/web/src/app/app.component.scss +++ b/web/src/app/app.component.scss @@ -1,4 +1,5 @@ @import '../theme/variables'; +@import 'src/theme/_colors.scss'; .sidenav { position: absolute; @@ -6,7 +7,7 @@ bottom: 0; left: 0; right: 0; - margin-top: 0px; + margin-top: 0; } .sidenav__drawer { @@ -56,9 +57,11 @@ outline: none; } -mat-sidenav-content { - height: unset; +.taskana-main__progress-bar { + height: 4px; + background-color: $light-grey; } + ::ng-deep .mat-drawer-inner-container { overflow: visible !important; } diff --git a/web/src/app/app.component.ts b/web/src/app/app.component.ts index 11defbd31..6c041068d 100644 --- a/web/src/app/app.component.ts +++ b/web/src/app/app.component.ts @@ -1,6 +1,6 @@ import { Component, HostListener, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { NavigationStart, Router } from '@angular/router'; -import { Subscription } from 'rxjs'; +import { Subject } from 'rxjs'; import { FormsValidatorService } from 'app/shared/services/forms-validator/forms-validator.service'; import { SidenavService } from './shared/services/sidenav/sidenav.service'; import { RequestInProgressService } from './shared/services/request-in-progress/request-in-progress.service'; @@ -12,6 +12,7 @@ import { TaskanaEngineService } from './shared/services/taskana-engine/taskana-e import { WindowRefService } from 'app/shared/services/window/window.service'; import { environment } from 'environments/environment'; import { MatSidenav } from '@angular/material/sidenav'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'taskana-root', @@ -25,14 +26,12 @@ export class AppComponent implements OnInit, OnDestroy { requestInProgress = false; currentProgressValue = 0; - requestInProgressSubscription: Subscription; - selectedRouteSubscription: Subscription; - routerSubscription: Subscription; - uploadingFileSubscription: Subscription; error: ErrorModel; version: string; toggle: boolean = false; + destroy$ = new Subject(); + constructor( private router: Router, private requestInProgressService: RequestInProgressService, @@ -53,33 +52,43 @@ export class AppComponent implements OnInit, OnDestroy { @ViewChild('sidenav') public sidenav: MatSidenav; ngOnInit() { - this.routerSubscription = this.router.events.subscribe((event) => { + this.router.events.pipe(takeUntil(this.destroy$)).subscribe((event) => { if (event instanceof NavigationStart) { this.selectedRouteService.selectRoute(event); this.formsValidatorService.formSubmitAttempt = false; } }); - this.requestInProgressSubscription = this.requestInProgressService + this.requestInProgressService .getRequestInProgress() + .pipe(takeUntil(this.destroy$)) .subscribe((value: boolean) => { this.requestInProgress = value; }); - this.selectedRouteSubscription = this.selectedRouteService.getSelectedRoute().subscribe((value: string) => { - if (value.indexOf('classifications') !== -1) { - this.workbasketsRoute = false; - } - this.selectedRoute = value; - }); + this.selectedRouteService + .getSelectedRoute() + .pipe(takeUntil(this.destroy$)) + .subscribe((value: string) => { + if (value.indexOf('classifications') !== -1) { + this.workbasketsRoute = false; + } + this.selectedRoute = value; + }); - this.uploadingFileSubscription = this.uploadService.getCurrentProgressObservable().subscribe((value) => { - this.currentProgressValue = value; - }); + this.uploadService + .getCurrentProgressObservable() + .pipe(takeUntil(this.destroy$)) + .subscribe((value) => { + this.currentProgressValue = value; + }); - this.taskanaEngineService.getVersion().subscribe((restVersion) => { - this.version = restVersion.version; - }); + this.taskanaEngineService + .getVersion() + .pipe(takeUntil(this.destroy$)) + .subscribe((restVersion) => { + this.version = restVersion.version; + }); } logout() { @@ -97,17 +106,7 @@ export class AppComponent implements OnInit, OnDestroy { } ngOnDestroy() { - if (this.routerSubscription) { - this.routerSubscription.unsubscribe(); - } - if (this.requestInProgressSubscription) { - this.requestInProgressSubscription.unsubscribe(); - } - if (this.selectedRouteSubscription) { - this.selectedRouteSubscription.unsubscribe(); - } - if (this.uploadingFileSubscription) { - this.uploadingFileSubscription.unsubscribe(); - } + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/web/src/app/app.module.ts b/web/src/app/app.module.ts index ed425bc1e..9ec0d53a4 100644 --- a/web/src/app/app.module.ts +++ b/web/src/app/app.module.ts @@ -60,6 +60,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatSelectModule } from '@angular/material/select'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; const MODULES = [ TabsModule.forRoot(), @@ -92,7 +93,7 @@ export function startupServiceFactory(startupService: StartupService): () => Pro @NgModule({ declarations: DECLARATIONS, - imports: [MODULES, MatSidenavModule, MatIconModule, MatToolbarModule, MatProgressBarModule], + imports: [MODULES, MatSidenavModule, MatIconModule, MatToolbarModule, MatProgressBarModule, MatProgressSpinnerModule], providers: [ WindowRefService, DomainService, diff --git a/web/src/app/shared/components/progress-bar/progress-bar.component.html b/web/src/app/shared/components/progress-bar/progress-bar.component.html deleted file mode 100644 index a27f124a8..000000000 --- a/web/src/app/shared/components/progress-bar/progress-bar.component.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
-

{{currentValue}}%

-
-

Uploading file

-
-
\ No newline at end of file diff --git a/web/src/app/shared/components/progress-bar/progress-bar.component.scss b/web/src/app/shared/components/progress-bar/progress-bar.component.scss deleted file mode 100644 index 4fd67d066..000000000 --- a/web/src/app/shared/components/progress-bar/progress-bar.component.scss +++ /dev/null @@ -1,11 +0,0 @@ -.upload-file-container { - z-index: 3000; - width: 170px; - position: absolute; - left: 50%; - top: 33%; - margin-left: -85px; - > .item { - margin-left: 10px; - } -} diff --git a/web/src/app/shared/components/progress-bar/progress-bar.component.spec.ts b/web/src/app/shared/components/progress-bar/progress-bar.component.spec.ts deleted file mode 100644 index 8aa26f01e..000000000 --- a/web/src/app/shared/components/progress-bar/progress-bar.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ProgressBarComponent } from './progress-bar.component'; - -describe('ProgressBarComponent', () => { - let component: ProgressBarComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ProgressBarComponent] - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ProgressBarComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/web/src/app/shared/components/progress-bar/progress-bar.component.ts b/web/src/app/shared/components/progress-bar/progress-bar.component.ts deleted file mode 100644 index fe58cbe58..000000000 --- a/web/src/app/shared/components/progress-bar/progress-bar.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Component, OnInit, Input, SimpleChanges, OnChanges } from '@angular/core'; -@Component({ - selector: 'taskana-shared-progress-bar', - templateUrl: './progress-bar.component.html', - styleUrls: ['./progress-bar.component.scss'] -}) -export class ProgressBarComponent implements OnInit, OnChanges { - @Input() - currentValue = 0; - - @Input() - min = 0; - - @Input() - max = 100; - - inProgress = false; - - ngOnInit() {} - - ngOnChanges(changes: SimpleChanges) { - if (!this.inProgress && changes.currentValue.currentValue > this.min) { - this.inProgress = true; - } - if (this.inProgress && changes.currentValue.currentValue >= this.max) { - this.inProgress = false; - } - } -} diff --git a/web/src/app/shared/components/progress-spinner/progress-spinner.component.html b/web/src/app/shared/components/progress-spinner/progress-spinner.component.html new file mode 100644 index 000000000..2212d0bd0 --- /dev/null +++ b/web/src/app/shared/components/progress-spinner/progress-spinner.component.html @@ -0,0 +1,7 @@ +
+
+ +
+

Uploading file

+
+
diff --git a/web/src/app/shared/components/progress-spinner/progress-spinner.component.scss b/web/src/app/shared/components/progress-spinner/progress-spinner.component.scss new file mode 100644 index 000000000..e2f5fce41 --- /dev/null +++ b/web/src/app/shared/components/progress-spinner/progress-spinner.component.scss @@ -0,0 +1,30 @@ +@import 'src/theme/_colors.scss'; + +.progress-spinner-component { + z-index: 3000; + width: 170px; + height: 160px; + position: absolute; + left: 50%; + top: 33%; + margin-left: -85px; + > .item { + margin-left: 10px; + } + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.progress-spinner-component__spinner-wrapper { + display: flex; + justify-content: center; +} + +.progress-spinner-component__text { + color: $aquamarine; + text-align: center; +} + +//TODO: Change color of spinner + diff --git a/web/src/app/shared/components/progress-spinner/progress-spinner.component.spec.ts b/web/src/app/shared/components/progress-spinner/progress-spinner.component.spec.ts new file mode 100644 index 000000000..294f4bf25 --- /dev/null +++ b/web/src/app/shared/components/progress-spinner/progress-spinner.component.spec.ts @@ -0,0 +1,26 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProgressSpinnerComponent } from './progress-spinner.component'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; + +describe('ProgressSpinnerComponent', () => { + let component: ProgressSpinnerComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ProgressSpinnerComponent], + imports: [MatProgressSpinnerModule] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ProgressSpinnerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create component', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/shared/components/progress-spinner/progress-spinner.component.ts b/web/src/app/shared/components/progress-spinner/progress-spinner.component.ts new file mode 100644 index 000000000..25cfde517 --- /dev/null +++ b/web/src/app/shared/components/progress-spinner/progress-spinner.component.ts @@ -0,0 +1,11 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'taskana-shared-progress-spinner', + templateUrl: './progress-spinner.component.html', + styleUrls: ['./progress-spinner.component.scss'] +}) +export class ProgressSpinnerComponent { + @Input() + currentValue = 0; +} diff --git a/web/src/app/shared/shared.module.ts b/web/src/app/shared/shared.module.ts index 62c61cb2d..1617ac884 100644 --- a/web/src/app/shared/shared.module.ts +++ b/web/src/app/shared/shared.module.ts @@ -29,7 +29,7 @@ import { MatRadioModule } from '@angular/material/radio'; import { SortComponent } from './components/sort/sort.component'; import { PaginationComponent } from './components/pagination/pagination.component'; import { NumberPickerComponent } from './components/number-picker/number-picker.component'; -import { ProgressBarComponent } from './components/progress-bar/progress-bar.component'; +import { ProgressSpinnerComponent } from './components/progress-spinner/progress-spinner.component'; import { DatePickerComponent } from './components/date-picker/date-picker.component'; import { DropdownComponent } from './components/dropdown/dropdown.component'; @@ -60,6 +60,7 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatSelectModule } from '@angular/material/select'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; const MODULES = [ CommonModule, @@ -97,7 +98,7 @@ const DECLARATIONS = [ FieldErrorDisplayComponent, PaginationComponent, NumberPickerComponent, - ProgressBarComponent, + ProgressSpinnerComponent, DatePickerComponent, DropdownComponent, ToastComponent, @@ -116,7 +117,8 @@ const DECLARATIONS = [ MatTooltipModule, MatPaginatorModule, MatSelectModule, - ReactiveFormsModule + ReactiveFormsModule, + MatProgressSpinnerModule ], exports: DECLARATIONS, providers: [ diff --git a/web/src/app/workplace/components/task/task.component.html b/web/src/app/workplace/components/task/task.component.html index 5bdec6476..7ee8c0e2f 100644 --- a/web/src/app/workplace/components/task/task.component.html +++ b/web/src/app/workplace/components/task/task.component.html @@ -1,4 +1,3 @@ -
diff --git a/web/src/app/workplace/components/task/task.component.ts b/web/src/app/workplace/components/task/task.component.ts index e3a218a8d..9696c6de1 100644 --- a/web/src/app/workplace/components/task/task.component.ts +++ b/web/src/app/workplace/components/task/task.component.ts @@ -8,6 +8,7 @@ import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.ser import { Subscription } from 'rxjs'; import { ClassificationsService } from 'app/shared/services/classifications/classifications.service'; import { take } from 'rxjs/operators'; +import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service'; @Component({ selector: 'taskana-task', @@ -16,7 +17,6 @@ import { take } from 'rxjs/operators'; }) export class TaskComponent implements OnInit, OnDestroy { routeSubscription: Subscription; - requestInProgress = false; regex = /\${(.*?)}/g; address = 'https://bing.com/'; @@ -29,6 +29,7 @@ export class TaskComponent implements OnInit, OnDestroy { private taskService: TaskService, private workbasketService: WorkbasketService, private classificationService: ClassificationsService, + private requestInProgressService: RequestInProgressService, private route: ActivatedRoute, private router: Router, private sanitizer: DomSanitizer @@ -39,20 +40,20 @@ export class TaskComponent implements OnInit, OnDestroy { const { id } = params; this.getTask(id); - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); this.taskService .claimTask(id) .pipe(take(1)) .subscribe((task) => { this.task = task; this.taskService.publishUpdatedTask(task); - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); }); }); } async getTask(id: string) { - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); this.task = await this.taskService.getTask(id).toPromise(); this.taskService.selectTask(this.task); const classification = await this.classificationService @@ -61,13 +62,13 @@ export class TaskComponent implements OnInit, OnDestroy { this.address = this.extractUrl(classification.applicationEntryPoint) || `${this.address}/?q=${this.task.name}`; this.link = this.sanitizer.bypassSecurityTrustResourceUrl(this.address); this.getWorkbaskets(); - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); } getWorkbaskets() { - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); this.workbasketService.getAllWorkBaskets().subscribe((workbaskets) => { - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); this.workbaskets = workbaskets.workbaskets; const index = this.workbaskets.findIndex((workbasket) => workbasket.name === this.task.workbasketSummary.name); @@ -78,18 +79,18 @@ export class TaskComponent implements OnInit, OnDestroy { } transferTask(workbasket: Workbasket) { - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); this.taskService.transferTask(this.task.taskId, workbasket.workbasketId).subscribe((task) => { - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); this.task = task; }); this.navigateBack(); } completeTask() { - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); this.taskService.completeTask(this.task.taskId).subscribe((task) => { - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); this.task = task; this.taskService.publishUpdatedTask(task); this.navigateBack(); @@ -97,14 +98,14 @@ export class TaskComponent implements OnInit, OnDestroy { } cancelClaimTask() { - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); this.taskService .cancelClaimTask(this.task.taskId) .pipe(take(1)) .subscribe((task) => { this.task = task; this.taskService.publishUpdatedTask(task); - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); }); this.navigateBack(); } diff --git a/web/src/app/workplace/components/taskdetails/taskdetails.component.html b/web/src/app/workplace/components/taskdetails/taskdetails.component.html index 29dfead20..dff1b1835 100644 --- a/web/src/app/workplace/components/taskdetails/taskdetails.component.html +++ b/web/src/app/workplace/components/taskdetails/taskdetails.component.html @@ -1,4 +1,3 @@ -
diff --git a/web/src/app/workplace/components/taskdetails/taskdetails.component.ts b/web/src/app/workplace/components/taskdetails/taskdetails.component.ts index b812292b7..d0ecf4e19 100644 --- a/web/src/app/workplace/components/taskdetails/taskdetails.component.ts +++ b/web/src/app/workplace/components/taskdetails/taskdetails.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; import { TaskService } from 'app/workplace/services/task.service'; @@ -12,6 +12,7 @@ import { WorkplaceService } from 'app/workplace/services/workplace.service'; import { MasterAndDetailService } from 'app/shared/services/master-and-detail/master-and-detail.service'; import { NOTIFICATION_TYPES } from '../../../shared/models/notifications'; import { NotificationService } from '../../../shared/services/notifications/notification.service'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'taskana-task-details', @@ -26,6 +27,7 @@ export class TaskdetailsComponent implements OnInit, OnDestroy { currentWorkbasket: Workbasket; currentId: string; showDetail = false; + destroy$ = new Subject(); private routeSubscription: Subscription; private workbasketSubscription: Subscription; @@ -47,6 +49,7 @@ export class TaskdetailsComponent implements OnInit, OnDestroy { this.workbasketSubscription = this.workplaceService.getSelectedWorkbasket().subscribe((workbasket) => { this.currentWorkbasket = workbasket; }); + this.routeSubscription = this.route.params.subscribe((params) => { this.currentId = params.id; // redirect if user enters through a deep-link @@ -55,9 +58,17 @@ export class TaskdetailsComponent implements OnInit, OnDestroy { } this.getTask(); }); + this.masterAndDetailSubscription = this.masterAndDetailService.getShowDetail().subscribe((showDetail) => { this.showDetail = showDetail; }); + + this.requestInProgressService + .getRequestInProgress() + .pipe(takeUntil(this.destroy$)) + .subscribe((value) => { + this.requestInProgress = value; + }); } resetTask(): void { @@ -69,14 +80,14 @@ export class TaskdetailsComponent implements OnInit, OnDestroy { } getTask(): void { - this.requestInProgress = true; + this.requestInProgressService.setRequestInProgress(true); if (this.currentId === 'new-task') { - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); this.task = new Task('', new ObjectReference(), this.currentWorkbasket); } else { this.taskService.getTask(this.currentId).subscribe( (task) => { - this.requestInProgress = false; + this.requestInProgressService.setRequestInProgress(false); this.task = task; this.cloneTask(); this.taskService.selectTask(task); @@ -139,6 +150,9 @@ export class TaskdetailsComponent implements OnInit, OnDestroy { if (this.deleteTaskSubscription) { this.deleteTaskSubscription.unsubscribe(); } + + this.destroy$.next(); + this.destroy$.complete(); } private onSave() {