working app, everything except login and metadata is stored in browser storage.

This commit is contained in:
Jason Kulatunga 2022-10-07 09:00:24 -07:00
parent 17fa95008a
commit 99a9ac67d7
10 changed files with 62 additions and 29 deletions

View File

@ -18,7 +18,7 @@ const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' }, { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent, canActivate: [ CanActivateAuthGuard] }, { path: 'dashboard', component: DashboardComponent, canActivate: [ CanActivateAuthGuard] },
{ path: 'source/:source_id', component: SourceDetailComponent, canActivate: [ CanActivateAuthGuard] }, { path: 'source/:source_id', component: SourceDetailComponent, canActivate: [ CanActivateAuthGuard] },
{ path: 'source/:source_id/resource/:resource_id', component: ResourceDetailComponent, canActivate: [ CanActivateAuthGuard] }, { path: 'resource/:resource_id', component: ResourceDetailComponent, canActivate: [ CanActivateAuthGuard] },
{ path: 'sources', component: MedicalSourcesComponent, canActivate: [ CanActivateAuthGuard] }, { path: 'sources', component: MedicalSourcesComponent, canActivate: [ CanActivateAuthGuard] },
{ path: 'sources/callback/:source_type', component: MedicalSourcesComponent, canActivate: [ CanActivateAuthGuard] }, { path: 'sources/callback/:source_type', component: MedicalSourcesComponent, canActivate: [ CanActivateAuthGuard] },

View File

@ -9,6 +9,6 @@
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item" *ngFor="let resource of resourceList"> <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> <a routerLink="/resource/{{getResourceIdEncoded(resource)}}">{{resource.source_resource_id}}</a>
</li> </li>
</ul> </ul>

View File

