Merge remote-tracking branch 'alexandre/fix-create-relationship-not-working-between-same-types_contribute-7.6' into w2p-113560_edit-item-add-relationships-one-by-one

(cherry picked from commit 1c50dbf4c6)
This commit is contained in:
Art Lowel
2024-05-09 13:40:03 +02:00
committed by Alexandre Vryghem
parent b8a4c83353
commit 65a629387a
5 changed files with 94 additions and 133 deletions

View File

@@ -1,5 +1,5 @@
<h2 class="h4"> <h2 class="h4">
{{getRelationshipMessageKey$ | async | translate}} {{relationshipMessageKey$ | async | translate}}
<button class="ml-2 btn btn-success" [disabled]="(hasChanges | async)" (click)="openLookup()"> <button class="ml-2 btn btn-success" [disabled]="(hasChanges | async)" (click)="openLookup()">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.relationships.edit.buttons.add" | translate}}</span> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.relationships.edit.buttons.add" | translate}}</span>

View File

@@ -72,10 +72,10 @@ import {
import { DsDynamicLookupRelationModalComponent } from '../../../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component'; import { DsDynamicLookupRelationModalComponent } from '../../../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component';
import { RelationshipOptions } from '../../../../shared/form/builder/models/relationship-options.model'; import { RelationshipOptions } from '../../../../shared/form/builder/models/relationship-options.model';
import { ThemedLoadingComponent } from '../../../../shared/loading/themed-loading.component'; import { ThemedLoadingComponent } from '../../../../shared/loading/themed-loading.component';
import { ItemSearchResult } from '../../../../shared/object-collection/shared/item-search-result.model';
import { SelectableListService } from '../../../../shared/object-list/selectable-list/selectable-list.service'; import { SelectableListService } from '../../../../shared/object-list/selectable-list/selectable-list.service';
import { PaginationComponent } from '../../../../shared/pagination/pagination.component'; import { PaginationComponent } from '../../../../shared/pagination/pagination.component';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
import { SearchResult } from '../../../../shared/search/models/search-result.model';
import { FollowLinkConfig } from '../../../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../../../shared/utils/follow-link-config.model';
import { ObjectValuesPipe } from '../../../../shared/utils/object-values-pipe'; import { ObjectValuesPipe } from '../../../../shared/utils/object-values-pipe';
import { itemLinksToFollow } from '../../../../shared/utils/relation-query.utils'; import { itemLinksToFollow } from '../../../../shared/utils/relation-query.utils';
@@ -133,7 +133,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
/** /**
* The event emmiter to submit the new information * The event emmiter to submit the new information
*/ */
@Output() submit: EventEmitter<any> = new EventEmitter(); @Output() submitModal: EventEmitter<void> = new EventEmitter();
/** /**
* Observable that emits the left and right item type of {@link relationshipType} simultaneously. * Observable that emits the left and right item type of {@link relationshipType} simultaneously.
@@ -148,7 +148,10 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
private relatedEntityType$: Observable<ItemType>; private relatedEntityType$: Observable<ItemType>;
getRelationshipMessageKey$: Observable<string>; /**
* The translation key for the entity type
*/
relationshipMessageKey$: Observable<string>;
/** /**
* The list ID to save selected entities under * The list ID to save selected entities under
@@ -215,6 +218,29 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
this.fetchThumbnail = this.appConfig.browseBy.showThumbnails; this.fetchThumbnail = this.appConfig.browseBy.showThumbnails;
} }
/**
* Get the i18n message key for this relationship type
*/
public getRelationshipMessageKey(): Observable<string> {
return observableCombineLatest([
this.getLabel(),
this.relatedEntityType$,
]).pipe(
map(([label, relatedEntityType]) => {
if (hasValue(label) && label.indexOf('is') > -1 && label.indexOf('Of') > -1) {
const relationshipLabel = `${label.substring(2, label.indexOf('Of'))}`;
if (relationshipLabel !== relatedEntityType.label) {
return `relationships.is${relationshipLabel}Of.${relatedEntityType.label}`;
} else {
return `relationships.is${relationshipLabel}Of`;
}
} else {
return label;
}
}),
);
}
/** /**
* Get the relevant label for this relationship type * Get the relevant label for this relationship type
*/ */
@@ -264,11 +290,11 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
modalComp.collection = collection; modalComp.collection = collection;
}); });
modalComp.select = (...selectableObjects: SearchResult<Item>[]) => { modalComp.select = (...selectableObjects: ItemSearchResult[]) => {
selectableObjects.forEach((searchResult) => { selectableObjects.forEach((searchResult) => {
const relatedItem: Item = searchResult.indexableObject; const relatedItem: Item = searchResult.indexableObject;
const foundIndex = modalComp.toRemove.findIndex( el => el.uuid === relatedItem.uuid); const foundIndex = modalComp.toRemove.findIndex((itemSearchResult: ItemSearchResult) => itemSearchResult.indexableObject.uuid === relatedItem.uuid);
if (foundIndex !== -1) { if (foundIndex !== -1) {
modalComp.toRemove.splice(foundIndex,1); modalComp.toRemove.splice(foundIndex,1);
@@ -292,7 +318,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
} }
}); });
}; };
modalComp.deselect = (...selectableObjects: SearchResult<Item>[]) => { modalComp.deselect = (...selectableObjects: ItemSearchResult[]) => {
selectableObjects.forEach((searchResult) => { selectableObjects.forEach((searchResult) => {
const relatedItem: Item = searchResult.indexableObject; const relatedItem: Item = searchResult.indexableObject;
@@ -360,7 +386,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
).subscribe({ ).subscribe({
complete: () => { complete: () => {
this.editItemRelationshipsService.submit(this.item, this.url); this.editItemRelationshipsService.submit(this.item, this.url);
this.submit.emit(); this.submitModal.emit();
}, },
}); });
}; };
@@ -403,67 +429,6 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
); );
} }
/**
* Get the existing field updates regarding a relationship with a given item
* @param relatedItem The item for which to get the existing field updates
*/
private getFieldUpdatesForRelatedItem(relatedItem: Item): Observable<RelationshipIdentifiable[]> {
return this.updates$.pipe(
take(1),
map((updates) => Object.values(updates)
.map((update) => update.field as RelationshipIdentifiable)
.filter((field) => field.relationship),
),
mergeMap((identifiables) =>
observableCombineLatest(
identifiables.map((identifiable) => this.getRelatedItem(identifiable.relationship)),
).pipe(
defaultIfEmpty([]),
map((relatedItems) => {
return identifiables.filter( (identifiable, index) => {
return relatedItems[index].uuid === relatedItem.uuid;
});
},
),
),
),
);
}
/**
* Check if the given item is related with the item we are editing relationships
* @param relatedItem The item for which to get the existing field updates
*/
private getIsRelatedItem(relatedItem: Item): Observable<boolean> {
return this.currentItemIsLeftItem$.pipe(
take(1),
map( isLeft => {
if (isLeft) {
const listOfRelatedItems = this.item.allMetadataValues( 'relation.' + this.relationshipType.leftwardType );
return !!listOfRelatedItems.find( (uuid) => uuid === relatedItem.uuid );
} else {
const listOfRelatedItems = this.item.allMetadataValues( 'relation.' + this.relationshipType.rightwardType );
return !!listOfRelatedItems.find( (uuid) => uuid === relatedItem.uuid );
}
}),
);
}
/**
* Get the related item for a given relationship
* @param relationship The relationship for which to get the related item
*/
private getRelatedItem(relationship: Relationship): Observable<Item> {
return this.relationshipService.isLeftItem(relationship, this.item).pipe(
switchMap((isLeftItem) => isLeftItem ? relationship.rightItem : relationship.leftItem),
getFirstSucceededRemoteData(),
getRemoteDataPayload(),
) as Observable<Item>;
}
ngOnInit(): void { ngOnInit(): void {
// store the left and right type of the relationship in a single observable // store the left and right type of the relationship in a single observable
this.relationshipLeftAndRightType$ = observableCombineLatest([ this.relationshipLeftAndRightType$ = observableCombineLatest([
@@ -475,7 +440,13 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
))) as Observable<[ItemType, ItemType]>; ))) as Observable<[ItemType, ItemType]>;
this.relatedEntityType$ = this.relationshipLeftAndRightType$.pipe( this.relatedEntityType$ = this.relationshipLeftAndRightType$.pipe(
map((relatedTypes: ItemType[]) => relatedTypes.find((relatedType) => relatedType.uuid !== this.itemType.uuid)), map(([leftType, rightType]: [ItemType, ItemType]) => {
if (leftType.uuid !== this.itemType.uuid) {
return leftType;
} else {
return rightType;
}
}),
hasValueOperator(), hasValueOperator(),
); );
@@ -485,6 +456,8 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
(relatedEntityType) => this.listId = `edit-relationship-${this.itemType.id}-${relatedEntityType.id}`, (relatedEntityType) => this.listId = `edit-relationship-${this.itemType.id}-${relatedEntityType.id}`,
); );
this.relationshipMessageKey$ = this.getRelationshipMessageKey();
this.subs.push(this.relationshipLeftAndRightType$.pipe( this.subs.push(this.relationshipLeftAndRightType$.pipe(
map(([leftType, rightType]: [ItemType, ItemType]) => { map(([leftType, rightType]: [ItemType, ItemType]) => {
if (leftType.id === this.itemType.id) { if (leftType.id === this.itemType.id) {
@@ -503,24 +476,6 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy {
this.currentItemIsLeftItem$.next(nextValue); this.currentItemIsLeftItem$.next(nextValue);
})); }));
this.getRelationshipMessageKey$ = observableCombineLatest(
this.getLabel(),
this.relatedEntityType$,
).pipe(
map(([label, relatedEntityType]) => {
if (hasValue(label) && label.indexOf('is') > -1 && label.indexOf('Of') > -1) {
const relationshipLabel = `${label.substring(2, label.indexOf('Of'))}`;
if (relationshipLabel !== relatedEntityType.label) {
return `relationships.is${relationshipLabel}Of.${relatedEntityType.label}`;
} else {
return `relationships.is${relationshipLabel}Of`;
}
} else {
return label;
}
}),
);
// initialize the pagination options // initialize the pagination options
this.paginationConfig = new PaginationComponentOptions(); this.paginationConfig = new PaginationComponentOptions();

View File

@@ -1,59 +1,57 @@
<div class="item-relationships"> <div class="item-relationships">
<ng-container *ngVar="entityType$ | async as entityType"> <ng-container *ngIf="entityType$ | async as entityType">
<ng-container *ngIf="entityType"> <div class="button-row top d-flex space-children-mr">
<div class="button-row top d-flex space-children-mr"> <button class="btn btn-danger ml-auto" *ngIf="(isReinstatable() | async) !== true"
<button class="btn btn-danger ml-auto" *ngIf="(isReinstatable() | async) !== true" [disabled]="(hasChanges() | async) !== true"
(click)="discard()"><i
class="fas fa-times"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.discard-button" | translate}}</span>
</button>
<button class="btn btn-warning ml-auto" *ngIf="isReinstatable() | async"
(click)="reinstate()"><i
class="fas fa-undo-alt"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.reinstate-button" | translate}}</span>
</button>
<button class="btn btn-primary" [disabled]="(hasChanges() | async) !== true"
(click)="submit()"><i
class="fas fa-save"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.save-button" | translate}}</span>
</button>
</div>
<div *ngIf="relationshipTypes$ | async as relationshipTypes; else loading" class="mb-4">
<div *ngFor="let relationshipType of relationshipTypes; trackBy: trackById" class="mb-4">
<ds-edit-relationship-list
[url]="url"
[item]="item"
[itemType]="entityType"
[relationshipType]="relationshipType"
[hasChanges]="hasChanges()"
></ds-edit-relationship-list>
</div>
</div>
<ng-template #loading>
<ds-loading></ds-loading>
</ng-template>
<div class="button-row bottom">
<div class="float-right space-children-mr ml-gap">
<button class="btn btn-danger" *ngIf="(isReinstatable() | async) !== true"
[disabled]="(hasChanges() | async) !== true" [disabled]="(hasChanges() | async) !== true"
(click)="discard()"><i (click)="discard()"><i
class="fas fa-times"></i> class="fas fa-times"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.discard-button" | translate}}</span> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.discard-button" | translate}}</span>
</button> </button>
<button class="btn btn-warning ml-auto" *ngIf="isReinstatable() | async" <button class="btn btn-warning" *ngIf="isReinstatable() | async"
(click)="reinstate()"><i (click)="reinstate()"><i
class="fas fa-undo-alt"></i> class="fas fa-undo-alt"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.reinstate-button" | translate}}</span> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.reinstate-button" | translate}}</span>
</button> </button>
<button class="btn btn-primary" [disabled]="(hasChanges() | async) !== true" <button class="btn btn-primary" [disabled]="(hasChanges() | async) !== true"
(click)="submit()"><i (click)="submit()"><i
class="fas fa-save"></i> class="fas fa-save"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.save-button" | translate}}</span> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.save-button" | translate}}</span>
</button> </button>
</div> </div>
<ng-container *ngVar="relationshipTypes$ | async as relationshipTypes"> </div>
<ng-container *ngIf="relationshipTypes">
<div *ngFor="let relationshipType of relationshipTypes" class="mb-4">
<ds-edit-relationship-list
[url]="url"
[item]="item"
[itemType]="entityType$ | async"
[relationshipType]="relationshipType"
[hasChanges]="hasChanges()"
></ds-edit-relationship-list>
</div>
</ng-container>
<ds-loading *ngIf="!relationshipTypes"></ds-loading>
</ng-container>
<div class="button-row bottom">
<div class="float-right space-children-mr ml-gap">
<button class="btn btn-danger" *ngIf="(isReinstatable() | async) !== true"
[disabled]="(hasChanges() | async) !== true"
(click)="discard()"><i
class="fas fa-times"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.discard-button" | translate}}</span>
</button>
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
(click)="reinstate()"><i
class="fas fa-undo-alt"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.reinstate-button" | translate}}</span>
</button>
<button class="btn btn-primary" [disabled]="(hasChanges() | async) !== true"
(click)="submit()"><i
class="fas fa-save"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.save-button" | translate}}</span>
</button>
</div>
</div>
</ng-container>
<div *ngIf="!entityType" <div *ngIf="!entityType"
class="alert alert-info mt-2" role="alert"> class="alert alert-info mt-2" role="alert">
{{ 'item.edit.relationships.no-entity-type' | translate }} {{ 'item.edit.relationships.no-entity-type' | translate }}

