@openmrs/esm-form-engine-lib 4.2.2-pre.2469 → 4.2.2-pre.2471

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.
@@ -53,7 +53,7 @@ const UiSelectExtended = ({ field, errors, warnings, setFieldValue })=>{
53
53
  dataSource.fetchData(searchTerm, config).then((dataItems)=>{
54
54
  if (dataItems.length) {
55
55
  const currentSelectedItem = items.find((item)=>item.uuid == value);
56
- const newItems = dataItems.map(dataSource.toUuidAndDisplay);
56
+ const newItems = dataItems.map((item)=>dataSource.toUuidAndDisplay(item, config));
57
57
  if (currentSelectedItem && !newItems.some((item)=>item.uuid == currentSelectedItem.uuid)) {
58
58
  newItems.unshift(currentSelectedItem);
59
59
  }
@@ -88,7 +88,7 @@ const UiSelectExtended = ({ field, errors, warnings, setFieldValue })=>{
88
88
  referencedValue: dataSourceDependentValue
89
89
  }).then((dataItems)=>{
90
90
  if (!ignore) {
91
- setItems(dataItems.map(dataSource.toUuidAndDisplay));
91
+ setItems(dataItems.map((item)=>dataSource.toUuidAndDisplay(item, config)));
92
92
  setIsLoading(false);
93
93
  }
94
94
  }).catch((err)=>{
@@ -121,10 +121,10 @@ const UiSelectExtended = ({ field, errors, warnings, setFieldValue })=>{
121
121
  if (value && !isDirty && dataSource && isSearchable && !items.length) {
122
122
  // For search-based instances, fetch the initial item to resolve its display property
123
123
  setIsLoading(true);
124
- dataSource.fetchSingleItem(value).then((item)=>{
124
+ dataSource.fetchSingleItem(value, config).then((item)=>{
125
125
  if (!ignore) {
126
126
  setItems([
127
- dataSource.toUuidAndDisplay(item)
127
+ dataSource.toUuidAndDisplay(item, config)
128
128
  ]);
129
129
  setIsLoading(false);
130
130
  }
@@ -143,14 +143,15 @@ const UiSelectExtended = ({ field, errors, warnings, setFieldValue })=>{
143
143
  isDirty,
144
144
  dataSource,
145
145
  isSearchable,
146
- items
146
+ items,
147
+ config
147
148
  ]);
148
149
  if (isLoading) {
149
150
  return /*#__PURE__*/ React.createElement(DropdownSkeleton, null);
150
151
  }
151
152
  return isViewMode(sessionMode) || isTrue(field.readonly) ? /*#__PURE__*/ React.createElement(FieldValueView, {
152
153
  label: t(field.label),
153
- value: value ? items.find((item)=>item.uuid == value)?.display : value,
154
+ value: value ? getItemText(items.find((item)=>item.uuid == value)) : value,
154
155
  conceptName: field.meta?.concept?.display,
155
156
  isInline: isInline
156
157
  }) : !field.isHidden && /*#__PURE__*/ React.createElement("div", {
@@ -161,7 +162,14 @@ const UiSelectExtended = ({ field, errors, warnings, setFieldValue })=>{
161
162
  field: field
162
163
  }),
163
164
  items: items,
164
- itemToString: (item)=>item?.display,
165
+ itemToElement: (item)=>/*#__PURE__*/ React.createElement("span", {
166
+ className: styles.item
167
+ }, item?.code && /*#__PURE__*/ React.createElement("span", {
168
+ className: styles.code
169
+ }, item.code), /*#__PURE__*/ React.createElement("span", {
170
+ className: styles.label
171
+ }, item?.display)),
172
+ itemToString: getItemText,
165
173
  selectedItem: selectedItem,
166
174
  placeholder: isSearchable ? t('search', 'Search') + '...' : null,
167
175
  onChange: ({ selectedItem })=>{
@@ -198,3 +206,6 @@ const UiSelectExtended = ({ field, errors, warnings, setFieldValue })=>{
198
206
  })));
199
207
  };
200
208
  export default UiSelectExtended;
209
+ export function getItemText(item) {
210
+ return item?.code ? `${item.code} ${item.display}` : item?.display ?? '';
211
+ }
@@ -17,3 +17,27 @@
17
17
  .loader {
18
18
  padding: 0.25rem 0.5rem;
19
19
  }
20
+
21
+ .item {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 0.5rem;
25
+ min-width: 0;
26
+ }
27
+
28
+ .code {
29
+ flex: none;
30
+ min-width: 4rem;
31
+ max-width: 8rem;
32
+ overflow: hidden;
33
+ color: inherit;
34
+ font-family: 'IBM Plex Mono', 'Menlo', 'Consolas', monospace;
35
+ font-weight: 600;
36
+ text-overflow: ellipsis;
37
+ }
38
+
39
+ .label {
40
+ overflow: hidden;
41
+ text-overflow: ellipsis;
42
+ white-space: nowrap;
43
+ }
@@ -1,12 +1,16 @@
1
1
  import { openmrsFetch, restBaseUrl } from "@openmrs/esm-framework";
2
2
  import { BaseOpenMRSDataSource } from "./data-source.js";
3
3
  import { isEmpty } from "../validators/form-validator.js";
4
+ const baseConceptRepresentation = 'custom:(uuid,display,conceptClass:(uuid,display))';
5
+ const codedConceptRepresentation = 'custom:(uuid,display,conceptClass:(uuid,display),mappings:(conceptMapType:(uuid,display),conceptReferenceTerm:(code,conceptSource:(uuid))))';
6
+ const sameAsConceptMapTypeUuid = '35543629-7d8c-11e1-909d-c80aa9edcf4e';
4
7
  export class ConceptDataSource extends BaseOpenMRSDataSource {
5
8
  fetchData(searchTerm, config) {
6
9
  if (isEmpty(config?.class) && isEmpty(config?.concept) && !config?.useSetMembersByConcept && isEmpty(searchTerm)) {
7
10
  return Promise.resolve([]);
8
11
  }
9
- let searchUrl = `${restBaseUrl}/concept?name=&searchType=fuzzy&v=custom:(uuid,display,conceptClass:(uuid,display))`;
12
+ const representation = config?.conceptSourceUuid ? codedConceptRepresentation : baseConceptRepresentation;
13
+ let searchUrl = `${restBaseUrl}/concept?name=&searchType=fuzzy&v=${representation}`;
10
14
  if (config?.class) {
11
15
  if (typeof config.class == 'string') {
12
16
  const urlParts = searchUrl.split('searchType=fuzzy');
@@ -19,7 +23,8 @@ export class ConceptDataSource extends BaseOpenMRSDataSource {
19
23
  }
20
24
  if (config?.concept && config?.useSetMembersByConcept) {
21
25
  let urlParts = searchUrl.split('?name=&searchType=fuzzy&v=');
22
- searchUrl = `${urlParts[0]}/${config.concept}?v=custom:(uuid,setMembers:(uuid,display))`;
26
+ const setMemberRepresentation = config?.conceptSourceUuid ? 'custom:(uuid,setMembers:(uuid,display,mappings:(conceptMapType:(uuid,display),conceptReferenceTerm:(code,conceptSource:(uuid)))))' : 'custom:(uuid,setMembers:(uuid,display))';
27
+ searchUrl = `${urlParts[0]}/${config.concept}?v=${setMemberRepresentation}`;
23
28
  return openmrsFetch(searchTerm ? `${searchUrl}&q=${searchTerm}` : searchUrl).then(({ data })=>{
24
29
  // return the setMembers from the retrieved concept object
25
30
  return data['setMembers'];
@@ -29,7 +34,26 @@ export class ConceptDataSource extends BaseOpenMRSDataSource {
29
34
  return data.results;
30
35
  });
31
36
  }
37
+ fetchSingleItem(uuid, config) {
38
+ const representation = config?.conceptSourceUuid ? codedConceptRepresentation : baseConceptRepresentation;
39
+ return openmrsFetch(`${restBaseUrl}/concept/${uuid}?v=${representation}`).then(({ data })=>data);
40
+ }
41
+ toUuidAndDisplay(data, config) {
42
+ const item = super.toUuidAndDisplay(data);
43
+ const code = this.getConceptSourceMapping(data, config?.conceptSourceUuid)?.conceptReferenceTerm?.code;
44
+ return code ? {
45
+ ...item,
46
+ code
47
+ } : item;
48
+ }
49
+ getConceptSourceMapping(concept, conceptSourceUuid) {
50
+ if (!conceptSourceUuid) {
51
+ return undefined;
52
+ }
53
+ const mappingsToSource = concept.mappings?.filter((mapping)=>mapping.conceptReferenceTerm?.conceptSource?.uuid === conceptSourceUuid) ?? [];
54
+ return mappingsToSource.find((mapping)=>mapping.conceptMapType?.uuid === sameAsConceptMapTypeUuid || mapping.conceptMapType?.display?.toUpperCase() === 'SAME-AS') ?? mappingsToSource[0];
55
+ }
32
56
  constructor(){
33
- super(`${restBaseUrl}/concept?v=custom:(uuid,display,conceptClass:(uuid,display))`);
57
+ super(`${restBaseUrl}/concept?v=${baseConceptRepresentation}`);
34
58
  }
35
59
  }
@@ -2,4 +2,8 @@ import React from 'react';
2
2
  import { type FormFieldInputProps } from '../../../types';
3
3
  declare const UiSelectExtended: React.FC<FormFieldInputProps>;
4
4
  export default UiSelectExtended;
5
+ export declare function getItemText(item?: {
6
+ code?: string;
7
+ display?: string;
8
+ }): string;
5
9
  //# sourceMappingURL=ui-select-extended.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ui-select-extended.component.d.ts","sourceRoot":"","sources":["../../../../../src/components/inputs/ui-select-extended/ui-select-extended.component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAYjF,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAO3E,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA2LnD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"ui-select-extended.component.d.ts","sourceRoot":"","sources":["../../../../../src/components/inputs/ui-select-extended/ui-select-extended.component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAYjF,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAO3E,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAiMnD,CAAC;AAEF,eAAe,gBAAgB,CAAC;AAEhC,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE9E"}
@@ -1,6 +1,26 @@
1
+ import { type OpenmrsResource } from '@openmrs/esm-framework';
1
2
  import { BaseOpenMRSDataSource } from './data-source';
3
+ interface ConceptMapping {
4
+ conceptMapType?: {
5
+ uuid?: string;
6
+ display?: string;
7
+ };
8
+ conceptReferenceTerm?: {
9
+ code?: string;
10
+ conceptSource?: {
11
+ uuid?: string;
12
+ };
13
+ };
14
+ }
15
+ interface ConceptResource extends OpenmrsResource {
16
+ mappings?: Array<ConceptMapping>;
17
+ }
2
18
  export declare class ConceptDataSource extends BaseOpenMRSDataSource {
3
19
  constructor();
4
20
  fetchData(searchTerm: string, config?: Record<string, any>): Promise<any[]>;
21
+ fetchSingleItem(uuid: string, config?: Record<string, any>): Promise<ConceptResource | null>;
22
+ toUuidAndDisplay(data: ConceptResource, config?: Record<string, any>): OpenmrsResource;
23
+ private getConceptSourceMapping;
5
24
  }
25
+ export {};
6
26
  //# sourceMappingURL=concept-data-source.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"concept-data-source.d.ts","sourceRoot":"","sources":["../../../src/datasources/concept-data-source.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,qBAAa,iBAAkB,SAAQ,qBAAqB;;IAK1D,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAgC5E"}
1
+ {"version":3,"file":"concept-data-source.d.ts","sourceRoot":"","sources":["../../../src/datasources/concept-data-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAQtD,UAAU,cAAc;IACtB,cAAc,CAAC,EAAE;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,oBAAoB,CAAC,EAAE;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE;YACd,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,CAAC;KACH,CAAC;CACH;AAED,UAAU,eAAgB,SAAQ,eAAe;IAC/C,QAAQ,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;CAClC;AAED,qBAAa,iBAAkB,SAAQ,qBAAqB;;IAK1D,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAqC3E,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAK5F,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,eAAe;IAMtF,OAAO,CAAC,uBAAuB;CAgBhC"}
@@ -61,11 +61,11 @@ export interface DataSource<T> {
61
61
  * Fetches a single item from the data source based on its UUID.
62
62
  * This is used for value binding with previously selected values.
63
63
  */
64
- fetchSingleItem(uuid: string): Promise<T | null>;
64
+ fetchSingleItem(uuid: string, config?: Record<string, any>): Promise<T | null>;
65
65
  /**
66
66
  * Maps a data source item to an object with a uuid and display property
67
67
  */
68
- toUuidAndDisplay(item: T): OpenmrsResource;
68
+ toUuidAndDisplay(item: T, config?: Record<string, any>): OpenmrsResource;
69
69
  }
70
70
  export interface ControlTemplate {
71
71
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -265,10 +265,12 @@ function handleQuestionsWithObsComments(sectionQuestions) {
265
265
  }
266
266
  function handleDiagnosis(question) {
267
267
  if ('dataSource' in question.questionOptions && question.questionOptions['dataSource'] === 'diagnoses' || question.type === 'diagnosis') {
268
+ const datasourceConfig = question.questionOptions.datasource?.config;
268
269
  question.questionOptions.datasource = {
269
270
  name: 'problem_datasource',
270
271
  config: {
271
- class: question.questionOptions.diagnosis?.conceptClasses
272
+ ...datasourceConfig,
273
+ class: question.questionOptions.diagnosis?.conceptClasses ?? datasourceConfig?.class
272
274
  }
273
275
  };
274
276
  if (question.questionOptions.diagnosis?.conceptSet) {
@@ -278,6 +280,8 @@ function handleDiagnosis(question) {
278
280
  datasource: {
279
281
  name: 'problem_datasource',
280
282
  config: {
283
+ ...datasourceConfig,
284
+ concept: question.questionOptions.diagnosis.conceptSet,
281
285
  useSetMembersByConcept: true
282
286
  }
283
287
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-form-engine-lib",
3
- "version": "4.2.2-pre.2469",
3
+ "version": "4.2.2-pre.2471",
4
4
  "description": "React Form Engine for O3",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -55,7 +55,7 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
55
55
  .then((dataItems) => {
56
56
  if (dataItems.length) {
57
57
  const currentSelectedItem = items.find((item) => item.uuid == value);
58
- const newItems = dataItems.map(dataSource.toUuidAndDisplay);
58
+ const newItems = dataItems.map((item) => dataSource.toUuidAndDisplay(item, config));
59
59
  if (currentSelectedItem && !newItems.some((item) => item.uuid == currentSelectedItem.uuid)) {
60
60
  newItems.unshift(currentSelectedItem);
61
61
  }
@@ -98,7 +98,7 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
98
98
  .fetchData(null, { ...config, referencedValue: dataSourceDependentValue })
99
99
  .then((dataItems) => {
100
100
  if (!ignore) {
101
- setItems(dataItems.map(dataSource.toUuidAndDisplay));
101
+ setItems(dataItems.map((item) => dataSource.toUuidAndDisplay(item, config)));
102
102
  setIsLoading(false);
103
103
  }
104
104
  })
@@ -128,10 +128,10 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
128
128
  // For search-based instances, fetch the initial item to resolve its display property
129
129
  setIsLoading(true);
130
130
  dataSource
131
- .fetchSingleItem(value)
131
+ .fetchSingleItem(value, config)
132
132
  .then((item) => {
133
133
  if (!ignore) {
134
- setItems([dataSource.toUuidAndDisplay(item)]);
134
+ setItems([dataSource.toUuidAndDisplay(item, config)]);
135
135
  setIsLoading(false);
136
136
  }
137
137
  })
@@ -146,7 +146,7 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
146
146
  return () => {
147
147
  ignore = true;
148
148
  };
149
- }, [value, isDirty, dataSource, isSearchable, items]);
149
+ }, [value, isDirty, dataSource, isSearchable, items, config]);
150
150
 
151
151
  if (isLoading) {
152
152
  return <DropdownSkeleton />;
@@ -155,7 +155,7 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
155
155
  return isViewMode(sessionMode) || isTrue(field.readonly) ? (
156
156
  <FieldValueView
157
157
  label={t(field.label)}
158
- value={value ? items.find((item) => item.uuid == value)?.display : value}
158
+ value={value ? getItemText(items.find((item) => item.uuid == value)) : value}
159
159
  conceptName={field.meta?.concept?.display}
160
160
  isInline={isInline}
161
161
  />
@@ -167,7 +167,13 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
167
167
  id={field.id}
168
168
  titleText={<FieldLabel field={field} />}
169
169
  items={items}
170
- itemToString={(item) => item?.display}
170
+ itemToElement={(item) => (
171
+ <span className={styles.item}>
172
+ {item?.code && <span className={styles.code}>{item.code}</span>}
173
+ <span className={styles.label}>{item?.display}</span>
174
+ </span>
175
+ )}
176
+ itemToString={getItemText}
171
177
  selectedItem={selectedItem}
172
178
  placeholder={isSearchable ? t('search', 'Search') + '...' : null}
173
179
  onChange={({ selectedItem }) => {
@@ -207,3 +213,7 @@ const UiSelectExtended: React.FC<FormFieldInputProps> = ({ field, errors, warnin
207
213
  };
208
214
 
209
215
  export default UiSelectExtended;
216
+
217
+ export function getItemText(item?: { code?: string; display?: string }): string {
218
+ return item?.code ? `${item.code} ${item.display}` : item?.display ?? '';
219
+ }
@@ -17,3 +17,27 @@
17
17
  .loader {
18
18
  padding: 0.25rem 0.5rem;
19
19
  }
20
+
21
+ .item {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 0.5rem;
25
+ min-width: 0;
26
+ }
27
+
28
+ .code {
29
+ flex: none;
30
+ min-width: 4rem;
31
+ max-width: 8rem;
32
+ overflow: hidden;
33
+ color: inherit;
34
+ font-family: 'IBM Plex Mono', 'Menlo', 'Consolas', monospace;
35
+ font-weight: 600;
36
+ text-overflow: ellipsis;
37
+ }
38
+
39
+ .label {
40
+ overflow: hidden;
41
+ text-overflow: ellipsis;
42
+ white-space: nowrap;
43
+ }
@@ -1,10 +1,32 @@
1
- import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
1
+ import { openmrsFetch, restBaseUrl, type OpenmrsResource } from '@openmrs/esm-framework';
2
2
  import { BaseOpenMRSDataSource } from './data-source';
3
3
  import { isEmpty } from '../validators/form-validator';
4
4
 
5
+ const baseConceptRepresentation = 'custom:(uuid,display,conceptClass:(uuid,display))';
6
+ const codedConceptRepresentation =
7
+ 'custom:(uuid,display,conceptClass:(uuid,display),mappings:(conceptMapType:(uuid,display),conceptReferenceTerm:(code,conceptSource:(uuid))))';
8
+ const sameAsConceptMapTypeUuid = '35543629-7d8c-11e1-909d-c80aa9edcf4e';
9
+
10
+ interface ConceptMapping {
11
+ conceptMapType?: {
12
+ uuid?: string;
13
+ display?: string;
14
+ };
15
+ conceptReferenceTerm?: {
16
+ code?: string;
17
+ conceptSource?: {
18
+ uuid?: string;
19
+ };
20
+ };
21
+ }
22
+
23
+ interface ConceptResource extends OpenmrsResource {
24
+ mappings?: Array<ConceptMapping>;
25
+ }
26
+
5
27
  export class ConceptDataSource extends BaseOpenMRSDataSource {
6
28
  constructor() {
7
- super(`${restBaseUrl}/concept?v=custom:(uuid,display,conceptClass:(uuid,display))`);
29
+ super(`${restBaseUrl}/concept?v=${baseConceptRepresentation}`);
8
30
  }
9
31
 
10
32
  fetchData(searchTerm: string, config?: Record<string, any>): Promise<any[]> {
@@ -12,7 +34,8 @@ export class ConceptDataSource extends BaseOpenMRSDataSource {
12
34
  return Promise.resolve([]);
13
35
  }
14
36
 
15
- let searchUrl = `${restBaseUrl}/concept?name=&searchType=fuzzy&v=custom:(uuid,display,conceptClass:(uuid,display))`;
37
+ const representation = config?.conceptSourceUuid ? codedConceptRepresentation : baseConceptRepresentation;
38
+ let searchUrl = `${restBaseUrl}/concept?name=&searchType=fuzzy&v=${representation}`;
16
39
  if (config?.class) {
17
40
  if (typeof config.class == 'string') {
18
41
  const urlParts = searchUrl.split('searchType=fuzzy');
@@ -28,7 +51,10 @@ export class ConceptDataSource extends BaseOpenMRSDataSource {
28
51
 
29
52
  if (config?.concept && config?.useSetMembersByConcept) {
30
53
  let urlParts = searchUrl.split('?name=&searchType=fuzzy&v=');
31
- searchUrl = `${urlParts[0]}/${config.concept}?v=custom:(uuid,setMembers:(uuid,display))`;
54
+ const setMemberRepresentation = config?.conceptSourceUuid
55
+ ? 'custom:(uuid,setMembers:(uuid,display,mappings:(conceptMapType:(uuid,display),conceptReferenceTerm:(code,conceptSource:(uuid)))))'
56
+ : 'custom:(uuid,setMembers:(uuid,display))';
57
+ searchUrl = `${urlParts[0]}/${config.concept}?v=${setMemberRepresentation}`;
32
58
  return openmrsFetch(searchTerm ? `${searchUrl}&q=${searchTerm}` : searchUrl).then(({ data }) => {
33
59
  // return the setMembers from the retrieved concept object
34
60
  return data['setMembers'];
@@ -39,4 +65,32 @@ export class ConceptDataSource extends BaseOpenMRSDataSource {
39
65
  return data.results;
40
66
  });
41
67
  }
68
+
69
+ fetchSingleItem(uuid: string, config?: Record<string, any>): Promise<ConceptResource | null> {
70
+ const representation = config?.conceptSourceUuid ? codedConceptRepresentation : baseConceptRepresentation;
71
+ return openmrsFetch(`${restBaseUrl}/concept/${uuid}?v=${representation}`).then(({ data }) => data);
72
+ }
73
+
74
+ toUuidAndDisplay(data: ConceptResource, config?: Record<string, any>): OpenmrsResource {
75
+ const item = super.toUuidAndDisplay(data);
76
+ const code = this.getConceptSourceMapping(data, config?.conceptSourceUuid)?.conceptReferenceTerm?.code;
77
+ return code ? { ...item, code } : item;
78
+ }
79
+
80
+ private getConceptSourceMapping(concept: ConceptResource, conceptSourceUuid?: string): ConceptMapping | undefined {
81
+ if (!conceptSourceUuid) {
82
+ return undefined;
83
+ }
84
+
85
+ const mappingsToSource =
86
+ concept.mappings?.filter((mapping) => mapping.conceptReferenceTerm?.conceptSource?.uuid === conceptSourceUuid) ??
87
+ [];
88
+ return (
89
+ mappingsToSource.find(
90
+ (mapping) =>
91
+ mapping.conceptMapType?.uuid === sameAsConceptMapTypeUuid ||
92
+ mapping.conceptMapType?.display?.toUpperCase() === 'SAME-AS',
93
+ ) ?? mappingsToSource[0]
94
+ );
95
+ }
42
96
  }
@@ -295,10 +295,12 @@ function handleDiagnosis(question: FormField) {
295
295
  ('dataSource' in question.questionOptions && question.questionOptions['dataSource'] === 'diagnoses') ||
296
296
  question.type === 'diagnosis'
297
297
  ) {
298
+ const datasourceConfig = question.questionOptions.datasource?.config;
298
299
  question.questionOptions.datasource = {
299
300
  name: 'problem_datasource',
300
301
  config: {
301
- class: question.questionOptions.diagnosis?.conceptClasses,
302
+ ...datasourceConfig,
303
+ class: question.questionOptions.diagnosis?.conceptClasses ?? datasourceConfig?.class,
302
304
  },
303
305
  };
304
306
  if (question.questionOptions.diagnosis?.conceptSet) {
@@ -308,6 +310,8 @@ function handleDiagnosis(question: FormField) {
308
310
  datasource: {
309
311
  name: 'problem_datasource',
310
312
  config: {
313
+ ...datasourceConfig,
314
+ concept: question.questionOptions.diagnosis.conceptSet,
311
315
  useSetMembersByConcept: true,
312
316
  },
313
317
  },
@@ -75,12 +75,12 @@ export interface DataSource<T> {
75
75
  * Fetches a single item from the data source based on its UUID.
76
76
  * This is used for value binding with previously selected values.
77
77
  */
78
- fetchSingleItem(uuid: string): Promise<T | null>;
78
+ fetchSingleItem(uuid: string, config?: Record<string, any>): Promise<T | null>;
79
79
 
80
80
  /**
81
81
  * Maps a data source item to an object with a uuid and display property
82
82
  */
83
- toUuidAndDisplay(item: T): OpenmrsResource;
83
+ toUuidAndDisplay(item: T, config?: Record<string, any>): OpenmrsResource;
84
84
  }
85
85
 
86
86
  export interface ControlTemplate {