83707: Add spinners to in progress buttons

This commit is contained in:
Yana De Pauw
2021-10-12 09:50:56 +02:00
parent e434bb8952
commit 72d1235b2c
5 changed files with 45 additions and 7 deletions

View File

@@ -18,21 +18,36 @@
<span>{{contentSource?.lastHarvested ? contentSource?.lastHarvested : 'collection.source.controls.harvest.no-information'|translate }}</span> <span>{{contentSource?.lastHarvested ? contentSource?.lastHarvested : 'collection.source.controls.harvest.no-information'|translate }}</span>
</div> </div>
<button class=" btn btn-secondary" <button *ngIf="!(testConfigRunning$ |async)" class="btn btn-secondary"
[disabled]="!(isEnabled)" [disabled]="!(isEnabled)"
(click)="testConfiguration(contentSource)"> (click)="testConfiguration(contentSource)">
<span class="d-none d-sm-inline">{{'collection.source.controls.test.submit' | translate}}</span> <span>{{'collection.source.controls.test.submit' | translate}}</span>
</button> </button>
<button class="btn btn-primary" <button *ngIf="(testConfigRunning$ |async)" class="btn btn-secondary"
[disabled]="true">
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span>
<span>{{'collection.source.controls.test.running' | translate}}</span>
</button>
<button *ngIf="!(importRunning$ |async)" class="btn btn-primary"
[disabled]="!(isEnabled)" [disabled]="!(isEnabled)"
(click)="importNow()"> (click)="importNow()">
<span class="d-none d-sm-inline">{{'collection.source.controls.import.submit' | translate}}</span> <span class="d-none d-sm-inline">{{'collection.source.controls.import.submit' | translate}}</span>
</button> </button>
<button class="btn btn-primary" <button *ngIf="(importRunning$ |async)" class="btn btn-primary"
[disabled]="true">
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span>
<span class="d-none d-sm-inline">{{'collection.source.controls.import.running' | translate}}</span>
</button>
<button *ngIf="!(reImportRunning$ |async)" class="btn btn-primary"
[disabled]="!(isEnabled)" [disabled]="!(isEnabled)"
(click)="resetAndReimport()"> (click)="resetAndReimport()">
<span class="d-none d-sm-inline">&nbsp;{{'collection.source.controls.reset.submit' | translate}}</span> <span class="d-none d-sm-inline">&nbsp;{{'collection.source.controls.reset.submit' | translate}}</span>
</button> </button>
<button *ngIf="(reImportRunning$ |async)" class="btn btn-primary"
[disabled]="true">
<span class="spinner-border spinner-border-sm spinner-button" role="status" aria-hidden="true"></span>
<span class="d-none d-sm-inline">&nbsp;{{'collection.source.controls.reset.running' | translate}}</span>
</button>
</div> </div>

View File

@@ -0,0 +1,3 @@
.spinner-button {
margin-bottom: calc((var(--bs-line-height-base) * 1rem - var(--bs-font-size-base)) / 2);
}

View File

