@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.
- package/dist/components/inputs/ui-select-extended/ui-select-extended.component.js +18 -7
- package/dist/components/inputs/ui-select-extended/ui-select-extended.scss +24 -0
- package/dist/datasources/concept-data-source.js +27 -3
- package/dist/src/components/inputs/ui-select-extended/ui-select-extended.component.d.ts +4 -0
- package/dist/src/components/inputs/ui-select-extended/ui-select-extended.component.d.ts.map +1 -1
- package/dist/src/datasources/concept-data-source.d.ts +20 -0
- package/dist/src/datasources/concept-data-source.d.ts.map +1 -1
- package/dist/src/types/index.d.ts +2 -2
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/transformers/default-schema-transformer.js +5 -1
- package/package.json +1 -1
- package/src/components/inputs/ui-select-extended/ui-select-extended.component.tsx +17 -7
- package/src/components/inputs/ui-select-extended/ui-select-extended.scss +24 -0
- package/src/datasources/concept-data-source.ts +58 -4
- package/src/transformers/default-schema-transformer.ts +5 -1
- package/src/types/index.ts +2 -2
|
@@ -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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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":"
|
|
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":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC3F,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,eAAe,CAAC;AAEtE,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC;IACtB,QAAQ,EAAE,UAAU,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,IAAI,CAAC;IAClB,QAAQ,EAAE,eAAe,CAAC;IAC1B,eAAe,EAAE,eAAe,CAAC;IACjC,UAAU,EAAE,UAAU,CAAC;IACvB,iBAAiB,CAAC,EAAE,eAAe,CAAC;IACpC,yBAAyB,CAAC,EAAE,eAAe,CAAC;IAC5C,SAAS,EAAE,aAAa,CAAC;IACzB,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAC1D,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACzD,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,mBAAmB,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB,KAAK,GAAG,CAAC;IACtF;;;;OAIG;IACH,eAAe,EAAE,CACf,KAAK,EAAE,SAAS,EAChB,YAAY,EAAE,eAAe,EAC7B,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACxB;;OAEG;IACH,gBAAgB,EAAE,CAChB,KAAK,EAAE,SAAS,EAChB,YAAY,EAAE,eAAe,EAC7B,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC;IAChD;;OAEG;IACH,eAAe,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC;IACvD;;OAEG;IACH,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B;;OAEG;IACH,SAAS,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhF;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;
|
|
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
|
-
|
|
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
|
@@ -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)
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
},
|
package/src/types/index.ts
CHANGED
|
@@ -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 {
|