Skip to content

Commit b373434

Browse files
authored
Merge pull request #4 from fastenhealth/research_study
WIP research study component.
2 parents 4680d2c + a208b35 commit b373434

File tree

9 files changed

+236
-5
lines changed

9 files changed

+236
-5
lines changed

src/app/app-routing.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
MedicalRecordsExportCallbackComponent
99
} from './pages/medical-records-export-callback/medical-records-export-callback.component';
1010
import { TefcaIasBetaComponent } from './pages/tefca-ias-beta/tefca-ias-beta.component';
11+
import { ResearchStudyComponent } from './pages/research-study/research-study.component';
1112

1213
const routes: Routes = [
1314

@@ -17,6 +18,7 @@ const routes: Routes = [
1718
{ path: 'catalog/editor', component: MedicalSourcesEditorComponent },
1819
{ path: 'records/export', component: MedicalRecordsExportComponent },
1920
{ path: 'records/export/callback', component: MedicalRecordsExportCallbackComponent },
21+
{ path: 'research/nyp-study', component: ResearchStudyComponent },
2022

2123
{ path: '**', redirectTo: 'catalog/editor' },
2224
];

src/app/app.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class AppComponent implements OnInit {
3030

3131
modifyHeader(event) {
3232
if (event instanceof NavigationEnd) {
33-
if (event.url?.startsWith('/auth')) {
33+
if (event.url?.startsWith('/auth') || event.url?.startsWith('/research')) {
3434
this.showHeader = false;
3535
} else {
3636
// console.log("NU")

src/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {LoadingSpinnerComponent} from './components/loading-spinner/loading-spin
2424
import { MedicalRecordsExportComponent } from './pages/medical-records-export/medical-records-export.component';
2525
import { MedicalRecordsExportCallbackComponent } from './pages/medical-records-export-callback/medical-records-export-callback.component';
2626
import { TefcaIasBetaComponent } from './pages/tefca-ias-beta/tefca-ias-beta.component';
27+
import { ResearchStudyComponent } from './pages/research-study/research-study.component';
2728

2829
// register Handsontable's modules
2930
registerAllModules();
@@ -39,6 +40,7 @@ registerAllModules();
3940
MedicalRecordsExportComponent,
4041
MedicalRecordsExportCallbackComponent,
4142
TefcaIasBetaComponent,
43+
ResearchStudyComponent,
4244
],
4345
imports: [
4446
FormsModule,
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<div class="research-study container py-5">
2+
<div class="row justify-content-center">
3+
<div class="col-lg-8 col-xl-7">
4+
<header class="mb-5 text-center">
5+
<h1 class="mb-3">Research Study Enrollment</h1>
6+
<p class="lead mb-0">Please confirm your participation details to continue and securely share your medical records with the research team.</p>
7+
</header>
8+
9+
<section class="study-form card shadow-sm mb-4">
10+
<div class="card-body p-4">
11+
<form [formGroup]="studyForm" (ngSubmit)="submitForm()" novalidate>
12+
<div class="mb-3">
13+
<label class="form-label" for="fullName">Full name</label>
14+
<input
15+
id="fullName"
16+
type="text"
17+
class="form-control"
18+
[class.is-invalid]="controlInvalid('fullName')"
19+
formControlName="fullName"
20+
placeholder="Enter your name"
21+
/>
22+
<div class="invalid-feedback d-block" *ngIf="controlInvalid('fullName')">
23+
Please provide your full name.
24+
</div>
25+
</div>
26+
27+
<div class="mb-3">
28+
<label class="form-label" for="email">Email address</label>
29+
<input
30+
id="email"
31+
type="email"
32+
class="form-control"
33+
[class.is-invalid]="controlInvalid('email')"
34+
formControlName="email"
35+
placeholder="your@email.com"
36+
/>
37+
<div class="invalid-feedback d-block" *ngIf="controlInvalid('email')">
38+
Please provide a valid email address.
39+
</div>
40+
</div>
41+
42+
<div class="mb-4">
43+
<label class="form-label" for="studyId">Study ID</label>
44+
<input
45+
id="studyId"
46+
type="text"
47+
class="form-control"
48+
[class.is-invalid]="controlInvalid('studyId')"
49+
formControlName="studyId"
50+
placeholder="Provided Study ID"
51+
/>
52+
<div class="invalid-feedback d-block" *ngIf="controlInvalid('studyId')">
53+
Your Study ID is required to continue.
54+
</div>
55+
</div>
56+
57+
<div class="d-flex flex-column flex-sm-row align-items-stretch align-items-sm-center gap-3">
58+
<button class="btn btn-primary flex-grow-1" type="submit">Continue</button>
59+
<small class="text-muted text-center">We will only use this information to match your records with the study.</small>
60+
</div>
61+
</form>
62+
</div>
63+
</section>
64+
65+
<section *ngIf="formSubmitted && participantSummary" class="stitch-section card shadow-sm">
66+
<div class="card-body p-4">
67+
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
68+
<div>
69+
<h2 class="h4 mb-2">Share your medical records</h2>
70+
<p class="mb-0">Hi {{ participantSummary.fullName }}, click the button below to connect your health portals through Fasten Stitch.</p>
71+
</div>
72+
<button type="button" class="btn btn-link p-0" (click)="editDetails()">Edit details</button>
73+
</div>
74+
75+
<div class="widget-card">
76+
<fasten-stitch-element
77+
#stitchElement
78+
[attr.external-id]="participantSummary.studyId"
79+
[attr.metadata]="widgetMetadata"
80+
public-id="public_live_2rzrytsspr29g36o7fgbyo7k28ds4uyqbot3h0k80p0bm"
81+
>Share Medical Records</fasten-stitch-element>
82+
</div>
83+
</div>
84+
</section>
85+
</div>
86+
</div>
87+
</div>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.research-study {
2+
header {
3+
h1 {
4+
font-weight: 600;
5+
}
6+
7+
.lead {
8+
color: #5f6c7b;
9+
}
10+
}
11+
12+
.study-form {
13+
border: none;
14+
}
15+
16+
.stitch-section {
17+
border: none;
18+
19+
.widget-card {
20+
display: flex;
21+
justify-content: center;
22+
padding: 1.5rem;
23+
background: #f7f9fc;
24+
border-radius: 0.75rem;
25+
26+
fasten-stitch-element {
27+
width: 100%;
28+
max-width: 320px;
29+
text-align: center;
30+
}
31+
}
32+
}
33+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { ReactiveFormsModule } from '@angular/forms';
3+
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
4+
5+
import { ResearchStudyComponent } from './research-study.component';
6+
7+
describe('ResearchStudyComponent', () => {
8+
let component: ResearchStudyComponent;
9+
let fixture: ComponentFixture<ResearchStudyComponent>;
10+
11+
beforeEach(async () => {
12+
await TestBed.configureTestingModule({
13+
declarations: [ResearchStudyComponent],
14+
imports: [ReactiveFormsModule],
15+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
16+
}).compileComponents();
17+
18+
fixture = TestBed.createComponent(ResearchStudyComponent);
19+
component = fixture.componentInstance;
20+
fixture.detectChanges();
21+
});
22+
23+
it('should create', () => {
24+
expect(component).toBeTruthy();
25+
});
26+
});
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { Component, ElementRef, ViewChild } from '@angular/core';
2+
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
3+
4+
interface ParticipantDetails {
5+
fullName: string;
6+
email: string;
7+
studyId: string;
8+
}
9+
10+
@Component({
11+
selector: 'app-research-study',
12+
templateUrl: './research-study.component.html',
13+
styleUrls: ['./research-study.component.scss']
14+
})
15+
export class ResearchStudyComponent {
16+
@ViewChild('stitchElement') stitchElement?: ElementRef<any>;
17+
18+
readonly studyForm: FormGroup;
19+
formSubmitted = false;
20+
participantSummary: ParticipantDetails | null = null;
21+
22+
constructor(private readonly fb: FormBuilder) {
23+
this.studyForm = this.fb.group({
24+
fullName: ['', Validators.required],
25+
email: ['', [Validators.required, Validators.email]],
26+
studyId: ['', Validators.required],
27+
});
28+
}
29+
30+
get widgetMetadata(): string | null {
31+
if (!this.participantSummary) {
32+
return null;
33+
}
34+
35+
return JSON.stringify({
36+
studyId: this.participantSummary.studyId,
37+
participantName: this.participantSummary.fullName,
38+
participantEmail: this.participantSummary.email,
39+
});
40+
}
41+
42+
submitForm(): void {
43+
if (this.studyForm.invalid) {
44+
this.studyForm.markAllAsTouched();
45+
return;
46+
}
47+
48+
const { fullName, email, studyId } = this.studyForm.value as ParticipantDetails;
49+
50+
this.participantSummary = { fullName, email, studyId };
51+
this.formSubmitted = true;
52+
53+
setTimeout(() => {
54+
const element = this.stitchElement?.nativeElement;
55+
if (element && typeof element.show === 'function') {
56+
element.show();
57+
}
58+
}, 0);
59+
}
60+
61+
editDetails(): void {
62+
this.formSubmitted = false;
63+
this.participantSummary = null;
64+
this.studyForm.markAsPristine();
65+
this.studyForm.markAsUntouched();
66+
}
67+
68+
controlInvalid(controlName: keyof ParticipantDetails): boolean {
69+
const control = this.studyForm.get(controlName);
70+
return !!control && control.invalid && (control.dirty || control.touched);
71+
}
72+
}

src/app/pages/tefca-ias-beta/tefca-ias-beta.component.html

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ <h1>Fasten Health TEFCA IAS Beta</h1>
2525
<div class="form-card">
2626
<h2 class="card-title">Request Beta Access</h2>
2727
<p class="card-subtitle">Tell us who you are and where you receive care to start the request.</p>
28+
<p class="alert alert-warning card-disclaimer small">
29+
Fasten collects the details below only to analyze TEFCA network health and response rates during this beta.
30+
TEFCA itself does not require this information—your identity is verified through our partner, Clear.
31+
</p>
2832

2933
<form [formGroup]="requestForm" (ngSubmit)="onSubmit()" novalidate>
3034
<div class="form-group">
@@ -39,16 +43,13 @@ <h2 class="card-title">Request Beta Access</h2>
3943
<label for="email">Email address</label>
4044
<input id="email" type="email" class="form-control" formControlName="email" placeholder="john@example.com" />
4145
<div class="invalid-feedback" *ngIf="requestForm.get('email')?.touched && requestForm.get('email')?.invalid">
42-
Enter a valid email so we can send your access link.
46+
Enter a valid email so we can send your verification code.
4347
</div>
4448
</div>
4549

4650
<div class="form-group institutions" [formGroup]="searchForm">
4751
<label>Healthcare institutions</label>
4852
<p class="helper-text">Search for each organization that holds your records. Add at least one.</p>
49-
<p class="small alert alert-warning">
50-
Adding all the places you receive care is not required for Fasten to function but helps us measure TEFCA response rates.
51-
</p>
5253
<div class="d-flex inputs">
5354
<div class="flex-grow-1">
5455
<input

src/app/pages/tefca-ias-beta/tefca-ias-beta.component.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ $primary-soft: lighten($primary-color, 36%);
8181
margin-bottom: 2rem;
8282
}
8383

84+
.card-disclaimer {
85+
margin-bottom: 1.5rem;
86+
border: 1px;
87+
border-color: #856404;
88+
border-style: dotted;
89+
padding: 15px;
90+
}
91+
8492
.form-group {
8593
margin-bottom: 1.5rem;
8694

0 commit comments

Comments
 (0)