@openmrs/esm-form-builder-app 2.0.2-pre.574 → 2.0.2-pre.586
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/README.md +43 -17
- package/dist/127.js +1 -1
- package/dist/127.js.map +1 -1
- package/dist/150.js +1 -1
- package/dist/150.js.map +1 -1
- package/dist/153.js +1 -1
- package/dist/153.js.map +1 -1
- package/dist/164.js +1 -1
- package/dist/256.js +1 -1
- package/dist/319.js +1 -1
- package/dist/447.js +1 -1
- package/dist/447.js.map +1 -1
- package/dist/515.js +2 -0
- package/dist/{773.js.LICENSE.txt → 515.js.LICENSE.txt} +9 -0
- package/dist/515.js.map +1 -0
- package/dist/527.js +1 -0
- package/dist/527.js.map +1 -0
- package/dist/574.js +1 -1
- package/dist/757.js +1 -1
- package/dist/788.js +1 -1
- package/dist/800.js +1 -1
- package/dist/800.js.map +1 -1
- package/dist/807.js +1 -1
- package/dist/833.js +1 -1
- package/dist/878.js +2 -0
- package/dist/{208.js.LICENSE.txt → 878.js.LICENSE.txt} +2 -1
- package/dist/878.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-form-builder-app.js +1 -1
- package/dist/openmrs-esm-form-builder-app.js.buildmanifest.json +154 -175
- package/dist/openmrs-esm-form-builder-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +35 -32
- package/src/components/action-buttons/action-buttons.component.tsx +65 -101
- package/src/components/dashboard/dashboard.component.tsx +98 -174
- package/src/components/dashboard/dashboard.test.tsx +51 -81
- package/src/components/empty-state/empty-data-illustration.component.tsx +4 -16
- package/src/components/empty-state/empty-state.component.tsx +11 -15
- package/src/components/error-state/error-state.component.tsx +11 -13
- package/src/components/form-editor/form-editor.component.tsx +97 -128
- package/src/components/form-renderer/form-renderer.component.tsx +30 -41
- package/src/components/interactive-builder/add-question-modal.component.tsx +129 -167
- package/src/components/interactive-builder/delete-page-modal.component.tsx +24 -37
- package/src/components/interactive-builder/delete-question-modal.component.tsx +25 -47
- package/src/components/interactive-builder/delete-section-modal.component.tsx +24 -37
- package/src/components/interactive-builder/draggable-question.component.tsx +21 -34
- package/src/components/interactive-builder/droppable-container.component.tsx +5 -5
- package/src/components/interactive-builder/edit-question-modal.component.tsx +191 -233
- package/src/components/interactive-builder/editable-value.component.tsx +12 -17
- package/src/components/interactive-builder/interactive-builder.component.tsx +134 -184
- package/src/components/interactive-builder/new-form-modal.component.tsx +35 -49
- package/src/components/interactive-builder/page-modal.component.tsx +29 -45
- package/src/components/interactive-builder/question-modal.scss +7 -0
- package/src/components/interactive-builder/section-modal.component.tsx +29 -40
- package/src/components/interactive-builder/value-editor.component.tsx +11 -16
- package/src/components/modals/save-form-modal.component.tsx +112 -165
- package/src/components/pagination/index.ts +2 -2
- package/src/components/pagination/pagination.component.tsx +8 -13
- package/src/components/pagination/usePaginationInfo.ts +4 -9
- package/src/components/schema-editor/schema-editor.component.tsx +11 -17
- package/src/config-schema.ts +28 -30
- package/src/declarations.d.ts +4 -3
- package/src/form-builder-admin-card-link.component.tsx +7 -11
- package/src/forms.resource.ts +66 -87
- package/src/hooks/useClobdata.ts +10 -12
- package/src/hooks/useConceptLookup.ts +5 -8
- package/src/hooks/useConceptName.ts +6 -9
- package/src/hooks/useEncounterTypes.ts +8 -8
- package/src/hooks/useForm.ts +7 -7
- package/src/hooks/useForms.ts +5 -8
- package/src/index.ts +11 -23
- package/src/root.component.tsx +4 -4
- package/src/setup-tests.ts +1 -9
- package/src/test-helpers.tsx +8 -15
- package/src/types.ts +16 -8
- package/dist/208.js +0 -2
- package/dist/208.js.map +0 -1
- package/dist/536.js +0 -1
- package/dist/536.js.map +0 -1
- package/dist/62.js +0 -1
- package/dist/62.js.map +0 -1
- package/dist/773.js +0 -2
- package/dist/773.js.map +0 -1
- package/src/constants.ts +0 -3
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import React, { useMemo, useState } from
|
|
2
|
-
import { useTranslation } from
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import debounce from 'lodash-es/debounce';
|
|
4
|
+
import flattenDeep from 'lodash-es/flattenDeep';
|
|
3
5
|
import {
|
|
4
6
|
Button,
|
|
5
7
|
ComposedModal,
|
|
6
8
|
Form,
|
|
7
9
|
FormGroup,
|
|
8
10
|
FormLabel,
|
|
9
|
-
Layer,
|
|
10
11
|
InlineLoading,
|
|
12
|
+
InlineNotification,
|
|
13
|
+
Layer,
|
|
11
14
|
ModalBody,
|
|
12
15
|
ModalFooter,
|
|
13
16
|
ModalHeader,
|
|
@@ -21,19 +24,17 @@ import {
|
|
|
21
24
|
Tag,
|
|
22
25
|
TextInput,
|
|
23
26
|
Tile,
|
|
24
|
-
} from
|
|
25
|
-
import { ArrowUpRight } from
|
|
26
|
-
import
|
|
27
|
-
import
|
|
28
|
-
|
|
29
|
-
import type {
|
|
30
|
-
|
|
31
|
-
import
|
|
32
|
-
import
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
type EditQuestionModalProps = {
|
|
27
|
+
} from '@carbon/react';
|
|
28
|
+
import { ArrowUpRight } from '@carbon/react/icons';
|
|
29
|
+
import { showNotification, showToast, useConfig } from '@openmrs/esm-framework';
|
|
30
|
+
import type { RenderType } from '@openmrs/openmrs-form-engine-lib';
|
|
31
|
+
|
|
32
|
+
import type { Concept, ConceptMapping, Question, QuestionType, Schema } from '../../types';
|
|
33
|
+
import { useConceptLookup } from '../../hooks/useConceptLookup';
|
|
34
|
+
import { useConceptName } from '../../hooks/useConceptName';
|
|
35
|
+
import styles from './question-modal.scss';
|
|
36
|
+
|
|
37
|
+
interface EditQuestionModalProps {
|
|
37
38
|
onModalChange: (showModal: boolean) => void;
|
|
38
39
|
onQuestionEdit: (question: Question) => void;
|
|
39
40
|
onSchemaChange: (schema: Schema) => void;
|
|
@@ -44,7 +45,17 @@ type EditQuestionModalProps = {
|
|
|
44
45
|
schema: Schema;
|
|
45
46
|
sectionIndex: number;
|
|
46
47
|
showModal: boolean;
|
|
47
|
-
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface Config {
|
|
51
|
+
fieldTypes: Array<RenderType>;
|
|
52
|
+
questionTypes: Array<QuestionType>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface Item {
|
|
56
|
+
id: string;
|
|
57
|
+
text: string;
|
|
58
|
+
}
|
|
48
59
|
|
|
49
60
|
const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
50
61
|
questionToEdit,
|
|
@@ -59,36 +70,45 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
59
70
|
onQuestionEdit,
|
|
60
71
|
}) => {
|
|
61
72
|
const { t } = useTranslation();
|
|
62
|
-
const { fieldTypes, questionTypes } = useConfig();
|
|
63
|
-
|
|
64
|
-
const [
|
|
65
|
-
const [
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const [conceptMappings, setConceptMappings] = useState<ConceptMapping
|
|
72
|
-
questionToEdit.questionOptions.conceptMappings
|
|
73
|
+
const { fieldTypes, questionTypes }: Config = useConfig();
|
|
74
|
+
|
|
75
|
+
const [answersChanged, setAnswersChanged] = useState(false);
|
|
76
|
+
const [answersFromConcept, setAnswersFromConcept] = useState<
|
|
77
|
+
Array<{
|
|
78
|
+
concept: string;
|
|
79
|
+
label: string;
|
|
80
|
+
}>
|
|
81
|
+
>([]);
|
|
82
|
+
const [conceptMappings, setConceptMappings] = useState<Array<ConceptMapping> | undefined>(
|
|
83
|
+
questionToEdit.questionOptions.conceptMappings,
|
|
73
84
|
);
|
|
74
|
-
const [
|
|
75
|
-
const [
|
|
76
|
-
const [
|
|
77
|
-
const [
|
|
85
|
+
const [conceptToLookup, setConceptToLookup] = useState('');
|
|
86
|
+
const [fieldType, setFieldType] = useState<RenderType | null>(null);
|
|
87
|
+
const [isQuestionRequired, setIsQuestionRequired] = useState(false);
|
|
88
|
+
const [max, setMax] = useState('');
|
|
89
|
+
const [min, setMin] = useState('');
|
|
90
|
+
const [questionId, setQuestionId] = useState('');
|
|
91
|
+
const [questionLabel, setQuestionLabel] = useState('');
|
|
92
|
+
const [questionType, setQuestionType] = useState<QuestionType | null>(null);
|
|
93
|
+
const [rows, setRows] = useState('');
|
|
94
|
+
const [selectedAnswers, setSelectedAnswers] = useState<
|
|
95
|
+
Array<{
|
|
96
|
+
id: string;
|
|
97
|
+
text: string;
|
|
98
|
+
}>
|
|
99
|
+
>([]);
|
|
100
|
+
const [selectedConcept, setSelectedConcept] = useState<Concept | null>(null);
|
|
101
|
+
|
|
78
102
|
const { concepts, isLoadingConcepts } = useConceptLookup(conceptToLookup);
|
|
79
|
-
const { conceptName, isLoadingConceptName } = useConceptName(
|
|
80
|
-
questionToEdit.questionOptions.concept
|
|
103
|
+
const { conceptName, conceptNameLookupError, isLoadingConceptName } = useConceptName(
|
|
104
|
+
questionToEdit.questionOptions.concept,
|
|
81
105
|
);
|
|
82
|
-
const [answersChanged, setAnswersChanged] = useState(false);
|
|
83
106
|
|
|
84
|
-
const hasConceptChanged =
|
|
85
|
-
selectedConcept &&
|
|
86
|
-
questionToEdit?.questionOptions?.concept !== selectedConcept;
|
|
107
|
+
const hasConceptChanged = selectedConcept && questionToEdit?.questionOptions?.concept !== selectedConcept?.uuid;
|
|
87
108
|
|
|
88
|
-
const debouncedSearch = useMemo(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
);
|
|
109
|
+
const debouncedSearch = useMemo(() => {
|
|
110
|
+
return debounce((searchTerm: string) => setConceptToLookup(searchTerm), 500) as (searchTerm: string) => void;
|
|
111
|
+
}, []);
|
|
92
112
|
|
|
93
113
|
const handleConceptChange = (searchTerm: string) => {
|
|
94
114
|
if (searchTerm) {
|
|
@@ -97,26 +117,24 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
97
117
|
};
|
|
98
118
|
|
|
99
119
|
const handleConceptSelect = (concept: Concept) => {
|
|
100
|
-
setConceptToLookup(
|
|
120
|
+
setConceptToLookup('');
|
|
101
121
|
setSelectedAnswers([]);
|
|
102
122
|
setSelectedConcept(concept);
|
|
103
123
|
setConceptMappings(
|
|
104
124
|
concept?.mappings?.map((conceptMapping) => {
|
|
105
|
-
const data = conceptMapping.display.split(
|
|
125
|
+
const data = conceptMapping.display.split(': ');
|
|
106
126
|
return {
|
|
107
127
|
relationship: conceptMapping.conceptMapType.display,
|
|
108
128
|
type: data[0],
|
|
109
129
|
value: data[1],
|
|
110
130
|
};
|
|
111
|
-
})
|
|
131
|
+
}),
|
|
112
132
|
);
|
|
113
133
|
setAnswersFromConcept(
|
|
114
|
-
concept?.answers?.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}))
|
|
119
|
-
: []
|
|
134
|
+
concept?.answers?.map((answer) => ({
|
|
135
|
+
concept: answer?.uuid,
|
|
136
|
+
label: answer?.display,
|
|
137
|
+
})) ?? [],
|
|
120
138
|
);
|
|
121
139
|
};
|
|
122
140
|
|
|
@@ -133,7 +151,7 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
133
151
|
});
|
|
134
152
|
});
|
|
135
153
|
|
|
136
|
-
const questionIds = flattenDeep(nestedIds);
|
|
154
|
+
const questionIds: Array<string> = flattenDeep(nestedIds);
|
|
137
155
|
|
|
138
156
|
return questionIds.includes(idToTest);
|
|
139
157
|
};
|
|
@@ -152,11 +170,7 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
152
170
|
}));
|
|
153
171
|
} else if (hasConceptChanged && answersFromConcept.length === 0) {
|
|
154
172
|
mappedAnswers = [];
|
|
155
|
-
} else if (
|
|
156
|
-
hasConceptChanged &&
|
|
157
|
-
answersFromConcept?.length > 0 &&
|
|
158
|
-
selectedAnswers?.length
|
|
159
|
-
) {
|
|
173
|
+
} else if (hasConceptChanged && answersFromConcept?.length > 0 && selectedAnswers?.length) {
|
|
160
174
|
mappedAnswers = selectedAnswers?.length
|
|
161
175
|
? selectedAnswers.map((answer) => ({
|
|
162
176
|
concept: answer.id,
|
|
@@ -171,30 +185,22 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
171
185
|
const data = {
|
|
172
186
|
label: questionLabel ? questionLabel : questionToEdit.label,
|
|
173
187
|
type: questionType ? questionType : questionToEdit.type,
|
|
174
|
-
required: isQuestionRequired
|
|
175
|
-
? isQuestionRequired
|
|
176
|
-
: /true/.test(questionToEdit?.required?.toString()),
|
|
188
|
+
required: isQuestionRequired ? isQuestionRequired : /true/.test(questionToEdit?.required?.toString()),
|
|
177
189
|
id: questionId ? questionId : questionToEdit.id,
|
|
178
190
|
questionOptions: {
|
|
179
|
-
rendering: fieldType
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
concept: selectedConcept?.uuid
|
|
183
|
-
? selectedConcept.uuid
|
|
184
|
-
: questionToEdit.questionOptions.concept,
|
|
185
|
-
conceptMappings: conceptMappings?.length
|
|
186
|
-
? conceptMappings
|
|
187
|
-
: questionToEdit.questionOptions.conceptMappings,
|
|
191
|
+
rendering: fieldType ? fieldType : questionToEdit.questionOptions.rendering,
|
|
192
|
+
concept: selectedConcept?.uuid ? selectedConcept.uuid : questionToEdit.questionOptions.concept,
|
|
193
|
+
conceptMappings: conceptMappings?.length ? conceptMappings : questionToEdit.questionOptions.conceptMappings,
|
|
188
194
|
answers: mappedAnswers,
|
|
189
195
|
},
|
|
190
196
|
};
|
|
191
197
|
|
|
192
|
-
schema.pages[pageIndex].sections[sectionIndex].questions[questionIndex] =
|
|
193
|
-
|
|
198
|
+
schema.pages[pageIndex].sections[sectionIndex].questions[questionIndex] = data;
|
|
199
|
+
|
|
194
200
|
onSchemaChange({ ...schema });
|
|
195
201
|
resetIndices();
|
|
196
|
-
setQuestionLabel(
|
|
197
|
-
setQuestionId(
|
|
202
|
+
setQuestionLabel('');
|
|
203
|
+
setQuestionId('');
|
|
198
204
|
setIsQuestionRequired(false);
|
|
199
205
|
setQuestionType(null);
|
|
200
206
|
setFieldType(null);
|
|
@@ -202,41 +208,38 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
202
208
|
setConceptMappings([]);
|
|
203
209
|
setSelectedAnswers([]);
|
|
204
210
|
onQuestionEdit(null);
|
|
211
|
+
|
|
205
212
|
showToast({
|
|
206
|
-
title: t(
|
|
207
|
-
kind:
|
|
213
|
+
title: t('success', 'Success!'),
|
|
214
|
+
kind: 'success',
|
|
208
215
|
critical: true,
|
|
209
|
-
description: t(
|
|
216
|
+
description: t('questionUpdated', 'Question updated'),
|
|
210
217
|
});
|
|
218
|
+
|
|
211
219
|
onModalChange(false);
|
|
212
220
|
} catch (error) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
221
|
+
if (error instanceof Error) {
|
|
222
|
+
showNotification({
|
|
223
|
+
title: t('errorUpdatingQuestion', 'Error updating question'),
|
|
224
|
+
kind: 'error',
|
|
225
|
+
critical: true,
|
|
226
|
+
description: error?.message,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
219
229
|
}
|
|
220
230
|
};
|
|
221
231
|
|
|
222
232
|
return (
|
|
223
|
-
<ComposedModal
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
preventCloseOnClickOutside
|
|
227
|
-
>
|
|
228
|
-
<ModalHeader title={t("editQuestion", "Edit question")} />
|
|
229
|
-
<Form
|
|
230
|
-
className={styles.form}
|
|
231
|
-
onSubmit={(event) => event.preventDefault()}
|
|
232
|
-
>
|
|
233
|
+
<ComposedModal open={showModal} onClose={() => onModalChange(false)} preventCloseOnClickOutside>
|
|
234
|
+
<ModalHeader title={t('editQuestion', 'Edit question')} />
|
|
235
|
+
<Form className={styles.form} onSubmit={(event: React.SyntheticEvent) => event.preventDefault()}>
|
|
233
236
|
<ModalBody hasScrollingContent>
|
|
234
237
|
<Stack gap={5}>
|
|
235
238
|
<TextInput
|
|
236
239
|
defaultValue={questionToEdit.label}
|
|
237
240
|
id={questionToEdit.id}
|
|
238
|
-
labelText={t(
|
|
239
|
-
onChange={(event) => setQuestionLabel(event.target.value)}
|
|
241
|
+
labelText={t('questionLabel', 'Label')}
|
|
242
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setQuestionLabel(event.target.value)}
|
|
240
243
|
required
|
|
241
244
|
/>
|
|
242
245
|
|
|
@@ -244,45 +247,35 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
244
247
|
defaultValue={questionToEdit.id}
|
|
245
248
|
id="questionId"
|
|
246
249
|
invalid={questionIdExists(questionId)}
|
|
247
|
-
invalidText={t(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
)}
|
|
251
|
-
labelText={t(
|
|
252
|
-
"questionId",
|
|
253
|
-
"Question ID (prefer using camel-case for IDs)"
|
|
254
|
-
)}
|
|
255
|
-
onChange={(event) => setQuestionId(event.target.value)}
|
|
250
|
+
invalidText={t('questionIdExists', 'This question ID already exists in your schema')}
|
|
251
|
+
labelText={t('questionId', 'Question ID (prefer using camel-case for IDs)')}
|
|
252
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setQuestionId(event.target.value)}
|
|
256
253
|
placeholder={t(
|
|
257
|
-
|
|
258
|
-
'Enter a unique ID e.g. "anaesthesiaType" for a question asking about the type of anaesthesia.'
|
|
254
|
+
'questionIdPlaceholder',
|
|
255
|
+
'Enter a unique ID e.g. "anaesthesiaType" for a question asking about the type of anaesthesia.',
|
|
259
256
|
)}
|
|
260
257
|
required
|
|
261
258
|
/>
|
|
262
259
|
|
|
263
260
|
<RadioButtonGroup
|
|
264
|
-
defaultSelected={
|
|
265
|
-
/true/.test(questionToEdit?.required?.toString())
|
|
266
|
-
? "required"
|
|
267
|
-
: "optional"
|
|
268
|
-
}
|
|
261
|
+
defaultSelected={/true/.test(questionToEdit?.required?.toString()) ? 'required' : 'optional'}
|
|
269
262
|
name="isQuestionRequired"
|
|
270
263
|
legendText={t(
|
|
271
|
-
|
|
272
|
-
|
|
264
|
+
'isQuestionRequiredOrOptional',
|
|
265
|
+
'Is this question a required or optional field? Required fields must be answered before the form can be submitted.',
|
|
273
266
|
)}
|
|
274
267
|
>
|
|
275
268
|
<RadioButton
|
|
276
269
|
id="questionIsNotRequired"
|
|
277
270
|
defaultChecked={true}
|
|
278
|
-
labelText={t(
|
|
271
|
+
labelText={t('optional', 'Optional')}
|
|
279
272
|
onClick={() => setIsQuestionRequired(false)}
|
|
280
273
|
value="optional"
|
|
281
274
|
/>
|
|
282
275
|
<RadioButton
|
|
283
276
|
id="questionIsRequired"
|
|
284
277
|
defaultChecked={false}
|
|
285
|
-
labelText={t(
|
|
278
|
+
labelText={t('required', 'Required')}
|
|
286
279
|
onClick={() => setIsQuestionRequired(true)}
|
|
287
280
|
value="required"
|
|
288
281
|
/>
|
|
@@ -290,99 +283,85 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
290
283
|
|
|
291
284
|
<Select
|
|
292
285
|
defaultValue={questionToEdit.type}
|
|
293
|
-
onChange={(event) =>
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
286
|
+
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
|
|
287
|
+
setQuestionType(event.target.value as QuestionType)
|
|
288
|
+
}
|
|
289
|
+
id={'questionType'}
|
|
290
|
+
invalidText={t('typeRequired', 'Type is required')}
|
|
291
|
+
labelText={t('questionType', 'Question type')}
|
|
297
292
|
required
|
|
298
293
|
>
|
|
299
|
-
{!questionType && (
|
|
300
|
-
<SelectItem
|
|
301
|
-
text={t("chooseQuestionType", "Choose a question type")}
|
|
302
|
-
value=""
|
|
303
|
-
/>
|
|
304
|
-
)}
|
|
294
|
+
{!questionType && <SelectItem text={t('chooseQuestionType', 'Choose a question type')} value="" />}
|
|
305
295
|
{questionTypes.map((questionType, key) => (
|
|
306
|
-
<SelectItem
|
|
307
|
-
text={questionType}
|
|
308
|
-
value={questionType}
|
|
309
|
-
key={key}
|
|
310
|
-
/>
|
|
296
|
+
<SelectItem text={questionType} value={questionType} key={key} />
|
|
311
297
|
))}
|
|
312
298
|
</Select>
|
|
313
299
|
|
|
314
300
|
<Select
|
|
315
301
|
defaultValue={questionToEdit.questionOptions.rendering}
|
|
316
|
-
onChange={(event) => setFieldType(event.target.value)}
|
|
302
|
+
onChange={(event: React.ChangeEvent<HTMLSelectElement>) => setFieldType(event.target.value as RenderType)}
|
|
317
303
|
id="renderingType"
|
|
318
|
-
invalidText={t(
|
|
319
|
-
|
|
320
|
-
"A valid field type value is required"
|
|
321
|
-
)}
|
|
322
|
-
labelText={t("fieldType", "Field type")}
|
|
304
|
+
invalidText={t('validFieldTypeRequired', 'A valid field type value is required')}
|
|
305
|
+
labelText={t('fieldType', 'Field type')}
|
|
323
306
|
required
|
|
324
307
|
>
|
|
325
|
-
{!fieldType && (
|
|
326
|
-
<SelectItem
|
|
327
|
-
text={t("chooseFieldType", "Choose a field type")}
|
|
328
|
-
value=""
|
|
329
|
-
/>
|
|
330
|
-
)}
|
|
308
|
+
{!fieldType && <SelectItem text={t('chooseFieldType', 'Choose a field type')} value="" />}
|
|
331
309
|
{fieldTypes.map((fieldType, key) => (
|
|
332
310
|
<SelectItem text={fieldType} value={fieldType} key={key} />
|
|
333
311
|
))}
|
|
334
312
|
</Select>
|
|
335
313
|
|
|
336
|
-
{fieldType ===
|
|
314
|
+
{fieldType === 'number' ? (
|
|
337
315
|
<>
|
|
338
316
|
<TextInput
|
|
339
317
|
id="min"
|
|
340
318
|
labelText="Min"
|
|
341
|
-
value={min ||
|
|
342
|
-
onChange={(event) => setMin(event.target.value)}
|
|
319
|
+
value={min || ''}
|
|
320
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setMin(event.target.value)}
|
|
343
321
|
required
|
|
344
322
|
/>
|
|
345
323
|
<TextInput
|
|
346
324
|
id="max"
|
|
347
325
|
labelText="Max"
|
|
348
|
-
value={max ||
|
|
349
|
-
onChange={(event) => setMax(event.target.value)}
|
|
326
|
+
value={max || ''}
|
|
327
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setMax(event.target.value)}
|
|
350
328
|
required
|
|
351
329
|
/>
|
|
352
330
|
</>
|
|
353
|
-
) : fieldType ===
|
|
331
|
+
) : fieldType === 'textarea' ? (
|
|
354
332
|
<TextInput
|
|
355
333
|
id="textAreaRows"
|
|
356
|
-
labelText={t(
|
|
357
|
-
value={rows ||
|
|
358
|
-
onChange={(event) => setRows(event.target.value)}
|
|
334
|
+
labelText={t('rows', 'Rows')}
|
|
335
|
+
value={rows || ''}
|
|
336
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setRows(event.target.value)}
|
|
359
337
|
required
|
|
360
338
|
/>
|
|
361
339
|
) : null}
|
|
362
340
|
|
|
363
|
-
{fieldType !==
|
|
341
|
+
{fieldType !== 'ui-select-extended' && (
|
|
364
342
|
<div>
|
|
365
343
|
<FormLabel className={styles.label}>
|
|
366
|
-
{t(
|
|
344
|
+
{t('searchForBackingConcept', 'Search for a backing concept')}
|
|
367
345
|
</FormLabel>
|
|
368
|
-
{
|
|
369
|
-
<
|
|
370
|
-
|
|
371
|
-
|
|
346
|
+
{conceptNameLookupError ? (
|
|
347
|
+
<InlineNotification
|
|
348
|
+
kind="error"
|
|
349
|
+
lowContrast
|
|
350
|
+
className={styles.error}
|
|
351
|
+
title={t('errorFetchingConceptName', "Couldn't resolve concept name")}
|
|
352
|
+
subtitle={t('conceptDoesNotExist', 'The linked concept does not exist in your dictionary.')}
|
|
372
353
|
/>
|
|
354
|
+
) : null}
|
|
355
|
+
{isLoadingConceptName ? (
|
|
356
|
+
<InlineLoading className={styles.loader} description={t('loading', 'Loading') + '...'} />
|
|
373
357
|
) : (
|
|
374
358
|
<>
|
|
375
359
|
<Search
|
|
376
360
|
defaultValue={conceptName}
|
|
377
361
|
id="conceptLookup"
|
|
378
362
|
onClear={() => setSelectedConcept(null)}
|
|
379
|
-
onChange={(e) =>
|
|
380
|
-
|
|
381
|
-
}
|
|
382
|
-
placeholder={t(
|
|
383
|
-
"searchConcept",
|
|
384
|
-
"Search using a concept name or UUID"
|
|
385
|
-
)}
|
|
363
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleConceptChange(e.target.value?.trim())}
|
|
364
|
+
placeholder={t('searchConcept', 'Search using a concept name or UUID')}
|
|
386
365
|
required
|
|
387
366
|
size="md"
|
|
388
367
|
value={selectedConcept?.display}
|
|
@@ -391,12 +370,9 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
391
370
|
if (!conceptToLookup) return null;
|
|
392
371
|
if (isLoadingConcepts)
|
|
393
372
|
return (
|
|
394
|
-
<InlineLoading
|
|
395
|
-
className={styles.loader}
|
|
396
|
-
description={t("searching", "Searching") + "..."}
|
|
397
|
-
/>
|
|
373
|
+
<InlineLoading className={styles.loader} description={t('searching', 'Searching') + '...'} />
|
|
398
374
|
);
|
|
399
|
-
if (concepts
|
|
375
|
+
if (concepts?.length && !isLoadingConcepts) {
|
|
400
376
|
return (
|
|
401
377
|
<ul className={styles.conceptList}>
|
|
402
378
|
{concepts?.map((concept, index) => (
|
|
@@ -416,10 +392,7 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
416
392
|
<Layer>
|
|
417
393
|
<Tile className={styles.emptyResults}>
|
|
418
394
|
<span>
|
|
419
|
-
{t(
|
|
420
|
-
"noMatchingConcepts",
|
|
421
|
-
"No concepts were found that match"
|
|
422
|
-
)}{" "}
|
|
395
|
+
{t('noMatchingConcepts', 'No concepts were found that match')}{' '}
|
|
423
396
|
<strong>"{conceptToLookup}".</strong>
|
|
424
397
|
</span>
|
|
425
398
|
</Tile>
|
|
@@ -427,19 +400,16 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
427
400
|
<div className={styles.oclLauncherBanner}>
|
|
428
401
|
{
|
|
429
402
|
<p className={styles.bodyShort01}>
|
|
430
|
-
{t(
|
|
431
|
-
"conceptSearchHelpText",
|
|
432
|
-
"Can't find a concept?"
|
|
433
|
-
)}
|
|
403
|
+
{t('conceptSearchHelpText', "Can't find a concept?")}
|
|
434
404
|
</p>
|
|
435
405
|
}
|
|
436
406
|
<a
|
|
437
407
|
className={styles.oclLink}
|
|
438
408
|
target="_blank"
|
|
439
409
|
rel="noopener noreferrer"
|
|
440
|
-
href={
|
|
410
|
+
href={'https://app.openconceptlab.org/'}
|
|
441
411
|
>
|
|
442
|
-
{t(
|
|
412
|
+
{t('searchInOCL', 'Search in OCL')}
|
|
443
413
|
<ArrowUpRight size={16} />
|
|
444
414
|
</a>
|
|
445
415
|
</div>
|
|
@@ -451,25 +421,23 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
451
421
|
</div>
|
|
452
422
|
)}
|
|
453
423
|
|
|
454
|
-
{conceptMappings
|
|
424
|
+
{conceptMappings?.length ? (
|
|
455
425
|
<FormGroup>
|
|
456
|
-
<FormLabel className={styles.label}>
|
|
457
|
-
{t("mappings", "Mappings")}
|
|
458
|
-
</FormLabel>
|
|
426
|
+
<FormLabel className={styles.label}>{t('mappings', 'Mappings')}</FormLabel>
|
|
459
427
|
<table className={styles.tableStriped}>
|
|
460
428
|
<thead>
|
|
461
429
|
<tr>
|
|
462
|
-
<th>{t(
|
|
463
|
-
<th>{t(
|
|
464
|
-
<th>{t(
|
|
430
|
+
<th>{t('relationship', 'Relationship')}</th>
|
|
431
|
+
<th>{t('source', 'Source')}</th>
|
|
432
|
+
<th>{t('code', 'Code')}</th>
|
|
465
433
|
</tr>
|
|
466
434
|
</thead>
|
|
467
435
|
<tbody>
|
|
468
436
|
{conceptMappings.map((mapping, index) => (
|
|
469
437
|
<tr key={`mapping-${index}`}>
|
|
470
|
-
<td>{mapping.relationship ??
|
|
471
|
-
<td>{mapping.type ??
|
|
472
|
-
<td>{mapping.value ??
|
|
438
|
+
<td>{mapping.relationship ?? '--'}</td>
|
|
439
|
+
<td>{mapping.type ?? '--'}</td>
|
|
440
|
+
<td>{mapping.value ?? '--'}</td>
|
|
473
441
|
</tr>
|
|
474
442
|
))}
|
|
475
443
|
</tbody>
|
|
@@ -477,48 +445,40 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
477
445
|
</FormGroup>
|
|
478
446
|
) : null}
|
|
479
447
|
|
|
480
|
-
{!hasConceptChanged &&
|
|
481
|
-
questionToEdit?.questionOptions?.answers &&
|
|
482
|
-
questionToEdit?.questionOptions.answers?.length ? (
|
|
448
|
+
{!hasConceptChanged && questionToEdit?.questionOptions.answers?.length ? (
|
|
483
449
|
<MultiSelect
|
|
484
450
|
className={styles.multiSelect}
|
|
485
451
|
direction="top"
|
|
486
452
|
id="selectAnswers"
|
|
487
|
-
itemToString={(item) => item.text}
|
|
488
|
-
initialSelectedItems={questionToEdit?.questionOptions?.answers?.map(
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
453
|
+
itemToString={(item: Item) => item.text}
|
|
454
|
+
initialSelectedItems={questionToEdit?.questionOptions?.answers?.map((answer) => ({
|
|
455
|
+
id: answer.concept,
|
|
456
|
+
text: answer.label,
|
|
457
|
+
}))}
|
|
458
|
+
items={questionToEdit?.questionOptions?.answers?.map((answer) => ({
|
|
459
|
+
id: answer.concept,
|
|
460
|
+
text: answer.label ?? '',
|
|
461
|
+
}))}
|
|
462
|
+
onChange={({
|
|
463
|
+
selectedItems,
|
|
464
|
+
}: {
|
|
465
|
+
selectedItems: Array<{
|
|
466
|
+
id: string;
|
|
467
|
+
text: string;
|
|
468
|
+
}>;
|
|
469
|
+
}) => {
|
|
501
470
|
setAnswersChanged(true);
|
|
502
471
|
setSelectedAnswers(selectedItems.sort());
|
|
503
472
|
}}
|
|
504
473
|
size="md"
|
|
505
|
-
titleText={t(
|
|
506
|
-
"selectAnswersToDisplay",
|
|
507
|
-
"Select answers to display"
|
|
508
|
-
)}
|
|
474
|
+
titleText={t('selectAnswersToDisplay', 'Select answers to display')}
|
|
509
475
|
/>
|
|
510
476
|
) : null}
|
|
511
477
|
|
|
512
|
-
{!hasConceptChanged &&
|
|
513
|
-
questionToEdit?.questionOptions?.answers?.length &&
|
|
514
|
-
!answersChanged ? (
|
|
478
|
+
{!hasConceptChanged && questionToEdit?.questionOptions?.answers?.length && !answersChanged ? (
|
|
515
479
|
<div>
|
|
516
480
|
{questionToEdit?.questionOptions?.answers?.map((answer) => (
|
|
517
|
-
<Tag
|
|
518
|
-
className={styles.tag}
|
|
519
|
-
key={answer?.concept}
|
|
520
|
-
type={"blue"}
|
|
521
|
-
>
|
|
481
|
+
<Tag className={styles.tag} key={answer?.concept} type={'blue'}>
|
|
522
482
|
{answer?.label}
|
|
523
483
|
</Tag>
|
|
524
484
|
))}
|
|
@@ -530,30 +490,28 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
530
490
|
className={styles.multiSelect}
|
|
531
491
|
direction="top"
|
|
532
492
|
id="selectAnswers"
|
|
533
|
-
itemToString={(item) => item.text}
|
|
493
|
+
itemToString={(item: Item) => item.text}
|
|
534
494
|
items={answersFromConcept.map((answer) => ({
|
|
535
495
|
id: answer.concept,
|
|
536
496
|
text: answer.label,
|
|
537
497
|
}))}
|
|
538
|
-
onChange={({
|
|
539
|
-
|
|
540
|
-
}
|
|
498
|
+
onChange={({
|
|
499
|
+
selectedItems,
|
|
500
|
+
}: {
|
|
501
|
+
selectedItems: Array<{
|
|
502
|
+
id: string;
|
|
503
|
+
text: string;
|
|
504
|
+
}>;
|
|
505
|
+
}) => setSelectedAnswers(selectedItems.sort())}
|
|
541
506
|
size="md"
|
|
542
|
-
titleText={t(
|
|
543
|
-
"selectAnswersToDisplay",
|
|
544
|
-
"Select answers to display"
|
|
545
|
-
)}
|
|
507
|
+
titleText={t('selectAnswersToDisplay', 'Select answers to display')}
|
|
546
508
|
/>
|
|
547
509
|
) : null}
|
|
548
510
|
|
|
549
|
-
{(hasConceptChanged
|
|
511
|
+
{(hasConceptChanged ?? answersChanged) && (
|
|
550
512
|
<div>
|
|
551
513
|
{selectedAnswers.map((selectedAnswer) => (
|
|
552
|
-
<Tag
|
|
553
|
-
className={styles.tag}
|
|
554
|
-
key={selectedAnswer.id}
|
|
555
|
-
type={"blue"}
|
|
556
|
-
>
|
|
514
|
+
<Tag className={styles.tag} key={selectedAnswer.id} type={'blue'}>
|
|
557
515
|
{selectedAnswer.text}
|
|
558
516
|
</Tag>
|
|
559
517
|
))}
|
|
@@ -563,10 +521,10 @@ const EditQuestionModal: React.FC<EditQuestionModalProps> = ({
|
|
|
563
521
|
</ModalBody>
|
|
564
522
|
<ModalFooter>
|
|
565
523
|
<Button onClick={() => onModalChange(false)} kind="secondary">
|
|
566
|
-
{t(
|
|
524
|
+
{t('cancel', 'Cancel')}
|
|
567
525
|
</Button>
|
|
568
526
|
<Button onClick={handleUpdateQuestion}>
|
|
569
|
-
<span>{t(
|
|
527
|
+
<span>{t('save', 'Save')}</span>
|
|
570
528
|
</Button>
|
|
571
529
|
</ModalFooter>
|
|
572
530
|
</Form>
|