Skip to content

Instantly share code, notes, and snippets.

@bvasilenko
Created October 28, 2017 13:17
Show Gist options
  • Save bvasilenko/f9dae95fed4a9ff743f1b5f13641fc49 to your computer and use it in GitHub Desktop.
Save bvasilenko/f9dae95fed4a9ff743f1b5f13641fc49 to your computer and use it in GitHub Desktop.
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;
}
}
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