Commit a7cec011 authored by Jérémy Destin's avatar Jérémy Destin Committed by Guillaume Cornut
Browse files

feat: Add error message when no variables to show. Minor fixes. GNP-5430.

parent 18a8a208
......@@ -12,7 +12,7 @@ import { SiteCardComponent } from './site-card/site-card.component';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { NavbarComponent } from './navbar/navbar.component';
import { MapComponent } from './map/map.component';
import { NgbPaginationModule, NgbTabsetModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { NgbAlertModule, NgbPaginationModule, NgbTabsetModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { ReactiveFormsModule } from '@angular/forms';
import { SuggestionFieldComponent } from './form/suggestion-field/suggestion-field.component';
import { DocumentComponent } from './result-page/document/document.component';
......@@ -44,6 +44,7 @@ import { FacetsComponent } from './result-page/facets/facets.component';
NgbTypeaheadModule,
NgbPaginationModule,
NgbTabsetModule,
NgbAlertModule,
ReactiveFormsModule,
HttpClientModule
],
......
......@@ -6,7 +6,10 @@
<div>
<p>
An unexpected error occurred.<br/>
Refresh the page or try later.<br/>
<a href="javascript:window.location.reload(true)">
Refresh the page
</a>
or try later.<br/>
</p>
<small *ngIf="error.status" id="error-status">
Status: {{ error.status }}
......
......@@ -57,7 +57,8 @@
<ngb-tab title="Variable">
<ng-template ngbTabContent>
<gpds-trait-ontology-widget
[criteria$]="criteria$">
[criteria$]="criteria$"
(initialized)="traitWidgetInitialized.emit($event)">
</gpds-trait-ontology-widget>
</ng-template>
</ngb-tab>
......
import { Component, Input } from '@angular/core';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { DataDiscoveryCriteria } from '../model/data-discovery.model';
import { BehaviorSubject } from 'rxjs';
......@@ -9,4 +9,5 @@ import { BehaviorSubject } from 'rxjs';
})
export class FormComponent {
@Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>;
@Output() traitWidgetInitialized = new EventEmitter();
}
<p *ngIf="variablesList.length === 0" class="mt-1">
<ngb-alert [dismissible]="false">
<strong>Warning!</strong> No variables for the current criteria.
</ngb-alert>
</p>
<div id="trait-ontology-widget" class="mb-4"></div>
import { Component, Injectable, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { Component, EventEmitter, Injectable, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { CropOntologyWidget } from 'trait-ontology-widget/dist/module/cropOntologyWidget.module';
import { DataDiscoveryCriteria } from '../../model/data-discovery.model';
import { BehaviorSubject } from 'rxjs';
......@@ -27,6 +27,9 @@ export class CropOntologyWidgetFactory {
export class TraitOntologyWidgetComponent implements OnInit {
@Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>;
@Output() initialized = new EventEmitter();
variablesList: string[] = [];
private localCriteria: DataDiscoveryCriteria = null;
......@@ -56,11 +59,8 @@ export class TraitOntologyWidgetComponent implements OnInit {
const selectedNodeIds = this.localCriteria.topSelectedTraitOntologyIds;
this.widget.jsTreePanel.load().then(() => {
this.setSelected(selectedNodeIds);
this.localCriteria = {
...this.localCriteria,
observationVariableIds: this.getBottomSelected()
};
this.criteria$.next(this.localCriteria);
this.localCriteria.observationVariableIds = this.getBottomSelected();
this.initialized.emit(true);
});
initialized = true;
} else {
......@@ -69,8 +69,10 @@ export class TraitOntologyWidgetComponent implements OnInit {
const field = 'observationVariableIds';
const fetchSize = 2147483647;
this.gnpisService.suggest(field, fetchSize, '', this.localCriteria)
.subscribe(ids => {
this.variablesList = ids;
this.widget.showOnly(ids);
});
});
......
import { BrapiResults } from './brapi.model';
import { Params } from '@angular/router';
import { asArray } from '../utils';
export const MAX_RESULTS = 10000;
export const DEFAULT_PAGE_SIZE = 10;
export interface DataDiscoveryCriteria {
accessions: string[];
......@@ -18,20 +24,54 @@ export interface DataDiscoveryCriteria {
pageSize: number;
}
export function emptyCriteria(): DataDiscoveryCriteria {
return {
accessions: null,
crops: null,
facetFields: ['sources', 'types'],
germplasmLists: null,
observationVariableIds: null,
topSelectedTraitOntologyIds: null,
sources: null,
types: null,
page: 0,
pageSize: 10
};
export class DataDiscoveryCriteriaUtils {
static emptyCriteria(): DataDiscoveryCriteria {
return {
accessions: null,
crops: null,
facetFields: ['sources', 'types'],
germplasmLists: null,
observationVariableIds: null,
topSelectedTraitOntologyIds: null,
sources: null,
types: null,
page: 0,
pageSize: DEFAULT_PAGE_SIZE
};
}
static fromQueryParams(queryParams: Params): DataDiscoveryCriteria {
return {
...DataDiscoveryCriteriaUtils.emptyCriteria(),
crops: asArray(queryParams.crops),
germplasmLists: asArray(queryParams.germplasmLists),
accessions: asArray(queryParams.accessions),
sources: asArray(queryParams.sources),
types: asArray(queryParams.types),
// The URL should only contain top selected trait ontology node ids
topSelectedTraitOntologyIds: asArray(queryParams.observationVariableIds),
page: queryParams.page - 1 || 0
};
}
static toQueryParams(newCriteria: DataDiscoveryCriteria) {
return {
crops: newCriteria.crops,
accessions: newCriteria.accessions,
germplasmLists: newCriteria.germplasmLists,
sources: newCriteria.sources,
types: newCriteria.types,
// The URL should only contain top selected trait ontology node ids
observationVariableIds: newCriteria.topSelectedTraitOntologyIds,
page: newCriteria.page + 1
};
}
}
export interface DataDiscoverySource {
......
<div class="row justify-content-end">
<!-- Column for form -->
<div class="col-lg-9">
<!-- Reset all button -->
<div class="text-right">
<button type="button" class="btn btn-sm btn-danger reset-all" (click)="resetAll()">
<div class="text-right reset-all-div">
<button type="button" class="btn btn-sm btn-danger mt-1" (click)="resetAll()">
Reset all
</button>
</div>
<!-- Form -->
<gpds-form [criteria$]="criteria$"></gpds-form>
<gpds-form
#form
[criteria$]="criteria$">
</gpds-form>
</div>
<!-- Column for facets -->
......@@ -19,22 +23,44 @@
class="col-12 col-lg-12 col-sm-6"
*ngFor="let facet of facets"
[criteria$]="criteria$"
[facet]="facet"></gpds-facets>
[facet]="facet">
</gpds-facets>
</div>
</div>
<!-- Column for result -->
<div *ngIf="documents.length" class="col-lg-9">
<hr/>
<!-- Pagination status -->
<div *ngIf="pagination.totalResult" class="mb-4 small text-muted mt-4">
Results from {{ pagination.startResult | number }} to {{ pagination.endResult | number }}
over {{ pagination.totalResult | number }} documents
<span *ngIf="pagination.totalResult > pagination.maxResults">
<div class="container">
<div class="row result align-content-center">
<span class="col-4" id="result-part">
Results :
</span>
<span *ngIf="pagination.totalResult" class="col-8 text-right small text-muted pt-1">
From {{ pagination.startResult | number }} to {{ pagination.endResult | number }}
over {{ pagination.totalResult | number }} documents
<span *ngIf="pagination.totalResult > pagination.maxResults">
(limited to {{ pagination.maxResults | number }})
</span>
</span>
</div>
</div>
<!--Top page navigator-->
<div class="d-flex justify-content-center mt-3" *ngIf="pagination.totalPages > 1">
<!-- we add 1 to the page because ngb-pagination is 1 based -->
<ngb-pagination [page]="pagination.currentPage + 1"
(pageChange)="changePage($event)"
[collectionSize]="resultCount()"
[pageSize]="pagination.pageSize"
[maxSize]="5"
[boundaryLinks]="true"
[ellipses]="false"
size="sm">
</ngb-pagination>
</div>
<!-- Result document -->
......@@ -44,11 +70,12 @@
</gpds-document>
<!-- Pagination -->
<div>
<!--Bottom page navigator-->
<div class="d-flex justify-content-center mt-5" *ngIf="pagination.totalPages > 1">
<!-- we add 1 to the page because ngb-pagination is 1 based -->
<ngb-pagination [page]="pagination.currentPage + 1"
(pageChange)="changePage($event)"
[collectionSize]="collectionSize()"
[collectionSize]="resultCount()"
[pageSize]="pagination.pageSize"
[maxSize]="5"
[boundaryLinks]="true"
......
......@@ -5,6 +5,17 @@
color: $gray-300;
}
.reset-all {
margin-bottom: -60px;
.result {
border-top: 3px solid #c2c2c2;
border-bottom: 1px solid #c2c2c2;
}
#result-part {
font-size: 1.2rem;
font-weight: bold;
display: block;
}
.reset-all-div {
height: 0;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ResultPageComponent, URLCriteria } from './result-page.component';
import { ResultPageComponent } from './result-page.component';
import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute, ActivatedRouteSnapshot, Params, Router } from '@angular/router';
import { of } from 'rxjs';
import { fakeRoute } from 'ngx-speculoos';
import { DocumentComponent } from './document/document.component';
import { DataDiscoveryCriteria, DataDiscoveryDocument, DataDiscoverySource, emptyCriteria } from '../model/data-discovery.model';
import { DataDiscoveryCriteria, DataDiscoveryDocument, DataDiscoverySource } from '../model/data-discovery.model';
import { GnpisService } from '../gnpis.service';
import { BrapiResults } from '../model/brapi.model';
......@@ -96,7 +96,7 @@ describe('ResultPageComponent', () => {
const criteria = { crops: ['Wheat', 'Vitis'] } as DataDiscoveryCriteria;
component.criteria$.next(criteria);
const newQueryParams: URLCriteria = {
const newQueryParams = {
crops: criteria.crops,
accessions: criteria.accessions,
germplasmLists: criteria.germplasmLists,
......@@ -112,8 +112,7 @@ describe('ResultPageComponent', () => {
});
it('should fetch documents', () => {
const criteria = emptyCriteria();
component.fetchDocuments(criteria);
component.fetchDocumentsAndFacets();
expect(component.documents).not.toBe(null);
});
......
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { DataDiscoveryCriteria, DataDiscoveryDocument, DataDiscoveryFacet, emptyCriteria } from '../model/data-discovery.model';
import {
DataDiscoveryCriteria,
DataDiscoveryCriteriaUtils,
DataDiscoveryDocument,
DataDiscoveryFacet,
DEFAULT_PAGE_SIZE,
MAX_RESULTS
} from '../model/data-discovery.model';
import { BehaviorSubject } from 'rxjs';
import { GnpisService } from '../gnpis.service';
import { asArray } from '../utils';
import { filter } from 'rxjs/operators';
import { FormComponent } from '../form/form.component';
export interface URLCriteria {
accessions: string[];
crops: string[];
germplasmLists: string[];
observationVariableIds: string[];
sources: string[];
types: string[];
page: number;
}
@Component({
selector: 'gpds-result',
templateUrl: './result-page.component.html',
......@@ -24,20 +21,19 @@ export interface URLCriteria {
})
export class ResultPageComponent implements OnInit {
static MAX_RESULTS = 10000;
static PAGE_SIZE = 10;
@ViewChild('form') form: FormComponent;
criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(emptyCriteria());
criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria());
documents: DataDiscoveryDocument[] = [];
facets: DataDiscoveryFacet[] = [];
pagination = {
startResult: 1,
endResult: 10,
endResult: DEFAULT_PAGE_SIZE,
totalResult: null,
currentPage: 0,
pageSize: ResultPageComponent.PAGE_SIZE,
pageSize: DEFAULT_PAGE_SIZE,
totalPages: null,
maxResults: ResultPageComponent.MAX_RESULTS
maxResults: MAX_RESULTS
};
constructor(private route: ActivatedRoute,
......@@ -46,66 +42,56 @@ export class ResultPageComponent implements OnInit {
) {
}
fetchDocuments(criteria: DataDiscoveryCriteria) {
fetchDocumentsAndFacets() {
const criteria = this.criteria$.value;
this.gnpisService.search(criteria)
.subscribe(({ metadata, result, facets }) => {
this.documents = result.data;
const { currentPage, pageSize, totalCount, totalPages } = metadata.pagination;
this.pagination.currentPage = currentPage;
this.pagination.pageSize = pageSize;
this.pagination.totalPages = totalPages;
this.pagination.startResult = pageSize * currentPage + 1;
this.pagination.endResult = this.pagination.startResult + pageSize - 1;
this.pagination.totalResult = totalCount;
this.updatePagination(metadata.pagination);
this.facets = facets;
});
}
private updatePagination({ currentPage, pageSize, totalCount, totalPages }) {
this.pagination.currentPage = currentPage;
this.pagination.pageSize = pageSize;
this.pagination.totalPages = totalPages;
this.pagination.startResult = pageSize * currentPage + 1;
this.pagination.endResult = this.pagination.startResult + pageSize - 1;
this.pagination.totalResult = totalCount;
}
ngOnInit(): void {
const queryParams = this.route.snapshot.queryParams;
// Update criteria using URL query params
const criteria: DataDiscoveryCriteria = {
crops: asArray(queryParams.crops),
germplasmLists: asArray(queryParams.germplasmLists),
accessions: asArray(queryParams.accessions),
topSelectedTraitOntologyIds: asArray(queryParams.observationVariableIds),
observationVariableIds: [],
sources: asArray(queryParams.sources),
types: asArray(queryParams.types),
facetFields: ['sources', 'types'],
page: queryParams.page - 1 || 0,
pageSize: ResultPageComponent.PAGE_SIZE
};
this.criteria$.next(criteria);
this.criteria$.subscribe(newCriteria => {
newCriteria.page = 0;
this.fetchDocuments(newCriteria);
const newQueryParams: URLCriteria = {
crops: newCriteria.crops,
accessions: newCriteria.accessions,
germplasmLists: newCriteria.germplasmLists,
observationVariableIds: newCriteria.topSelectedTraitOntologyIds,
sources: newCriteria.sources,
types: newCriteria.types,
page: 1
};
this.router.navigate(['.'], {
relativeTo: this.route,
queryParams: newQueryParams
});
// Parse criteria from URL query params
const initialCriteria = DataDiscoveryCriteriaUtils.fromQueryParams(queryParams);
this.criteria$.next(initialCriteria);
this.form.traitWidgetInitialized.subscribe(() => {
this.fetchDocumentsAndFacets();
});
this.criteria$
.pipe(filter(c => c !== initialCriteria))
.subscribe(newCriteria => {
// Reset pagination
newCriteria.page = 0;
// Fetch documents and facets
this.fetchDocumentsAndFacets();
// Update URL query params
this.router.navigate(['.'], {
relativeTo: this.route,
queryParams: DataDiscoveryCriteriaUtils.toQueryParams(newCriteria)
});
});
}
collectionSize() {
resultCount() {
return Math.min(
this.pagination.totalResult,
ResultPageComponent.MAX_RESULTS - ResultPageComponent.PAGE_SIZE
MAX_RESULTS - DEFAULT_PAGE_SIZE
);
}
......@@ -116,17 +102,18 @@ export class ResultPageComponent implements OnInit {
// Use of empty param to force re-parse of URL in ngOnInit
queryParams: { empty: [] }
});
this.criteria$.next(emptyCriteria());
this.criteria$.next(DataDiscoveryCriteriaUtils.emptyCriteria());
}
changePage(page: number) {
const criteria = this.criteria$.value;
criteria.page = page - 1;
this.fetchDocuments(criteria);
this.fetchDocumentsAndFacets();
this.router.navigate(['.'], {
relativeTo: this.route,
queryParams: { page },
queryParamsHandling: 'merge'
});
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment