67611: Import new external entity and add to selection + message changes/refactoring

This commit is contained in:
Kristof De Langhe
2019-12-06 16:52:30 +01:00
parent 3a7fa8dbaf
commit 1402dd9129
8 changed files with 116 additions and 40 deletions

View File

@@ -1546,7 +1546,19 @@
"submission.sections.describe.relationship-lookup.external-source.added": "Successfully added local entry to the selection", "submission.sections.describe.relationship-lookup.external-source.added": "Successfully added local entry to the selection",
"submission.sections.describe.relationship-lookup.external-source.import-button-title": "Import remote entry", "submission.sections.describe.relationship-lookup.external-source.import-button-title.Author": "Import remote author",
"submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal": "Import remote journal",
"submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Issue": "Import remote journal issue",
"submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Volume": "Import remote journal volume",
"submission.sections.describe.relationship-lookup.external-source.import-modal.Author.title": "Import Remote Author",
"submission.sections.describe.relationship-lookup.external-source.import-modal.Author.added.local-entity": "Successfully added local author to the selection",
"submission.sections.describe.relationship-lookup.external-source.import-modal.Author.added.new-entity": "Successfully imported and added external author to the selection",
"submission.sections.describe.relationship-lookup.external-source.import-modal.authority": "Authority", "submission.sections.describe.relationship-lookup.external-source.import-modal.authority": "Authority",
@@ -1570,9 +1582,25 @@
"submission.sections.describe.relationship-lookup.external-source.import-modal.import": "Import", "submission.sections.describe.relationship-lookup.external-source.import-modal.import": "Import",
"submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Select a local match:", "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.title": "Import Remote Journal",
"submission.sections.describe.relationship-lookup.external-source.import-modal.title": "Import Remote Entry", "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.added.local-entity": "Successfully added local journal to the selection",
"submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.added.new-entity": "Successfully imported and added external journal to the selection",
"submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.title": "Import Remote Journal Issue",
"submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.added.local-entity": "Successfully added local journal issue to the selection",
"submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.added.new-entity": "Successfully imported and added external journal issue to the selection",
"submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.title": "Import Remote Journal Volume",
"submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.added.local-entity": "Successfully added local journal volume to the selection",
"submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.added.new-entity": "Successfully imported and added external journal volume to the selection",
"submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Select a local match:",
"submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deselect all", "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deselect all",

View File

@@ -254,7 +254,7 @@ export class ItemDataService extends DataService<Item> {
* @param externalSourceEntry * @param externalSourceEntry
* @param collectionId * @param collectionId
*/ */
public importExternalSourceEntry(externalSourceEntry: ExternalSourceEntry, collectionId: string): Observable<RestResponse> { public importExternalSourceEntry(externalSourceEntry: ExternalSourceEntry, collectionId: string): Observable<RemoteData<Item>> {
const options: HttpOptions = Object.create({}); const options: HttpOptions = Object.create({});
let headers = new HttpHeaders(); let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'text/uri-list'); headers = headers.append('Content-Type', 'text/uri-list');
@@ -273,7 +273,13 @@ export class ItemDataService extends DataService<Item> {
return this.requestService.getByUUID(requestId).pipe( return this.requestService.getByUUID(requestId).pipe(
find((request: RequestEntry) => request.completed), find((request: RequestEntry) => request.completed),
map((request: RequestEntry) => request.response) getResponseFromEntry(),
map((response: any) => {
if (isNotEmpty(response.resourceSelfLinks)) {
return response.resourceSelfLinks[0];
}
}),
switchMap((selfLink: string) => this.findByHref(selfLink))
); );
} }

View File

@@ -15,6 +15,7 @@ import { getAllSucceededRemoteData, getRemoteDataPayload } from '../shared/opera
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ExternalSource } from '../shared/external-source.model'; import { ExternalSource } from '../shared/external-source.model';
import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { ExternalSourceEntry } from '../shared/external-source-entry.model';
import { RequestService } from './request.service';
/** /**
* A service for retrieving local and external entries information during a relation lookup * A service for retrieving local and external entries information during a relation lookup
@@ -35,7 +36,8 @@ export class LookupRelationService {
}); });
constructor(protected externalSourceService: ExternalSourceService, constructor(protected externalSourceService: ExternalSourceService,
protected searchService: SearchService) { protected searchService: SearchService,
protected requestService: RequestService) {
} }
/** /**
@@ -91,4 +93,11 @@ export class LookupRelationService {
startWith(0) startWith(0)
); );
} }
/**
* Remove cached requests from local results
*/
removeLocalResultsCache() {
this.searchService.getEndpoint().subscribe((href) => this.requestService.removeByHrefSubstring(href));
}
} }

