Skip to content

Commit

Permalink
Adding modular forms edit
Browse files Browse the repository at this point in the history
  • Loading branch information
alopezo committed Jan 23, 2024
1 parent e52545b commit f0498f1
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 17 deletions.
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@
<style>html,body{height:100%}body{margin:0;font-family:Roboto,Helvetica Neue,sans-serif}</style><link rel="stylesheet" href="styles.25b54d3c54f3517b.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.25b54d3c54f3517b.css"></noscript></head>
<body class="mat-typography">
<app-root></app-root>
<script src="runtime.c2d2548033d65b02.js" type="module"></script><script src="polyfills.75186c7b7fc1d123.js" type="module"></script><script src="scripts.a1e2309b4257c7b3.js" defer></script><script src="main.4ed5209b3b623529.js" type="module"></script>
<script src="runtime.c2d2548033d65b02.js" type="module"></script><script src="polyfills.75186c7b7fc1d123.js" type="module"></script><script src="scripts.a1e2309b4257c7b3.js" defer></script><script src="main.42d3c077cd573b8d.js" type="module"></script>

</body></html>
1 change: 1 addition & 0 deletions docs/main.42d3c077cd573b8d.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion docs/main.4ed5209b3b623529.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup } from '@angular/forms';
import { FhirService } from 'src/app/services/fhir.service';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
Expand All @@ -21,6 +21,7 @@ export class CreateRootModuleComponent {
constructor(
private fb: FormBuilder,
private fhirService: FhirService,
@Inject(MAT_DIALOG_DATA) public data: any,
public dialogRef: MatDialogRef<CreateRootModuleComponent>
) {}

Expand All @@ -29,15 +30,23 @@ export class CreateRootModuleComponent {
selectedQuestionnaire: [{value: '', disabled: true}],
assignedName: [{value: '', disabled: true}]
});

if (this.data) {
this.addedQuestionnaires = this.data.questionnaires;
this.questionnaireForm.get('assignedName')?.setValue(this.data.title);
}
this.questionnaireForm.get('selectedQuestionnaire')
this.userTag = this.fhirService.getUserTag();

this.loadAvailableQuestionnaires();
}

updateAvailableQuestionnaires() {
this.availableQuestionnaires = this.allQuestionnaires.filter(q =>
!this.addedQuestionnaires.some(addedQ => addedQ.id === q.id));
if (this.addedQuestionnaires) {
this.availableQuestionnaires = this.allQuestionnaires.filter(q =>
!this.addedQuestionnaires.some(addedQ => addedQ.id === q.id));
} else {
this.availableQuestionnaires = this.allQuestionnaires;
}
}

loadAvailableQuestionnaires() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@
<mat-icon>cloud_download</mat-icon>
</button>
<button mat-icon-button color="accent" (click)="editQuestionnaire(element)" class="action-button"
matTooltip="Edit Questionnaire">
*ngIf="!isRootQuestionnaire(element)" matTooltip="Edit Questionnaire">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button color="accent" (click)="openModularQuestionnaireModal(element)" class="action-button"
*ngIf="isRootQuestionnaire(element)" matTooltip="Edit Modular Questionnaire">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button color="accent" (click)="deleteQuestionnaire(element)" class="action-button"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { AfterViewInit, Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Subject, combineLatest, debounceTime, distinctUntilChanged } from 'rxjs';
import { Subject, combineLatest, debounceTime, distinctUntilChanged, first } from 'rxjs';
import { SnackAlertComponent } from 'src/app/alerts/snack-alert';
import { FhirService } from 'src/app/services/fhir.service';
import * as saveAs from 'file-saver';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { QuestionnaireService } from 'src/app/services/questionnaire.service';
import { MatDialog } from '@angular/material/dialog';
import { CreateRootModuleComponent } from '../create-root-module/create-root-module.component';

@Component({
selector: 'app-list-questionnaires',
Expand Down Expand Up @@ -35,7 +37,11 @@ export class ListQuestionnairesComponent implements OnInit, OnChanges, AfterView
private baseUrlChanged = new Subject<string>();
private userTagChanged = new Subject<string>();

constructor(private fhirService: FhirService, private questionnaireService: QuestionnaireService, private _snackBar: MatSnackBar) { }
constructor(
private fhirService: FhirService,
private questionnaireService: QuestionnaireService,
public dialog: MatDialog,
private _snackBar: MatSnackBar) { }

ngOnInit() {
combineLatest([
Expand Down Expand Up @@ -166,12 +172,12 @@ export class ListQuestionnairesComponent implements OnInit, OnChanges, AfterView
}

assemble(questionnaire: any) {
// In your component
this._snackBar.openFromComponent(SnackAlertComponent, {
duration: 2 * 1000,
data: "Assembling Questionnaire...",
panelClass: ['green-snackbar']
});
this.questionnaireService.assembleQuestionnaire(questionnaire).then(assembledQuestionnaire => {
// Use the assembled questionnaire as needed
// console.log('Assembled questionnaire:', assembledQuestionnaire);
// var blob = new Blob([JSON.stringify(assembledQuestionnaire, null, 2)], {type: "text/plain;charset=utf-8"});
// saveAs(blob, `${assembledQuestionnaire.title}-v${assembledQuestionnaire.meta.versionId}.json`);
assembledQuestionnaire.title = questionnaire.title + " (assembled)";
this.previewQuestionnaire.emit(assembledQuestionnaire);
}).catch(error => {
Expand All @@ -184,4 +190,53 @@ export class ListQuestionnairesComponent implements OnInit, OnChanges, AfterView
this.saveQuestionnaire(questionnaire);
window.open('https://lhcformbuilder.nlm.nih.gov/', '_blank');
}

async openModularQuestionnaireModal(modularQuestionnaire: any) {
let data = await this.questionnaireService.getRootQuestionnaireData(modularQuestionnaire);
const dialogRef = this.dialog.open(CreateRootModuleComponent, {
data: data,
width: '75%'
});

dialogRef.afterClosed().subscribe(result => {
if (result) {
this.handleDialogResult(result, modularQuestionnaire);
}
});
}

async handleDialogResult(result: any, modularQuestionnaire: any) {
this._snackBar.openFromComponent(SnackAlertComponent, {
duration: 5 * 1000,
data: "Updating questionnaire...",
panelClass: ['green-snackbar']
});
try {
let newRootQuestionnaire = await this.questionnaireService.generateRootQuestionnaire(result.title, result.questionnaires);
if (newRootQuestionnaire) {
modularQuestionnaire.title = result.title;
modularQuestionnaire.item = newRootQuestionnaire.item;
this.addQuestionnaire(modularQuestionnaire);
this.fhirService.updateOrCreateQuestionnaire(modularQuestionnaire, this.selectedUserTag).pipe(first()).subscribe(
(data: any) => {
this._snackBar.openFromComponent(SnackAlertComponent, {
duration: 5 * 1000,
data: "Questionnaire updated successfully",
panelClass: ['green-snackbar']
});
},
(error: any) => {
this._snackBar.openFromComponent(SnackAlertComponent, {
duration: 5 * 1000,
data: "Error saving questionnaire",
panelClass: ['red-snackbar']
});
});
}
} catch (error) {
console.error('Error handling dialog result:', error);
// Handle the error
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ <h2 class="header">
Upload
</button>
<button mat-flat-button color="accent" (click)="saveQuestionnaire()" [disabled]="validating || !questionnaire">Download</button>
<button mat-flat-button color="accent" (click)="openQuestionnaireModal()" [disabled]="validating" *ngIf="mode == 'Manager'">
<button mat-flat-button color="accent" (click)="openModularQuestionnaireModal()" [disabled]="validating" *ngIf="mode == 'Manager'">
Create Modular Questionnaire
</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ export class QuestionnairesMainComponent implements OnInit{
}
}

openQuestionnaireModal() {
openModularQuestionnaireModal() {
const dialogRef = this.dialog.open(CreateRootModuleComponent, {
width: '75%'
});
Expand Down
51 changes: 51 additions & 0 deletions src/app/services/questionnaire.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,32 @@ export class QuestionnaireService {
}
}

async getRootQuestionnaireData(rootQuestionnaire: any): Promise<{ title: string, questionnaires: any[] }> {
if (!rootQuestionnaire || !Array.isArray(rootQuestionnaire.item)) {
throw new Error('Invalid root questionnaire');
}

const title = rootQuestionnaire.title;
const questionnairePromises = rootQuestionnaire.item
.filter((item: any) => item.type === 'group' && item.item && Array.isArray(item.item))
.flatMap((groupItem: any) => groupItem.item)
.map(async (subItem: any) => {
const subQuestionnaireExtension = subItem.extension?.find((ext: any) =>
ext.url === "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-subQuestionnaire"
);
if (subQuestionnaireExtension) {
const url = subQuestionnaireExtension.valueCanonical;
return this.getQuestionnaireFromUrl(url);
}
return null;
});

const questionnaires = (await Promise.all(questionnairePromises)).filter(q => q !== null);

return { title, questionnaires };
}


checkForAssembleRoot(questionnaire: any): boolean {
if (!questionnaire || !questionnaire.extension || !Array.isArray(questionnaire.extension)) {
return false;
Expand Down Expand Up @@ -90,6 +116,31 @@ export class QuestionnaireService {
return questionnaire;
}

async disassembleQuestionnaire(rootQuestionnaire: any): Promise<{ title: any, questionnaires: any[] }> {
if (!rootQuestionnaire || !rootQuestionnaire.item || !Array.isArray(rootQuestionnaire.item)) {
throw new Error('Invalid root questionnaire');
}

const title = rootQuestionnaire.title;
const disassembledQuestionnaires = [];

for (const item of rootQuestionnaire.item) {
if (item.type === 'group' && item.extension && Array.isArray(item.extension)) {
const assembledFromExtension = item.extension.find( (ext: any) => ext.url === 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-assembledFrom');
if (assembledFromExtension) {
const subQuestionnaireUrl = assembledFromExtension.valueCanonical;
const subQuestionnaire = await this.getQuestionnaireFromUrl(subQuestionnaireUrl);
if (subQuestionnaire) {
disassembledQuestionnaires.push(subQuestionnaire);
}
}
}
}

return { title, questionnaires: disassembledQuestionnaires };
}


async getQuestionnaireFromUrl(url: string): Promise<any> {
try {
const data = await lastValueFrom(this.http.get(url));
Expand Down

0 comments on commit f0498f1

Please sign in to comment.