55693: Item mapper page + ItemSelectComponent

This commit is contained in:
Kristof De Langhe
2018-09-26 09:44:01 +02:00
parent bfb6b1f262
commit 6b986c8c91
12 changed files with 194 additions and 1 deletions

View File

@@ -13,6 +13,12 @@
"head": "Recent Submissions" "head": "Recent Submissions"
} }
} }
},
"item-mapper": {
"head": "Item Mapper - Map Items from Other Collections",
"collection": "Collection: \"<b>{{name}}</b>\"",
"description": "This is the item mapper tool that allows collection administrators to map items from other collections into this collection. You can search for items from other collections and map them, or browse the list of currently mapped items.",
"return": "Return"
} }
}, },
"community": { "community": {
@@ -43,6 +49,13 @@
"simple": "Simple item page", "simple": "Simple item page",
"full": "Full item page" "full": "Full item page"
} }
},
"select": {
"table": {
"collection": "Collection",
"author": "Author",
"title": "Title"
}
} }
}, },
"nav": { "nav": {

View File

@@ -0,0 +1,45 @@
<div class="container">
<div class="row">
<div class="col-12">
<h2>{{'collection.item-mapper.head' | translate}}</h2>
<p [innerHTML]="'collection.item-mapper.collection' | translate:{ name: (collectionRD$ | async)?.payload?.name }"></p>
<p>{{'collection.item-mapper.description' | translate}}</p>
<div class="row">
<div class="col-12 col-lg-6">
<ds-search-form id="search-form"
[query]="(searchOptions$ | async)?.query"
[scope]="(searchOptions$ | async)?.scope"
[currentUrl]="getCurrentUrl()">
</ds-search-form>
</div>
</div>
<ul class="nav nav-tabs mb-2">
<li class="nav-item">
<span class="tab nav-link" id="tab-browse-content" (click)="activeTab = 0" [ngClass]="(activeTab == 0)?'active':''">Browse</span>
</li>
<li class="nav-item">
<span class="tab nav-link" id="tab-map-content" (click)="activeTab = 1" [ngClass]="(activeTab == 1)?'active':''">Map</span>
</li>
</ul>
<div class="tab-content">
<!-- Browse Tab -->
<div *ngIf="activeTab == 0" id="tab-browse">
<ds-viewable-collection
[config]="(searchOptions$ | async)?.pagination"
[sortConfig]="(searchOptions$ | async)?.sort"
[objects]="collectionItemsRD$ | async">
</ds-viewable-collection>
</div>
<!-- Map Tab -->
<div *ngIf="activeTab == 1" id="tab-map">
<ds-item-select [items$]="mappingItemsRD$"></ds-item-select>
</div>
</div>
<button [routerLink]="['/collections/', (collectionRD$ | async)?.payload?.id]" class="btn btn-outline-secondary">{{'collection.item-mapper.return' | translate}}</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,5 @@
@import '../../../styles/variables.scss';
.tab:hover {
cursor: pointer;
}

View File

@@ -0,0 +1,69 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
import { CollectionDataService } from '../../core/data/collection-data.service';
import { ActivatedRoute, PRIMARY_OUTLET, Router, UrlSegmentGroup } from '@angular/router';
import { RemoteData } from '../../core/data/remote-data';
import { Observable } from 'rxjs/Observable';
import { Collection } from '../../core/shared/collection.model';
import { SearchConfigurationService } from '../../+search-page/search-service/search-configuration.service';
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
import { PaginatedList } from '../../core/data/paginated-list';
import { Item } from '../../core/shared/item.model';
import { combineLatest, flatMap, map, tap } from 'rxjs/operators';
import { getSucceededRemoteData, toDSpaceObjectListRD } from '../../core/shared/operators';
import { SearchService } from '../../+search-page/search-service/search.service';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model';
@Component({
selector: 'ds-collection-item-mapper',
styleUrls: ['./collection-item-mapper.component.scss'],
templateUrl: './collection-item-mapper.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [
fadeIn,
fadeInOut
]
})
export class CollectionItemMapperComponent implements OnInit {
collectionRD$: Observable<RemoteData<Collection>>;
searchOptions$: Observable<PaginatedSearchOptions>;
collectionItemsRD$: Observable<RemoteData<PaginatedList<DSpaceObject>>>;
mappingItemsRD$: Observable<RemoteData<PaginatedList<DSpaceObject>>>;
activeTab = 0;
constructor(private collectionDataService: CollectionDataService,
private route: ActivatedRoute,
private router: Router,
private searchConfigService: SearchConfigurationService,
private searchService: SearchService) {
}
ngOnInit(): void {
this.collectionRD$ = this.route.data.map((data) => data.collection);
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
this.collectionItemsRD$ = this.collectionRD$.pipe(
getSucceededRemoteData(),
combineLatest(this.searchOptions$),
flatMap(([collectionRD, options]) => {
return this.searchService.search(Object.assign(options, {
scope: collectionRD.payload.id
}));
}),
toDSpaceObjectListRD()
);
this.mappingItemsRD$ = this.searchOptions$.pipe(
flatMap((options: PaginatedSearchOptions) => this.searchService.search(options)),
toDSpaceObjectListRD()
);
}
getCurrentUrl(): string {
const urlTree = this.router.parseUrl(this.router.url);
const g: UrlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET];
return '/' + g.toString();
}
}

View File

@@ -3,6 +3,7 @@ import { RouterModule } from '@angular/router';
import { CollectionPageComponent } from './collection-page.component'; import { CollectionPageComponent } from './collection-page.component';
import { CollectionPageResolver } from './collection-page.resolver'; import { CollectionPageResolver } from './collection-page.resolver';
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
@NgModule({ @NgModule({
imports: [ imports: [
@@ -14,6 +15,14 @@ import { CollectionPageResolver } from './collection-page.resolver';
resolve: { resolve: {
collection: CollectionPageResolver collection: CollectionPageResolver
} }
},
{
path: ':id/mapper',
component: CollectionItemMapperComponent,
pathMatch: 'full',
resolve: {
collection: CollectionPageResolver
}
} }
]) ])
], ],

View File

@@ -6,6 +6,7 @@ import { SharedModule } from '../shared/shared.module';
import { CollectionPageComponent } from './collection-page.component'; import { CollectionPageComponent } from './collection-page.component';
import { CollectionPageRoutingModule } from './collection-page-routing.module'; import { CollectionPageRoutingModule } from './collection-page-routing.module';
import { SearchPageModule } from '../+search-page/search-page.module'; import { SearchPageModule } from '../+search-page/search-page.module';
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
@NgModule({ @NgModule({
imports: [ imports: [
@@ -16,6 +17,7 @@ import { SearchPageModule } from '../+search-page/search-page.module';
], ],
declarations: [ declarations: [
CollectionPageComponent, CollectionPageComponent,
CollectionItemMapperComponent
] ]
}) })
export class CollectionPageModule { export class CollectionPageModule {

View File

@@ -0,0 +1,20 @@
<div class="table-responsive">
<table id="item-select" class="table table-striped table-hover">
<thead>
<tr>
<th></th>
<th scope="col">{{'item.select.table.collection' | translate}}</th>
<th scope="col">{{'item.select.table.author' | translate}}</th>
<th scope="col">{{'item.select.table.title' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of (items$ | async)?.payload?.page ; let i = index">
<td><input [(ngModel)]="checked[i]" type="checkbox"></td>
<td><a [routerLink]="['/items', item.id]">{{(item.owningCollection | async)?.payload?.name}}</a></td>
<td><a [routerLink]="['/items', item.id]">{{item.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])[0].value}}</a></td>
<td><a [routerLink]="['/items', item.id]">{{item.findMetadata("dc.title")}}</a></td>
</tr>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,28 @@
import { Component, Input, OnInit } from '@angular/core';
import { ItemDataService } from '../../core/data/item-data.service';
import { PaginatedList } from '../../core/data/paginated-list';
import { RemoteData } from '../../core/data/remote-data';
import { Observable } from 'rxjs/Observable';
import { Item } from '../../core/shared/item.model';
@Component({
selector: 'ds-item-select',
styleUrls: ['./item-select.component.scss'],
templateUrl: './item-select.component.html'
})
export class ItemSelectComponent implements OnInit {
@Input()
items$: Observable<RemoteData<PaginatedList<Item>>>;
checked: boolean[] = [];
constructor(private itemDataService: ItemDataService) {
}
ngOnInit(): void {
this.items$ = this.itemDataService.findAll({});
}
}

View File

@@ -83,6 +83,7 @@ import { InputSuggestionsComponent } from './input-suggestions/input-suggestions
import { CapitalizePipe } from './utils/capitalize.pipe'; import { CapitalizePipe } from './utils/capitalize.pipe';
import { MomentModule } from 'angular2-moment'; import { MomentModule } from 'angular2-moment';
import { ObjectKeysPipe } from './utils/object-keys-pipe'; import { ObjectKeysPipe } from './utils/object-keys-pipe';
import { ItemSelectComponent } from './item-select/item-select.component';
const MODULES = [ const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here // Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -156,7 +157,8 @@ const COMPONENTS = [
TruncatableComponent, TruncatableComponent,
TruncatablePartComponent, TruncatablePartComponent,
BrowseByComponent, BrowseByComponent,
InputSuggestionsComponent InputSuggestionsComponent,
ItemSelectComponent
]; ];
const ENTRY_COMPONENTS = [ const ENTRY_COMPONENTS = [