View File

@@ -25,14 +25,14 @@
[title]="'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + source.id | translate : {count: (totalExternal$ | async)[idx]}"> [title]="'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + source.id | translate : {count: (totalExternal$ | async)[idx]}">
<ng-template ngbTabContent> <ng-template ngbTabContent>
<ds-dynamic-lookup-relation-external-source-tab <ds-dynamic-lookup-relation-external-source-tab
[label]="label"
[listId]="listId" [listId]="listId"
[item]="item" [item]="item"
[collection]="collection" [collection]="collection"
[relationship]="relationshipOptions" [relationship]="relationshipOptions"
[context]="context" [context]="context"
[externalSource]="source" [externalSource]="source"
(selectObject)="select($event)" (importedObject)="imported($event)"
(deselectObject)="deselect($event)"
class="d-block pt-3"> class="d-block pt-3">
</ds-dynamic-lookup-relation-external-source-tab> </ds-dynamic-lookup-relation-external-source-tab>
</ng-template> </ng-template>

View File

@@ -179,6 +179,15 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
) )
} }
/**
* Called when an external object has been imported, resets the total values and adds the object to the selected list
* @param object
*/
imported(object) {
this.setTotals();
this.select(object);
}
/** /**
* Calculate and set the total entries available for each tab * Calculate and set the total entries available for each tab
*/ */

View File