View File

@@ -139,6 +139,13 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent {
} }
/**
* Method to prevent unnecessary for loop re-rendering
*/
trackById(index: number, relationshipType: RelationshipType): string {
return relationshipType.id;
}
getRelationshipTypeFollowLinks() { getRelationshipTypeFollowLinks() {
return [ return [
followLink('leftType'), followLink('leftType'),

View File

@@ -55,6 +55,7 @@ import {
isNotEmpty, isNotEmpty,
} from '../../../../empty.util'; } from '../../../../empty.util';
import { ThemedLoadingComponent } from '../../../../loading/themed-loading.component'; import { ThemedLoadingComponent } from '../../../../loading/themed-loading.component';
import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
import { ListableObject } from '../../../../object-collection/shared/listable-object.model'; import { ListableObject } from '../../../../object-collection/shared/listable-object.model';
import { SelectableListState } from '../../../../object-list/selectable-list/selectable-list.reducer'; import { SelectableListState } from '../../../../object-list/selectable-list/selectable-list.reducer';
import { SelectableListService } from '../../../../object-list/selectable-list/selectable-list.service'; import { SelectableListService } from '../../../../object-list/selectable-list/selectable-list.service';
@@ -193,12 +194,12 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
/** /**
* Maintain the list of the related items to be added * Maintain the list of the related items to be added
*/ */
toAdd = []; toAdd: ItemSearchResult[] = [];
/** /**
* Maintain the list of the related items to be removed * Maintain the list of the related items to be removed
*/ */
toRemove = []; toRemove: ItemSearchResult[] = [];
/** /**
* Disable buttons while the submit button is pressed * Disable buttons while the submit button is pressed