adding list fallback for unknown components (so users can still see the raw data).

Make sure patient information is included in the header for the source detail page.
This commit is contained in:
Jason Kulatunga 2022-09-25 21:02:12 -07:00
parent 61ed115dae
commit 9e7f6b5819
12 changed files with 168 additions and 5 deletions

View File

@ -0,0 +1,14 @@
<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="/source/{{resource.source_id}}/resource/{{resource.source_resource_id}}">{{resource.source_resource_id}}</a>
</li>
</ul>

View File

@ -0,0 +1,23 @@
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();
});
});

View File

@ -0,0 +1,24 @@
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()
}
}

View File

@ -1,6 +1,4 @@
<div> <div>
<h3>INSIDE THE GERNERIC COMPONENT</h3>
<ngx-datatable <ngx-datatable
#table #table
class="bootstrap" class="bootstrap"

View File

@ -29,6 +29,7 @@ import {ListAppointmentComponent} from '../list-generic-resource/list-appointmen
import {ListDeviceComponent} from '../list-generic-resource/list-device.component'; import {ListDeviceComponent} from '../list-generic-resource/list-device.component';
import {ListDiagnosticReportComponent} from '../list-generic-resource/list-diagnostic-report.component'; import {ListDiagnosticReportComponent} from '../list-generic-resource/list-diagnostic-report.component';
import {ListGoalComponent} from '../list-generic-resource/list-goal.component'; import {ListGoalComponent} from '../list-generic-resource/list-goal.component';
import {ListFallbackResourceComponent} from '../list-fallback-resource/list-fallback-resource.component';
@Component({ @Component({
selector: 'source-resource-list', selector: 'source-resource-list',
@ -90,6 +91,10 @@ export class ResourceListComponent implements OnInit, OnChanges {
} }
typeLookup(resourceType: string): Type<any> { typeLookup(resourceType: string): Type<any> {
if(!resourceType){
//dont try to render anything if the resourceType isnt set.
return null
}
switch(resourceType) { switch(resourceType) {
case "Appointment": { case "Appointment": {
return ListAppointmentComponent; return ListAppointmentComponent;
@ -158,8 +163,8 @@ export class ResourceListComponent implements OnInit, OnChanges {
return ListServiceRequestComponent; return ListServiceRequestComponent;
} }
default: { default: {
console.error("UNKNOWN COMPONENT TYPE", resourceType) console.warn("Unknown component type, using fallback", resourceType)
return null return ListFallbackResourceComponent;
} }
} }

View File

@ -30,6 +30,7 @@ import {ListAppointmentComponent} from './list-generic-resource/list-appointment
import {ListDeviceComponent} from './list-generic-resource/list-device.component'; import {ListDeviceComponent} from './list-generic-resource/list-device.component';
import {ListDiagnosticReportComponent} from './list-generic-resource/list-diagnostic-report.component'; import {ListDiagnosticReportComponent} from './list-generic-resource/list-diagnostic-report.component';
import {ListGoalComponent} from './list-generic-resource/list-goal.component'; import {ListGoalComponent} from './list-generic-resource/list-goal.component';
import { ListFallbackResourceComponent } from './list-fallback-resource/list-fallback-resource.component';
@NgModule({ @NgModule({
imports: [ imports: [
@ -66,6 +67,7 @@ import {ListGoalComponent} from './list-generic-resource/list-goal.component';
ListGoalComponent, ListGoalComponent,
ResourceListComponent, ResourceListComponent,
ResourceListOutletDirective, ResourceListOutletDirective,
ListFallbackResourceComponent,
], ],
exports: [ exports: [
ComponentsSidebarComponent, ComponentsSidebarComponent,

View File

@ -1,4 +1,5 @@
import {Source} from './source'; import {Source} from './source';
import {ResourceFhir} from './resource_fhir';
export class ResourceTypeCounts { export class ResourceTypeCounts {
count: number count: number
@ -9,4 +10,5 @@ export class ResourceTypeCounts {
export class SourceSummary { export class SourceSummary {
source: Source source: Source
resource_type_counts: ResourceTypeCounts[] resource_type_counts: ResourceTypeCounts[]
patient?: ResourceFhir
} }

View File

@ -144,7 +144,7 @@
<td class="align-middle"> <td class="align-middle">
<div class="media"> <div class="media">
<img [src]="'assets/sources/'+source.source_type+'.png'" <img [src]="'assets/sources/'+source.source_type+'.png'"
alt="Geeks" alt="{{source.source_type}}"
class="mr-3" class="mr-3"
style="width:100px;"> style="width:100px;">
<div class="media-body"> <div class="media-body">

View File

@ -1,5 +1,52 @@
<div class="az-content"> <div class="az-content">
<div class="container bg-light mb-4 p-2">
<div class="az-content-body">
<div class="panel panel-default patient col-xs-12">
<div class="row">
<div class="col-xs-2 col-sm-2 col-md-1">
<div class="">
<div class="patient-image-wrap embed-responsive-item">
<img [src]="'assets/sources/'+selectedSource.source_type+'.png'"
alt="{{selectedSource.source_type}}"
class="img-fluid">
</div>
</div>
</div>
<div class="col-xs-10 col-md-11">
<div class="patient-row row">
<div class="col-7 patient-name"><h3 class="pull-left text-primary">{{getPatientName()}}</h3></div>
<div class="col-5">
<a routerLink="/source/{{selectedSource.id}}/resource/{{selectedPatient.source_resource_id}}" class="btn btn-indigo btn-icon float-right">
<i class="fas fa-info-circle"></i>
</a>
</div>
</div>
<div class="row">
<div class="col-xs-4 col-sm-2 col-lg-1 text-right text-muted">Gender:</div>
<div class="col-xs-8 col-sm-3 col-lg-3 text-left p-0">{{getPatientGender()}}</div>
<div class="col-xs-4 col-sm-2 col-lg-1 text-right text-muted">DOB:</div>
<div class="col-xs-8 col-sm-5 col-lg-3 text-left p-0">{{getPatientDOB()}}</div>
<div class="col-xs-4 col-sm-2 col-lg-1 text-right text-muted">Age:</div>
<div class="col-xs-8 col-sm-3 col-lg-3 text-left p-0">58 year</div>
<div class="col-xs-4 col-sm-2 col-lg-1 text-right text-muted">Email</div>
<div class="col-xs-8 col-sm-5 col-lg-3 text-left p-0">{{getPatientEmail()}}</div>
<div class="col-xs-4 col-sm-2 col-lg-1 text-right text-muted">Phone:</div>
<div class="col-xs-8 col-sm-3 col-lg-3 text-left p-0">{{getPatientPhone()}}</div>
<div class="col-xs-4 col-sm-2 col-lg-1 text-right text-muted">Address:</div>
<div class="col-xs-8 col-sm-5 col-lg-3 text-left p-0">{{getPatientAddress()}}</div>
<div class="col-xs-4 col-sm-2 col-lg-1 text-right text-muted">ID:</div>
<div class="col-xs-8 col-sm-3 col-lg-3 text-left p-0">{{selectedPatient?.source_resource_id}}</div>
<div class="col-xs-4 col-sm-2 col-lg-1 text-right text-muted">MRN:</div>
<div class="col-xs-8 col-sm-5 col-lg-3 text-left p-0">{{getPatientMRN()}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container"> <div class="container">
<div class="az-content-left"> <div class="az-content-left">
<div class="az-content-breadcrumb"> <div class="az-content-breadcrumb">

View File

@ -2,6 +2,8 @@ import {Component, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {Source} from '../../models/fasten/source'; import {Source} from '../../models/fasten/source';
import {FastenApiService} from '../../services/fasten-api.service'; import {FastenApiService} from '../../services/fasten-api.service';
import {ResourceFhir} from '../../models/fasten/resource_fhir';
import {getPath} from '../../components/list-generic-resource/utils';
@Component({ @Component({
@ -12,6 +14,7 @@ import {FastenApiService} from '../../services/fasten-api.service';
export class SourceDetailComponent implements OnInit { export class SourceDetailComponent implements OnInit {
selectedSource: Source = null selectedSource: Source = null
selectedPatient: ResourceFhir = null
selectedResourceType: string = null selectedResourceType: string = null
resourceTypeCounts: { [name: string]: number } = {} resourceTypeCounts: { [name: string]: number } = {}
@ -27,6 +30,7 @@ export class SourceDetailComponent implements OnInit {
//always request the source summary //always request the source summary
this.fastenApi.getSourceSummary(this.route.snapshot.paramMap.get('source_id')).subscribe((sourceSummary) => { this.fastenApi.getSourceSummary(this.route.snapshot.paramMap.get('source_id')).subscribe((sourceSummary) => {
this.selectedSource = sourceSummary.source; this.selectedSource = sourceSummary.source;
this.selectedPatient = sourceSummary.patient;
for(let resourceTypeCount of sourceSummary.resource_type_counts){ for(let resourceTypeCount of sourceSummary.resource_type_counts){
this.resourceTypeCounts[resourceTypeCount.resource_type] = resourceTypeCount.count this.resourceTypeCounts[resourceTypeCount.resource_type] = resourceTypeCount.count
} }
@ -36,4 +40,42 @@ export class SourceDetailComponent implements OnInit {
selectResourceType(resourceType: string) { selectResourceType(resourceType: string) {
this.selectedResourceType = resourceType this.selectedResourceType = resourceType
} }
//functions to call on patient
getPatientName(){
// @ts-ignore
return `${getPath(this.selectedPatient?.payload, 'name.0.family')}, ${getPath(this.selectedPatient?.payload, 'name.0.given').join(' ')}`
}
getPatientGender(){
return getPath(this.selectedPatient?.payload, 'gender')
}
getPatientMRN(){
return getPath(this.selectedPatient?.payload, 'identifier.0.value')
}
getPatientEmail(){
// @ts-ignore
return (this.selectedPatient?.payload?.telecom || []).filter(
telecom => telecom.system === 'email',
)[0]?.value
}
getPatientDOB(){
return getPath(this.selectedPatient?.payload, 'birthDate')
}
getPatientPhone(){
// @ts-ignore
return (this.selectedPatient?.payload?.telecom || []).filter(
telecom => telecom.system === 'phone',
)[0]?.value
}
getPatientAge(){
return ''
}
getPatientAddress(){
const line = getPath(this.selectedPatient?.payload, 'address.0.line')
const city = getPath(this.selectedPatient?.payload, 'address.0.city')
const state = getPath(this.selectedPatient?.payload, 'address.0.state')
return `${line}, ${city}, ${state}`
}
} }

View File

@ -15,6 +15,12 @@
cursor: auto !important; cursor: auto !important;
} }
// if text is too long, we can truncate
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
/* /*