@@ -22,8 +22,6 @@ import { hasValue } from '../../../../../empty.util';
import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service'; import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service';
import { Item } from '../../../../../../core/shared/item.model'; import { Item } from '../../../../../../core/shared/item.model';
import { Collection } from '../../../../../../core/shared/collection.model'; import { Collection } from '../../../../../../core/shared/collection.model';
import { NotificationsService } from '../../../../../notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
@Component({ @Component({
selector: 'ds-dynamic-lookup-relation-external-source-tab', selector: 'ds-dynamic-lookup-relation-external-source-tab',
@@ -45,6 +43,11 @@ import { TranslateService } from '@ngx-translate/core';
* Shows a list of entries matching the current search query with the option to import them into the repository * Shows a list of entries matching the current search query with the option to import them into the repository
*/ */
export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit, OnDestroy { export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit, OnDestroy {
/**
* The label to use for all messages (added to the end of relevant i18n keys)
*/
@Input() label: string;
/** /**
* The ID of the list of selected entries * The ID of the list of selected entries
*/ */
@@ -71,14 +74,9 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
@Input() context: Context; @Input() context: Context;
/** /**
* Emit an event when an object has been deselected * Emit an event when an object has been imported (or selected from similar local entries)
*/ */
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>(); @Output() importedObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
/**
* Emit an event when an object has been selected (or imported)
*/
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
/** /**
* The initial pagination options * The initial pagination options
@@ -101,9 +99,7 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
/** /**
* Config to use for the import buttons * Config to use for the import buttons
*/ */
importConfig = { importConfig;
buttonLabel: 'submission.sections.describe.relationship-lookup.external-source.import-button-title'
};
/** /**
* The modal for importing the entry * The modal for importing the entry
@@ -119,9 +115,7 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
public searchConfigService: SearchConfigurationService, public searchConfigService: SearchConfigurationService,
private externalSourceService: ExternalSourceService, private externalSourceService: ExternalSourceService,
private modalService: NgbModal, private modalService: NgbModal,
private selectableListService: SelectableListService, private selectableListService: SelectableListService) {
private notificationsService: NotificationsService,
private translateService: TranslateService) {
} }
/** /**
@@ -131,7 +125,10 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
this.entriesRD$ = this.searchConfigService.paginatedSearchOptions.pipe( this.entriesRD$ = this.searchConfigService.paginatedSearchOptions.pipe(
switchMap((searchOptions: PaginatedSearchOptions) => switchMap((searchOptions: PaginatedSearchOptions) =>
this.externalSourceService.getExternalSourceEntries(this.externalSource.id, searchOptions).pipe(startWith(undefined))) this.externalSourceService.getExternalSourceEntries(this.externalSource.id, searchOptions).pipe(startWith(undefined)))
) );
this.importConfig = {
buttonLabel: 'submission.sections.describe.relationship-lookup.external-source.import-button-title.' + this.label
};
} }
/** /**
@@ -148,10 +145,10 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
modalComp.item = this.item; modalComp.item = this.item;
modalComp.collection = this.collection; modalComp.collection = this.collection;
modalComp.relationship = this.relationship; modalComp.relationship = this.relationship;
modalComp.label = this.label;
this.importObjectSub = modalComp.importedObject.subscribe((object) => { this.importObjectSub = modalComp.importedObject.subscribe((object) => {
this.selectableListService.selectSingle(this.listId, object); this.selectableListService.selectSingle(this.listId, object);
this.notificationsService.success(this.translateService.get('submission.sections.describe.relationship-lookup.external-source.added')); this.importedObject.emit(object);
this.selectObject.emit(object);
}); });
} }

View File

@@ -1,26 +1,26 @@
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title" id="modal-title">{{ ('submission.sections.describe.relationship-lookup.external-source.import-modal.title') | translate }}</h4> <h4 class="modal-title" id="modal-title">{{ (labelPrefix + label + '.title') | translate }}</h4>
<button type="button" class="close" aria-label="Close button" aria-describedby="modal-title" <button type="button" class="close" aria-label="Close button" aria-describedby="modal-title"
(click)="modal.dismiss()"> (click)="modal.dismiss()">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<h4>{{ ('submission.sections.describe.relationship-lookup.external-source.import-modal.head.' + externalSourceEntry.externalSource | translate) }}</h4> <h4>{{ (labelPrefix + 'head.' + externalSourceEntry.externalSource | translate) }}</h4>
<div id="external-source-entry-information" class="mb-3"> <div id="external-source-entry-information" class="mb-3">
<div><span>{{externalSourceEntry.display}}</span></div> <div><span>{{externalSourceEntry.display}}</span></div>
<div *ngIf="uri"><a href="{{uri.value}}">{{uri.value}}</a></div> <div *ngIf="uri"><a href="{{uri.value}}">{{uri.value}}</a></div>
</div> </div>
<h4>{{ ('submission.sections.describe.relationship-lookup.external-source.import-modal.select' | translate) }}</h4> <h4>{{ (labelPrefix + 'select' | translate) }}</h4>
<div id="external-source-entry-collection" class="mb-3"> <div id="external-source-entry-collection" class="mb-3">
<div class="form-group"> <div class="form-group">
<label for="collection">{{ ('submission.sections.describe.relationship-lookup.external-source.import-modal.collection' | translate) }}</label> <label for="collection">{{ (labelPrefix + 'collection' | translate) }}</label>
<input type="text" class="form-control" id="collection" placeholder="Enter collection ID" [value]="collectionId"> <input type="text" class="form-control" id="collection" placeholder="Enter collection ID" [(ngModel)]="collectionId">
</div> </div>
</div> </div>
<div id="external-source-entry-entities" class="mb-3"> <div id="external-source-entry-entities" class="mb-3">
<h5 class="font-weight-bold">{{ ('submission.sections.describe.relationship-lookup.external-source.import-modal.entities' | translate) }}</h5> <h5 class="font-weight-bold">{{ (labelPrefix + 'entities' | translate) }}</h5>
<ds-search-results *ngIf="(localEntitiesRD$ | async)?.payload?.page?.length > 0" <ds-search-results *ngIf="(localEntitiesRD$ | async)?.payload?.page?.length > 0"
[searchResults]="(localEntitiesRD$ | async)" [searchResults]="(localEntitiesRD$ | async)"
@@ -37,23 +37,23 @@
</ds-search-results> </ds-search-results>
<div class="ml-4"> <div class="ml-4">
<input class="form-check-input" type="radio" name="new-entity" id="new-entity" value="new-entity" (click)="selectNewEntity()" [checked]="selectedImportType === importType.NewEntity" /> <input class="form-check-input" type="radio" name="new-entity" id="new-entity" value="new-entity" (click)="selectNewEntity()" [checked]="selectedImportType === importType.NewEntity" />
<label class="form-check-label" for="new-entity">{{ ('submission.sections.describe.relationship-lookup.external-source.import-modal.entities.new' | translate) }}</label> <label class="form-check-label" for="new-entity">{{ (labelPrefix + 'entities.new' | translate) }}</label>
</div> </div>
</div> </div>
<div id="external-source-entry-authority"> <div id="external-source-entry-authority">
<h5 class="font-weight-bold">{{ ('submission.sections.describe.relationship-lookup.external-source.import-modal.authority' | translate) }}</h5> <h5 class="font-weight-bold">{{ (labelPrefix + 'authority' | translate) }}</h5>
<div class="ml-4"> <div class="ml-4">
<input class="form-check-input" type="radio" name="new-authority" id="new-authority" value="new-authority" (click)="selectNewAuthority()" [checked]="selectedImportType === importType.NewAuthority" /> <input class="form-check-input" type="radio" name="new-authority" id="new-authority" value="new-authority" (click)="selectNewAuthority()" [checked]="selectedImportType === importType.NewAuthority" />
<label class="form-check-label" for="new-authority">{{ ('submission.sections.describe.relationship-lookup.external-source.import-modal.authority.new' | translate) }}</label> <label class="form-check-label" for="new-authority">{{ (labelPrefix + 'authority.new' | translate) }}</label>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<div> <div>
<button type="button" class="btn btn-outline-secondary" (click)="close()">{{ ('submission.sections.describe.relationship-lookup.external-source.import-modal.cancel' | translate) }}</button> <button type="button" class="btn btn-outline-secondary" (click)="close()">{{ (labelPrefix + 'cancel' | translate) }}</button>
</div> </div>
<div> <div>
<button type="button" class="btn btn-primary" [disabled]="selectedImportType === importType.None" (click)="import()">{{ ('submission.sections.describe.relationship-lookup.external-source.import-modal.import' | translate) }}</button> <button type="button" class="btn btn-primary" [disabled]="selectedImportType === importType.None" (click)="import()">{{ (labelPrefix + 'import' | translate) }}</button>
</div> </div>
</div> </div>

View File

@@ -18,6 +18,11 @@ import { ListableObject } from '../../../../../../object-collection/shared/lista
import { Collection } from '../../../../../../../core/shared/collection.model'; import { Collection } from '../../../../../../../core/shared/collection.model';
import { ItemDataService } from '../../../../../../../core/data/item-data.service'; import { ItemDataService } from '../../../../../../../core/data/item-data.service';
import { PaginationComponentOptions } from '../../../../../../pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../../../../../pagination/pagination-component-options.model';
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../../../../core/shared/operators';
import { take } from 'rxjs/operators';
import { ItemSearchResult } from '../../../../../../object-collection/shared/item-search-result.model';
import { NotificationsService } from '../../../../../../notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
/** /**
* The possible types of import for the external entry * The possible types of import for the external entry
@@ -42,6 +47,16 @@ export enum ImportType {
* The other option is to import the external entry as a new entity or authority into the repository. * The other option is to import the external entry as a new entity or authority into the repository.
*/ */
export class ExternalSourceEntryImportModalComponent implements OnInit { export class ExternalSourceEntryImportModalComponent implements OnInit {
/**
* The prefix for every i18n key within this modal
*/
labelPrefix = 'submission.sections.describe.relationship-lookup.external-source.import-modal.';
/**
* The label to use for all messages (added to the end of relevant i18n keys)
*/
label: string;
/** /**
* The external source entry * The external source entry
*/ */
@@ -130,7 +145,9 @@ export class ExternalSourceEntryImportModalComponent implements OnInit {
constructor(public modal: NgbActiveModal, constructor(public modal: NgbActiveModal,
public lookupRelationService: LookupRelationService, public lookupRelationService: LookupRelationService,
private selectService: SelectableListService, private selectService: SelectableListService,
private itemService: ItemDataService) { private itemService: ItemDataService,
private notificationsService: NotificationsService,
private translateService: TranslateService) {
} }
ngOnInit(): void { ngOnInit(): void {
@@ -180,6 +197,7 @@ export class ExternalSourceEntryImportModalComponent implements OnInit {
*/ */
importLocalEntity() { importLocalEntity() {
if (this.selectedEntity !== undefined) { if (this.selectedEntity !== undefined) {
this.notificationsService.success(this.translateService.get(this.labelPrefix + this.label + '.added.local-entity'));
this.importedObject.emit(this.selectedEntity); this.importedObject.emit(this.selectedEntity);
} }
} }
@@ -188,8 +206,17 @@ export class ExternalSourceEntryImportModalComponent implements OnInit {
* Create and import a new entity from the external entry * Create and import a new entity from the external entry
*/ */
importNewEntity() { importNewEntity() {
this.itemService.importExternalSourceEntry(this.externalSourceEntry, this.collectionId).subscribe((response) => { this.itemService.importExternalSourceEntry(this.externalSourceEntry, this.collectionId).pipe(
console.log(response); getSucceededRemoteData(),
getRemoteDataPayload(),
take(1)
).subscribe((item: Item) => {
this.lookupRelationService.removeLocalResultsCache();
const searchResult = Object.assign(new ItemSearchResult(), {
indexableObject: item
});
this.notificationsService.success(this.translateService.get(this.labelPrefix + this.label + '.added.new-entity'));
this.importedObject.emit(searchResult);
}); });
} }