-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #105 from EBISPOT/develop
publication ui
- Loading branch information
Showing
15 changed files
with
447 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
src/app/core/models/rest/api-responses/publicationListApiResponse.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { ApiResponse } from './apiResponse'; | ||
import { Publication } from '../../publication'; | ||
|
||
export interface PublicationListApiResponse extends ApiResponse { | ||
_embedded: { | ||
solrPublicationDToes: Publication[] | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { PublicationService } from './publication.service'; | ||
|
||
describe('PublicationService', () => { | ||
let service: PublicationService; | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({}); | ||
service = TestBed.inject(PublicationService); | ||
}); | ||
|
||
it('should be created', () => { | ||
expect(service).toBeTruthy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { CurationHttpService } from './curation-http.service'; | ||
import { Observable } from 'rxjs'; | ||
import { HttpParams } from '@angular/common/http'; | ||
import { PublicationListApiResponse } from '../models/rest/api-responses/publicationListApiResponse'; | ||
|
||
@Injectable({ | ||
providedIn: 'root' | ||
}) | ||
export class PublicationService { | ||
|
||
constructor(private http: CurationHttpService) { } | ||
|
||
getPublications(size: number, page: number, sort: string, order: string, pmid: string, title: string, curator: string): Observable<PublicationListApiResponse> { | ||
let params: HttpParams = new HttpParams(); | ||
params = params | ||
.set('size', String(size)) | ||
.set('page', String(page)); | ||
if (sort) { | ||
params = params.set('sort', sort + ',' + order); | ||
} | ||
if (pmid) { | ||
params = params.set('pmid', pmid); | ||
} | ||
if (title) { | ||
params = params.set('title', title); | ||
} | ||
if (curator) { | ||
params = params.set('curator', curator); | ||
} | ||
return this.http.get('/publications', params); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/app/feature/publication/pages/publications-list/publications-list.component.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
.loading-shade { | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
right: 0; | ||
bottom: 0; | ||
background: rgba(0, 0, 0, 0.15); | ||
z-index: 1; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
.ellipsis-col { | ||
overflow: hidden; | ||
max-width: 200px; | ||
text-overflow: ellipsis; | ||
white-space: nowrap; | ||
} | ||
|
||
.mat-cell { | ||
padding: 0 4px; | ||
} |
155 changes: 155 additions & 0 deletions
155
src/app/feature/publication/pages/publications-list/publications-list.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
<div class="mt-0 grid gap-0 grid-cols-12 card-3b" | ||
style="min-height: 6.5rem; margin-bottom: 15px; !important; border:1px solid #999; background-color: white; padding: 20px 30px 15px "> | ||
<div class="col-span-12 sm:col-span-6"> | ||
<div class="col-span-12"> | ||
<div style="font-size: 1.5rem; line-height: 2.0rem;" class="font-normal">Publications</div> | ||
</div> | ||
<div class="col-span-6"> | ||
<div style="margin-top: 5px;"><a href="#" style="margin-right: 5px;">Dashboard</a> / <a> | ||
Publications</a></div> | ||
</div> | ||
</div> | ||
<div class="col-span-12 sm:col-span-6"> | ||
<div class="mt-5 sm:mt-1 sm:float-right"> | ||
<button mat-raised-button color="primary" style="font-size:12px; margin-right: -10px;" class="gwas-elevation"> | ||
<mat-icon>share</mat-icon> | ||
Export Data | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="mt-4 grid gap-0 grid-cols-12"> | ||
<aside *ngIf="showFilter" | ||
class="col-span-12 fixed z-40 overflow-y-scroll bottom-0 top-60 | ||
sm:overflow-y-hidden md:relative md:inset-y-auto md:col-span-3 h-96 | ||
lg:col-span-3 xl:col-span-2 ml-2 card-3b bg-white"> | ||
<div class="p-3" style="background-color: #EDF8F9; border-bottom: 1px solid #ccc;"> | ||
<span class="text-sm font-normal"> Filter By: </span> | ||
<a style="float: right; cursor: pointer;" (click)="toggleDisplay('filter')"> | ||
<mat-icon> chevron_left</mat-icon> | ||
</a> | ||
</div> | ||
|
||
<div class="p-2"> | ||
<form [formGroup]="filterForm" (ngSubmit)="submitFilter()" class="flex flex-col items-center mt-7"> | ||
<mat-form-field appearance="fill" class="w-4/5"> | ||
<mat-label>PMID</mat-label> | ||
<input matInput placeholder="Enter PMID" formControlName="pmid" type="number"> | ||
</mat-form-field> | ||
<mat-form-field appearance="fill" class="w-4/5"> | ||
<mat-label>Title</mat-label> | ||
<input matInput placeholder="Enter title" formControlName="title"> | ||
</mat-form-field> | ||
<mat-form-field appearance="fill" class="w-4/5"> | ||
<mat-label>Curator</mat-label> | ||
<input type="text" | ||
placeholder="Select curator" | ||
matInput | ||
formControlName="curator" | ||
[matAutocomplete]="auto"> | ||
<mat-autocomplete #auto="matAutocomplete"> | ||
<mat-option *ngFor="let option of filteredOptions | async" [value]="option"> | ||
{{option}} | ||
</mat-option> | ||
</mat-autocomplete> | ||
</mat-form-field> | ||
<div class=" justify-center mt-2"> | ||
<div> | ||
<button mat-raised-button type="submit" color="primary" (click)="search()"> | ||
<mat-icon>filter_alt</mat-icon> | ||
Filter | ||
</button> | ||
</div> | ||
</div> | ||
</form> | ||
</div> | ||
</aside> | ||
<main [ngClass]="{'sm:col-span-12 md:col-span-9 lg:col-span-9 xl:col-span-10 card-3b': showFilter}" class="col-span-12 ml-2"> | ||
<div class="card-3 mr-3"> | ||
<div class="p-3 bg-white" style="border-bottom: 1px solid #ccc;"> | ||
<span class="text-sm ml-1"> Manage Publications </span> | ||
</div> | ||
<div class="mat-elevation-z1 relative"> | ||
<div class="loading-shade" *ngIf="isLoadingResults"> | ||
<mat-spinner *ngIf="isLoadingResults"></mat-spinner> | ||
</div> | ||
<table mat-table [dataSource]="dataSource" matSort (matSortChange)="resetPaging()" matSortDisableClear class="w-full"> | ||
<ng-container matColumnDef="pmid"> | ||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>PMID</th> | ||
<td mat-cell *matCellDef="let row"> | ||
{{row.pmid}} | ||
</td> | ||
</ng-container> | ||
<ng-container matColumnDef="firstAuthor"> | ||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>First Author</th> | ||
<td mat-cell *matCellDef="let row"> | ||
{{row.firstAuthor}} | ||
</td> | ||
</ng-container> | ||
<ng-container matColumnDef="title"> | ||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>Title</th> | ||
<td mat-cell *matCellDef="let row" class="ellipsis-col"> | ||
<span matTooltip="{{row?.title}}" matTooltipClass="mat-tooltip">{{row?.title || 'NA'}}</span> | ||
</td> | ||
</ng-container> | ||
<ng-container matColumnDef="submitter"> | ||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>Submitter</th> | ||
<td mat-cell *matCellDef="let row"> | ||
{{row.submitter}} | ||
</td> | ||
</ng-container> | ||
<ng-container matColumnDef="curator"> | ||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>Curator</th> | ||
<td mat-cell *matCellDef="let row"> | ||
<mat-form-field class="mt-1"> | ||
<input type="text" | ||
placeholder="Curator" | ||
matInput | ||
[matAutocomplete]="auto" | ||
[(ngModel)]="row.curator"> | ||
<mat-autocomplete #auto="matAutocomplete"> | ||
<mat-option *ngFor="let option of filteredOptions | async" [value]="option"> | ||
{{option}} | ||
</mat-option> | ||
</mat-autocomplete> | ||
</mat-form-field> | ||
</td> | ||
</ng-container> | ||
<ng-container matColumnDef="curationStatus"> | ||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>Curation status</th> | ||
<td mat-cell *matCellDef="let row"> | ||
<mat-form-field class="mt-1"> | ||
<mat-label>Status</mat-label> | ||
<mat-select [(ngModel)]="row.curationStatus"> | ||
<mat-option *ngFor="let status of curationStatuses" [value]="status"> | ||
{{status}} | ||
</mat-option> | ||
</mat-select> | ||
</mat-form-field> | ||
</td> | ||
</ng-container> | ||
<ng-container matColumnDef="publicationDate"> | ||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>Publication Date</th> | ||
<td mat-cell *matCellDef="let row"> | ||
{{row.publicationDate}} | ||
</td> | ||
</ng-container> | ||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | ||
<tr class="hover:bg-gray-200 cursor-pointer" mat-row *matRowDef="let row; | ||
columns: displayedColumns; let index = index;" [ngClass]="{gray: index % 2 == 0}"></tr> | ||
<tr class="mat-row" *matNoDataRow> | ||
<td class="mat-cell" colspan="9999"> | ||
No data matching the search criteria. | ||
</td> | ||
</tr> | ||
</table> | ||
<mat-paginator [length]="resultsLength" [pageSize]="10" [pageSizeOptions]="[10, 20, 50, 100]" showFirstLastButtons="true"></mat-paginator> | ||
</div> | ||
</div> | ||
</main> | ||
<div class="fab-button-container" *ngIf="!showFilter"> | ||
<button (click)="toggleDisplay('filter')" mat-fab color="primary"> | ||
<mat-icon>filter_list</mat-icon> | ||
</button> | ||
</div> | ||
</div> |
25 changes: 25 additions & 0 deletions
25
src/app/feature/publication/pages/publications-list/publications-list.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { PublicationsListComponent } from './publications-list.component'; | ||
|
||
describe('PublicationsListComponent', () => { | ||
let component: PublicationsListComponent; | ||
let fixture: ComponentFixture<PublicationsListComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
declarations: [ PublicationsListComponent ] | ||
}) | ||
.compileComponents(); | ||
}); | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(PublicationsListComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
105 changes: 105 additions & 0 deletions
105
src/app/feature/publication/pages/publications-list/publications-list.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'; | ||
import { FormControl, FormGroup, Validators } from '@angular/forms'; | ||
import { merge, Observable, of } from 'rxjs'; | ||
import { catchError, map, startWith, switchMap } from 'rxjs/operators'; | ||
import { MatTableDataSource } from '@angular/material/table'; | ||
import { Publication } from '../../../../core/models/publication'; | ||
import { PublicationService } from '../../../../core/services/publication.service'; | ||
import { MatSort } from '@angular/material/sort'; | ||
import { MatPaginator } from '@angular/material/paginator'; | ||
|
||
@Component({ | ||
selector: 'app-publications-list', | ||
templateUrl: './publications-list.component.html', | ||
styleUrls: ['./publications-list.component.css'] | ||
}) | ||
export class PublicationsListComponent implements OnInit, AfterViewInit { | ||
isLoadingResults = true; | ||
pmid: string; | ||
options: string[] = ['Elliot Sollis', 'Laura Harris', 'Santhi Ramachandran', 'Elizabeth Lewis']; | ||
filteredOptions: Observable<string[]>; | ||
filterForm = new FormGroup({ | ||
pmid: new FormControl(''), | ||
title: new FormControl(''), | ||
curator: new FormControl('') | ||
}); | ||
showFilter = true; | ||
menuShow = false; | ||
visualization = false; | ||
@ViewChild(MatSort) sort: MatSort; | ||
@ViewChild(MatPaginator) paginator: MatPaginator; | ||
dataSource: MatTableDataSource<Publication>; | ||
displayedColumns: string[] = ['pmid', 'firstAuthor', 'title', 'submitter', 'curator', 'curationStatus', 'publicationDate']; | ||
curationStatuses: string[] = ['Level 1 ancestry done','Level 2 ancestry done','Level 1 curation done','Level 2 curation done','Publish study','Awaiting Curation','Outstanding Query','CNV Paper','Curation Abandoned','Conversion Problem','Unpublished from catalog','Pending author query','Awaiting EFO assignment','Requires re-curation','Preliminary review done','Level 3 curation done','Awaiting mapping','Awaiting literature','Permanently unpublished from catalog','Scientific pilot','Requires Review']; | ||
resultsLength = 0; | ||
|
||
constructor(private publicationService: PublicationService) { } | ||
|
||
ngOnInit(): void { | ||
this.filteredOptions = this.filterForm.controls.curator.valueChanges.pipe( | ||
startWith(''), | ||
map(value => this._filterCurator(value || '')), | ||
); | ||
} | ||
|
||
ngAfterViewInit() { | ||
// add this.sort.sortChange to merge() params and delete datasource.sort = sort in subscribe() to enable server-side sorting | ||
merge(this.paginator.page, this.sort.sortChange) | ||
.pipe( | ||
startWith({}), | ||
switchMap(() => { | ||
this.isLoadingResults = true; | ||
return this.publicationService | ||
.getPublications(this.paginator.pageSize, this.paginator.pageIndex, this.sort.active, this.sort.direction, null, null, null); | ||
}), | ||
map(data => { | ||
this.isLoadingResults = false; | ||
this.resultsLength = data.page.totalElements; | ||
return data?._embedded?.solrPublicationDToes; | ||
}), | ||
catchError(() => { | ||
this.isLoadingResults = false; | ||
return of([]); | ||
}) | ||
) | ||
.subscribe(value => { | ||
this.dataSource = new MatTableDataSource<Publication>(value); | ||
}); | ||
} | ||
|
||
resetFilters() { | ||
|
||
} | ||
|
||
search() { | ||
this.isLoadingResults = true; | ||
this.publicationService.getPublications(this.paginator.pageSize, this.paginator.pageIndex, this.sort.active, this.sort.direction, | ||
this.filterForm.value.pmid, this.filterForm.value.title, this.filterForm.value.curator).subscribe(value => { | ||
this.dataSource = new MatTableDataSource<Publication>(value?._embedded?.solrPublicationDToes); | ||
this.isLoadingResults = false; | ||
}); | ||
} | ||
|
||
toggleDisplay(compType: string) { | ||
if (compType === 'filter') { | ||
this.showFilter = (this.showFilter !== true); | ||
} else if (compType === 'menuShow') { | ||
this.menuShow = (this.menuShow !== true); | ||
} else if (compType === 'visualization') { | ||
this.visualization = (this.visualization !== true); | ||
} | ||
} | ||
|
||
submitFilter() { | ||
|
||
} | ||
|
||
private _filterCurator(value: string): string[] { | ||
const filterValue = value.toLowerCase(); | ||
return this.options.filter(option => option.toLowerCase().includes(filterValue)); | ||
} | ||
|
||
resetPaging() { | ||
this.paginator.pageIndex = 0; | ||
} | ||
} |
Oops, something went wrong.