Skip to content

Commit

Permalink
search: add date range facet widget
Browse files Browse the repository at this point in the history
Creates a new widget to allow filtering search result on date range
interval.

Co-authored-by: Renaud Michotte <renaud.michotte@gmail.com>
  • Loading branch information
zannkukai authored and iGor milhit committed Oct 12, 2021
1 parent 557f66d commit 3fd4530
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 8 deletions.
4 changes: 3 additions & 1 deletion projects/rero/ng-core/src/lib/record/record.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import { RecordSearchComponent } from './search/record-search.component';
import { JsonComponent } from './search/result/item/json.component';
import { RecordSearchResultComponent } from './search/result/record-search-result.component';
import { RecordSearchResultDirective } from './search/result/record-search-result.directive';
import { AggregationDateRangeComponent } from './search/aggregation/date-range/date-range.component';

@NgModule({
declarations: [
Expand Down Expand Up @@ -103,7 +104,8 @@ import { RecordSearchResultDirective } from './search/result/record-search-resul
LabelComponent,
TextareaFieldComponent,
CustomSelectFieldComponent,
MarkdownFieldComponent
MarkdownFieldComponent,
AggregationDateRangeComponent
],
imports: [
// NOTE : BrowserAnimationModule **should** be include in application core module.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,26 @@ <h5 class="mb-0">
<div class="card-body pl-3 pr-2 py-3">
<ng-container [ngSwitch]="aggregation.type">
<!-- Range -->
<ng-core-aggregation-slider [key]="aggregation.key" [min]="aggregation.config.min || 1"
[max]="aggregation.config.max || 100" [step]="aggregation.config.step || 1"
[buckets]="aggregation.value.buckets" *ngSwitchCase="'range'">
<ng-core-aggregation-slider *ngSwitchCase="'range'"
[key]="aggregation.key"
[min]="aggregation.config.min || 1"
[max]="aggregation.config.max || 100"
[step]="aggregation.config.step || 1"
[buckets]="aggregation.value.buckets">
</ng-core-aggregation-slider>

<!-- Date range -->
<ng-core-aggregation-date-range *ngSwitchCase="'date-range'"
[key]="aggregation.key"
[config]="aggregation.config"
[buckets]="aggregation.value.buckets">
</ng-core-aggregation-date-range>
<!-- Terms (default) -->
<ng-container *ngSwitchDefault>
<ng-core-record-search-aggregation-buckets [buckets]="aggregation.value.buckets"
[aggregationKey]="aggregation.key" [size]="aggregation.bucketSize"
*ngIf="aggregation.value.buckets.length > 0; else noResult">
<ng-core-record-search-aggregation-buckets
*ngIf="aggregation.value.buckets.length > 0; else noResult"
[buckets]="aggregation.value.buckets"
[aggregationKey]="aggregation.key"
[size]="aggregation.bucketSize">
</ng-core-record-search-aggregation-buckets>
<ng-template #noResult>
<i class="small" translate *ngIf="aggregation.loaded">No result</i>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!--
RERO angular core
Copyright (C) 2021 RERO
Copyright (C) 2021 UCLouvain
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<div class="row mt-3">
<div class="col-12 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-calendar"></i></span>
</div>
<input class="form-control"
[(ngModel)]="dateRangeModel"
[bsValue]="dateRangeModel"
[bsConfig]="bsConfig"
bsDaterangepicker>
</div>
</div>
</div>
<div class="text-center mt-2">
<small>
<a href class="mx-3" (click)="$event.preventDefault(); updateFilter()" translate>Filter</a>
<a href class="mx-3" (click)="$event.preventDefault(); clearFilter()" *ngIf="hasQueryParam" translate>Clear filter</a>
</small>
</div>

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* RERO angular core
* Copyright (C) 2021 RERO
* Copyright (C) 2021 UCLouvain
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { AggregationDateRangeComponent } from './date-range.component';
import {TranslateModule} from '@ngx-translate/core';

describe('AggregationDateRangeComponent', () => {
let component: AggregationDateRangeComponent;
let fixture: ComponentFixture<AggregationDateRangeComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot()
],
declarations: [ AggregationDateRangeComponent ]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(AggregationDateRangeComponent);
component = fixture.componentInstance;
component.config = {
min: new Date(),
max: new Date(),
};
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* RERO angular core
* Copyright (C) 2021 RERO
* Copyright (C) 2021 UCLouvain
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {RecordSearchService} from '../../record-search.service';
import {Subscription} from 'rxjs';
import {TranslateService} from '@ngx-translate/core';

@Component({
selector: 'ng-core-aggregation-date-range',
templateUrl: './date-range.component.html'
})
export class AggregationDateRangeComponent implements OnInit, OnDestroy {

// COMPONENTS ATTRIBUTES ====================================================
/** Buckets list. */
@Input() buckets: Array<any>;
/** Aggregation key. */
@Input() key: string;
/** Components configuration. */
@Input() config: any;

/** model used for bsDatePicker field */
dateRangeModel: Date[];
/** bsDatePicker configuration. */
bsConfig: any = {
isAnimated: true,
containerClass: 'theme-dark-blue'
};
/** True if route have aggregation query param. */
hasQueryParam = false;

/** Subscription to search service. */
private _searchServiceSubscription: Subscription;

// CONSTRUCTOR & HOOKS ======================================================
constructor(
private _recordSearchService: RecordSearchService,
private _translateService: TranslateService
) { }

/** OnInit hook */
ngOnInit(): void {
// Define min/max values if not exists
if (!this.config.hasOwnProperty('min') || this.config.min < 0) {
this.config.min = 0;
}
if (!this.config.hasOwnProperty('max')) {
this.config.max = new Date();
this.config.max.setFullYear(this.config.max.getFullYear() + 1).getTime();
}

// Build bsDateRangePicker configuration
this.bsConfig.maxDate = new Date(this.config.max);
this.bsConfig.minDate = new Date(this.config.min);
const today = new Date();
this.bsConfig.ranges = [{
value: [new Date(new Date().setDate(today.getDate() - 7)), today],
label: this._translateService.instant(_('Last 7 days'))
}, {
value: [new Date(new Date().setDate(today.getDate() - 31)), today],
label: this._translateService.instant(_('Last month'))
}, {
value: [new Date(new Date().setDate(today.getDate() - 366)), today],
label: this._translateService.instant(_('Last year'))
}, {
value: [new Date(today.getFullYear(), today.getMonth(), 1), new Date(today.getFullYear(), today.getMonth() + 1, 0)],
label: this._translateService.instant(_('This month'))
}, {
value: [new Date(today.getFullYear(), 0, 1), new Date(today.getFullYear() + 1, 0, 1)],
label: this._translateService.instant(_('This year'))
}];

this.dateRangeModel = [this.config.min, this.config.max];
this._searchServiceSubscription = this._recordSearchService.aggregationsFilters.subscribe((filters: any) => {
if (!filters) {
return;
}
let filter = filters.find((element: any) => element.key === this.key);
if (filter) {
filter = filter.values[0].split('--').map((item: string) => {
return new Date(+item);
});
this.hasQueryParam = true;
this.dateRangeModel = filter;
} else {
this.dateRangeModel = [];
}
});
}

/** OnDestroy hook */
ngOnDestroy(): void {
this._searchServiceSubscription.unsubscribe();
}

// COMPONENTS FUNCTIONS =====================================================
/** Update aggregation filter. */
updateFilter() {
if (this.dateRangeModel && this.dateRangeModel[0] && this.dateRangeModel[1]) {
this._recordSearchService.updateAggregationFilter(this.key, [
`${this.dateRangeModel[0].getTime()}--${this.dateRangeModel[1].getTime()}`
]);
}
}

/** Clear aggregation filter. */
clearFilter() {
this._recordSearchService.updateAggregationFilter(this.key, []);
}

}

0 comments on commit 3fd4530

Please sign in to comment.