fixed name variants in previews

This commit is contained in:
lotte
2019-11-20 15:59:23 +01:00
parent 0c4a1bc610
commit 3487231af1
25 changed files with 148 additions and 58 deletions

View File

@@ -318,13 +318,15 @@ export class RelationshipService extends DataService<Relationship> {
})
)
),
tap(() => this.removeRelationshipItemsFromCache(item1)),
tap(() => this.removeRelationshipItemsFromCache(item2)),
switchMap((relationshipAndType: { relation: Relationship, type: RelationshipType }) => {
const { relation, type } = relationshipAndType;
let updatedRelationship;
if (relationshipLabel === type.leftwardType) {
updatedRelationship = Object.assign(new Relationship(), relation, { leftwardValue: nameVariant });
} else {
updatedRelationship = Object.assign(new Relationship(), relation, { rightwardValue: nameVariant });
} else {
updatedRelationship = Object.assign(new Relationship(), relation, { leftwardValue: nameVariant });
}
return this.update(updatedRelationship);
})

View File

@@ -81,6 +81,9 @@ export interface MetadataValueFilter {
/** The value constraint. */
value?: string;
/** The authority constraint. */
authority?: string;
/** Whether the value constraint should match without regard to case. */
ignoreCase?: boolean;

View File

@@ -8,8 +8,8 @@ import {
} from './metadata.models';
import { Metadata } from './metadata.utils';
const mdValue = (value: string, language?: string): MetadataValue => {
return Object.assign(new MetadataValue(), { uuid: uuidv4(), value: value, language: isUndefined(language) ? null : language, place: 0, authority: undefined, confidence: undefined });
const mdValue = (value: string, language?: string, authority?: string): MetadataValue => {
return Object.assign(new MetadataValue(), { uuid: uuidv4(), value: value, language: isUndefined(language) ? null : language, place: 0, authority: isUndefined(authority) ? null : authority, confidence: undefined });
};
const dcDescription = mdValue('Some description');
@@ -184,6 +184,8 @@ describe('Metadata', () => {
testValueMatches(mdValue('a'), true, { language: null });
testValueMatches(mdValue('a'), false, { language: 'en_US' });
testValueMatches(mdValue('a', 'en_US'), true, { language: 'en_US' });
testValueMatches(mdValue('a', undefined, '4321'), true, { authority: '4321' });
testValueMatches(mdValue('a', undefined, '4321'), false, { authority: '1234' });
});
describe('toViewModelList method', () => {

View File

@@ -127,6 +127,8 @@ export class Metadata {
return true;
} else if (filter.language && filter.language !== mdValue.language) {
return false;
} else if (filter.authority && filter.authority !== mdValue.authority) {
return false;
} else if (filter.value) {
let fValue = filter.value;
let mValue = mdValue.value;

View File

@@ -12,7 +12,7 @@ import { DSpaceObjectType } from '../dspace-object-type.model';
import { SortDirection, SortOptions } from '../../cache/models/sort-options.model';
import { RouteService } from '../../services/route.service';
import { getSucceededRemoteData } from '../operators';
import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util';
import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../../shared/empty.util';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
/**
@@ -195,6 +195,13 @@ export class SearchConfigurationService implements OnDestroy {
}));
}
/**
* @returns {Observable<string>} Emits the current fixed filter as a string
*/
getCurrentFixedFilter(): Observable<string> {
return this.routeService.getRouteParameterValue('fixedFilterQuery');
}
/**
* @returns {Observable<Params>} Emits the current active filters with their values as they are displayed in the frontend URL
*/
@@ -214,6 +221,7 @@ export class SearchConfigurationService implements OnDestroy {
this.getQueryPart(defaults.query),
this.getDSOTypePart(),
this.getFiltersPart(),
this.getFixedFilterPart()
).subscribe((update) => {
const currentValue: SearchOptions = this.searchOptions.getValue();
const updatedValue: SearchOptions = Object.assign(new PaginatedSearchOptions({}), currentValue, update);
@@ -235,6 +243,7 @@ export class SearchConfigurationService implements OnDestroy {
this.getQueryPart(defaults.query),
this.getDSOTypePart(),
this.getFiltersPart(),
this.getFixedFilterPart()
).subscribe((update) => {
const currentValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue();
const updatedValue: PaginatedSearchOptions = Object.assign(new PaginatedSearchOptions({}), currentValue, update);
@@ -331,4 +340,16 @@ export class SearchConfigurationService implements OnDestroy {
return { filters }
}));
}
/**
* @returns {Observable<string>} Emits the current fixed filter as a partial SearchOptions object
*/
private getFixedFilterPart(): Observable<any> {
return this.getCurrentFixedFilter().pipe(
isNotEmptyOperator(),
map((fixedFilter) => {
return { fixedFilter }
}),
);
}
}

View File

@@ -1,8 +1,8 @@
<div class="row">
<div class="col-2">
<div class="d-flex">
<div class="person-thumbnail pr-2">
<ds-thumbnail [thumbnail]="dso.getThumbnail() | async" [defaultImage]="'assets/images/person-placeholder.svg'"></ds-thumbnail>
</div>
<div class="col-10">
<div class="flex-grow-1">
<ds-person-input-suggestions [suggestions]="allSuggestions" [(ngModel)]="selectedName" (clickSuggestion)="select($event)" (submitSuggestion)="selectCustom($event)"></ds-person-input-suggestions>
<span class="text-muted">
<span *ngIf="dso.allMetadata(['person.jobTitle']).length > 0"
@@ -14,4 +14,3 @@
</span>
</div>
</div>

View File

@@ -0,0 +1,7 @@
@import '../../../../../../styles/variables';
$submission-relationship-thumbnail-width: 80px;
.person-thumbnail {
width: $submission-relationship-thumbnail-width;
}

View File

@@ -0,0 +1,18 @@
form {
z-index: 1;
&:before {
position: absolute;
font-weight: 900;
font-family: "Font Awesome 5 Free";
content: "\f0d7";
top: 7px;
right: 0;
height: 20px;
width: 20px;
z-index: -1;
}
input.suggestion_input {
background: transparent;
}
}

View File

@@ -1,11 +1,10 @@
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { InputSuggestion } from '../../../../../../shared/input-suggestions/input-suggestions.model';
import { InputSuggestionsComponent } from '../../../../../../shared/input-suggestions/input-suggestions.component';
@Component({
selector: 'ds-person-input-suggestions',
styleUrls: ['./../../../../../../shared/input-suggestions/input-suggestions.component.scss'],
styleUrls: ['./person-input-suggestions.component.scss', './../../../../../../shared/input-suggestions/input-suggestions.component.scss'],
templateUrl: './person-input-suggestions.component.html',
providers: [
{

View File

@@ -56,14 +56,16 @@
<ul class="list-unstyled">
<li *ngFor="let result of ( relationships$ | async); let i = index">
<ng-container *ngVar="result.indexableObject as v">
<ng-container *ngIf="(modelValueMDRepresentation | async) && (modelValueMDRepresentation | async)[i]">
<button type="button" class="close float-left" aria-label="Close button"
(click)="removeSelection(result)">
<span aria-hidden="true">&times;</span>
</button>
<span class="d-inline-block align-middle ml-1">
<ds-listable-object-component-loader [context]="context" [object]="v" [index]="i"></ds-listable-object-component-loader>
<ds-metadata-representation-loader [mdRepresentation]="(modelValueMDRepresentation | async)[i]"></ds-metadata-representation-loader>
</span>
</ng-container>
</ng-container>
</li>
</ul>
</div>

View File

@@ -70,7 +70,9 @@ import { DsDynamicFormArrayComponent } from './models/array-group/dynamic-form-a
import { DsDynamicRelationGroupComponent } from './models/relation-group/dynamic-relation-group.components';
import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './models/relation-group/dynamic-relation-group.model';
import { DsDatePickerInlineComponent } from './models/date-picker-inline/dynamic-date-picker-inline.component';
import { map, switchMap, tap } from 'rxjs/operators';
import { map, switchMap, take, tap } from 'rxjs/operators';
import { zip as observableZip } from 'rxjs';
import { combineLatest as observableCombineLatest } from 'rxjs';
import { SelectableListState } from '../../../object-list/selectable-list/selectable-list.reducer';
import { Observable } from 'rxjs';
import { SearchResult } from '../../../search/search-result.model';
@@ -81,7 +83,7 @@ import { SelectableListService } from '../../../object-list/selectable-list/sele
import { DsDynamicDisabledComponent } from './models/disabled/dynamic-disabled.component';
import { DYNAMIC_FORM_CONTROL_TYPE_DISABLED } from './models/disabled/dynamic-disabled.model';
import { DsDynamicLookupRelationModalComponent } from './relation-lookup-modal/dynamic-lookup-relation-modal.component';
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
import { getAllSucceededRemoteData, getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
import { RemoteData } from '../../../../core/data/remote-data';
import { Item } from '../../../../core/shared/item.model';
import { ItemDataService } from '../../../../core/data/item-data.service';
@@ -92,6 +94,9 @@ import { SubmissionObjectDataService } from '../../../../core/submission/submiss
import { SubmissionObject } from '../../../../core/submission/models/submission-object.model';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
import { MetadataValue } from '../../../../core/shared/metadata.models';
import * as uuidv4 from 'uuid/v4';
export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type<DynamicFormControl> | null {
switch (model.type) {
@@ -181,6 +186,8 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
item$: Observable<Item>;
listId: string;
searchConfig: string;
modelValueMDRepresentation;
/* tslint:disable:no-output-rename */
@Output('dfBlur') blur: EventEmitter<DynamicFormControlEvent> = new EventEmitter<DynamicFormControlEvent>();
@Output('dfChange') change: EventEmitter<DynamicFormControlEvent> = new EventEmitter<DynamicFormControlEvent>();
@@ -213,6 +220,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
}
ngOnInit(): void {
const q = uuidv4();
this.hasRelationLookup = hasValue(this.model.relationship);
if (this.hasRelationLookup) {
this.listId = 'list-' + this.model.relationship.relationshipType;
@@ -220,9 +228,9 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
.findById(this.model.submissionId).pipe(
getSucceededRemoteData(),
getRemoteDataPayload(),
switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable<RemoteData<Item>>).pipe(getSucceededRemoteData(), getRemoteDataPayload())));
switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload())));
this.item$.pipe(
take(1),
switchMap((item: Item) => this.relationService.getRelatedItemsByLabel(item, this.model.relationship.relationshipType)),
map((items: RemoteData<PaginatedList<Item>>) => items.payload.page.map((item) => Object.assign(new ItemSearchResult(), { indexableObject: item }))),
).subscribe((relatedItems: SearchResult<Item>[]) => this.selectableListService.select(this.listId, relatedItems));
@@ -230,6 +238,23 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
this.relationships$ = this.selectableListService.getSelectableList(this.listId).pipe(
map((listState: SelectableListState) => hasValue(listState) && hasValue(listState.selection) ? listState.selection : []),
) as Observable<SearchResult<Item>[]>;
this.modelValueMDRepresentation =
observableCombineLatest(this.item$, this.relationships$).pipe(
map(([item, relatedItems]: [Item, SearchResult<DSpaceObject>[]]) =>
relatedItems
.map((element: SearchResult<DSpaceObject>) => {
const relationMD: MetadataValue = item.firstMetadata(this.model.relationship.metadataField, { value: element.indexableObject.uuid });
if (hasValue(relationMD)) {
const metadataRepresentationMD: MetadataValue = item.firstMetadata(this.model.metadataFields, { authority: relationMD.authority });
return Object.assign(
new ItemMetadataRepresentation(metadataRepresentationMD),
element.indexableObject
)
}
})
)
);
}
}
@@ -275,16 +300,16 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
}
openLookup() {
this.item$.subscribe((item: Item) => {
this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent, { size: 'lg', centered: true });
const modalComp = this.modalRef.componentInstance;
modalComp.repeatable = this.model.repeatable;
modalComp.listId = this.listId;
modalComp.relationshipOptions = this.model.relationship;
modalComp.label = this.model.label;
modalComp.item = item;
modalComp.metadataFields = this.model.metadataFields;
})
this.item$.subscribe((item: Item) => {
modalComp.item = item;
});
}
removeSelection(object: SearchResult<Item>) {

View File

@@ -113,9 +113,9 @@ export class RelationshipEffects {
mergeMap((type: RelationshipType) => {
const isSwitched = type.rightwardType === relationshipType;
if (isSwitched) {
return this.relationshipService.addRelationship(type.id, item2, item1, undefined, nameVariant);
return this.relationshipService.addRelationship(type.id, item2, item1, nameVariant, undefined);
} else {
return this.relationshipService.addRelationship(type.id, item1, item2, nameVariant, undefined);
return this.relationshipService.addRelationship(type.id, item1, item2, undefined, nameVariant);
}
}
)

View File

@@ -80,7 +80,7 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest
/* TODO: in Rxjs 6.4.0 and up, we can replace this with takeWhile(predicate, true) - see https://stackoverflow.com/a/44644237 */
multicast(
() => new ReplaySubject(1),
subject => subject.pipe(
(subject) => subject.pipe(
takeWhile((rd: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>) => rd.isLoading),
concat(subject.pipe(take(1)))
)
@@ -146,7 +146,7 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest
this.allSelected = false;
this.selection$
.pipe(take(1))
.subscribe((selection: SearchResult<Item>[]) => this.deselectObject.emit(...selection));
.subscribe((selection: Array<SearchResult<Item>>) => this.deselectObject.emit(...selection));
this.selectableListService.deselectAll(this.listId);
}

View File

@@ -1,6 +1,12 @@
export interface RelationshipOptions {
const RELATION_METADATA_PREFIX = "relation."
export class RelationshipOptions {
relationshipType: string;
filter: string;
searchConfiguration: string;
nameVariants: boolean;
get metadataField() {
return RELATION_METADATA_PREFIX + this.relationshipType
}
}

View File

@@ -13,6 +13,7 @@ import { DynamicFormControlLayout } from '@ng-dynamic-forms/core';
import { setLayout } from './parser.utils';
import { AuthorityOptions } from '../../../../core/integration/models/authority-options.model';
import { ParserOptions } from './parser-options';
import { RelationshipOptions } from '../models/relationship-options.model';
export const SUBMISSION_ID: InjectionToken<string> = new InjectionToken<string>('submissionId');
export const CONFIG_DATA: InjectionToken<FormFieldModel> = new InjectionToken<FormFieldModel>('configData');
@@ -28,7 +29,8 @@ export abstract class FieldParser {
@Inject(CONFIG_DATA) protected configData: FormFieldModel,
@Inject(INIT_FORM_VALUES) protected initFormValues: any,
@Inject(PARSER_OPTIONS) protected parserOptions: ParserOptions
) {}
) {
}
public abstract modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any;
@@ -196,7 +198,9 @@ export abstract class FieldParser {
// Set read only option
controlModel.readOnly = this.parserOptions.readOnly;
controlModel.disabled = this.parserOptions.readOnly;
controlModel.relationship = this.configData.selectableRelationship;
if (hasValue(this.configData.selectableRelationship)) {
controlModel.relationship = Object.assign(new RelationshipOptions(), this.configData.selectableRelationship);
}
controlModel.repeatable = this.configData.repeatable;
controlModel.metadataFields = isNotEmpty(this.configData.selectableMetadata) ? this.configData.selectableMetadata.map((metadataObject) => metadataObject.metadata) : [];
controlModel.submissionId = this.submissionId;

View File

@@ -1,5 +0,0 @@
@import 'src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.scss';
::ng-deep .noUi-connect {
background: $info;
}

View File

@@ -1,4 +1,4 @@
@import 'src/app/+search-page/search-filters/search-filter/search-filter.component.scss';
@import 'src/app/shared/search/search-filters/search-filter/search-filter.component.scss';
.facet-filter {
background-color: map-get($theme-colors, light);

View File

@@ -0,0 +1,5 @@
@import 'src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.scss';
::ng-deep .noUi-connect {
background: $info;
}

View File

@@ -1,4 +1,4 @@
@import 'src/app/+search-page/search-settings/search-settings.component.scss';
@import 'src/app/shared/search/search-settings/search-settings.component.scss';
.setting-option {
background-color: map-get($theme-colors, light);