Skip to content

Commit 39b5bdf

Browse files
LNK-4455: Fhir List Configuration Changes for POI (#1245)
1 parent c9c65e3 commit 39b5bdf

5 files changed

Lines changed: 125 additions & 125 deletions

File tree

Web/Admin.UI/src/app/components/data-acquisition/data-acquisition-fhir-list-config-form/data-acquisition-fhir-list-config-form.component.html

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
<input matInput formControlName="fhirServerBaseUrl" [disabled]="viewOnly" placeholder="Fhir Server Base URL">
1313
@if (fhirServerBaseUrlControl.value && !viewOnly) {
1414
<button matSuffix mat-icon-button aria-label="Clear"
15-
(click)="clearFhirServerBaseUrl()">
15+
(click)="clearFhirServerBaseUrl()">
1616
<mat-icon>close</mat-icon>
1717
</button>
1818
}
19-
@if ( !viewOnly && fhirServerBaseUrlControl.hasError('required')) {
19+
@if (!viewOnly && fhirServerBaseUrlControl.hasError('required')) {
2020
<mat-error>
2121
A fhir server base url is <strong>required</strong>
2222
</mat-error>
@@ -25,48 +25,45 @@
2525
</div>
2626

2727
<div formArrayName="patientListControl">
28+
<!-- Header Row -->
29+
<div class="patient-row header-row">
30+
<div class="column">Status</div>
31+
<div class="column">Time Frame</div>
32+
<div class="column">FhirId</div>
33+
</div>
34+
2835
@for (patientForm of patientListControl.controls; track patientForm; let i = $index) {
29-
<div [formGroup]="patientForm">
30-
<div class="form-input">
31-
<mat-form-field appearance="outline" class="subject-input form-input">
32-
<mat-label>List IDs(Comma separated)*</mat-label>
33-
<input matInput formControlName="listIds" [readonly]="viewOnly" placeholder="List Ids">
34-
</mat-form-field>
35-
</div>
36-
<div class="scheduled-reports-container">
37-
<mat-form-field class="form-select" appearance="outline">
38-
<mat-label>Measure IDs</mat-label>
39-
<mat-select matInput formControlName="measureIds" [compareWith]="compareReportTypes" multiple>
40-
@for (reportType of reportTypes; track reportType) {
41-
<mat-option [disabled]="viewOnly" [value]="reportType" >
42-
{{ reportType }}
43-
</mat-option>
36+
<div [formGroup]="patientForm" class="patient-container">
37+
<!-- Value Row -->
38+
<div class="patient-row value-row">
39+
<div class="column">{{ patientForm.get('status')?.value }}</div>
40+
<div class="column">{{ patientForm.get('timeFrame')?.value }}</div>
41+
<div class="column">
42+
<mat-form-field appearance="outline" class="form-input fhir-field">
43+
<input
44+
[readonly]="viewOnly"
45+
matInput
46+
formControlName="fhirId"
47+
placeholder="FhirId"
48+
/>
49+
<!-- Error messages -->
50+
@if ("patientForm.get('fhirId')?.hasError('required') && (patientForm.get('fhirId')?.touched || patientForm.get('fhirId')?.value == '')"){
51+
<mat-error>
52+
FhirId is required
53+
</mat-error>
4454
}
45-
</mat-select>
46-
</mat-form-field>
55+
</mat-form-field>
56+
</div>
57+
58+
@if (!viewOnly){
59+
<button mat-icon-button type="button" (click)="clearFhirId(patientForm)">
60+
<mat-icon>close</mat-icon>
61+
</button>
62+
}
4763
</div>
48-
<!--<div class="form-input">
49-
<mat-label>Measure IDs(Comma separated)*</mat-label>
50-
<mat-form-field appearance="outline" class="subject-input form-input">
51-
<input matInput formControlName="measureIds" [readonly]="viewOnly" placeholder="Measure Ids">
52-
</mat-form-field>
53-
</div>-->
54-
<div>
55-
@if (!viewOnly) {
56-
<button mat-stroked-button (click)="addPatientList(i)">
57-
Add Patient List
58-
</button>
59-
}
60-
@if (!viewOnly) {
61-
<button mat-stroked-button color="warn" style="margin-left: .5em;" (click)="removePatientList(i)"
62-
>
63-
Delete Patient List
64-
</button>
65-
}
6664
</div>
67-
</div>
68-
}
69-
</div>
65+
}
66+
</div>
7067
</form>
7168

7269

Original file line numberDiff line numberDiff line change
@@ -1,9 +1,38 @@
11
@use '../../../../styles/core.scss';
22

3-
.scheduled-reports-container {
3+
.patient-container {
4+
width: 100%;
45
display: flex;
5-
flex-direction: column; /* Stack items vertically */
6-
gap: 10px; /* Add some space between dropdowns */
7-
margin-top: 10px
6+
flex-direction: column;
7+
gap: 0.1rem;
88
}
99

10+
.patient-row {
11+
display: flex;
12+
justify-content: flex-start;
13+
gap: 1rem;
14+
padding-top: 0.25rem;
15+
padding-bottom: 0.25rem;
16+
}
17+
18+
.column {
19+
flex: 1;
20+
text-align: left;
21+
}
22+
23+
.header-row {
24+
font-weight: 600;
25+
border-bottom: 1px solid #ccc;
26+
padding-top: 0.1rem;
27+
padding-bottom: 0.1rem;
28+
}
29+
30+
.value-row .column {
31+
display: flex;
32+
align-items: center;
33+
}
34+
35+
.fhir-field {
36+
width: 100%;
37+
max-width: 180px;
38+
}

Web/Admin.UI/src/app/components/data-acquisition/data-acquisition-fhir-list-config-form/data-acquisition-fhir-list-config-form.component.ts

Lines changed: 50 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {IEntityCreatedResponse} from 'src/app/interfaces/entity-created-response
1818
import {ENTER, COMMA} from '@angular/cdk/keycodes';
1919
import {DataAcquisitionService} from 'src/app/services/gateway/data-acquisition/data-acquisition.service';
2020
import {MatSelectModule} from "@angular/material/select";
21-
import {MeasureDefinitionService} from "../../../services/gateway/measure-definition/measure.service";
2221

2322
@Component({
2423
selector: 'app-data-acquisition-fhir-list-config-form',
@@ -36,11 +35,11 @@ import {MeasureDefinitionService} from "../../../services/gateway/measure-defini
3635
MatSelectModule,
3736
ReactiveFormsModule,
3837
MatSelectModule
39-
],
38+
],
4039
templateUrl: './data-acquisition-fhir-list-config-form.component.html',
4140
styleUrls: ['./data-acquisition-fhir-list-config-form.component.scss']
4241
})
43-
export class DataAcquisitionFhirListConfigFormComponent {
42+
export class DataAcquisitionFhirListConfigFormComponent implements OnInit, OnChanges {
4443
@Input() item!: IDataAcquisitionFhirListConfigModel;
4544

4645
@Input() formMode!: FormMode;
@@ -64,35 +63,34 @@ export class DataAcquisitionFhirListConfigFormComponent {
6463
readonly separatorKeysCodes = [ENTER, COMMA] as const;
6564

6665
reportTypes: string[] = [];
67-
68-
constructor(private snackBar: MatSnackBar, private dataAcquisitionService: DataAcquisitionService, private measureDefinitionConfigurationService: MeasureDefinitionService, private fb: FormBuilder) {
69-
70-
//initialize form with fields based on IDataAcquisitionQueryConfigModel
66+
statuses = ['Admit', 'Discharge'];
67+
timeCategories: string[] = ['LessThan24Hours', 'Between24To48Hours', 'MoreThan48Hours'];
68+
69+
constructor(
70+
private snackBar: MatSnackBar,
71+
private dataAcquisitionService: DataAcquisitionService,
72+
private fb: FormBuilder
73+
) {
74+
// Initialize form with a single patient row
7175
this.configForm = this.fb.group({
7276
facilityId: this.fb.control('', Validators.required),
7377
fhirServerBaseUrl: this.fb.control('', Validators.required),
74-
patientListControl: this.fb.array([ // Initialize FormArray with an array of FormGroups
75-
this.fb.group({
76-
listIds: ['', Validators.required],
77-
measureIds: ['', Validators.required]
78-
})
79-
])
78+
patientListControl: this.fb.array([this.createPatientFormGroup()])
79+
});
80+
}
81+
82+
// Method must be a class-level method, not inside constructor
83+
createPatientFormGroup(ehrPatient?: IEhrPatientListModel): FormGroup {
84+
return this.fb.group({
85+
status: this.fb.control(ehrPatient?.status ?? '', Validators.required),
86+
timeFrame: this.fb.control(ehrPatient?.timeFrame ?? '', Validators.required),
87+
fhirId: this.fb.control(ehrPatient?.fhirId ?? '', Validators.required)
8088
});
8189
}
8290

8391
ngOnInit(): void {
8492
this.configForm.reset();
8593

86-
this.measureDefinitionConfigurationService.getMeasureDefinitionConfigurations().subscribe(
87-
{
88-
next: (response) => {
89-
this.reportTypes = response.map(model => model.id);
90-
},
91-
error: (err) => {
92-
this.submittedConfiguration.emit({id: '', message: err.message});
93-
}
94-
});
95-
9694
if (this.item) {
9795
console.log("DataAcquisitionFhirListConfigFormComponent ngOnInit");
9896
console.log(this.item);
@@ -103,10 +101,12 @@ export class DataAcquisitionFhirListConfigFormComponent {
103101
this.fhirServerBaseUrlControl.setValue(this.item.fhirBaseServerUrl);
104102
this.fhirServerBaseUrlControl.updateValueAndValidity();
105103

106-
this.loadPatientLists(this.item.ehrPatientLists);
104+
// this.loadPatientLists(this.item.ehrPatientLists);
105+
this.populateAllCombinations(this.item.ehrPatientLists);
107106
this.patientListControl.updateValueAndValidity();
108107

109108
} else {
109+
this.populateAllCombinations();
110110
this.formMode = FormMode.Create;
111111
}
112112

@@ -115,6 +115,21 @@ export class DataAcquisitionFhirListConfigFormComponent {
115115
});
116116
}
117117

118+
private populateAllCombinations(ehrPatientList?: IEhrPatientListModel[]): void {
119+
this.patientListControl.clear();
120+
121+
for (const status of this.statuses) {
122+
for (const time of this.timeCategories) {
123+
const existingPatient = ehrPatientList?.find(p => p.status === status && p.timeFrame === time);
124+
this.patientListControl.push(this.fb.group({
125+
status: [{value: status, disabled: true}],
126+
timeFrame: [{value: time, disabled: true}],
127+
fhirId: [existingPatient?.fhirId ?? '', Validators.required]
128+
}));
129+
}
130+
}
131+
}
132+
118133
ngOnChanges(changes: SimpleChanges) {
119134

120135
if (changes['item'] && changes['item'].currentValue) {
@@ -124,7 +139,8 @@ export class DataAcquisitionFhirListConfigFormComponent {
124139
this.fhirServerBaseUrlControl.setValue(this.item.fhirBaseServerUrl);
125140
this.fhirServerBaseUrlControl.updateValueAndValidity();
126141

127-
this.loadPatientLists(this.item.ehrPatientLists);
142+
//this.loadPatientLists(this.item.ehrPatientLists);
143+
this.populateAllCombinations(this.item.ehrPatientLists);
128144
this.patientListControl.updateValueAndValidity();
129145

130146
// toggle view
@@ -161,21 +177,21 @@ export class DataAcquisitionFhirListConfigFormComponent {
161177
this.fhirServerBaseUrlControl.updateValueAndValidity();
162178
}
163179

164-
clearPatientList(): void {
165-
this.patientListControl.setValue([]);
166-
this.patientListControl.updateValueAndValidity();
180+
clearFhirId(patientForm: FormGroup) {
181+
const control = patientForm.get('fhirId');
182+
control?.setValue(''); // Clear the value
183+
control?.markAsTouched(); // Mark it touched so error shows immediately
167184
}
168185

169186
submitConfiguration(): void {
170187
if (this.configForm.valid) {
171-
const ehrPatientLists = this.patientListControl.controls.map((control, index) => {
188+
const ehrPatientLists = this.patientListControl.controls.map(control => {
172189
const patientForm = control as FormGroup;
173190
return {
174-
measureIds: patientForm.value.measureIds,
175-
listIds: patientForm.value.listIds
176-
? patientForm.value.listIds.split(',')
177-
: []
178-
};
191+
status: patientForm.get('status')?.value,
192+
timeFrame: patientForm.get('timeFrame')?.value,
193+
fhirId: patientForm.get('fhirId')?.value
194+
} as IEhrPatientListModel;
179195
});
180196
if (this.formMode == FormMode.Create) {
181197
this.dataAcquisitionService.createFhirListConfiguration(this.facilityIdControl.value, {
@@ -217,48 +233,4 @@ export class DataAcquisitionFhirListConfigFormComponent {
217233
}
218234
}
219235

220-
addPatientList(itemIndex: number) {
221-
const patientForm = this.fb.group({
222-
measureIds: this.fb.control('', Validators.required),
223-
listIds: this.fb.control('', Validators.required)
224-
});
225-
this.patientListControl.push(patientForm);
226-
}
227-
228-
removePatientList(itemIndex: number) {
229-
this.patientListControl.removeAt(itemIndex);
230-
}
231-
232-
private loadPatientLists(ehrPatientList: IEhrPatientListModel[]): void {
233-
234-
this.patientListControl.clear();
235-
this.patientListControl.updateValueAndValidity();
236-
237-
if (ehrPatientList?.length) {
238-
239-
ehrPatientList.forEach((ehrPatientListItem: IEhrPatientListModel) => {
240-
241-
let measureIds = (ehrPatientListItem.measureIds ?? []);
242-
let listIds = (ehrPatientListItem.listIds ?? []).join(", ");
243-
244-
const patientForm = this.fb.group({
245-
measureIds: this.fb.control(measureIds, Validators.required),
246-
listIds: this.fb.control(listIds, Validators.required)
247-
});
248-
this.patientListControl.push(patientForm);
249-
});
250-
} else {
251-
const patientForm = this.fb.group({
252-
measureIds: this.fb.control('', Validators.required),
253-
listIds: this.fb.control('', Validators.required)
254-
});
255-
this.patientListControl.push(patientForm);
256-
}
257-
258-
}
259-
260-
compareReportTypes(object1: any, object2: any) {
261-
return (object1 && object2) && object1 === object2;
262-
}
263-
264236
}

Web/Admin.UI/src/app/components/tenant/facility-edit/facility-edit.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,8 @@ export class FacilityEditComponent implements OnInit {
345345
showDataAcqFhirListDialog(): void {
346346
this.dialog.open(DataAcquisitionFhirListConfigDialogComponent,
347347
{
348-
width: '75%',
348+
width: '50vw',
349+
maxWidth: '50vw',
349350
data: {
350351
dialogTitle: 'Fhir Query List Configuration',
351352
formMode: this.showNoDataAcqFhirListConfigAlert ? FormMode.Create : FormMode.Edit,

Web/Admin.UI/src/app/interfaces/data-acquisition/data-acquisition-fhir-list-config-model.interface.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface IDataAcquisitionFhirListConfigModel {
99
}
1010

1111
export interface IEhrPatientListModel {
12-
listIds: string[];
13-
measureIds: string[];
12+
status?: string;
13+
timeFrame?: string;
14+
fhirId?: string;
1415
}

0 commit comments

Comments
 (0)