Created
October 28, 2017 13:17
-
-
Save bvasilenko/f9dae95fed4a9ff743f1b5f13641fc49 to your computer and use it in GitHub Desktop.
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
import { Injectable } from '@angular/core'; | |
import { Observable } from 'rxjs/Observable'; | |
import { Subscription } from 'rxjs/Subscription'; | |
import { Patient, PatientFilter, PatientDetailsResponse, TreatmentPlan } from 'app/models'; | |
import { Pagination, Sorting } from 'app/models/core'; | |
import { HttpService } from 'app/services'; | |
import { Status, Datasource, DatasourceFactory } from './core'; | |
import { sortBy, descendingSorter, randomInt, groupBy, map2array } from 'app/utils'; | |
interface PatientsDatasourceState { | |
status: Status; | |
data: Patient[]; | |
count: number; | |
sorting: Sorting; | |
filter: PatientFilter, | |
hasMore: boolean; | |
} | |
interface PatientsResponse { | |
count: number; | |
data: PatientDetailsResponse.Patient[]; | |
} | |
@Injectable() | |
export class PatientsDatasource { | |
private _store: Datasource<PatientsDatasourceState>; | |
private _request: Subscription; | |
constructor( | |
storeFactory: DatasourceFactory<PatientsDatasourceState>, | |
private _http: HttpService, | |
) { | |
this._store = storeFactory.new(this._emptyState()); | |
} | |
get patients() { | |
return this._store.state.filter(state => state.status !== Status.NotInitialized).map(state => state.data); | |
} | |
get status() { | |
return this._store.snapshot.status; | |
} | |
get count() { | |
return this._store.snapshot.count; | |
} | |
get sorting() { | |
return this._store.snapshot.sorting; | |
} | |
get filter() { | |
return this._store.snapshot.filter; | |
} | |
get hasMore() { | |
return this._store.snapshot.hasMore; | |
} | |
getPage(sorting: Sorting, filter: PatientFilter, pagination: Pagination) { | |
this._store.nextState(state => ({ | |
status: Status.Loading, | |
resources: [], | |
sorting, | |
filter, | |
})); | |
if (this._request) { | |
this._request.unsubscribe(); | |
} | |
this._request = | |
this._http.post('http://0.233.5.60/api/patients/', { | |
offset: pagination.offset, | |
limit: pagination.limit, | |
sortby: sorting.sortBy, | |
sortdesc: sorting.descending, | |
filter, | |
}) | |
.map(response => this._parsePatientsResponse(response.json() as PatientsResponse)) | |
.subscribe( | |
({data, count}) => { | |
this._store.nextState(state => ({ | |
status: Status.Ready, | |
data, | |
count, | |
sorting, | |
filter, | |
hasMore: pagination.limit === data.length, | |
})); | |
}, | |
error => { | |
this._store.nextState(state => ({ | |
status: Status.Failed, | |
})); | |
}); | |
} | |
loadMore(pagination: Pagination) { | |
this._store.nextState(state => { | |
state.status = Status.Updating; | |
}); | |
if (this._request) { | |
this._request.unsubscribe(); | |
} | |
this._request = | |
this._http.post('http://0.233.5.60/api/patients/', { | |
offset: pagination.offset, | |
limit: pagination.limit, | |
sortby: this.sorting.sortBy, | |
sortdesc: this.sorting.descending, | |
filter: this.filter, | |
}) | |
.map(response => this._parsePatientsResponse(response.json() as PatientsResponse)) | |
.subscribe( | |
({data, count}) => { | |
this._store.nextState(state => { | |
state.status = Status.Ready; | |
state.data = state.data.concat(data); | |
state.hasMore = data.length === pagination.limit; | |
}); | |
}, | |
error => { | |
this._store.nextState(state => ({ | |
status: Status.Failed, | |
})); | |
}); | |
} | |
private _emptyState() { | |
return { | |
status: Status.NotInitialized, | |
} as PatientsDatasourceState; | |
} | |
private _parsePatientsResponse(response: PatientsResponse) { | |
return { | |
data: response.data.map(patient => this._parsePatient(patient)), | |
count: response.count, | |
}; | |
} | |
private _parsePatient(obj: PatientDetailsResponse.Patient) { | |
const patient = { | |
firstName: obj.firstname, | |
treatmentPlans: obj.treatmentplans.map(treatmentPlan => this._parseTreatmentPlan(treatmentPlan)), | |
hoh: obj.hoh, | |
sex: obj.sex.toUpperCase(), | |
age: obj.age, | |
insuranceInfo: { | |
remainingBenefits: obj.insuranceplan_primary.remainingbenefits, | |
remainingDeductible: obj.insuranceplan_primary.remainingdeductible, | |
insuranceCompanyName: obj.insuranceplan_primary.employer.insurance_name, | |
groupNumber: obj.insuranceplan_primary.employer.groupnumber, | |
resetMonth: obj.insuranceplan_primary.resetmonth, | |
employerName: obj.insuranceplan_primary.employer.name, | |
}, | |
lastVisit: obj.lastvisit, | |
middleName: obj.middlename, | |
responsiblePartyId: obj.responsiblePartyId, | |
intelliRank: obj.intelliRank.overall, | |
middleInitial: obj.middleinitial, | |
address: { | |
country: obj.address.country, | |
city: obj.address.city, | |
address: obj.address.full_address, | |
zipcode: obj.address.zipcode, | |
state: obj.address.state, | |
}, | |
id: obj.id, | |
lastName: obj.lastname, | |
score: obj.intelliRank, | |
treatmentPlan: null, | |
playbook: null, | |
financeInfo: { | |
balance: -450, | |
overdue: true, | |
overdueDays: 35, | |
isCreditCardAttached: true, | |
isFinancialPolicySigned: true, | |
isMedicalConsentSigned: false | |
}, | |
notes: "Very friendly person who likes sports", | |
} as Patient; | |
this._setRandomProperties(patient); | |
return patient; | |
} | |
private _parseTreatmentPlan(obj: PatientDetailsResponse.TreatmentPlan) { | |
return { | |
"totalCost": obj.totalcost, | |
"name": obj.name, | |
"id": obj.id, | |
"phases": map2array(groupBy(obj.services, service => service.phase.toString()), (key, value) => ({ | |
phase: key, | |
items: value, | |
subtotal: value.reduce((sum, next) => sum + next.fee, 0), | |
})), | |
} as TreatmentPlan; | |
} | |
} |
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
import { Subject } from 'rxjs/Subject'; | |
import { Component, Input, OnInit, ChangeDetectorRef, ViewChild, ElementRef, HostListener, OnDestroy } from '@angular/core'; | |
import { ScrollService, ScrollInfo } from 'app/browser'; | |
import { BaseComponent } from 'app/components'; | |
import { UnsubscriberFactory } from 'app/services'; | |
import { PatientsDatasource } from 'app/datasources'; | |
import { Patient, PatientFilter } from 'app/models'; | |
import { Sorting } from 'app/models/core'; | |
import { Status } from 'app/datasources/core'; | |
import { ObjectMap, skipNulls } from 'app/utils'; | |
import { Animations } from 'app/shared'; | |
const DEFAULT_SORTING = { sortBy: 'fullname', descending: false }; | |
const PAGE_SIZE = 15; | |
@Component({ | |
selector: 'patients-page', | |
templateUrl: './patients.page.html', | |
animations: [ Animations.ngIfFadeIn ], | |
}) | |
export class PatientsPage extends BaseComponent implements OnInit, OnDestroy { | |
patients: Patient[]; | |
isTreatmentExpanded = {} as ObjectMap<boolean>; | |
currentPage = 0; | |
totalPages: number; | |
isScoreMode: boolean; | |
animateScore: boolean; | |
tableWidth = 0; | |
@ViewChild('table') table: ElementRef; | |
isFilterOpen = false; | |
filter = {} as PatientFilter; | |
searchText: string; | |
itemInViewport = {} as ObjectMap<boolean>; | |
private _applyFilter = new Subject<void>(); | |
private _scoreExpanded = {} as ObjectMap<boolean>; | |
constructor( | |
changeDetector: ChangeDetectorRef, | |
unsubscriberFactory: UnsubscriberFactory, | |
private _patientsDatasource: PatientsDatasource, | |
private _scrollService: ScrollService, | |
) { | |
super(changeDetector, unsubscriberFactory); | |
} | |
get isLoading() { | |
return this._patientsDatasource.status == Status.Loading; | |
} | |
get isLoadingMore() { | |
return this._patientsDatasource.status === Status.Updating; | |
} | |
get sorting() { | |
return this._patientsDatasource.sorting; | |
} | |
get hasMore() { | |
return this._patientsDatasource.hasMore; | |
} | |
@HostListener('window:scroll', ['$event']) | |
@HostListener('window:resize', ['$event']) | |
@HostListener('window:orientationchange', ['$event']) | |
onResize(): void { | |
if (this.table && this.table.nativeElement.clientWidth) { | |
this.tableWidth = this.table.nativeElement.clientWidth; | |
} | |
} | |
ngOnInit() { | |
this.when(skipNulls(this._patientsDatasource.patients), patients => this.onPatients(patients)); | |
this.when(this._applyFilter.debounceTime(500), () => this._setFilter(this._combineFilter(this.searchText, this.filter))); | |
this.getPage(DEFAULT_SORTING, this.filter, 0); | |
this._scrollService.attach(); | |
this.when(this._scrollService.scroll, e => this.onScroll(e)); | |
this.onResize(); | |
} | |
onPatients(patients) { | |
this.patients = patients; | |
this.totalPages = Math.ceil(this._patientsDatasource.count / PAGE_SIZE); | |
setTimeout(() => { | |
window.scrollBy(0, 1); | |
}, 100); | |
} | |
getPage(sorting: Sorting, filter: PatientFilter, page: number) { | |
this._patientsDatasource.getPage(sorting, filter, { offset: page * PAGE_SIZE, limit: PAGE_SIZE}); | |
} | |
loadMore() { | |
this.currentPage++; | |
this._patientsDatasource.loadMore({ offset: this.currentPage * PAGE_SIZE, limit: PAGE_SIZE}); | |
} | |
onScroll(e: ScrollInfo) { | |
if (this.isLoading || this.isLoadingMore) { | |
return; | |
} | |
if (e.isScrolledToBottom && this.hasMore) { | |
this.loadMore(); | |
} | |
} | |
applyFilter() { | |
this._applyFilter.next(); | |
} | |
isScoreExpanded(patientId: string) { | |
return this._scoreExpanded[patientId]; | |
} | |
setScoreExpanded(patientId: string, value: boolean) { | |
this._scoreExpanded[patientId] = value; | |
} | |
toggleScoreMode() { | |
this.isScoreMode = !this.isScoreMode; | |
this.animateScore = true; | |
setTimeout(() => { | |
this.animateScore = false; | |
}, 250); | |
} | |
ngOnDestroy() { | |
super.ngOnDestroy(); | |
this._scrollService.detach(); | |
} | |
private _setFilter(filter: PatientFilter) { | |
this.currentPage = 0; | |
this.getPage(this._patientsDatasource.sorting, filter, 0); | |
} | |
private _combineFilter(searchText: string, filter: PatientFilter) { | |
filter.searchText = searchText; | |
return filter; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment