adding pagination to the explore page. fixes #216
simplified fallback component for table list
This commit is contained in:
parent
83114f0067
commit
410696c26f
|
@ -3,6 +3,8 @@ package pkg
|
|||
type ResourceGraphType string
|
||||
|
||||
const (
|
||||
ResourceListPageSize int = 20
|
||||
|
||||
ContextKeyTypeConfig string = "CONFIG"
|
||||
ContextKeyTypeDatabase string = "REPOSITORY"
|
||||
ContextKeyTypeLogger string = "LOGGER"
|
||||
|
|
|
@ -400,13 +400,18 @@ func (sr *SqliteRepository) ListResources(ctx context.Context, queryOptions mode
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results := queryBuilder.
|
||||
queryBuilder = queryBuilder.
|
||||
Where(queryParam).
|
||||
Table(tableName).
|
||||
Find(&wrappedResourceModels)
|
||||
Table(tableName)
|
||||
|
||||
return wrappedResourceModels, results.Error
|
||||
if queryOptions.Limit > 0 {
|
||||
queryBuilder = queryBuilder.Limit(queryOptions.Limit).Offset(queryOptions.Offset)
|
||||
}
|
||||
return wrappedResourceModels, queryBuilder.Find(&wrappedResourceModels).Error
|
||||
} else {
|
||||
if queryOptions.Limit > 0 {
|
||||
queryBuilder = queryBuilder.Limit(queryOptions.Limit).Offset(queryOptions.Offset)
|
||||
}
|
||||
//there is no FHIR Resource name specified, so we're querying across all FHIR resources
|
||||
return sr.getResourcesFromAllTables(queryBuilder, queryParam)
|
||||
}
|
||||
|
|
|
@ -17,4 +17,8 @@ type ListResourceQueryOptions struct {
|
|||
SourceID string
|
||||
SourceResourceType string
|
||||
SourceResourceID string
|
||||
|
||||
//pagination
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -48,7 +49,16 @@ func ListResourceFhir(c *gin.Context) {
|
|||
if len(c.Query("sourceResourceID")) > 0 {
|
||||
listResourceQueryOptions.SourceResourceID = c.Query("sourceResourceID")
|
||||
}
|
||||
|
||||
if len(c.Query("page")) > 0 {
|
||||
listResourceQueryOptions.Limit = pkg.ResourceListPageSize //hardcoded number of resources per page
|
||||
pageNumb, err := strconv.Atoi(c.Query("page"))
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while calculating page number", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
listResourceQueryOptions.Offset = pageNumb * listResourceQueryOptions.Limit
|
||||
}
|
||||
wrappedResourceModels, err := databaseRepo.ListResources(c, listResourceQueryOptions)
|
||||
|
||||
if c.Query("sortBy") == "title" {
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<div class="alert alert-warning" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<strong>Warning!</strong> Fasten does not know how to display this resource type (yet). Click an item in the list below to see the raw data
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" *ngFor="let resource of resourceList">
|
||||
<a routerLink="/explore/{{resource.source_id}}/resource/{{resource.source_resource_id}}">{{resource.source_resource_id}}</a>
|
||||
</li>
|
||||
</ul>
|
|
@ -1,23 +0,0 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ListFallbackResourceComponent } from './list-fallback-resource.component';
|
||||
|
||||
describe('ListFallbackResourceComponent', () => {
|
||||
let component: ListFallbackResourceComponent;
|
||||
let fixture: ComponentFixture<ListFallbackResourceComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ListFallbackResourceComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ListFallbackResourceComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -1,25 +0,0 @@
|
|||
import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {ResourceListComponentInterface} from '../list-generic-resource/list-generic-resource.component';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-fallback-resource',
|
||||
templateUrl: './list-fallback-resource.component.html',
|
||||
styleUrls: ['./list-fallback-resource.component.scss']
|
||||
})
|
||||
export class ListFallbackResourceComponent implements OnInit, ResourceListComponentInterface {
|
||||
|
||||
@Input() resourceList: ResourceFhir[] = []
|
||||
|
||||
constructor(public changeRef: ChangeDetectorRef, public router: Router) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log("RESOURCE LIST INSIDE FALLBACK", this.resourceList)
|
||||
}
|
||||
|
||||
markForCheck(){
|
||||
this.changeRef.markForCheck()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<div>
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<strong>Warning!</strong> Fasten does not know how to display this resource type (yet). Click an item in the list below to see the raw data
|
||||
</div>
|
||||
|
||||
<ngx-datatable
|
||||
#table
|
||||
class="bootstrap"
|
||||
[columns]="columns"
|
||||
[columnMode]="ColumnMode.force"
|
||||
[headerHeight]="50"
|
||||
[footerHeight]="50"
|
||||
rowHeight="auto"
|
||||
[externalPaging]="true"
|
||||
[count]="totalElements"
|
||||
[offset]="currentPage.offset"
|
||||
[limit]="20"
|
||||
[rows]="rows"
|
||||
[selectionType]="SelectionType.single"
|
||||
(select)="onSelect($event)"
|
||||
(page)="changePage($event)"
|
||||
>
|
||||
</ngx-datatable>
|
||||
</div>
|
|
@ -0,0 +1,14 @@
|
|||
import {Component, OnChanges, OnInit} from '@angular/core';
|
||||
import {GenericColumnDefn, ListGenericResourceComponent, ResourceListComponentInterface} from './list-generic-resource.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-fallback',
|
||||
templateUrl: './list-fallback.component.html',
|
||||
styleUrls: ['./list-generic-resource.component.scss']
|
||||
})
|
||||
export class ListFallbackComponent extends ListGenericResourceComponent {
|
||||
columnDefinitions: GenericColumnDefn[] = [
|
||||
{ title: 'Id', versions: '*', getter: e => e.id },
|
||||
{ title: 'Title', versions: '*', getter: e => e.reasonCode?.[0] },
|
||||
]
|
||||
}
|
|
@ -19,11 +19,14 @@
|
|||
[headerHeight]="50"
|
||||
[footerHeight]="50"
|
||||
rowHeight="auto"
|
||||
[limit]="10"
|
||||
[externalPaging]="true"
|
||||
[count]="totalElements"
|
||||
[offset]="currentPage.offset"
|
||||
[limit]="20"
|
||||
[rows]="rows"
|
||||
[selectionType]="SelectionType.single"
|
||||
(select)="onSelect($event)"
|
||||
|
||||
(page)="changePage($event)"
|
||||
>
|
||||
</ngx-datatable>
|
||||
</div>
|
||||
|
|
|
@ -3,10 +3,15 @@ import {DatatableComponent, ColumnMode, SelectionType} from '@swimlane/ngx-datat
|
|||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {FORMATTERS, getPath, obsValue, attributeXTime} from './utils';
|
||||
import {Router} from '@angular/router';
|
||||
import {Observable, of} from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
|
||||
//all Resource list components must implement this Interface
|
||||
export interface ResourceListComponentInterface {
|
||||
resourceList: ResourceFhir[];
|
||||
resourceListType: string;
|
||||
totalElements: number;
|
||||
sourceId: string;
|
||||
markForCheck()
|
||||
}
|
||||
|
||||
|
@ -20,14 +25,22 @@ export class GenericColumnDefn {
|
|||
defaultValue?: string
|
||||
}
|
||||
|
||||
class PageInfo {
|
||||
offset: number = 0 //this is the current page number. 0 is the first page. Matches the ng-datatable structure
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-generic-resource',
|
||||
templateUrl: './list-generic-resource.component.html',
|
||||
styleUrls: ['./list-generic-resource.component.scss']
|
||||
})
|
||||
export class ListGenericResourceComponent implements OnInit, ResourceListComponentInterface {
|
||||
@Input() totalElements: number;
|
||||
@Input() resourceListType: string;
|
||||
@Input() sourceId: string;
|
||||
|
||||
@Input() resourceList: ResourceFhir[] = []
|
||||
currentPage: PageInfo = {offset: 0}
|
||||
// @Input() resourceList: ResourceFhir[] = []
|
||||
|
||||
// description: string
|
||||
// c: ListGenericResourceComponentColumn[] = []
|
||||
|
@ -40,25 +53,53 @@ export class ListGenericResourceComponent implements OnInit, ResourceListCompone
|
|||
ColumnMode = ColumnMode;
|
||||
SelectionType = SelectionType;
|
||||
|
||||
constructor(public changeRef: ChangeDetectorRef, public router: Router) {}
|
||||
constructor(public changeRef: ChangeDetectorRef, public router: Router, public fastenApi: FastenApiService) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log("INIT GENERIC RESOURCE")
|
||||
this.currentPage = {offset: 0}
|
||||
|
||||
this.renderList()
|
||||
this.changePage(this.currentPage)
|
||||
}
|
||||
markForCheck(){
|
||||
this.changeRef.markForCheck()
|
||||
}
|
||||
|
||||
renderList(){
|
||||
changePage(page: PageInfo){
|
||||
console.log("Requesting page:" + JSON.stringify(page))
|
||||
this.currentPage = page;
|
||||
this.fastenApi.getResources(this.resourceListType, this.sourceId, null, this.currentPage.offset)
|
||||
.subscribe((resourceList: ResourceFhir[]) => {
|
||||
this.renderList(resourceList)
|
||||
this.markForCheck()
|
||||
});
|
||||
}
|
||||
|
||||
// getResources(page?: number): Observable<ResourceFhir[]>{
|
||||
// // if(this.resourceListType && !this.resourceListCache[this.resourceListType]){
|
||||
// // // this resource type list has not been downloaded yet, do so now
|
||||
// return
|
||||
// .pipe(map((resourceList: ResourceFhir[]) => {
|
||||
// //cache this response so we can skip the request next time
|
||||
// // this.resourceListCache[this.resourceListType] = resourceList
|
||||
// return resourceList
|
||||
// }))
|
||||
// // } else {
|
||||
// // return of(this.resourceListCache[this.resourceListType] || [])
|
||||
// // }
|
||||
// }
|
||||
|
||||
|
||||
renderList(resourceList: ResourceFhir[]){
|
||||
console.log("GENERIC RESOURCE RENDERLSIT")
|
||||
this.columns = this.columnDefinitions.map((defn) => {
|
||||
let column = {name: defn.title, prop: defn.title.replace(/[^A-Z0-9]/ig, "_")}
|
||||
return column
|
||||
})
|
||||
|
||||
this.rows = this.resourceList.map((resource) => {
|
||||
this.rows = resourceList.map((resource) => {
|
||||
let row = {
|
||||
source_id: resource.source_id,
|
||||
source_resource_type: resource.source_resource_type,
|
||||
|
|
|
@ -29,7 +29,7 @@ import {ListAppointmentComponent} from '../list-generic-resource/list-appointmen
|
|||
import {ListDeviceComponent} from '../list-generic-resource/list-device.component';
|
||||
import {ListDiagnosticReportComponent} from '../list-generic-resource/list-diagnostic-report.component';
|
||||
import {ListGoalComponent} from '../list-generic-resource/list-goal.component';
|
||||
import {ListFallbackResourceComponent} from '../list-fallback-resource/list-fallback-resource.component';
|
||||
import {ListFallbackComponent} from '../list-generic-resource/list-fallback.component';
|
||||
|
||||
@Component({
|
||||
selector: 'source-resource-list',
|
||||
|
@ -41,9 +41,7 @@ export class ResourceListComponent implements OnInit, OnChanges {
|
|||
|
||||
@Input() source: Source;
|
||||
@Input() resourceListType: string;
|
||||
resourceListCache: { [name:string]: ResourceFhir[] } = {}
|
||||
|
||||
|
||||
@Input() selectedTotalElements: number;
|
||||
|
||||
//location to dynamically load the resource list
|
||||
@ViewChild(ResourceListOutletDirective, {static: true}) resourceListOutlet!: ResourceListOutletDirective;
|
||||
|
@ -63,30 +61,14 @@ export class ResourceListComponent implements OnInit, OnChanges {
|
|||
const viewContainerRef = this.resourceListOutlet.viewContainerRef;
|
||||
viewContainerRef.clear();
|
||||
|
||||
this.getResources().subscribe((resourceList) => {
|
||||
let componentType = this.typeLookup(this.resourceListType)
|
||||
if(componentType != null){
|
||||
console.log("Attempting to create component", this.resourceListType, componentType)
|
||||
const componentRef = viewContainerRef.createComponent<ResourceListComponentInterface>(componentType);
|
||||
componentRef.instance.resourceList = resourceList;
|
||||
componentRef.instance.markForCheck()
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getResources(): Observable<ResourceFhir[]>{
|
||||
|
||||
if(this.resourceListType && !this.resourceListCache[this.resourceListType]){
|
||||
// this resource type list has not been downloaded yet, do so now
|
||||
return this.fastenApi.getResources(this.resourceListType, this.source.id)
|
||||
.pipe(map((resourceList: ResourceFhir[]) => {
|
||||
//cache this response so we can skip the request next time
|
||||
this.resourceListCache[this.resourceListType] = resourceList
|
||||
return resourceList
|
||||
}))
|
||||
} else {
|
||||
return of(this.resourceListCache[this.resourceListType] || [])
|
||||
let componentType = this.typeLookup(this.resourceListType)
|
||||
if(componentType != null){
|
||||
console.log("Attempting to create component", this.resourceListType, componentType)
|
||||
const componentRef = viewContainerRef.createComponent<ResourceListComponentInterface>(componentType);
|
||||
componentRef.instance.totalElements = this.selectedTotalElements;
|
||||
componentRef.instance.resourceListType = this.resourceListType;
|
||||
componentRef.instance.sourceId = this.source.id;
|
||||
componentRef.instance.markForCheck()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,7 +146,7 @@ export class ResourceListComponent implements OnInit, OnChanges {
|
|||
}
|
||||
default: {
|
||||
console.warn("Unknown component type, using fallback", resourceType)
|
||||
return ListFallbackResourceComponent;
|
||||
return ListFallbackComponent;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import {ListEncounterComponent} from './list-generic-resource/list-encounter.com
|
|||
import {ListImmunizationComponent} from './list-generic-resource/list-immunization.component'
|
||||
import {ListMedicationAdministrationComponent} from './list-generic-resource/list-medication-administration.component';
|
||||
import {ListMedicationComponent} from './list-generic-resource/list-medication.component'
|
||||
import {ListFallbackComponent} from './list-generic-resource/list-fallback.component'
|
||||
import {ListMedicationDispenseComponent} from './list-generic-resource/list-medication-dispense.component';
|
||||
import {ListMedicationRequestComponent} from './list-generic-resource/list-medication-request.component'
|
||||
import {ListNutritionOrderComponent} from './list-generic-resource/list-nutrition-order.component';
|
||||
|
@ -30,7 +31,6 @@ import {ListAppointmentComponent} from './list-generic-resource/list-appointment
|
|||
import {ListDeviceComponent} from './list-generic-resource/list-device.component';
|
||||
import {ListDiagnosticReportComponent} from './list-generic-resource/list-diagnostic-report.component';
|
||||
import {ListGoalComponent} from './list-generic-resource/list-goal.component';
|
||||
import { ListFallbackResourceComponent } from './list-fallback-resource/list-fallback-resource.component';
|
||||
import {NgbCollapseModule, NgbModule} from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastComponent } from './toast/toast.component';
|
||||
import { MomentModule } from 'ngx-moment';
|
||||
|
@ -145,7 +145,7 @@ import { CodableConceptComponent } from './fhir/datatypes/codable-concept/codabl
|
|||
ListGoalComponent,
|
||||
ResourceListComponent,
|
||||
ResourceListOutletDirective,
|
||||
ListFallbackResourceComponent,
|
||||
ListFallbackComponent,
|
||||
ToastComponent,
|
||||
ReportHeaderComponent,
|
||||
ReportMedicalHistoryEditorComponent,
|
||||
|
@ -155,6 +155,7 @@ import { CodableConceptComponent } from './fhir/datatypes/codable-concept/codabl
|
|||
FhirResourceComponent,
|
||||
FhirResourceOutletDirective,
|
||||
FallbackComponent,
|
||||
ListFallbackComponent,
|
||||
DiagnosticReportComponent,
|
||||
NlmTypeaheadComponent,
|
||||
DocumentReferenceComponent,
|
||||
|
@ -171,6 +172,7 @@ import { CodableConceptComponent } from './fhir/datatypes/codable-concept/codabl
|
|||
DiagnosticReportComponent,
|
||||
DocumentReferenceComponent,
|
||||
FallbackComponent,
|
||||
ListFallbackComponent,
|
||||
FhirResourceComponent,
|
||||
FhirResourceOutletDirective,
|
||||
ImmunizationComponent,
|
||||
|
|
|
@ -63,7 +63,11 @@
|
|||
</div>
|
||||
<div class="az-content-body pd-lg-l-40 d-flex flex-column">
|
||||
|
||||
<source-resource-list [source]="selectedSource" [resourceListType]="selectedResourceType"></source-resource-list>
|
||||
<source-resource-list
|
||||
[source]="selectedSource"
|
||||
[resourceListType]="selectedResourceType"
|
||||
[selectedTotalElements]="selectedTotalElements"
|
||||
></source-resource-list>
|
||||
|
||||
</div><!-- az-content-body -->
|
||||
</div><!-- container -->
|
||||
|
|
|
@ -16,6 +16,7 @@ export class SourceDetailComponent implements OnInit {
|
|||
selectedSource: Source = null
|
||||
selectedPatient: ResourceFhir = null
|
||||
selectedResourceType: string = null
|
||||
selectedTotalElements: number = 0
|
||||
|
||||
resourceTypeCounts: { [name: string]: number } = {}
|
||||
|
||||
|
@ -43,6 +44,7 @@ export class SourceDetailComponent implements OnInit {
|
|||
|
||||
selectResourceType(resourceType: string) {
|
||||
this.selectedResourceType = resourceType
|
||||
this.selectedTotalElements = this.resourceTypeCounts[resourceType]
|
||||
}
|
||||
|
||||
//functions to call on patient
|
||||
|
|
|
@ -138,7 +138,7 @@ export class FastenApiService {
|
|||
);
|
||||
}
|
||||
|
||||
getResources(sourceResourceType?: string, sourceID?: string, sourceResourceID?: string): Observable<ResourceFhir[]> {
|
||||
getResources(sourceResourceType?: string, sourceID?: string, sourceResourceID?: string, page?: number): Observable<ResourceFhir[]> {
|
||||
let queryParams = {}
|
||||
if(sourceResourceType){
|
||||
queryParams["sourceResourceType"] = sourceResourceType
|
||||
|
@ -150,6 +150,9 @@ export class FastenApiService {
|
|||
if(sourceResourceID){
|
||||
queryParams["sourceResourceID"] = sourceResourceID
|
||||
}
|
||||
if(page !== undefined){
|
||||
queryParams["page"] = page
|
||||
}
|
||||
|
||||
return this._httpClient.get<any>(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/resource/fhir`, {params: queryParams})
|
||||
.pipe(
|
||||
|
|
Loading…
Reference in New Issue