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.POST("/support/request", handler.SupportRequest)
|
||||
api.POST("/healthsystem/request", handler.HealthSystemRequest)
|
||||
|
||||
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 { 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 { FormRequestHealthSystemComponent } from './form-request-health-system/form-request-health-system.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -87,6 +88,7 @@ import { MedicalRecordWizardAddLabResultsComponent } from './medical-record-wiza
|
|||
MedicalSourcesConnectedComponent,
|
||||
MedicalSourcesCategoryLookupPipe,
|
||||
MedicalSourcesCardComponent,
|
||||
FormRequestHealthSystemComponent,
|
||||
],
|
||||
exports: [
|
||||
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"
|
||||
|
||||
*ngFor="let lighthouseBrand of availableLighthouseBrandList"
|
||||
[sourceInfo]="lighthouseBrand"
|
||||
[status]="status[lighthouseBrand.brand.id]"
|
||||
(onClick)="connectModalHandler(contentModalRef, $event)"
|
||||
></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 class="ht-40"></div>
|
||||
|
@ -144,7 +153,7 @@
|
|||
</div>
|
||||
|
||||
<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/>
|
||||
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 {PatientAccessBrand} from '../../models/patient-access-brands';
|
||||
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)
|
||||
|
||||
|
@ -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 {
|
||||
List
|
||||
} from 'fhir/r4';
|
||||
import {FormRequestHealthSystem} from '../models/fasten/form-request-health-system';
|
||||
@Injectable({
|
||||
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