mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
59695: Starts-with implementation for browse-by date
This commit is contained in:
@@ -72,5 +72,14 @@ module.exports = {
|
|||||||
code: 'nl',
|
code: 'nl',
|
||||||
label: 'Nederlands',
|
label: 'Nederlands',
|
||||||
active: false,
|
active: false,
|
||||||
}]
|
}],
|
||||||
|
// Browse-By Pages
|
||||||
|
browseBy: {
|
||||||
|
// Amount of years to display using jumps of one year (current year - oneYearLimit)
|
||||||
|
oneYearLimit: 10,
|
||||||
|
// Limit for years to display using jumps of five years (current year - fiveYearLimit)
|
||||||
|
fiveYearLimit: 30,
|
||||||
|
// The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items)
|
||||||
|
defaultLowerLimit: 1900
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@@ -281,6 +281,11 @@
|
|||||||
},
|
},
|
||||||
"browse": {
|
"browse": {
|
||||||
"title": "Browsing {{ collection }} by {{ field }} {{ value }}",
|
"title": "Browsing {{ collection }} by {{ field }} {{ value }}",
|
||||||
|
"startsWith": {
|
||||||
|
"choose_year": "(Choose year)",
|
||||||
|
"type_year": "Or type in a year:",
|
||||||
|
"submit": "Go"
|
||||||
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
"author": "Author",
|
"author": "Author",
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
BrowseByMetadataPageComponent,
|
BrowseByMetadataPageComponent,
|
||||||
browseParamsToOptions
|
browseParamsToOptions
|
||||||
@@ -6,6 +6,14 @@ import {
|
|||||||
import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model';
|
import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model';
|
||||||
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
|
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
|
||||||
import { BrowseByStartsWithType } from '../../shared/browse-by/browse-by.component';
|
import { BrowseByStartsWithType } from '../../shared/browse-by/browse-by.component';
|
||||||
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
|
import { PaginatedList } from '../../core/data/paginated-list';
|
||||||
|
import { Item } from '../../core/shared/item.model';
|
||||||
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { BrowseService } from '../../core/browse/browse.service';
|
||||||
|
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||||
|
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-browse-by-date-page',
|
selector: 'ds-browse-by-date-page',
|
||||||
@@ -19,8 +27,15 @@ import { BrowseByStartsWithType } from '../../shared/browse-by/browse-by.compone
|
|||||||
*/
|
*/
|
||||||
export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
|
export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
|
||||||
|
|
||||||
oneYearLimit = 10;
|
defaultMetadataField = 'dc.date.issued';
|
||||||
fiveYearLimit = 30;
|
|
||||||
|
public constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected browseService: BrowseService,
|
||||||
|
protected dsoService: DSpaceObjectDataService,
|
||||||
|
protected router: Router) {
|
||||||
|
super(route, browseService, dsoService, router);
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.startsWithType = BrowseByStartsWithType.date;
|
this.startsWithType = BrowseByStartsWithType.date;
|
||||||
@@ -34,29 +49,53 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
|
|||||||
return Object.assign({}, params, queryParams, data);
|
return Object.assign({}, params, queryParams, data);
|
||||||
})
|
})
|
||||||
.subscribe((params) => {
|
.subscribe((params) => {
|
||||||
|
const metadataField = params.metadataField || this.defaultMetadataField;
|
||||||
this.metadata = params.metadata || this.defaultMetadata;
|
this.metadata = params.metadata || this.defaultMetadata;
|
||||||
this.startsWith = +params.startsWith || params.startsWith;
|
this.startsWith = +params.startsWith || params.startsWith;
|
||||||
const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.metadata);
|
const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.metadata);
|
||||||
this.updatePageWithItems(searchOptions, this.value);
|
this.updatePageWithItems(searchOptions, this.value);
|
||||||
this.updateParent(params.scope);
|
this.updateParent(params.scope);
|
||||||
|
this.updateStartsWithOptions(this.metadata, metadataField, params.scope);
|
||||||
}));
|
}));
|
||||||
const options = [];
|
}
|
||||||
const currentYear = new Date().getFullYear();
|
|
||||||
const oneYearBreak = Math.floor((currentYear - this.oneYearLimit) / 5) * 5;
|
updateStartsWithOptions(definition: string, metadataField: string, scope?: string) {
|
||||||
const fiveYearBreak = Math.floor((currentYear - this.fiveYearLimit) / 10) * 10;
|
this.subs.push(
|
||||||
const lowerLimit = 1900; // Hardcoded atm, this should be the lowest date issued
|
this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData<PaginatedList<Item>>) => {
|
||||||
let i = currentYear;
|
let lowerLimit = this.config.browseBy.defaultLowerLimit;
|
||||||
while (i > lowerLimit) {
|
if (firstItemRD.payload.page.length > 0) {
|
||||||
options.push(i);
|
const date = firstItemRD.payload.page[0].findMetadata(metadataField);
|
||||||
if (i <= fiveYearBreak) {
|
if (hasValue(date) && hasValue(+date.split('-')[0])) {
|
||||||
i -= 10;
|
lowerLimit = +date.split('-')[0];
|
||||||
} else if (i <= oneYearBreak) {
|
}
|
||||||
i -= 5;
|
}
|
||||||
} else {
|
const options = [];
|
||||||
i--;
|
const currentYear = new Date().getFullYear();
|
||||||
}
|
const oneYearBreak = Math.floor((currentYear - this.config.browseBy.oneYearLimit) / 5) * 5;
|
||||||
}
|
const fiveYearBreak = Math.floor((currentYear - this.config.browseBy.fiveYearLimit) / 10) * 10;
|
||||||
console.log(options);
|
if (lowerLimit <= fiveYearBreak) {
|
||||||
|
lowerLimit -= 10;
|
||||||
|
} else if (lowerLimit <= oneYearBreak) {
|
||||||
|
lowerLimit -= 5;
|
||||||
|
} else {
|
||||||
|
lowerLimit -= 1;
|
||||||
|
}
|
||||||
|
let i = currentYear;
|
||||||
|
while (i > lowerLimit) {
|
||||||
|
options.push(i);
|
||||||
|
if (i <= fiveYearBreak) {
|
||||||
|
i -= 10;
|
||||||
|
} else if (i <= oneYearBreak) {
|
||||||
|
i -= 5;
|
||||||
|
} else {
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isNotEmpty(options)) {
|
||||||
|
this.startsWithOptions = options;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@ import { BrowseByDatePageComponent } from './+browse-by-date-page/browse-by-date
|
|||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
{ path: 'title', component: BrowseByTitlePageComponent },
|
{ path: 'title', component: BrowseByTitlePageComponent },
|
||||||
{ path: 'dateissued', component: BrowseByDatePageComponent, data: { metadata: 'dateissued' } },
|
{ path: 'dateissued', component: BrowseByDatePageComponent, data: { metadata: 'dateissued', metadataField: 'dc.date.issued' } },
|
||||||
{ path: ':metadata', component: BrowseByMetadataPageComponent }
|
{ path: ':metadata', component: BrowseByMetadataPageComponent }
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
|
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
import { distinctUntilChanged, map, startWith, take } from 'rxjs/operators';
|
import { distinctUntilChanged, map, startWith, take } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
ensureArrayHasValue,
|
ensureArrayHasValue, hasValue,
|
||||||
hasValueOperator,
|
hasValueOperator,
|
||||||
isEmpty,
|
isEmpty,
|
||||||
isNotEmpty,
|
isNotEmpty,
|
||||||
@@ -162,6 +162,28 @@ export class BrowseService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFirstItemFor(definition: string, scope?: string): Observable<RemoteData<PaginatedList<Item>>> {
|
||||||
|
return this.getBrowseDefinitions().pipe(
|
||||||
|
getBrowseDefinitionLinks(definition),
|
||||||
|
hasValueOperator(),
|
||||||
|
map((_links: any) => _links.items),
|
||||||
|
hasValueOperator(),
|
||||||
|
map((href: string) => {
|
||||||
|
const args = [];
|
||||||
|
if (hasValue(scope)) {
|
||||||
|
args.push(`scope=${scope}`);
|
||||||
|
}
|
||||||
|
args.push('page=0');
|
||||||
|
args.push('size=1');
|
||||||
|
if (isNotEmpty(args)) {
|
||||||
|
href = new URLCombiner(href, `?${args.join('&')}`).toString();
|
||||||
|
}
|
||||||
|
return href;
|
||||||
|
}),
|
||||||
|
getBrowseItemsFor(this.requestService, this.responseCache, this.rdb)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getPrevBrowseItems(items: RemoteData<PaginatedList<Item>>): Observable<RemoteData<PaginatedList<Item>>> {
|
getPrevBrowseItems(items: RemoteData<PaginatedList<Item>>): Observable<RemoteData<PaginatedList<Item>>> {
|
||||||
return observableOf(items.payload.prev).pipe(
|
return observableOf(items.payload.prev).pipe(
|
||||||
getBrowseItemsFor(this.requestService, this.responseCache, this.rdb)
|
getBrowseItemsFor(this.requestService, this.responseCache, this.rdb)
|
||||||
|
@@ -1,6 +1,56 @@
|
|||||||
import { Inject } from '@angular/core';
|
import { Inject, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { hasValue } from '../../empty.util';
|
||||||
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { FormControl, FormGroup } from '@angular/forms';
|
||||||
|
|
||||||
export class BrowseByStartsWithAbstractComponent {
|
export class BrowseByStartsWithAbstractComponent implements OnInit, OnDestroy {
|
||||||
public constructor(@Inject('startsWithOptions') public startsWithOptions: any[]) {
|
startsWith: string;
|
||||||
|
|
||||||
|
formData: FormGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of subscriptions
|
||||||
|
*/
|
||||||
|
subs: Subscription[] = [];
|
||||||
|
|
||||||
|
public constructor(@Inject('startsWithOptions') public startsWithOptions: any[],
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected router: Router) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.subs.push(
|
||||||
|
this.route.queryParams.subscribe((params) => {
|
||||||
|
this.startsWith = params.startsWith;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.formData = new FormGroup({
|
||||||
|
startsWith: new FormControl()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setStartsWith(event: Event) {
|
||||||
|
this.startsWith = (event.target as HTMLInputElement).value;
|
||||||
|
this.setStartsWithParam();
|
||||||
|
}
|
||||||
|
|
||||||
|
setStartsWithParam() {
|
||||||
|
if (this.startsWith === '-1') {
|
||||||
|
this.startsWith = undefined;
|
||||||
|
}
|
||||||
|
this.router.navigate([], {
|
||||||
|
queryParams: Object.assign({ startsWith: this.startsWith }),
|
||||||
|
queryParamsHandling: 'merge'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
submitForm(data) {
|
||||||
|
this.startsWith = data.startsWith;
|
||||||
|
this.setStartsWithParam();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,22 @@
|
|||||||
<h3>Test Starts-With Date</h3>
|
<form class="w-100" [formGroup]="formData" (ngSubmit)="submitForm(formData.value)">
|
||||||
<p>
|
<div class="container">
|
||||||
<span *ngFor="let element of startsWithOptions">{{element}}, </span>
|
<div class="row mb-2">
|
||||||
</p>
|
<select class="form-control col-xs-5 col-sm-3" (change)="setStartsWith($event)">
|
||||||
|
<option [value]="-1" [selected]="!startsWith">
|
||||||
|
{{'browse.startsWith.choose_year' | translate}}
|
||||||
|
</option>
|
||||||
|
<option *ngFor="let option of startsWithOptions"
|
||||||
|
[value]="option"
|
||||||
|
[selected]="option === startsWith || option === +startsWith ? 'selected': null">
|
||||||
|
{{option}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-group input-group col-xs-7 col-sm-6">
|
||||||
|
<input class="form-control" placeholder="{{'browse.startsWith.type_year' | translate}}" type="number" name="startsWith" formControlName="startsWith" [value]="+startsWith" />
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary" type="submit">{{'browse.startsWith.submit' | translate}}</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@@ -0,0 +1,7 @@
|
|||||||
|
@import '../../../../../styles/variables.scss';
|
||||||
|
|
||||||
|
// temporary fix for bootstrap 4 beta btn color issue
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: $input-bg;
|
||||||
|
color: $input-color;
|
||||||
|
}
|
||||||
|
@@ -1,4 +1 @@
|
|||||||
<h3>Test Starts-With Text</h3>
|
<!-- To be implemented -->
|
||||||
<p>
|
|
||||||
<span *ngFor="let element of startsWithOptions">{{element}}, </span>
|
|
||||||
</p>
|
|
||||||
|
7
src/config/browse-by-config.interface.ts
Normal file
7
src/config/browse-by-config.interface.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Config } from './config.interface';
|
||||||
|
|
||||||
|
export interface BrowseByConfig extends Config {
|
||||||
|
oneYearLimit: number;
|
||||||
|
fiveYearLimit: number;
|
||||||
|
defaultLowerLimit: number;
|
||||||
|
}
|
@@ -5,6 +5,7 @@ import { UniversalConfig } from './universal-config.interface';
|
|||||||
import { INotificationBoardOptions } from './notifications-config.interfaces';
|
import { INotificationBoardOptions } from './notifications-config.interfaces';
|
||||||
import { FormConfig } from './form-config.interfaces';
|
import { FormConfig } from './form-config.interfaces';
|
||||||
import {LangConfig} from './lang-config.interface';
|
import {LangConfig} from './lang-config.interface';
|
||||||
|
import { BrowseByConfig } from './browse-by-config.interface';
|
||||||
|
|
||||||
export interface GlobalConfig extends Config {
|
export interface GlobalConfig extends Config {
|
||||||
ui: ServerConfig;
|
ui: ServerConfig;
|
||||||
@@ -19,4 +20,5 @@ export interface GlobalConfig extends Config {
|
|||||||
debug: boolean;
|
debug: boolean;
|
||||||
defaultLanguage: string;
|
defaultLanguage: string;
|
||||||
languages: LangConfig[];
|
languages: LangConfig[];
|
||||||
|
browseBy: BrowseByConfig;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user