@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,5 +1,6 @@
|
|
|
1
|
-
import React, { useState } from
|
|
2
|
-
import { useTranslation } from
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import flattenDeep from 'lodash-es/flattenDeep';
|
|
3
4
|
import {
|
|
4
5
|
Button,
|
|
5
6
|
ComposedModal,
|
|
@@ -21,23 +22,16 @@ import {
|
|
|
21
22
|
Tag,
|
|
22
23
|
TextInput,
|
|
23
24
|
Tile,
|
|
24
|
-
} from
|
|
25
|
-
import { ArrowUpRight } from
|
|
26
|
-
import
|
|
27
|
-
import {
|
|
28
|
-
import { RenderType } from "@openmrs/openmrs-form-engine-lib";
|
|
25
|
+
} from '@carbon/react';
|
|
26
|
+
import { ArrowUpRight } from '@carbon/react/icons';
|
|
27
|
+
import { showNotification, showToast, useConfig } from '@openmrs/esm-framework';
|
|
28
|
+
import type { RenderType } from '@openmrs/openmrs-form-engine-lib';
|
|
29
29
|
|
|
30
|
-
import type {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
ConceptMapping,
|
|
34
|
-
Question,
|
|
35
|
-
Schema,
|
|
36
|
-
} from "../../types";
|
|
37
|
-
import { useConceptLookup } from "../../hooks/useConceptLookup";
|
|
38
|
-
import styles from "./question-modal.scss";
|
|
30
|
+
import type { Answer, Concept, ConceptMapping, Question, Schema } from '../../types';
|
|
31
|
+
import { useConceptLookup } from '../../hooks/useConceptLookup';
|
|
32
|
+
import styles from './question-modal.scss';
|
|
39
33
|
|
|
40
|
-
|
|
34
|
+
interface AddQuestionModalProps {
|
|
41
35
|
onModalChange: (showModal: boolean) => void;
|
|
42
36
|
onQuestionEdit: (question: Question) => void;
|
|
43
37
|
onSchemaChange: (schema: Schema) => void;
|
|
@@ -47,7 +41,16 @@ type AddQuestionModalProps = {
|
|
|
47
41
|
schema: Schema;
|
|
48
42
|
sectionIndex: number;
|
|
49
43
|
showModal: boolean;
|
|
50
|
-
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface Config {
|
|
47
|
+
fieldTypes: Array<RenderType>;
|
|
48
|
+
questionTypes: Array<string>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface Item {
|
|
52
|
+
text: string;
|
|
53
|
+
}
|
|
51
54
|
|
|
52
55
|
const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
53
56
|
schema,
|
|
@@ -60,43 +63,49 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
60
63
|
onModalChange,
|
|
61
64
|
}) => {
|
|
62
65
|
const { t } = useTranslation();
|
|
63
|
-
const { fieldTypes, questionTypes } = useConfig();
|
|
64
|
-
|
|
65
|
-
const [
|
|
66
|
-
const [
|
|
67
|
-
const [
|
|
66
|
+
const { fieldTypes, questionTypes }: Config = useConfig();
|
|
67
|
+
|
|
68
|
+
const [answers, setAnswers] = useState<Array<Answer>>([]);
|
|
69
|
+
const [conceptMappings, setConceptMappings] = useState<Array<ConceptMapping>>([]);
|
|
70
|
+
const [conceptToLookup, setConceptToLookup] = useState('');
|
|
71
|
+
const [fieldType, setFieldType] = useState<RenderType | null>(null);
|
|
68
72
|
const [isQuestionRequired, setIsQuestionRequired] = useState(false);
|
|
69
|
-
const [
|
|
70
|
-
const [
|
|
71
|
-
const [
|
|
72
|
-
const [
|
|
73
|
-
const [
|
|
74
|
-
const [rows, setRows] = useState(
|
|
75
|
-
const [
|
|
76
|
-
const [selectedAnswers, setSelectedAnswers] = useState
|
|
73
|
+
const [max, setMax] = useState('');
|
|
74
|
+
const [min, setMin] = useState('');
|
|
75
|
+
const [questionId, setQuestionId] = useState('');
|
|
76
|
+
const [questionLabel, setQuestionLabel] = useState('');
|
|
77
|
+
const [questionType, setQuestionType] = useState<string | null>(null);
|
|
78
|
+
const [rows, setRows] = useState('');
|
|
79
|
+
const [selectedConcept, setSelectedConcept] = useState<Concept | null>(null);
|
|
80
|
+
const [selectedAnswers, setSelectedAnswers] = useState<
|
|
81
|
+
Array<{
|
|
82
|
+
id: string;
|
|
83
|
+
text: string;
|
|
84
|
+
}>
|
|
85
|
+
>([]);
|
|
86
|
+
|
|
77
87
|
const { concepts, isLoadingConcepts } = useConceptLookup(conceptToLookup);
|
|
78
88
|
|
|
79
|
-
const handleConceptChange = (event: React.ChangeEvent<HTMLInputElement>) =>
|
|
80
|
-
setConceptToLookup(event.target.value);
|
|
89
|
+
const handleConceptChange = (event: React.ChangeEvent<HTMLInputElement>) => setConceptToLookup(event.target.value);
|
|
81
90
|
|
|
82
91
|
const handleConceptSelect = (concept: Concept) => {
|
|
83
|
-
setConceptToLookup(
|
|
92
|
+
setConceptToLookup('');
|
|
84
93
|
setSelectedConcept(concept);
|
|
85
94
|
setAnswers(
|
|
86
95
|
concept?.answers?.map((answer) => ({
|
|
87
96
|
concept: answer?.uuid,
|
|
88
97
|
label: answer?.display,
|
|
89
|
-
}))
|
|
98
|
+
})),
|
|
90
99
|
);
|
|
91
100
|
setConceptMappings(
|
|
92
101
|
concept?.mappings?.map((conceptMapping) => {
|
|
93
|
-
const data = conceptMapping.display.split(
|
|
102
|
+
const data = conceptMapping.display.split(': ');
|
|
94
103
|
return {
|
|
95
104
|
relationship: conceptMapping.conceptMapType.display,
|
|
96
105
|
type: data[0],
|
|
97
106
|
value: data[1],
|
|
98
107
|
};
|
|
99
|
-
})
|
|
108
|
+
}),
|
|
100
109
|
);
|
|
101
110
|
};
|
|
102
111
|
|
|
@@ -109,7 +118,7 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
109
118
|
});
|
|
110
119
|
});
|
|
111
120
|
|
|
112
|
-
const questionIds = flattenDeep(nestedIds);
|
|
121
|
+
const questionIds: Array<string> = flattenDeep(nestedIds);
|
|
113
122
|
|
|
114
123
|
return questionIds.includes(idToTest);
|
|
115
124
|
};
|
|
@@ -121,9 +130,7 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
121
130
|
|
|
122
131
|
const createQuestion = () => {
|
|
123
132
|
try {
|
|
124
|
-
const computedQuestionId = `question${questionIndex + 1}Section${
|
|
125
|
-
sectionIndex + 1
|
|
126
|
-
}Page-${pageIndex + 1}`;
|
|
133
|
+
const computedQuestionId = `question${questionIndex + 1}Section${sectionIndex + 1}Page-${pageIndex + 1}`;
|
|
127
134
|
|
|
128
135
|
schema.pages[pageIndex]?.sections?.[sectionIndex]?.questions?.push({
|
|
129
136
|
label: questionLabel,
|
|
@@ -132,9 +139,9 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
132
139
|
id: questionId ?? computedQuestionId,
|
|
133
140
|
questionOptions: {
|
|
134
141
|
rendering: fieldType,
|
|
135
|
-
concept: selectedConcept
|
|
142
|
+
concept: selectedConcept?.uuid,
|
|
136
143
|
conceptMappings: conceptMappings,
|
|
137
|
-
answers: selectedAnswers
|
|
144
|
+
answers: selectedAnswers?.map((answer) => ({
|
|
138
145
|
concept: answer.id,
|
|
139
146
|
label: answer.text,
|
|
140
147
|
})),
|
|
@@ -145,8 +152,8 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
145
152
|
onSchemaChange({ ...schema });
|
|
146
153
|
|
|
147
154
|
resetIndices();
|
|
148
|
-
setQuestionLabel(
|
|
149
|
-
setQuestionId(
|
|
155
|
+
setQuestionLabel('');
|
|
156
|
+
setQuestionId('');
|
|
150
157
|
setIsQuestionRequired(false);
|
|
151
158
|
setQuestionType(null);
|
|
152
159
|
setFieldType(null);
|
|
@@ -156,62 +163,51 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
156
163
|
setSelectedAnswers([]);
|
|
157
164
|
|
|
158
165
|
showToast({
|
|
159
|
-
title: t(
|
|
160
|
-
kind:
|
|
166
|
+
title: t('success', 'Success!'),
|
|
167
|
+
kind: 'success',
|
|
161
168
|
critical: true,
|
|
162
|
-
description: t(
|
|
169
|
+
description: t('questionCreated', 'New question created'),
|
|
163
170
|
});
|
|
164
171
|
} catch (error) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
if (error instanceof Error) {
|
|
173
|
+
showNotification({
|
|
174
|
+
title: t('errorCreatingQuestion', 'Error creating question'),
|
|
175
|
+
kind: 'error',
|
|
176
|
+
critical: true,
|
|
177
|
+
description: error?.message,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
171
180
|
}
|
|
172
181
|
};
|
|
173
182
|
|
|
174
183
|
return (
|
|
175
|
-
<ComposedModal
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
preventCloseOnClickOutside
|
|
179
|
-
>
|
|
180
|
-
<ModalHeader title={t("createNewQuestion", "Create a new question")} />
|
|
181
|
-
<Form
|
|
182
|
-
className={styles.form}
|
|
183
|
-
onSubmit={(event) => event.preventDefault()}
|
|
184
|
-
>
|
|
184
|
+
<ComposedModal open={showModal} onClose={() => onModalChange(false)} preventCloseOnClickOutside>
|
|
185
|
+
<ModalHeader title={t('createNewQuestion', 'Create a new question')} />
|
|
186
|
+
<Form className={styles.form} onSubmit={(event: React.SyntheticEvent) => event.preventDefault()}>
|
|
185
187
|
<ModalBody hasScrollingContent>
|
|
186
|
-
<FormGroup legendText={
|
|
188
|
+
<FormGroup legendText={''}>
|
|
187
189
|
<Stack gap={5}>
|
|
188
190
|
<TextInput
|
|
189
191
|
id="questionLabel"
|
|
190
|
-
labelText={t(
|
|
191
|
-
placeholder={t(
|
|
192
|
+
labelText={t('questionLabel', 'Label')}
|
|
193
|
+
placeholder={t('labelPlaceholder', 'e.g. Type of Anaesthesia')}
|
|
192
194
|
value={questionLabel}
|
|
193
|
-
onChange={(event) => setQuestionLabel(event.target.value)}
|
|
195
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setQuestionLabel(event.target.value)}
|
|
194
196
|
required
|
|
195
197
|
/>
|
|
196
198
|
|
|
197
199
|
<TextInput
|
|
198
200
|
id="questionId"
|
|
199
201
|
invalid={questionIdExists(questionId)}
|
|
200
|
-
invalidText={t(
|
|
201
|
-
|
|
202
|
-
"This question ID already exists in your schema"
|
|
203
|
-
)}
|
|
204
|
-
labelText={t(
|
|
205
|
-
"questionId",
|
|
206
|
-
"Question ID (prefer using camel-case for IDs)"
|
|
207
|
-
)}
|
|
202
|
+
invalidText={t('questionIdExists', 'This question ID already exists in your schema')}
|
|
203
|
+
labelText={t('questionId', 'Question ID (prefer using camel-case for IDs)')}
|
|
208
204
|
value={questionId}
|
|
209
|
-
onChange={(event) => {
|
|
205
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
210
206
|
setQuestionId(event.target.value);
|
|
211
207
|
}}
|
|
212
208
|
placeholder={t(
|
|
213
|
-
|
|
214
|
-
'Enter a unique ID e.g. "anaesthesiaType" for a question asking about the type of anaesthesia.'
|
|
209
|
+
'questionIdPlaceholder',
|
|
210
|
+
'Enter a unique ID e.g. "anaesthesiaType" for a question asking about the type of anaesthesia.',
|
|
215
211
|
)}
|
|
216
212
|
required
|
|
217
213
|
/>
|
|
@@ -220,21 +216,21 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
220
216
|
defaultSelected="optional"
|
|
221
217
|
name="isQuestionRequired"
|
|
222
218
|
legendText={t(
|
|
223
|
-
|
|
224
|
-
|
|
219
|
+
'isQuestionRequiredOrOptional',
|
|
220
|
+
'Is this question a required or optional field? Required fields must be answered before the form can be submitted.',
|
|
225
221
|
)}
|
|
226
222
|
>
|
|
227
223
|
<RadioButton
|
|
228
224
|
id="questionIsNotRequired"
|
|
229
225
|
defaultChecked={true}
|
|
230
|
-
labelText={t(
|
|
226
|
+
labelText={t('optional', 'Optional')}
|
|
231
227
|
onClick={() => setIsQuestionRequired(false)}
|
|
232
228
|
value="optional"
|
|
233
229
|
/>
|
|
234
230
|
<RadioButton
|
|
235
231
|
id="questionIsRequired"
|
|
236
232
|
defaultChecked={false}
|
|
237
|
-
labelText={t(
|
|
233
|
+
labelText={t('required', 'Required')}
|
|
238
234
|
onClick={() => setIsQuestionRequired(true)}
|
|
239
235
|
value="required"
|
|
240
236
|
/>
|
|
@@ -242,93 +238,72 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
242
238
|
|
|
243
239
|
<Select
|
|
244
240
|
value={questionType}
|
|
245
|
-
onChange={(event) => setQuestionType(event.target.value)}
|
|
241
|
+
onChange={(event: React.ChangeEvent<HTMLSelectElement>) => setQuestionType(event.target.value)}
|
|
246
242
|
id="questionType"
|
|
247
|
-
invalidText={t(
|
|
248
|
-
labelText={t(
|
|
243
|
+
invalidText={t('typeRequired', 'Type is required')}
|
|
244
|
+
labelText={t('questionType', 'Question type')}
|
|
249
245
|
required
|
|
250
246
|
>
|
|
251
|
-
{!questionType && (
|
|
252
|
-
<SelectItem
|
|
253
|
-
text={t("chooseQuestionType", "Choose a question type")}
|
|
254
|
-
value=""
|
|
255
|
-
/>
|
|
256
|
-
)}
|
|
247
|
+
{!questionType && <SelectItem text={t('chooseQuestionType', 'Choose a question type')} value="" />}
|
|
257
248
|
{questionTypes.map((questionType, key) => (
|
|
258
|
-
<SelectItem
|
|
259
|
-
text={questionType}
|
|
260
|
-
value={questionType}
|
|
261
|
-
key={key}
|
|
262
|
-
/>
|
|
249
|
+
<SelectItem text={questionType} value={questionType} key={key} />
|
|
263
250
|
))}
|
|
264
251
|
</Select>
|
|
265
252
|
|
|
266
253
|
<Select
|
|
267
254
|
value={fieldType}
|
|
268
|
-
onChange={(event) =>
|
|
255
|
+
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
|
|
256
|
+
setFieldType(event.target.value as RenderType)
|
|
257
|
+
}
|
|
269
258
|
id="renderingType"
|
|
270
|
-
invalidText={t(
|
|
271
|
-
|
|
272
|
-
"A valid field type value is required"
|
|
273
|
-
)}
|
|
274
|
-
labelText={t("fieldType", "Field type")}
|
|
259
|
+
invalidText={t('validFieldTypeRequired', 'A valid field type value is required')}
|
|
260
|
+
labelText={t('fieldType', 'Field type')}
|
|
275
261
|
required
|
|
276
262
|
>
|
|
277
|
-
{!fieldType && (
|
|
278
|
-
<SelectItem
|
|
279
|
-
text={t("chooseFieldType", "Choose a field type")}
|
|
280
|
-
value=""
|
|
281
|
-
/>
|
|
282
|
-
)}
|
|
263
|
+
{!fieldType && <SelectItem text={t('chooseFieldType', 'Choose a field type')} value="" />}
|
|
283
264
|
{fieldTypes.map((fieldType, key) => (
|
|
284
265
|
<SelectItem text={fieldType} value={fieldType} key={key} />
|
|
285
266
|
))}
|
|
286
267
|
</Select>
|
|
287
268
|
|
|
288
|
-
{fieldType ===
|
|
269
|
+
{fieldType === 'number' ? (
|
|
289
270
|
<>
|
|
290
271
|
<TextInput
|
|
291
272
|
id="min"
|
|
292
273
|
labelText="Min"
|
|
293
|
-
value={min ||
|
|
294
|
-
onChange={(event) => setMin(event.target.value)}
|
|
274
|
+
value={min || ''}
|
|
275
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setMin(event.target.value)}
|
|
295
276
|
required
|
|
296
277
|
/>
|
|
297
278
|
<TextInput
|
|
298
279
|
id="max"
|
|
299
280
|
labelText="Max"
|
|
300
|
-
value={max ||
|
|
301
|
-
onChange={(event) => setMax(event.target.value)}
|
|
281
|
+
value={max || ''}
|
|
282
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setMax(event.target.value)}
|
|
302
283
|
required
|
|
303
284
|
/>
|
|
304
285
|
</>
|
|
305
|
-
) : fieldType ===
|
|
286
|
+
) : fieldType === 'textarea' ? (
|
|
306
287
|
<TextInput
|
|
307
288
|
id="textAreaRows"
|
|
308
|
-
labelText={t(
|
|
309
|
-
value={rows ||
|
|
310
|
-
onChange={(event) => setRows(event.target.value)}
|
|
289
|
+
labelText={t('rows', 'Rows')}
|
|
290
|
+
value={rows || ''}
|
|
291
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setRows(event.target.value)}
|
|
311
292
|
required
|
|
312
293
|
/>
|
|
313
294
|
) : null}
|
|
314
295
|
|
|
315
|
-
{fieldType !==
|
|
296
|
+
{fieldType !== 'ui-select-extended' && (
|
|
316
297
|
<div>
|
|
317
298
|
<FormLabel className={styles.label}>
|
|
318
|
-
{t(
|
|
319
|
-
"searchForBackingConcept",
|
|
320
|
-
"Search for a backing concept"
|
|
321
|
-
)}
|
|
299
|
+
{t('searchForBackingConcept', 'Search for a backing concept')}
|
|
322
300
|
</FormLabel>
|
|
323
301
|
<Search
|
|
324
302
|
size="md"
|
|
325
303
|
id="conceptLookup"
|
|
326
304
|
onClear={() => setSelectedConcept(null)}
|
|
327
305
|
onChange={handleConceptChange}
|
|
328
|
-
placeholder={t(
|
|
329
|
-
"searchConcept",
|
|
330
|
-
"Search using a concept name or UUID"
|
|
331
|
-
)}
|
|
306
|
+
placeholder={t('searchConcept', 'Search using a concept name or UUID')}
|
|
332
307
|
value={(() => {
|
|
333
308
|
if (conceptToLookup) {
|
|
334
309
|
return conceptToLookup;
|
|
@@ -336,7 +311,7 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
336
311
|
if (selectedConcept) {
|
|
337
312
|
return selectedConcept.display;
|
|
338
313
|
}
|
|
339
|
-
return
|
|
314
|
+
return '';
|
|
340
315
|
})()}
|
|
341
316
|
required
|
|
342
317
|
/>
|
|
@@ -344,12 +319,9 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
344
319
|
if (!conceptToLookup) return null;
|
|
345
320
|
if (isLoadingConcepts)
|
|
346
321
|
return (
|
|
347
|
-
<InlineLoading
|
|
348
|
-
className={styles.loader}
|
|
349
|
-
description={t("searching", "Searching") + "..."}
|
|
350
|
-
/>
|
|
322
|
+
<InlineLoading className={styles.loader} description={t('searching', 'Searching') + '...'} />
|
|
351
323
|
);
|
|
352
|
-
if (concepts
|
|
324
|
+
if (concepts?.length && !isLoadingConcepts) {
|
|
353
325
|
return (
|
|
354
326
|
<ul className={styles.conceptList}>
|
|
355
327
|
{concepts?.map((concept, index) => (
|
|
@@ -369,30 +341,20 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
369
341
|
<Layer>
|
|
370
342
|
<Tile className={styles.emptyResults}>
|
|
371
343
|
<span>
|
|
372
|
-
{t(
|
|
373
|
-
"noMatchingConcepts",
|
|
374
|
-
"No concepts were found that match"
|
|
375
|
-
)}{" "}
|
|
344
|
+
{t('noMatchingConcepts', 'No concepts were found that match')}{' '}
|
|
376
345
|
<strong>"{conceptToLookup}".</strong>
|
|
377
346
|
</span>
|
|
378
347
|
</Tile>
|
|
379
348
|
|
|
380
349
|
<div className={styles.oclLauncherBanner}>
|
|
381
|
-
{
|
|
382
|
-
<p className={styles.bodyShort01}>
|
|
383
|
-
{t(
|
|
384
|
-
"conceptSearchHelpText",
|
|
385
|
-
"Can't find a concept?"
|
|
386
|
-
)}
|
|
387
|
-
</p>
|
|
388
|
-
}
|
|
350
|
+
{<p className={styles.bodyShort01}>{t('conceptSearchHelpText', "Can't find a concept?")}</p>}
|
|
389
351
|
<a
|
|
390
352
|
className={styles.oclLink}
|
|
391
353
|
target="_blank"
|
|
392
354
|
rel="noopener noreferrer"
|
|
393
|
-
href={
|
|
355
|
+
href={'https://app.openconceptlab.org/'}
|
|
394
356
|
>
|
|
395
|
-
{t(
|
|
357
|
+
{t('searchInOCL', 'Search in OCL')}
|
|
396
358
|
<ArrowUpRight size={16} />
|
|
397
359
|
</a>
|
|
398
360
|
</div>
|
|
@@ -402,17 +364,15 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
402
364
|
</div>
|
|
403
365
|
)}
|
|
404
366
|
|
|
405
|
-
{conceptMappings
|
|
367
|
+
{conceptMappings?.length ? (
|
|
406
368
|
<FormGroup>
|
|
407
|
-
<FormLabel className={styles.label}>
|
|
408
|
-
{t("mappings", "Mappings")}
|
|
409
|
-
</FormLabel>
|
|
369
|
+
<FormLabel className={styles.label}>{t('mappings', 'Mappings')}</FormLabel>
|
|
410
370
|
<table className={styles.tableStriped}>
|
|
411
371
|
<thead>
|
|
412
372
|
<tr>
|
|
413
|
-
<th>{t(
|
|
414
|
-
<th>{t(
|
|
415
|
-
<th>{t(
|
|
373
|
+
<th>{t('relationship', 'Relationship')}</th>
|
|
374
|
+
<th>{t('source', 'Source')}</th>
|
|
375
|
+
<th>{t('code', 'Code')}</th>
|
|
416
376
|
</tr>
|
|
417
377
|
</thead>
|
|
418
378
|
<tbody>
|
|
@@ -428,31 +388,33 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
428
388
|
</FormGroup>
|
|
429
389
|
) : null}
|
|
430
390
|
|
|
431
|
-
{answers
|
|
391
|
+
{answers?.length ? (
|
|
432
392
|
<MultiSelect
|
|
433
393
|
className={styles.multiSelect}
|
|
434
394
|
direction="top"
|
|
435
395
|
id="selectAnswers"
|
|
436
|
-
itemToString={(item) => item.text}
|
|
396
|
+
itemToString={(item: Item) => item.text}
|
|
437
397
|
items={answers.map((answer) => ({
|
|
438
398
|
id: answer.concept,
|
|
439
399
|
text: answer.label,
|
|
440
400
|
}))}
|
|
441
|
-
onChange={({
|
|
442
|
-
|
|
443
|
-
}
|
|
401
|
+
onChange={({
|
|
402
|
+
selectedItems,
|
|
403
|
+
}: {
|
|
404
|
+
selectedItems: Array<{
|
|
405
|
+
id: string;
|
|
406
|
+
text: string;
|
|
407
|
+
}>;
|
|
408
|
+
}) => setSelectedAnswers(selectedItems.sort())}
|
|
444
409
|
size="md"
|
|
445
|
-
titleText={t(
|
|
446
|
-
"selectAnswersToDisplay",
|
|
447
|
-
"Select answers to display"
|
|
448
|
-
)}
|
|
410
|
+
titleText={t('selectAnswersToDisplay', 'Select answers to display')}
|
|
449
411
|
/>
|
|
450
412
|
) : null}
|
|
451
413
|
|
|
452
414
|
{selectedAnswers.length ? (
|
|
453
415
|
<div>
|
|
454
416
|
{selectedAnswers.map((answer) => (
|
|
455
|
-
<Tag className={styles.tag} key={answer.id} type={
|
|
417
|
+
<Tag className={styles.tag} key={answer.id} type={'blue'}>
|
|
456
418
|
{answer.text}
|
|
457
419
|
</Tag>
|
|
458
420
|
))}
|
|
@@ -464,7 +426,7 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
464
426
|
</Form>
|
|
465
427
|
<ModalFooter>
|
|
466
428
|
<Button onClick={() => onModalChange(false)} kind="secondary">
|
|
467
|
-
{t(
|
|
429
|
+
{t('cancel', 'Cancel')}
|
|
468
430
|
</Button>
|
|
469
431
|
<Button
|
|
470
432
|
disabled={
|
|
@@ -472,11 +434,11 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
|
|
|
472
434
|
!questionId ||
|
|
473
435
|
questionIdExists(questionId) ||
|
|
474
436
|
!fieldType ||
|
|
475
|
-
(fieldType !==
|
|
437
|
+
(fieldType !== 'ui-select-extended' && !selectedConcept)
|
|
476
438
|
}
|
|
477
439
|
onClick={handleCreateQuestion}
|
|
478
440
|
>
|
|
479
|
-
<span>{t(
|
|
441
|
+
<span>{t('save', 'Save')}</span>
|
|
480
442
|
</Button>
|
|
481
443
|
</ModalFooter>
|
|
482
444
|
</ComposedModal>
|
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { useTranslation } from
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
ModalBody,
|
|
7
|
-
ModalFooter,
|
|
8
|
-
ModalHeader,
|
|
9
|
-
} from "@carbon/react";
|
|
10
|
-
import { showNotification, showToast } from "@openmrs/esm-framework";
|
|
11
|
-
import type { Schema } from "../../types";
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Button, ComposedModal, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
|
|
4
|
+
import { showNotification, showToast } from '@openmrs/esm-framework';
|
|
5
|
+
import type { Schema } from '../../types';
|
|
12
6
|
|
|
13
|
-
|
|
7
|
+
interface DeletePageModalProps {
|
|
14
8
|
onModalChange: (showModal: boolean) => void;
|
|
15
9
|
onSchemaChange: (schema: Schema) => void;
|
|
16
10
|
resetIndices: () => void;
|
|
17
11
|
pageIndex: number;
|
|
18
12
|
schema: Schema;
|
|
19
13
|
showModal: boolean;
|
|
20
|
-
}
|
|
14
|
+
}
|
|
21
15
|
|
|
22
16
|
const DeletePageModal: React.FC<DeletePageModalProps> = ({
|
|
23
17
|
onModalChange,
|
|
@@ -37,44 +31,37 @@ const DeletePageModal: React.FC<DeletePageModalProps> = ({
|
|
|
37
31
|
resetIndices();
|
|
38
32
|
|
|
39
33
|
showToast({
|
|
40
|
-
title: t(
|
|
41
|
-
kind:
|
|
34
|
+
title: t('success', 'Success!'),
|
|
35
|
+
kind: 'success',
|
|
42
36
|
critical: true,
|
|
43
|
-
description: t(
|
|
37
|
+
description: t('pageDeleted', 'Page deleted'),
|
|
44
38
|
});
|
|
45
39
|
} catch (error) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
if (error instanceof Error) {
|
|
41
|
+
showNotification({
|
|
42
|
+
title: t('errorDeletingPage', 'Error deleting page'),
|
|
43
|
+
kind: 'error',
|
|
44
|
+
critical: true,
|
|
45
|
+
description: error?.message,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
52
48
|
}
|
|
53
49
|
};
|
|
54
50
|
|
|
55
51
|
return (
|
|
56
|
-
<ComposedModal
|
|
57
|
-
|
|
58
|
-
onClose={() => onModalChange(false)}
|
|
59
|
-
preventCloseOnClickOutside
|
|
60
|
-
>
|
|
61
|
-
<ModalHeader
|
|
62
|
-
title={t(
|
|
63
|
-
"deletePageConfirmation",
|
|
64
|
-
"Are you sure you want to delete this page?"
|
|
65
|
-
)}
|
|
66
|
-
/>
|
|
52
|
+
<ComposedModal open={showModal} onClose={() => onModalChange(false)} preventCloseOnClickOutside>
|
|
53
|
+
<ModalHeader title={t('deletePageConfirmation', 'Are you sure you want to delete this page?')} />
|
|
67
54
|
<ModalBody>
|
|
68
55
|
<p>
|
|
69
56
|
{t(
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
'deletePageExplainerText',
|
|
58
|
+
'Deleting this page will delete all the sections and questions associated with it. This action cannot be undone.',
|
|
72
59
|
)}
|
|
73
60
|
</p>
|
|
74
61
|
</ModalBody>
|
|
75
62
|
<ModalFooter>
|
|
76
63
|
<Button kind="secondary" onClick={() => onModalChange(false)}>
|
|
77
|
-
{t(
|
|
64
|
+
{t('cancel', 'Cancel')}
|
|
78
65
|
</Button>
|
|
79
66
|
<Button
|
|
80
67
|
kind="danger"
|
|
@@ -83,7 +70,7 @@ const DeletePageModal: React.FC<DeletePageModalProps> = ({
|
|
|
83
70
|
onModalChange(false);
|
|
84
71
|
}}
|
|
85
72
|
>
|
|
86
|
-
<span>{t(
|
|
73
|
+
<span>{t('deletePage', 'Delete page')}</span>
|
|
87
74
|
</Button>
|
|
88
75
|
</ModalFooter>
|
|
89
76
|
</ComposedModal>
|