Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
<input matInput formControlName="fhirServerBaseUrl" [disabled]="viewOnly" placeholder="Fhir Server Base URL">
@if (fhirServerBaseUrlControl.value && !viewOnly) {
<button matSuffix mat-icon-button aria-label="Clear"
(click)="clearFhirServerBaseUrl()">
(click)="clearFhirServerBaseUrl()">
<mat-icon>close</mat-icon>
</button>
}
@if ( !viewOnly && fhirServerBaseUrlControl.hasError('required')) {
@if (!viewOnly && fhirServerBaseUrlControl.hasError('required')) {
<mat-error>
A fhir server base url is <strong>required</strong>
</mat-error>
Expand All @@ -25,48 +25,45 @@
</div>

<div formArrayName="patientListControl">
<!-- Header Row -->
<div class="patient-row header-row">
<div class="column">Status</div>
<div class="column">Time Frame</div>
<div class="column">FhirId</div>
</div>

@for (patientForm of patientListControl.controls; track patientForm; let i = $index) {
<div [formGroup]="patientForm">
<div class="form-input">
<mat-form-field appearance="outline" class="subject-input form-input">
<mat-label>List IDs(Comma separated)*</mat-label>
<input matInput formControlName="listIds" [readonly]="viewOnly" placeholder="List Ids">
</mat-form-field>
</div>
<div class="scheduled-reports-container">
<mat-form-field class="form-select" appearance="outline">
<mat-label>Measure IDs</mat-label>
<mat-select matInput formControlName="measureIds" [compareWith]="compareReportTypes" multiple>
@for (reportType of reportTypes; track reportType) {
<mat-option [disabled]="viewOnly" [value]="reportType" >
{{ reportType }}
</mat-option>
<div [formGroup]="patientForm" class="patient-container">
<!-- Value Row -->
<div class="patient-row value-row">
<div class="column">{{ patientForm.get('status')?.value }}</div>
<div class="column">{{ patientForm.get('timeFrame')?.value }}</div>
<div class="column">
<mat-form-field appearance="outline" class="form-input fhir-field">
<input
[readonly]="viewOnly"
matInput
formControlName="fhirId"
placeholder="FhirId"
/>
<!-- Error messages -->
@if ("patientForm.get('fhirId')?.hasError('required') && (patientForm.get('fhirId')?.touched || patientForm.get('fhirId')?.value == '')"){
<mat-error>
FhirId is required
</mat-error>
Comment thread
arianamihailescu marked this conversation as resolved.
}
</mat-select>
</mat-form-field>
</mat-form-field>
</div>

@if (!viewOnly){
<button mat-icon-button type="button" (click)="clearFhirId(patientForm)">
<mat-icon>close</mat-icon>
</button>
}
</div>
<!--<div class="form-input">
<mat-label>Measure IDs(Comma separated)*</mat-label>
<mat-form-field appearance="outline" class="subject-input form-input">
<input matInput formControlName="measureIds" [readonly]="viewOnly" placeholder="Measure Ids">
</mat-form-field>
</div>-->
<div>
@if (!viewOnly) {
<button mat-stroked-button (click)="addPatientList(i)">
Add Patient List
</button>
}
@if (!viewOnly) {
<button mat-stroked-button color="warn" style="margin-left: .5em;" (click)="removePatientList(i)"
>
Delete Patient List
</button>
}
</div>
</div>
}
</div>
}
</div>
</form>


Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
@use '../../../../styles/core.scss';

.scheduled-reports-container {
.patient-container {
width: 100%;
display: flex;
flex-direction: column; /* Stack items vertically */
gap: 10px; /* Add some space between dropdowns */
margin-top: 10px
flex-direction: column;
gap: 0.1rem;
}

.patient-row {
display: flex;
justify-content: flex-start;
gap: 1rem;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}

.column {
flex: 1;
text-align: left;
}

.header-row {
font-weight: 600;
border-bottom: 1px solid #ccc;
padding-top: 0.1rem;
padding-bottom: 0.1rem;
}

.value-row .column {
display: flex;
align-items: center;
}

.fhir-field {
width: 100%;
max-width: 180px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {IEntityCreatedResponse} from 'src/app/interfaces/entity-created-response
import {ENTER, COMMA} from '@angular/cdk/keycodes';
import {DataAcquisitionService} from 'src/app/services/gateway/data-acquisition/data-acquisition.service';
import {MatSelectModule} from "@angular/material/select";
import {MeasureDefinitionService} from "../../../services/gateway/measure-definition/measure.service";

@Component({
selector: 'app-data-acquisition-fhir-list-config-form',
Expand All @@ -36,11 +35,11 @@ import {MeasureDefinitionService} from "../../../services/gateway/measure-defini
MatSelectModule,
ReactiveFormsModule,
MatSelectModule
],
],
templateUrl: './data-acquisition-fhir-list-config-form.component.html',
styleUrls: ['./data-acquisition-fhir-list-config-form.component.scss']
})
export class DataAcquisitionFhirListConfigFormComponent {
export class DataAcquisitionFhirListConfigFormComponent implements OnInit, OnChanges {
@Input() item!: IDataAcquisitionFhirListConfigModel;

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

reportTypes: string[] = [];

constructor(private snackBar: MatSnackBar, private dataAcquisitionService: DataAcquisitionService, private measureDefinitionConfigurationService: MeasureDefinitionService, private fb: FormBuilder) {

//initialize form with fields based on IDataAcquisitionQueryConfigModel
statuses = ['Admit', 'Discharge'];
timeCategories: string[] = ['LessThan24Hours', 'Between24To48Hours', 'MoreThan48Hours'];

constructor(
private snackBar: MatSnackBar,
private dataAcquisitionService: DataAcquisitionService,
private fb: FormBuilder
) {
// Initialize form with a single patient row
this.configForm = this.fb.group({
facilityId: this.fb.control('', Validators.required),
fhirServerBaseUrl: this.fb.control('', Validators.required),
patientListControl: this.fb.array([ // Initialize FormArray with an array of FormGroups
this.fb.group({
listIds: ['', Validators.required],
measureIds: ['', Validators.required]
})
])
patientListControl: this.fb.array([this.createPatientFormGroup()])
});
}

// Method must be a class-level method, not inside constructor
createPatientFormGroup(ehrPatient?: IEhrPatientListModel): FormGroup {
return this.fb.group({
status: this.fb.control(ehrPatient?.status ?? '', Validators.required),
timeFrame: this.fb.control(ehrPatient?.timeFrame ?? '', Validators.required),
fhirId: this.fb.control(ehrPatient?.fhirId ?? '', Validators.required)
});
}

ngOnInit(): void {
this.configForm.reset();

this.measureDefinitionConfigurationService.getMeasureDefinitionConfigurations().subscribe(
{
next: (response) => {
this.reportTypes = response.map(model => model.id);
},
error: (err) => {
this.submittedConfiguration.emit({id: '', message: err.message});
}
});

if (this.item) {
console.log("DataAcquisitionFhirListConfigFormComponent ngOnInit");
console.log(this.item);
Expand All @@ -103,10 +101,12 @@ export class DataAcquisitionFhirListConfigFormComponent {
this.fhirServerBaseUrlControl.setValue(this.item.fhirBaseServerUrl);
this.fhirServerBaseUrlControl.updateValueAndValidity();

this.loadPatientLists(this.item.ehrPatientLists);
// this.loadPatientLists(this.item.ehrPatientLists);
this.populateAllCombinations(this.item.ehrPatientLists);
this.patientListControl.updateValueAndValidity();

} else {
this.populateAllCombinations();
this.formMode = FormMode.Create;
}

Expand All @@ -115,6 +115,21 @@ export class DataAcquisitionFhirListConfigFormComponent {
});
}

private populateAllCombinations(ehrPatientList?: IEhrPatientListModel[]): void {
this.patientListControl.clear();

for (const status of this.statuses) {
for (const time of this.timeCategories) {
const existingPatient = ehrPatientList?.find(p => p.status === status && p.timeFrame === time);
this.patientListControl.push(this.fb.group({
status: [{value: status, disabled: true}],
timeFrame: [{value: time, disabled: true}],
fhirId: [existingPatient?.fhirId ?? '', Validators.required]
}));
}
}
}

ngOnChanges(changes: SimpleChanges) {

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

this.loadPatientLists(this.item.ehrPatientLists);
//this.loadPatientLists(this.item.ehrPatientLists);
this.populateAllCombinations(this.item.ehrPatientLists);
this.patientListControl.updateValueAndValidity();

// toggle view
Expand Down Expand Up @@ -161,21 +177,21 @@ export class DataAcquisitionFhirListConfigFormComponent {
this.fhirServerBaseUrlControl.updateValueAndValidity();
}

clearPatientList(): void {
this.patientListControl.setValue([]);
this.patientListControl.updateValueAndValidity();
clearFhirId(patientForm: FormGroup) {
const control = patientForm.get('fhirId');
control?.setValue(''); // Clear the value
control?.markAsTouched(); // Mark it touched so error shows immediately
}

submitConfiguration(): void {
if (this.configForm.valid) {
const ehrPatientLists = this.patientListControl.controls.map((control, index) => {
const ehrPatientLists = this.patientListControl.controls.map(control => {
const patientForm = control as FormGroup;
return {
measureIds: patientForm.value.measureIds,
listIds: patientForm.value.listIds
? patientForm.value.listIds.split(',')
: []
};
status: patientForm.get('status')?.value,
timeFrame: patientForm.get('timeFrame')?.value,
fhirId: patientForm.get('fhirId')?.value
} as IEhrPatientListModel;
});
if (this.formMode == FormMode.Create) {
this.dataAcquisitionService.createFhirListConfiguration(this.facilityIdControl.value, {
Expand Down Expand Up @@ -217,48 +233,4 @@ export class DataAcquisitionFhirListConfigFormComponent {
}
}

addPatientList(itemIndex: number) {
const patientForm = this.fb.group({
measureIds: this.fb.control('', Validators.required),
listIds: this.fb.control('', Validators.required)
});
this.patientListControl.push(patientForm);
}

removePatientList(itemIndex: number) {
this.patientListControl.removeAt(itemIndex);
}

private loadPatientLists(ehrPatientList: IEhrPatientListModel[]): void {

this.patientListControl.clear();
this.patientListControl.updateValueAndValidity();

if (ehrPatientList?.length) {

ehrPatientList.forEach((ehrPatientListItem: IEhrPatientListModel) => {

let measureIds = (ehrPatientListItem.measureIds ?? []);
let listIds = (ehrPatientListItem.listIds ?? []).join(", ");

const patientForm = this.fb.group({
measureIds: this.fb.control(measureIds, Validators.required),
listIds: this.fb.control(listIds, Validators.required)
});
this.patientListControl.push(patientForm);
});
} else {
const patientForm = this.fb.group({
measureIds: this.fb.control('', Validators.required),
listIds: this.fb.control('', Validators.required)
});
this.patientListControl.push(patientForm);
}

}

compareReportTypes(object1: any, object2: any) {
return (object1 && object2) && object1 === object2;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,8 @@ export class FacilityEditComponent implements OnInit {
showDataAcqFhirListDialog(): void {
this.dialog.open(DataAcquisitionFhirListConfigDialogComponent,
{
width: '75%',
width: '50vw',
maxWidth: '50vw',
data: {
dialogTitle: 'Fhir Query List Configuration',
formMode: this.showNoDataAcqFhirListConfigAlert ? FormMode.Create : FormMode.Edit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface IDataAcquisitionFhirListConfigModel {
}

export interface IEhrPatientListModel {
listIds: string[];
measureIds: string[];
status?: string;
timeFrame?: string;
fhirId?: string;
}
Loading