adding ability to request health system if missing.
This commit is contained in:
parent
3b6d262c8d
commit
baa4958854
|
@ -0,0 +1,8 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type RequestHealthSystem struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
StreetAddress string `json:"street_address"`
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HealthSystemRequest(c *gin.Context) {
|
||||||
|
|
||||||
|
var healthSystemRequest models.RequestHealthSystem
|
||||||
|
if err := c.ShouldBindJSON(&healthSystemRequest); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//submit support request to Google Form
|
||||||
|
//https://medium.com/front-end-augustus-study-notes/custom-google-form-en-f7be4c27a98b
|
||||||
|
|
||||||
|
//source: https://docs.google.com/forms/d/e/1FAIpQLScH77CFpd3XAuwlUASFDJkOR54pZmt4AHqHa4xtZMGLXb1JIA/viewform?usp=sf_link
|
||||||
|
formUrl := "https://docs.google.com/forms/u/0/d/e/1FAIpQLScH77CFpd3XAuwlUASFDJkOR54pZmt4AHqHa4xtZMGLXb1JIA/formResponse"
|
||||||
|
|
||||||
|
supportRequestResponse, err := http.PostForm(formUrl, url.Values{
|
||||||
|
"entry.583209151": {healthSystemRequest.Email},
|
||||||
|
"entry.1753315374": {healthSystemRequest.Name},
|
||||||
|
"entry.1863832106": {healthSystemRequest.Website},
|
||||||
|
"entry.751017705": {healthSystemRequest.StreetAddress},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer supportRequestResponse.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(supportRequestResponse.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
//handle read response error
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||||
|
return
|
||||||
|
} else if supportRequestResponse.StatusCode != 200 {
|
||||||
|
//handle non 200 response
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": fmt.Sprintf("status code error: %d %s", supportRequestResponse.StatusCode, supportRequestResponse.Status)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", string(body))
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||||
|
}
|
|
@ -94,6 +94,7 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) {
|
||||||
|
|
||||||
api.GET("/glossary/code", handler.GlossarySearchByCode)
|
api.GET("/glossary/code", handler.GlossarySearchByCode)
|
||||||
api.POST("/support/request", handler.SupportRequest)
|
api.POST("/support/request", handler.SupportRequest)
|
||||||
|
api.POST("/healthsystem/request", handler.HealthSystemRequest)
|
||||||
|
|
||||||
secure := api.Group("/secure").Use(middleware.RequireAuth())
|
secure := api.Group("/secure").Use(middleware.RequireAuth())
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
<form *ngIf="!submitSuccess; else requestSuccess" (ngSubmit)="submitForm()" #supportRequestForm="ngForm">
|
||||||
|
|
||||||
|
<div class="modal-header">
|
||||||
|
<h6 class="modal-title">Request a Health System?</h6>
|
||||||
|
<button type="button" class="btn close" aria-label="Close" (click)="activeModal.dismiss('cancel')">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
|
||||||
|
Sharing your email and health system helps us prioritize our EHR integrations.
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<p class="mg-t-10 mg-b-5">Your email address<span ngbTooltip="required" class="text-danger">*</span></p>
|
||||||
|
<input
|
||||||
|
class="form-control" placeholder="my@emailaddress.com"
|
||||||
|
[(ngModel)]="formRequestHealthSystem.email" name="email" #email="ngModel" required email
|
||||||
|
>
|
||||||
|
|
||||||
|
<div *ngIf="email.invalid && (email.dirty || email.touched)" class="alert alert-danger">
|
||||||
|
<div *ngIf="email.errors?.['required']">
|
||||||
|
Email is required.
|
||||||
|
</div>
|
||||||
|
<div *ngIf="email.errors?.['email']">
|
||||||
|
Email must be valid.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<p class="mg-t-10 mg-b-5">Health system name<span ngbTooltip="required" class="text-danger">*</span></p>
|
||||||
|
<input
|
||||||
|
class="form-control" placeholder="Ascension, Mayo Clinic"
|
||||||
|
[(ngModel)]="formRequestHealthSystem.name" name="name" #name="ngModel" required minlength="4"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
|
||||||
|
<div *ngIf="name.errors?.['required']">
|
||||||
|
Health system name is required.
|
||||||
|
</div>
|
||||||
|
<div *ngIf="name.errors?.['minlength']">
|
||||||
|
Health system name must be at least 4 characters long.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<p class="mg-t-10 mg-b-5">Website<span ngbTooltip="required" class="text-danger">*</span></p>
|
||||||
|
<input
|
||||||
|
class="form-control"
|
||||||
|
[(ngModel)]="formRequestHealthSystem.website" name="website" #website="ngModel" required minlength="4"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div *ngIf="website.invalid && (website.dirty || website.touched)" class="alert alert-danger">
|
||||||
|
<div *ngIf="website.errors?.['required']">
|
||||||
|
Website is required.
|
||||||
|
</div>
|
||||||
|
<div *ngIf="website.errors?.['minlength']">
|
||||||
|
Website must be at least 4 characters long.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<p class="mg-t-10 mg-b-5">Street Address</p>
|
||||||
|
<input
|
||||||
|
class="form-control"
|
||||||
|
[(ngModel)]="formRequestHealthSystem.street_address" name="street_address" #street_address="ngModel"
|
||||||
|
>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="errorMsg" class="alert alert-danger mt-3" role="alert">
|
||||||
|
<strong>Error</strong> {{errorMsg}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button [disabled]="!supportRequestForm.form.valid || loading" type="submit" class="btn btn-indigo">Submit</button>
|
||||||
|
<button (click)="activeModal.dismiss('cancel')" type="button" class="btn btn-outline-light">Cancel</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-template #requestSuccess>
|
||||||
|
<div class="tx-center pd-y-20 pd-x-20">
|
||||||
|
<i class="far fa-check-square tx-100 tx-success lh-1 mg-t-20 d-inline-block"></i>
|
||||||
|
<h4 class="tx-success mg-b-20">Success!</h4>
|
||||||
|
<p class="mg-b-20 mg-x-20">Your request has been recorded. <br/>Thanks!</p>
|
||||||
|
<button type="button" (click)="activeModal.close('success')" class="btn btn-success pd-x-25">Close</button>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { FormRequestHealthSystemComponent } from './form-request-health-system.component';
|
||||||
|
|
||||||
|
describe('FormRequestHealthSystemComponent', () => {
|
||||||
|
let component: FormRequestHealthSystemComponent;
|
||||||
|
let fixture: ComponentFixture<FormRequestHealthSystemComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ FormRequestHealthSystemComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(FormRequestHealthSystemComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import {NgbActiveModal, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import {SupportRequest} from '../../models/fasten/support-request';
|
||||||
|
import {FormRequestHealthSystem} from '../../models/fasten/form-request-health-system';
|
||||||
|
import {environment} from '../../../environments/environment';
|
||||||
|
import {versionInfo} from '../../../environments/versions';
|
||||||
|
import {FastenApiService} from '../../services/fasten-api.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-form-request-health-system',
|
||||||
|
templateUrl: './form-request-health-system.component.html',
|
||||||
|
styleUrls: ['./form-request-health-system.component.scss']
|
||||||
|
})
|
||||||
|
export class FormRequestHealthSystemComponent implements OnInit {
|
||||||
|
formRequestHealthSystem: FormRequestHealthSystem = null
|
||||||
|
|
||||||
|
loading: boolean = false
|
||||||
|
submitSuccess: boolean = false
|
||||||
|
errorMsg: string = ""
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fastenApi: FastenApiService,
|
||||||
|
public activeModal: NgbActiveModal,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
resetForm() {
|
||||||
|
this.submitSuccess = false
|
||||||
|
let requestForm = new FormRequestHealthSystem()
|
||||||
|
requestForm.email = ''
|
||||||
|
requestForm.name = ''
|
||||||
|
requestForm.street_address = ''
|
||||||
|
requestForm.website = ''
|
||||||
|
|
||||||
|
this.formRequestHealthSystem = requestForm
|
||||||
|
}
|
||||||
|
|
||||||
|
submitForm() {
|
||||||
|
this.loading = true
|
||||||
|
this.fastenApi.requestHealthSystem(this.formRequestHealthSystem).subscribe((resp: any) => {
|
||||||
|
this.loading = false
|
||||||
|
this.submitSuccess = true
|
||||||
|
//show success toast? close modal?
|
||||||
|
},
|
||||||
|
(err)=>{
|
||||||
|
this.loading = false
|
||||||
|
console.error("an error occurred during request submission",err)
|
||||||
|
this.errorMsg = err || "An error occurred while submitting your request. Please try again later."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ import {FhirCardModule} from './fhir-card/fhir-card.module';
|
||||||
import {FhirDatatableModule} from './fhir-datatable/fhir-datatable.module';
|
import {FhirDatatableModule} from './fhir-datatable/fhir-datatable.module';
|
||||||
import { MedicalRecordWizardAddEncounterComponent } from './medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component';
|
import { MedicalRecordWizardAddEncounterComponent } from './medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component';
|
||||||
import { MedicalRecordWizardAddLabResultsComponent } from './medical-record-wizard-add-lab-results/medical-record-wizard-add-lab-results.component';
|
import { MedicalRecordWizardAddLabResultsComponent } from './medical-record-wizard-add-lab-results/medical-record-wizard-add-lab-results.component';
|
||||||
|
import { FormRequestHealthSystemComponent } from './form-request-health-system/form-request-health-system.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -87,6 +88,7 @@ import { MedicalRecordWizardAddLabResultsComponent } from './medical-record-wiza
|
||||||
MedicalSourcesConnectedComponent,
|
MedicalSourcesConnectedComponent,
|
||||||
MedicalSourcesCategoryLookupPipe,
|
MedicalSourcesCategoryLookupPipe,
|
||||||
MedicalSourcesCardComponent,
|
MedicalSourcesCardComponent,
|
||||||
|
FormRequestHealthSystemComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
ComponentsSidebarComponent,
|
ComponentsSidebarComponent,
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { FormRequestHealthSystem } from './form-request-health-system';
|
||||||
|
|
||||||
|
describe('FormRequestHealthSystem', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new FormRequestHealthSystem()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
export class FormRequestHealthSystem {
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
website: string
|
||||||
|
street_address: string
|
||||||
|
}
|
|
@ -73,12 +73,21 @@
|
||||||
>
|
>
|
||||||
|
|
||||||
<app-medical-sources-card class="col-sm-3 mg-b-20 px-3"
|
<app-medical-sources-card class="col-sm-3 mg-b-20 px-3"
|
||||||
|
|
||||||
*ngFor="let lighthouseBrand of availableLighthouseBrandList"
|
*ngFor="let lighthouseBrand of availableLighthouseBrandList"
|
||||||
[sourceInfo]="lighthouseBrand"
|
[sourceInfo]="lighthouseBrand"
|
||||||
[status]="status[lighthouseBrand.brand.id]"
|
[status]="status[lighthouseBrand.brand.id]"
|
||||||
(onClick)="connectModalHandler(contentModalRef, $event)"
|
(onClick)="connectModalHandler(contentModalRef, $event)"
|
||||||
></app-medical-sources-card>
|
></app-medical-sources-card>
|
||||||
|
|
||||||
|
<div *ngIf="availableLighthouseBrandList.length == 0" class="mg-b-60 w-100">
|
||||||
|
<div class="card card-body bg-gray-200 bd-0">
|
||||||
|
<p class="card-text">
|
||||||
|
<strong>No results</strong> Try another search term or <a (click)="showRequestHealthSystemModal()" class="link">request this health system</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div><!-- row -->
|
</div><!-- row -->
|
||||||
|
|
||||||
<div class="ht-40"></div>
|
<div class="ht-40"></div>
|
||||||
|
@ -144,7 +153,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<h6>Fasten Does not natively support <a href="https://en.wikipedia.org/wiki/Consolidated_Clinical_Document_Architecture" externalLink >CCDA</a> Health Records</h6>
|
<h6>Fasten does not natively support <a href="https://en.wikipedia.org/wiki/Consolidated_Clinical_Document_Architecture" externalLink >CCDA</a> Health Records</h6>
|
||||||
|
|
||||||
<p>However we can convert it automatically using software generously donated by the team at <a href="https://www.health-samurai.io/" externalLink>Health Samurai</a><br/><br/>
|
<p>However we can convert it automatically using software generously donated by the team at <a href="https://www.health-samurai.io/" externalLink>Health Samurai</a><br/><br/>
|
||||||
This converter is hosted by <a href="https://www.fastenhealth.com/" externalLink>Fasten Health, Inc.</a> and is subject to our <a href="https://policy.fastenhealth.com/privacy_policy.html" externalLink>Privacy Policy</a>.
|
This converter is hosted by <a href="https://www.fastenhealth.com/" externalLink>Fasten Health, Inc.</a> and is subject to our <a href="https://policy.fastenhealth.com/privacy_policy.html" externalLink>Privacy Policy</a>.
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {FormControl, FormGroup} from '@angular/forms';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import {PatientAccessBrand} from '../../models/patient-access-brands';
|
import {PatientAccessBrand} from '../../models/patient-access-brands';
|
||||||
import {PlatformService} from '../../services/platform.service';
|
import {PlatformService} from '../../services/platform.service';
|
||||||
|
import {FormRequestHealthSystemComponent} from '../../components/form-request-health-system/form-request-health-system.component';
|
||||||
|
|
||||||
export const sourceConnectWindowTimeout = 24*5000 //wait 2 minutes (5 * 24 = 120)
|
export const sourceConnectWindowTimeout = 24*5000 //wait 2 minutes (5 * 24 = 120)
|
||||||
|
|
||||||
|
@ -354,6 +355,17 @@ export class MedicalSourcesComponent implements OnInit {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showRequestHealthSystemModal(): Promise<boolean> {
|
||||||
|
return this.modalService.open(FormRequestHealthSystemComponent).result.then<boolean>(
|
||||||
|
(result) => {
|
||||||
|
//convert button clicked, .close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
).catch((reason) => {
|
||||||
|
// x or cancel button clicked, .dismiss()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import {SupportRequest} from '../models/fasten/support-request';
|
||||||
import {
|
import {
|
||||||
List
|
List
|
||||||
} from 'fhir/r4';
|
} from 'fhir/r4';
|
||||||
|
import {FormRequestHealthSystem} from '../models/fasten/form-request-health-system';
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
|
@ -348,4 +349,14 @@ export class FastenApiService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestHealthSystem(requestHealth: FormRequestHealthSystem): Observable<any> {
|
||||||
|
return this._httpClient.post<any>(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/healthsystem/request`, requestHealth)
|
||||||
|
.pipe(
|
||||||
|
map((response: ResponseWrapper) => {
|
||||||
|
// @ts-ignore
|
||||||
|
return {}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue