@openmrs/ngx-formentry 3.2.1-pre.275 → 3.2.1-pre.276

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/package.json +5 -1
  2. package/src/app/adult-1.4.json +6858 -0
  3. package/src/app/adult-1.6.json +8082 -0
  4. package/src/app/adult.json +5700 -0
  5. package/src/app/app.component.html +25 -0
  6. package/src/app/app.component.spec.ts +23 -0
  7. package/src/app/app.component.ts +414 -0
  8. package/src/app/app.module.ts +24 -0
  9. package/src/app/mock/mock-form.ts +101 -0
  10. package/src/app/mock/mock-obs.ts +440 -0
  11. package/src/app/mock/mock-translations.ts +73 -0
  12. package/src/app/mock/obs.json +4188 -0
  13. package/src/app/mock/orders.json +338 -0
  14. package/src/app/mock/schema/adult-return.json +72 -0
  15. package/src/app/mock/schema/compiled-adult-return.json +706 -0
  16. package/src/app/mock/schema/component_art.json +1705 -0
  17. package/src/app/mock/schema/component_hospitalization.json +133 -0
  18. package/src/app/mock/schema/component_preclinic-review.json +480 -0
  19. package/src/app/ncd-registration-1.0.json +456 -0
  20. package/src/app/translate/json-loader.ts +10 -0
  21. package/src/app/translate/translate.module.ts +23 -0
  22. package/src/assets/.gitkeep +0 -0
  23. package/src/assets/carbon.select.theme.css +356 -0
  24. package/src/environments/environment.prod.ts +3 -0
  25. package/src/environments/environment.ts +15 -0
  26. package/src/favicon.ico +0 -0
  27. package/src/index.html +23 -0
  28. package/src/karma.conf.js +37 -0
  29. package/src/main.ts +13 -0
  30. package/src/polyfills.ts +68 -0
  31. package/src/styles.scss +8 -0
  32. package/src/test.ts +23 -0
  33. package/src/translations/en.json +53 -0
  34. package/src/translations/fr.json +53 -0
  35. package/src/tsconfig.app.json +13 -0
  36. package/src/tsconfig.spec.json +9 -0
  37. package/src/tslint.json +9 -0
  38. package/src/typings.d.ts +4 -0
  39. package/.editorconfig +0 -13
  40. package/.eslintrc.json +0 -45
  41. package/.prettierignore +0 -40
  42. package/.prettierrc +0 -6
  43. package/.turbo/turbo-build:lib.log +0 -37
  44. package/.yarn/plugins/@yarnpkg/plugin-version.cjs +0 -550
  45. package/angular.json +0 -164
  46. package/proxy.conf.json +0 -6
  47. package/scripts/build.sh +0 -2
  48. package/tsconfig.json +0 -29
  49. package/turbo.json +0 -18
  50. /package/{.yarn/versions/6b8f5374.yml → src/app/app.component.css} +0 -0
@@ -0,0 +1,25 @@
1
+ <div *ngIf="form && form.rootNode">
2
+ <form [formGroup]="form.rootNode.control">
3
+ <ofe-form-renderer
4
+ [node]="form.rootNode"
5
+ [labelMap]="labelMap"
6
+ ></ofe-form-renderer>
7
+ </form>
8
+
9
+ <div class="cds--offset-md-2">
10
+ <button
11
+ (click)="reset($event)"
12
+ class="cds--btn cds--btn--secondary"
13
+ type="button"
14
+ >
15
+ Cancel
16
+ </button>
17
+ <button
18
+ (click)="onSubmit($event)"
19
+ class="cds--btn cds--btn--primary"
20
+ type="button"
21
+ >
22
+ Save
23
+ </button>
24
+ </div>
25
+ </div>
@@ -0,0 +1,23 @@
1
+ import { TestBed, waitForAsync } from '@angular/core/testing';
2
+ import { FormEntryModule } from 'projects/ngx-formentry/src/public_api';
3
+ import { AppComponent } from './app.component';
4
+ import { BrowserModule } from '@angular/platform-browser';
5
+ import { ReactiveFormsModule } from '@angular/forms';
6
+ describe('AppComponent', () => {
7
+ beforeEach(
8
+ waitForAsync(() => {
9
+ TestBed.configureTestingModule({
10
+ declarations: [AppComponent],
11
+ imports: [BrowserModule, FormEntryModule, ReactiveFormsModule]
12
+ }).compileComponents();
13
+ })
14
+ );
15
+ it(
16
+ 'should create the app',
17
+ waitForAsync(() => {
18
+ const fixture = TestBed.createComponent(AppComponent);
19
+ const app = fixture.debugElement.componentInstance;
20
+ expect(app).toBeTruthy();
21
+ })
22
+ );
23
+ });
@@ -0,0 +1,414 @@
1
+ import { Component, OnInit } from '@angular/core';
2
+ import { HttpClient, HttpHeaders } from '@angular/common/http';
3
+ import { FormGroup } from '@angular/forms';
4
+
5
+ import { Subscriber, Observable, Subject, of, Observer } from 'rxjs';
6
+ import { TranslateService } from '@ngx-translate/core';
7
+
8
+ import {
9
+ QuestionFactory,
10
+ Form,
11
+ FormFactory,
12
+ ObsValueAdapter,
13
+ OrderValueAdapter,
14
+ EncounterAdapter,
15
+ DataSources,
16
+ FormErrorsService,
17
+ PersonAttribuAdapter
18
+ } from '@openmrs/ngx-formentry';
19
+ import { MockObs } from './mock/mock-obs';
20
+ import { mockTranslationsData } from './mock/mock-translations';
21
+ import { PatientIdentifierAdapter } from 'projects/ngx-formentry/src/form-entry/value-adapters/patient-identifier.adapter';
22
+
23
+ const adultForm = require('./adult-1.6.json');
24
+ const adultFormObs = require('./mock/obs.json');
25
+ const formOrdersPayload = require('./mock/orders.json');
26
+ @Component({
27
+ selector: 'app-root',
28
+ templateUrl: './app.component.html',
29
+ styleUrls: ['./app.component.css']
30
+ })
31
+ export class AppComponent implements OnInit {
32
+ data: any;
33
+ schema: any;
34
+ sections: {} = {};
35
+ formGroup: FormGroup;
36
+ activeTab = 0;
37
+ form: Form;
38
+ stack = [];
39
+ encounterObject = adultFormObs;
40
+ showingEncounterViewer = false;
41
+ public header = 'UMD Demo';
42
+ currentLanguage = 'en';
43
+ labelMap = {};
44
+
45
+ constructor(
46
+ private questionFactory: QuestionFactory,
47
+ private formFactory: FormFactory,
48
+ private obsValueAdapater: ObsValueAdapter,
49
+ private orderAdaptor: OrderValueAdapter,
50
+ private encAdapter: EncounterAdapter,
51
+ private dataSources: DataSources,
52
+ private formErrorsService: FormErrorsService,
53
+ private http: HttpClient,
54
+ private translate: TranslateService,
55
+ private personAttributeAdapter: PersonAttribuAdapter,
56
+ private patientIdenfierAdapter: PatientIdentifierAdapter
57
+ ) {
58
+ this.schema = adultForm;
59
+ }
60
+
61
+ ngOnInit() {
62
+ this.dataSources.registerDataSource('drug', {
63
+ searchOptions: this.sampleSearch,
64
+ resolveSelectedValue: this.sampleResolve
65
+ });
66
+ this.dataSources.registerDataSource('personAttribute', {
67
+ searchOptions: this.sampleSearch,
68
+ resolveSelectedValue: this.sampleResolve
69
+ });
70
+ this.dataSources.registerDataSource('problem', {
71
+ searchOptions: this.sampleSearch,
72
+ resolveSelectedValue: this.sampleResolve
73
+ });
74
+ this.dataSources.registerDataSource('location', {
75
+ searchOptions: this.sampleSearch,
76
+ resolveSelectedValue: this.sampleResolve
77
+ });
78
+ this.dataSources.registerDataSource('provider', {
79
+ searchOptions: this.sampleSearch,
80
+ resolveSelectedValue: this.sampleResolve
81
+ });
82
+ this.dataSources.registerDataSource('diagnoses', {
83
+ searchOptions: this.sampleSearch,
84
+ resolveSelectedValue: this.sampleResolve
85
+ });
86
+
87
+ const ds = {
88
+ dataSourceOptions: { concept: undefined },
89
+ searchOptions: (text?: string) => {
90
+ if (ds.dataSourceOptions && ds.dataSourceOptions.concept) {
91
+ const items: Array<any> = [
92
+ { value: 1, label: 'Stage 1 Symptom' },
93
+ { value: 2, label: 'Stage 2 Symptom' }
94
+ ];
95
+
96
+ return new Observable((observer: Observer<object>) => {
97
+ setTimeout(() => {
98
+ observer.next(items);
99
+ }, 1000);
100
+ });
101
+ }
102
+ },
103
+
104
+ resolveSelectedValue: (key: string) => {
105
+ if (ds.dataSourceOptions && ds.dataSourceOptions.concept) {
106
+ const item = { value: 1, label: 'Stage 1 Symptom' };
107
+ return new Observable((observer: Observer<object>) => {
108
+ setTimeout(() => {
109
+ observer.next(item);
110
+ }, 1000);
111
+ });
112
+ }
113
+ }
114
+ };
115
+
116
+ this.dataSources.registerDataSource('conceptAnswers', ds);
117
+
118
+ const obs = new MockObs();
119
+ this.dataSources.registerDataSource('rawPrevEnc', obs.getObs());
120
+ this.dataSources.registerDataSource('rawPrevObs', obs.getObs());
121
+
122
+ this.dataSources.registerDataSource('patient', { sex: 'M', age: 50 }, true);
123
+
124
+ this.dataSources.registerDataSource('patientInfo', {
125
+ name: 'Test Patient',
126
+ age: '37',
127
+ birthdate: '7/7/1982',
128
+ mui: '447062073-5',
129
+ nid: '1234567'
130
+ });
131
+
132
+ this.dataSources.registerDataSource('file', {
133
+ fileUpload: (data) => {
134
+ return of({ image: 'https://unsplash.it/1040/720' });
135
+ },
136
+ fetchFile: (url) => {
137
+ return new Observable((observer: Subscriber<any>) => {
138
+ let objectUrl: string = null;
139
+ const headers = new HttpHeaders({
140
+ Accept: 'image/png,image/jpeg,image/gif,application/pdf'
141
+ });
142
+ this.http
143
+ .get('https://unsplash.it/1040/720', {
144
+ headers,
145
+ responseType: 'json'
146
+ })
147
+ .subscribe((res: any) => {
148
+ const blob = new Blob(res.body);
149
+ objectUrl = URL.createObjectURL(blob);
150
+ observer.next(objectUrl);
151
+ });
152
+
153
+ return () => {
154
+ if (objectUrl) {
155
+ URL.revokeObjectURL(objectUrl);
156
+ objectUrl = null;
157
+ }
158
+ };
159
+ });
160
+ }
161
+ });
162
+
163
+ // Create form
164
+ this.createForm();
165
+
166
+ // Set encounter, obs, orders
167
+ adultFormObs.orders = formOrdersPayload.orders;
168
+ this.encAdapter.populateForm(this.form, adultFormObs);
169
+
170
+ this.setUpCascadeSelectForWHOStaging();
171
+ if (!this.form.valid) {
172
+ this.form.showErrors = false;
173
+ this.form.rootNode.control.markAsDirty();
174
+ }
175
+
176
+ // Alternative is to set individually for obs and orders as show below
177
+ // // Set obs
178
+ // this.obsValueAdapater.populateForm(this.form, adultFormObs.obs);
179
+
180
+ // // Set orders
181
+ // this.orderAdaptor.populateForm(this.form, formOrdersPayload);
182
+ }
183
+
184
+ public setUpCascadeSelectForWHOStaging() {
185
+ const subject = new Subject();
186
+ const source = this.dataSources.dataSources['conceptAnswers'];
187
+ source.dataFromSourceChanged = subject.asObservable();
188
+
189
+ const whoStageQuestion = this.form.searchNodeByQuestionId(
190
+ 'adultWHOStage'
191
+ )[0];
192
+ if (whoStageQuestion) {
193
+ whoStageQuestion.control.valueChanges.subscribe((val) => {
194
+ if (source.dataFromSourceChanged) {
195
+ if (val === 'a89b2606-1350-11df-a1f1-0026b9348838') {
196
+ subject.next([
197
+ { value: 3, label: 'Stage 3 Symptom' },
198
+ { value: 4, label: 'Stage 4 Symptom' }
199
+ ]);
200
+ } else {
201
+ subject.next([
202
+ { value: 5, label: 'Stage 5 Symptom' },
203
+ { value: 6, label: 'Stage 6 Symptom' }
204
+ ]);
205
+ }
206
+ }
207
+ });
208
+ }
209
+ }
210
+
211
+ public getSectionData(sectionId) {
212
+ let data = {};
213
+ data = this.sections[sectionId];
214
+ return data;
215
+ }
216
+
217
+ public clickTab(tabNumber) {
218
+ this.activeTab = tabNumber;
219
+ }
220
+
221
+ public createForm() {
222
+ this.form = this.formFactory.createForm(
223
+ this.schema,
224
+ this.dataSources.dataSources
225
+ );
226
+
227
+ // Get concepts with no label
228
+ const concepts = this.traverseForUnlabeledConcepts(this.form.rootNode);
229
+
230
+ // Fetch missing labels from concept dictionary
231
+ this.fetchMockedConceptData(concepts).then((conceptData: any) => {
232
+ this.labelMap = [];
233
+ conceptData.forEach((concept: any) => {
234
+ this.labelMap[concept.reqId] = concept.display;
235
+ });
236
+ });
237
+
238
+ this.translate.currentLang = this.currentLanguage;
239
+ this.fetchMockedTranslationsData().then((translationsData: any) => {
240
+ this.translate.setTranslation(
241
+ translationsData?.language,
242
+ translationsData?.translations
243
+ );
244
+ });
245
+ }
246
+
247
+ fetchMockedTranslationsData() {
248
+ const promise = new Promise(function (resolve, reject) {
249
+ setTimeout(function () {
250
+ const translationsData = mockTranslationsData.find(
251
+ (translation) => translation.language === 'en'
252
+ );
253
+ resolve(translationsData);
254
+ }, 2000);
255
+ });
256
+ return promise;
257
+ }
258
+
259
+ fetchMockedConceptData(concepts) {
260
+ const promise = new Promise(function (resolve, reject) {
261
+ // Simulate a server response with some delay
262
+ setTimeout(function () {
263
+ const conceptData = [
264
+ {
265
+ uuid: 'a89ff9a6-1350-11df-a1f1-0026b9348838',
266
+ display: 'Was this visit scheduled?',
267
+ reqId: concepts[0]
268
+ },
269
+ {
270
+ uuid: 'a89b6440-1350-11df-a1f1-0026b9348838',
271
+ display: 'Scheduled visit',
272
+ reqId: concepts[1]
273
+ },
274
+ {
275
+ uuid: 'a89ff816-1350-11df-a1f1-0026b9348838',
276
+ display: 'Unscheduled visit early',
277
+ reqId: concepts[2]
278
+ },
279
+ {
280
+ uuid: 'a89ff8de-1350-11df-a1f1-0026b9348838',
281
+ display: 'Unscheduled visit late',
282
+ reqId: concepts[3]
283
+ }
284
+ ];
285
+ resolve(conceptData);
286
+ }, 2000);
287
+ });
288
+ return promise;
289
+ }
290
+
291
+ traverseForUnlabeledConcepts(o, type?) {
292
+ let concepts = [];
293
+ if (o.children) {
294
+ if (o.children instanceof Array) {
295
+ const returned = this.traverseRepeatingGroupForUnlabeledConcepts(
296
+ o.children
297
+ );
298
+ return returned;
299
+ }
300
+ if (o.children instanceof Object) {
301
+ for (const key in o.children) {
302
+ if (o.children.hasOwnProperty(key)) {
303
+ const question = o.children[key].question;
304
+ switch (question.renderingType) {
305
+ case 'page':
306
+ case 'section':
307
+ case 'group':
308
+ const childrenConcepts = this.traverseForUnlabeledConcepts(
309
+ o.children[key]
310
+ );
311
+ concepts = concepts.concat(childrenConcepts);
312
+ break;
313
+ case 'repeating':
314
+ const repeatingConcepts = this.traverseRepeatingGroupForUnlabeledConcepts(
315
+ o.children[key].children
316
+ );
317
+ concepts = concepts.concat(repeatingConcepts);
318
+ break;
319
+ default:
320
+ if (!question.label && question.extras.questionOptions) {
321
+ concepts.push(question.extras.questionOptions.concept);
322
+ }
323
+ if (question.extras.questionOptions.answers) {
324
+ question.extras.questionOptions.answers.forEach((answer) => {
325
+ if (!answer.label) {
326
+ concepts.push(answer.concept);
327
+ }
328
+ });
329
+ }
330
+ break;
331
+ }
332
+ }
333
+ }
334
+ }
335
+ }
336
+ return concepts;
337
+ }
338
+
339
+ traverseRepeatingGroupForUnlabeledConcepts(nodes) {
340
+ const toReturn = [];
341
+ for (const node of nodes) {
342
+ toReturn.push(this.traverseForUnlabeledConcepts(node));
343
+ }
344
+ return toReturn;
345
+ }
346
+
347
+ public sampleResolve(): Observable<any> {
348
+ const item = { value: '1', label: 'Art3mis' };
349
+ return Observable.create((observer: Subject<any>) => {
350
+ setTimeout(() => {
351
+ observer.next(item);
352
+ }, 1000);
353
+ });
354
+ }
355
+
356
+ public sampleSearch(): Observable<any> {
357
+ const items: Array<any> = [
358
+ { value: '0', label: 'Aech' },
359
+ { value: '5b6e58ea-1359-11df-a1f1-0026b9348838', label: 'Art3mis' },
360
+ { value: '2', label: 'Daito' },
361
+ { value: '3', label: 'Parzival' },
362
+ { value: '4', label: 'Shoto' }
363
+ ];
364
+
365
+ return Observable.create((observer: Subject<any>) => {
366
+ setTimeout(() => {
367
+ observer.next(items);
368
+ }, 1000);
369
+ });
370
+ }
371
+
372
+ public onSubmit($event) {
373
+ $event.preventDefault();
374
+ // Set valueProcessingInfo
375
+ this.form.valueProcessingInfo = {
376
+ patientUuid: 'patientUuid',
377
+ visitUuid: 'visitUuid',
378
+ encounterTypeUuid: 'encounterTypeUuid',
379
+ formUuid: 'formUuid',
380
+ encounterUuid: 'encounterUuid',
381
+ providerUuid: 'providerUuid',
382
+ utcOffset: '+0300',
383
+ locationUuid: 'some-location-uuid'
384
+ };
385
+ if (this.form.valid) {
386
+ this.form.showErrors = false;
387
+ // const payload = this.encAdapter.generateFormPayload(this.form);
388
+
389
+ // Alternative is to populate for each as shown below
390
+ // // generate obs payload
391
+ // let payload = this.obsValueAdapater.generateFormPayload(this.form);
392
+
393
+ // // generate orders payload
394
+ // let ordersPayload = this.orderAdaptor.generateFormPayload(this.form);
395
+
396
+ // generate patient identifiers
397
+ //const patientIdenfitiers = this.patientIdenfierAdapter.generateFormPayload(this.form,this.form.valueProcessingInfo['locationUuid']);
398
+ } else {
399
+ this.form.showErrors = true;
400
+ this.form.markInvalidControls(this.form.rootNode);
401
+ }
402
+ }
403
+
404
+ public reset($event) {
405
+ $event.preventDefault();
406
+ this.form.rootNode.control.reset();
407
+ }
408
+
409
+ public toggleEncounterViewer() {
410
+ this.showingEncounterViewer === true
411
+ ? (this.showingEncounterViewer = false)
412
+ : (this.showingEncounterViewer = true);
413
+ }
414
+ }
@@ -0,0 +1,24 @@
1
+ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
2
+ import { HttpClientModule } from '@angular/common/http';
3
+ import { BrowserModule } from '@angular/platform-browser';
4
+ import { ReactiveFormsModule } from '@angular/forms';
5
+ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
6
+ import { FormEntryModule } from '@openmrs/ngx-formentry';
7
+ import { AppComponent } from './app.component';
8
+ import { NgxTranslateModule } from './translate/translate.module';
9
+
10
+ @NgModule({
11
+ declarations: [AppComponent],
12
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
13
+ imports: [
14
+ BrowserModule,
15
+ BrowserAnimationsModule,
16
+ FormEntryModule,
17
+ HttpClientModule,
18
+ ReactiveFormsModule,
19
+ NgxTranslateModule
20
+ ],
21
+ providers: [],
22
+ bootstrap: [AppComponent]
23
+ })
24
+ export class AppModule {}
@@ -0,0 +1,101 @@
1
+ import { TextInputQuestion } from 'projects/ngx-formentry/src/public_api';
2
+ import { QuestionGroup } from 'projects/ngx-formentry/src/public_api';
3
+ import { RepeatingQuestion } from 'projects/ngx-formentry/src/public_api';
4
+ import { FormGroup } from '@angular/forms';
5
+ export class MockForm {
6
+ section1: FormGroup;
7
+ data = {
8
+ form: this.section1,
9
+ questions: [
10
+ new TextInputQuestion({
11
+ type: 'text',
12
+ key: 'things',
13
+ label: 'Things You Like',
14
+ defaultValue: 'Hello',
15
+ placeholder: ''
16
+ }),
17
+ new TextInputQuestion({
18
+ type: 'text',
19
+ key: 'things1',
20
+ label: 'Things You Like 1',
21
+ defaultValue: 'Hello',
22
+ placeholder: ''
23
+ }),
24
+ new QuestionGroup({
25
+ type: 'group',
26
+ key: 'nested',
27
+ questions: [
28
+ new TextInputQuestion({
29
+ type: 'text',
30
+ key: 'nestedText',
31
+ label: 'Nested In Group',
32
+ defaultValue: 'Nested In Group',
33
+ placeholder: ''
34
+ }),
35
+ new QuestionGroup({
36
+ type: 'group',
37
+ key: 'nestedDeep',
38
+ questions: [
39
+ new TextInputQuestion({
40
+ type: 'text',
41
+ key: 'nestedTextDeep',
42
+ label: 'Nested In Deep Group',
43
+ defaultValue: 'Nested In Deep Group',
44
+ placeholder: ''
45
+ }),
46
+ new RepeatingQuestion({
47
+ type: 'repeating',
48
+ key: 'repeating',
49
+ label: 'Repeated In Group',
50
+ questions: [
51
+ new TextInputQuestion({
52
+ type: 'text',
53
+ key: 'reatingPrvi4',
54
+ label: 'Am Repeated in group',
55
+ placeholder: 'Am Repeated in group'
56
+ })
57
+ ]
58
+ })
59
+ ]
60
+ })
61
+ ]
62
+ }),
63
+ new RepeatingQuestion({
64
+ type: 'repeating',
65
+ key: 'repeating1',
66
+ label: 'Repeated',
67
+ questions: [
68
+ new TextInputQuestion({
69
+ type: 'text',
70
+ key: 'reatingPrvi2',
71
+ label: 'Am Repeated',
72
+ placeholder: 'Am Repeated'
73
+ }),
74
+ new TextInputQuestion({
75
+ type: 'text',
76
+ key: 'reatingPrvi1',
77
+ label: 'Am Repeated Second',
78
+ placeholder: 'Am Repeated Second'
79
+ })
80
+ ]
81
+ }),
82
+ new TextInputQuestion({
83
+ type: 'text',
84
+ key: 'Drugi',
85
+ label: 'I Reference',
86
+ defaultValue: '',
87
+ placeholder: 'I Reference'
88
+ }),
89
+ new TextInputQuestion({
90
+ type: 'text',
91
+ key: 'Prvi',
92
+ label: 'Am Referenced',
93
+ defaultValue: '',
94
+ placeholder: 'Am Referenced'
95
+ })
96
+ ]
97
+ };
98
+ getMockForm() {
99
+ return this.data;
100
+ }
101
+ }