@@ -22,12 +22,14 @@ import { TranslateService } from '@ngx-translate/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { ContentSourceSetSerializer } from '../../../../core/shared/content-source-set-serializer'; import { ContentSourceSetSerializer } from '../../../../core/shared/content-source-set-serializer';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
/** /**
* Component that contains the controls to run, reset and test the harvest * Component that contains the controls to run, reset and test the harvest
*/ */
@Component({ @Component({
selector: 'ds-collection-source-controls', selector: 'ds-collection-source-controls',
styleUrls: ['./collection-source-controls.component.scss'],
templateUrl: './collection-source-controls.component.html', templateUrl: './collection-source-controls.component.html',
}) })
export class CollectionSourceControlsComponent implements OnDestroy { export class CollectionSourceControlsComponent implements OnDestroy {
@@ -50,6 +52,10 @@ export class CollectionSourceControlsComponent implements OnDestroy {
contentSource$: Observable<ContentSource>; contentSource$: Observable<ContentSource>;
private subs: Subscription[] = []; private subs: Subscription[] = [];
testConfigRunning$ = new BehaviorSubject(false);
importRunning$ = new BehaviorSubject(false);
reImportRunning$ = new BehaviorSubject(false);
constructor(private scriptDataService: ScriptDataService, constructor(private scriptDataService: ScriptDataService,
private processDataService: ProcessDataService, private processDataService: ProcessDataService,
private requestService: RequestService, private requestService: RequestService,
@@ -75,6 +81,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
* @param contentSource - The content source to be tested * @param contentSource - The content source to be tested
*/ */
testConfiguration(contentSource) { testConfiguration(contentSource) {
this.testConfigRunning$.next(true);
this.subs.push(this.scriptDataService.invoke('harvest', [ this.subs.push(this.scriptDataService.invoke('harvest', [
{name: '-g', value: null}, {name: '-g', value: null},
{name: '-a', value: contentSource.oaiSource}, {name: '-a', value: contentSource.oaiSource},
@@ -85,6 +92,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
if (rd.hasFailed) { if (rd.hasFailed) {
// show a notification when the script invocation fails // show a notification when the script invocation fails
this.notificationsService.error(this.translateService.get('collection.source.controls.test.submit.error')); this.notificationsService.error(this.translateService.get('collection.source.controls.test.submit.error'));
this.testConfigRunning$.next(false);
} }
}), }),
// filter out responses that aren't successful since the pinging of the process only needs to happen when the invocation was successful. // filter out responses that aren't successful since the pinging of the process only needs to happen when the invocation was successful.
@@ -104,6 +112,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
} }
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.FAILED].toString()) { if (process.processStatus.toString() === ProcessStatus[ProcessStatus.FAILED].toString()) {
this.notificationsService.error(this.translateService.get('collection.source.controls.test.failed')); this.notificationsService.error(this.translateService.get('collection.source.controls.test.failed'));
this.testConfigRunning$.next(false);
} }
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) { if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) {
this.bitstreamService.findByHref(process._links.output.href).pipe(getFirstSucceededRemoteDataPayload()).subscribe((bitstream) => { this.bitstreamService.findByHref(process._links.output.href).pipe(getFirstSucceededRemoteDataPayload()).subscribe((bitstream) => {
@@ -114,6 +123,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
this.notificationsService.info(this.translateService.get('collection.source.controls.test.completed'), output); this.notificationsService.info(this.translateService.get('collection.source.controls.test.completed'), output);
}); });
}); });
this.testConfigRunning$.next(false);
} }
} }
)); ));
@@ -123,6 +133,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
* Start the harvest for the current collection * Start the harvest for the current collection
*/ */
importNow() { importNow() {
this.importRunning$.next(true);
this.subs.push(this.scriptDataService.invoke('harvest', [ this.subs.push(this.scriptDataService.invoke('harvest', [
{name: '-r', value: null}, {name: '-r', value: null},
{name: '-c', value: this.collection.uuid}, {name: '-c', value: this.collection.uuid},
@@ -132,6 +143,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
tap((rd) => { tap((rd) => {
if (rd.hasFailed) { if (rd.hasFailed) {
this.notificationsService.error(this.translateService.get('collection.source.controls.import.submit.error')); this.notificationsService.error(this.translateService.get('collection.source.controls.import.submit.error'));
this.importRunning$.next(false);
} else { } else {
this.notificationsService.success(this.translateService.get('collection.source.controls.import.submit.success')); this.notificationsService.success(this.translateService.get('collection.source.controls.import.submit.success'));
} }
@@ -153,10 +165,12 @@ export class CollectionSourceControlsComponent implements OnDestroy {
} }
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.FAILED].toString()) { if (process.processStatus.toString() === ProcessStatus[ProcessStatus.FAILED].toString()) {
this.notificationsService.error(this.translateService.get('collection.source.controls.import.failed')); this.notificationsService.error(this.translateService.get('collection.source.controls.import.failed'));
this.importRunning$.next(false);
} }
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) { if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) {
this.notificationsService.success(this.translateService.get('collection.source.controls.import.completed')); this.notificationsService.success(this.translateService.get('collection.source.controls.import.completed'));
this.requestService.setStaleByHrefSubstring(this.collection._links.self.href); this.requestService.setStaleByHrefSubstring(this.collection._links.self.href);
this.importRunning$.next(false);
} }
} }
)); ));
@@ -166,6 +180,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
* Reset and reimport the current collection * Reset and reimport the current collection
*/ */
resetAndReimport() { resetAndReimport() {
this.reImportRunning$.next(true);
this.subs.push(this.scriptDataService.invoke('harvest', [ this.subs.push(this.scriptDataService.invoke('harvest', [
{name: '-o', value: null}, {name: '-o', value: null},
{name: '-c', value: this.collection.uuid}, {name: '-c', value: this.collection.uuid},
@@ -175,6 +190,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
tap((rd) => { tap((rd) => {
if (rd.hasFailed) { if (rd.hasFailed) {
this.notificationsService.error(this.translateService.get('collection.source.controls.reset.submit.error')); this.notificationsService.error(this.translateService.get('collection.source.controls.reset.submit.error'));
this.reImportRunning$.next(false);
} else { } else {
this.notificationsService.success(this.translateService.get('collection.source.controls.reset.submit.success')); this.notificationsService.success(this.translateService.get('collection.source.controls.reset.submit.success'));
} }
@@ -196,10 +212,12 @@ export class CollectionSourceControlsComponent implements OnDestroy {
} }
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.FAILED].toString()) { if (process.processStatus.toString() === ProcessStatus[ProcessStatus.FAILED].toString()) {
this.notificationsService.error(this.translateService.get('collection.source.controls.reset.failed')); this.notificationsService.error(this.translateService.get('collection.source.controls.reset.failed'));
this.reImportRunning$.next(false);
} }
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) { if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) {
this.notificationsService.success(this.translateService.get('collection.source.controls.reset.completed')); this.notificationsService.success(this.translateService.get('collection.source.controls.reset.completed'));
this.requestService.setStaleByHrefSubstring(this.collection._links.self.href); this.requestService.setStaleByHrefSubstring(this.collection._links.self.href);
this.reImportRunning$.next(false);
} }
} }
)); ));

View File

@@ -1,4 +1,4 @@
import { autoserializeAs, deserializeAs, deserialize, serializeAs } from 'cerialize'; import { autoserializeAs, deserialize, deserializeAs, serializeAs } from 'cerialize';
import { HALLink } from './hal-link.model'; import { HALLink } from './hal-link.model';
import { MetadataConfig } from './metadata-config.model'; import { MetadataConfig } from './metadata-config.model';
import { CacheableObject } from '../cache/object-cache.reducer'; import { CacheableObject } from '../cache/object-cache.reducer';
@@ -6,7 +6,6 @@ import { typedObject } from '../cache/builders/build-decorators';
import { CONTENT_SOURCE } from './content-source.resource-type'; import { CONTENT_SOURCE } from './content-source.resource-type';
import { excludeFromEquals } from '../utilities/equals.decorators'; import { excludeFromEquals } from '../utilities/equals.decorators';
import { ResourceType } from './resource-type'; import { ResourceType } from './resource-type';
import { IDToUUIDSerializer } from '../cache/id-to-uuid-serializer';
import { ContentSourceSetSerializer } from './content-source-set-serializer'; import { ContentSourceSetSerializer } from './content-source-set-serializer';
/** /**

View File

@@ -886,9 +886,11 @@
"collection.source.controls.test.failed": "The script to test the settings has failed", "collection.source.controls.test.failed": "The script to test the settings has failed",
"collection.source.controls.test.completed": "The script to test the settings has successfully finished", "collection.source.controls.test.completed": "The script to test the settings has successfully finished",
"collection.source.controls.test.submit": "Test configuration", "collection.source.controls.test.submit": "Test configuration",
"collection.source.controls.test.running": "Testing configuration...",
"collection.source.controls.import.submit.success": "The import has been successfully initiated", "collection.source.controls.import.submit.success": "The import has been successfully initiated",
"collection.source.controls.import.submit.error": "Something went wrong with initiating the import", "collection.source.controls.import.submit.error": "Something went wrong with initiating the import",
"collection.source.controls.import.submit": "Import now", "collection.source.controls.import.submit": "Import now",
"collection.source.controls.import.running": "Importing...",
"collection.source.controls.import.failed": "An error occurred during the import", "collection.source.controls.import.failed": "An error occurred during the import",
"collection.source.controls.import.completed": "The import completed", "collection.source.controls.import.completed": "The import completed",
"collection.source.controls.reset.submit.success": "The reset and reimport has been successfully initiated", "collection.source.controls.reset.submit.success": "The reset and reimport has been successfully initiated",
@@ -896,6 +898,7 @@
"collection.source.controls.reset.failed": "An error occurred during the reset and reimport", "collection.source.controls.reset.failed": "An error occurred during the reset and reimport",
"collection.source.controls.reset.completed": "The reset and reimport completed", "collection.source.controls.reset.completed": "The reset and reimport completed",
"collection.source.controls.reset.submit": "Reset and reimport", "collection.source.controls.reset.submit": "Reset and reimport",
"collection.source.controls.reset.running": "Resetting and reimporting...",
"collection.source.controls.harvest.status": "Harvest status:", "collection.source.controls.harvest.status": "Harvest status:",
"collection.source.controls.harvest.start": "Harvest start time:", "collection.source.controls.harvest.start": "Harvest start time:",
"collection.source.controls.harvest.last": "Last time harvested:", "collection.source.controls.harvest.last": "Last time harvested:",