@ -1,7 +1,8 @@
import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
import {ResourceFhir} from '../../models/fasten/resource_fhir'; import {ResourceFhir} from '../../../lib/models/database/resource_fhir';
import {ResourceListComponentInterface} from '../list-generic-resource/list-generic-resource.component'; import {ResourceListComponentInterface} from '../list-generic-resource/list-generic-resource.component';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {Base64} from '../../../lib/utils/base64';
@Component({ @Component({
selector: 'app-list-fallback-resource', selector: 'app-list-fallback-resource',
@ -21,4 +22,10 @@ export class ListFallbackResourceComponent implements OnInit, ResourceListCompo
markForCheck(){ markForCheck(){
this.changeRef.markForCheck() this.changeRef.markForCheck()
} }
//TODO this should eb moved to a Pipe
getResourceIdEncoded(resource: ResourceFhir){
return Base64.Encode(resource._id)
}
} }

View File

@ -1,9 +1,9 @@
import {ChangeDetectorRef, Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild} from '@angular/core'; import {ChangeDetectorRef, Component, Input, OnInit, ViewChild} from '@angular/core';
import {DatatableComponent, ColumnMode, SelectionType} from '@swimlane/ngx-datatable'; import {DatatableComponent, ColumnMode, SelectionType} from '@swimlane/ngx-datatable';
import {ResourceFhir} from '../../models/fasten/resource_fhir'; import {ResourceFhir} from '../../../lib/models/database/resource_fhir';
import {FORMATTERS, getPath, obsValue, attributeXTime} from './utils'; import {FORMATTERS, getPath, obsValue, attributeXTime} from './utils';
import {observableToBeFn} from 'rxjs/internal/testing/TestScheduler';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {Base64} from '../../../lib/utils/base64';
//all Resource list components must implement this Interface //all Resource list components must implement this Interface
export interface ResourceListComponentInterface { export interface ResourceListComponentInterface {
@ -61,6 +61,7 @@ export class ListGenericResourceComponent implements OnInit, ResourceListCompone
this.rows = this.resourceList.map((resource) => { this.rows = this.resourceList.map((resource) => {
let row = { let row = {
_id: resource._id,
source_id: resource.source_id, source_id: resource.source_id,
source_resource_type: resource.source_resource_type, source_resource_type: resource.source_resource_type,
source_resource_id: resource.source_resource_id source_resource_id: resource.source_resource_id
@ -68,7 +69,7 @@ export class ListGenericResourceComponent implements OnInit, ResourceListCompone
this.columnDefinitions.forEach((defn) => { this.columnDefinitions.forEach((defn) => {
try{ try{
let resourceProp = defn.getter(resource.payload) let resourceProp = defn.getter(resource.resource_raw)
let resourceFormatted = defn.format ? FORMATTERS[defn.format](resourceProp) : resourceProp let resourceFormatted = defn.format ? FORMATTERS[defn.format](resourceProp) : resourceProp
row[defn.title.replace(/[^A-Z0-9]/ig, "_")] = resourceFormatted row[defn.title.replace(/[^A-Z0-9]/ig, "_")] = resourceFormatted
}catch (e){ }catch (e){
@ -79,9 +80,14 @@ export class ListGenericResourceComponent implements OnInit, ResourceListCompone
}) })
} }
/**
* The selected object is NOT a ResourceFHIR, its actually a dynamically created row object
* created in renderList()
* @param selected
*/
onSelect({ selected }) { onSelect({ selected }) {
console.log('Select Event', selected); console.log('Select Event', selected);
this.router.navigateByUrl(`/source/${selected[0].source_id}/resource/${selected[0].source_resource_id}`); this.router.navigateByUrl(`/resource/${Base64.Encode(selected[0]._id)}`);
} }
} }

View File

@ -1,8 +1,8 @@
import {ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, SimpleChanges, Type, ViewChild} from '@angular/core'; import {ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, SimpleChanges, Type, ViewChild} from '@angular/core';
import {FastenApiService} from '../../services/fasten-api.service'; import {FastenDbService} from '../../services/fasten-db.service';
import {Source} from '../../models/fasten/source'; import {Source} from '../../../lib/models/database/source';
import {Observable, of} from 'rxjs'; import {Observable, of} from 'rxjs';
import {ResourceFhir} from '../../models/fasten/resource_fhir'; import {ResourceFhir} from '../../../lib/models/database/resource_fhir';
import {ListAdverseEventComponent} from '../list-generic-resource/list-adverse-event.component'; import {ListAdverseEventComponent} from '../list-generic-resource/list-adverse-event.component';
import {ListCommunicationComponent} from '../list-generic-resource/list-communication.component'; import {ListCommunicationComponent} from '../list-generic-resource/list-communication.component';
import {ListConditionComponent} from '../list-generic-resource/list-condition.component'; import {ListConditionComponent} from '../list-generic-resource/list-condition.component';
@ -49,7 +49,7 @@ export class ResourceListComponent implements OnInit, OnChanges {
@ViewChild(ResourceListOutletDirective, {static: true}) resourceListOutlet!: ResourceListOutletDirective; @ViewChild(ResourceListOutletDirective, {static: true}) resourceListOutlet!: ResourceListOutletDirective;
constructor(private fastenApi: FastenApiService) { } constructor(private fastenDb: FastenDbService) { }
ngOnInit(): void { ngOnInit(): void {
this.loadComponent() this.loadComponent()
@ -63,7 +63,7 @@ export class ResourceListComponent implements OnInit, OnChanges {
const viewContainerRef = this.resourceListOutlet.viewContainerRef; const viewContainerRef = this.resourceListOutlet.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
this.getResources().subscribe((resourceList) => { this.getResources().then((resourceList) => {
let componentType = this.typeLookup(this.resourceListType) let componentType = this.typeLookup(this.resourceListType)
if(componentType != null){ if(componentType != null){
console.log("Attempting to create component", this.resourceListType, componentType) console.log("Attempting to create component", this.resourceListType, componentType)
@ -75,18 +75,19 @@ export class ResourceListComponent implements OnInit, OnChanges {
}) })
} }
getResources(): Observable<ResourceFhir[]>{ getResources(): Promise<ResourceFhir[]>{
if(this.resourceListType && !this.resourceListCache[this.resourceListType]){ if(this.resourceListType && !this.resourceListCache[this.resourceListType]){
// this resource type list has not been downloaded yet, do so now // this resource type list has not been downloaded yet, do so now
return this.fastenApi.getResources(this.resourceListType, this.source.id) return this.fastenDb.GetResourcesForSource(this.source._id, this.resourceListType)
.pipe(map((resourceList: ResourceFhir[]) => { .then((paginatedResponse) => {
let resourceList = paginatedResponse.rows as ResourceFhir[]
//cache this response so we can skip the request next time //cache this response so we can skip the request next time
this.resourceListCache[this.resourceListType] = resourceList this.resourceListCache[this.resourceListType] = resourceList
return resourceList return resourceList
})) })
} else { } else {
return of(this.resourceListCache[this.resourceListType] || []) return Promise.resolve(this.resourceListCache[this.resourceListType] || [])
} }
} }

View File

@ -3,11 +3,11 @@
<div class="az-content-body"> <div class="az-content-body">
<div class="az-content-breadcrumb"> <div class="az-content-breadcrumb">
<span>Resource</span> <span>Resource</span>
<span>{{resource.source_resource_type}}</span> <span>{{resource?.source_resource_type}}</span>
<span>{{resource.source_resource_id}}</span> <span>{{resource?.source_resource_id}}</span>
</div> </div>
<pre *ngIf="resource"><code [highlight]="resource.payload | json"></code></pre> <pre *ngIf="resource"><code [highlight]="resource.resource_raw | json"></code></pre>
</div><!-- az-content-body --> </div><!-- az-content-body -->
</div> </div>

View File

@ -1,8 +1,8 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import {FastenApiService} from '../../services/fasten-api.service'; import {FastenDbService} from '../../services/fasten-db.service';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {Source} from '../../models/fasten/source'; import {ResourceFhir} from '../../../lib/models/database/resource_fhir';
import {ResourceFhir} from '../../models/fasten/resource_fhir'; import {Base64} from '../../../lib/utils/base64';
@Component({ @Component({
selector: 'app-resource-detail', selector: 'app-resource-detail',
@ -13,12 +13,13 @@ export class ResourceDetailComponent implements OnInit {
resource: ResourceFhir = null resource: ResourceFhir = null
constructor(private fastenApi: FastenApiService, private router: Router, private route: ActivatedRoute) { constructor(private fastenDb: FastenDbService, private router: Router, private route: ActivatedRoute) {
} }
ngOnInit(): void { ngOnInit(): void {
//always request the resource by id //always request the resource by id
this.fastenApi.getResourceBySourceId(this.route.snapshot.paramMap.get('source_id'), this.route.snapshot.paramMap.get('resource_id')).subscribe((resourceFhir) => { this.fastenDb.GetResource(Base64.Decode(this.route.snapshot.paramMap.get('resource_id')))
.then((resourceFhir) => {
this.resource = resourceFhir; this.resource = resourceFhir;
}); });
} }

View File

@ -45,7 +45,7 @@ export class SourceDetailComponent implements OnInit {
//functions to call on patient //functions to call on patient
getPatientName(){ getPatientName(){
// @ts-ignore // @ts-ignore
return `${getPath(this.selectedPatient?.payload, 'name.0.family')}, ${getPath(this.selectedPatient?.payload, 'name.0.given').join(' ')}` return `${getPath(this.selectedPatient?.resource_raw, 'name.0.family')}, ${getPath(this.selectedPatient?.resource_raw, 'name.0.given').join(' ')}`
} }
getPatientGender(){ getPatientGender(){
return getPath(this.selectedPatient?.resource_raw, 'gender') return getPath(this.selectedPatient?.resource_raw, 'gender')

View File

@ -42,4 +42,5 @@ export interface IDatabaseRepository {
CreateResources(resources: ResourceFhir[]): Promise<string[]> CreateResources(resources: ResourceFhir[]): Promise<string[]>
GetResource(resource_id: string): Promise<ResourceFhir> GetResource(resource_id: string): Promise<ResourceFhir>
GetResources(): Promise<IDatabasePaginatedResponse> GetResources(): Promise<IDatabasePaginatedResponse>
GetResourcesForSource(source_id: string, source_resource_type?: string): Promise<IDatabasePaginatedResponse>
} }

View File

@ -183,6 +183,23 @@ export class PouchdbRepository implements IDatabaseRepository {
}) })
} }
public async GetResourcesForSource(source_id: string, source_resource_type?: string): Promise<IDatabasePaginatedResponse> {
let prefix = `${DocType.ResourceFhir}:${Base64.Encode(source_id)}`
if(source_resource_type){
prefix += `:${source_resource_type}`
}
return this.findDocumentByPrefix(prefix, true)
.then((docWrapper) => {
docWrapper.rows = docWrapper.rows.map((result) => {
return new ResourceFhir(result.doc)
})
return docWrapper
})
}
/////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////
// CRUD Operators // CRUD Operators