@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,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useState } from
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
Column,
|
|
@@ -16,33 +16,33 @@ import {
|
|
|
16
16
|
TabList,
|
|
17
17
|
TabPanels,
|
|
18
18
|
TabPanel,
|
|
19
|
-
} from
|
|
20
|
-
import { Download } from
|
|
21
|
-
import { useParams } from
|
|
22
|
-
import { useTranslation } from
|
|
23
|
-
import { ExtensionSlot } from
|
|
24
|
-
import type { OHRIFormSchema } from
|
|
25
|
-
import type { Schema
|
|
26
|
-
import { useClobdata } from
|
|
27
|
-
import { useForm } from
|
|
28
|
-
import ActionButtons from
|
|
29
|
-
import FormRenderer from
|
|
30
|
-
import InteractiveBuilder from
|
|
31
|
-
import SchemaEditor from
|
|
32
|
-
import styles from
|
|
19
|
+
} from '@carbon/react';
|
|
20
|
+
import { Download } from '@carbon/react/icons';
|
|
21
|
+
import { useParams } from 'react-router-dom';
|
|
22
|
+
import { useTranslation } from 'react-i18next';
|
|
23
|
+
import { ExtensionSlot } from '@openmrs/esm-framework';
|
|
24
|
+
import type { OHRIFormSchema } from '@openmrs/openmrs-form-engine-lib';
|
|
25
|
+
import type { Schema } from '../../types';
|
|
26
|
+
import { useClobdata } from '../../hooks/useClobdata';
|
|
27
|
+
import { useForm } from '../../hooks/useForm';
|
|
28
|
+
import ActionButtons from '../action-buttons/action-buttons.component';
|
|
29
|
+
import FormRenderer from '../form-renderer/form-renderer.component';
|
|
30
|
+
import InteractiveBuilder from '../interactive-builder/interactive-builder.component';
|
|
31
|
+
import SchemaEditor from '../schema-editor/schema-editor.component';
|
|
32
|
+
import styles from './form-editor.scss';
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
interface ErrorProps {
|
|
35
35
|
error: Error;
|
|
36
36
|
title: string;
|
|
37
|
-
}
|
|
37
|
+
}
|
|
38
38
|
|
|
39
|
-
type Status =
|
|
39
|
+
type Status = 'idle' | 'formLoaded' | 'schemaLoaded';
|
|
40
40
|
|
|
41
|
-
const
|
|
41
|
+
const ErrorNotification = ({ error, title }: ErrorProps) => {
|
|
42
42
|
return (
|
|
43
43
|
<InlineNotification
|
|
44
44
|
className={styles.errorNotification}
|
|
45
|
-
kind={
|
|
45
|
+
kind={'error'}
|
|
46
46
|
lowContrast
|
|
47
47
|
subtitle={error?.message}
|
|
48
48
|
title={title}
|
|
@@ -52,49 +52,34 @@ const Error = ({ error, title }: ErrorProps) => {
|
|
|
52
52
|
|
|
53
53
|
const FormEditor: React.FC = () => {
|
|
54
54
|
const { t } = useTranslation();
|
|
55
|
-
const { formUuid } = useParams<
|
|
55
|
+
const { formUuid } = useParams<{ formUuid: string }>();
|
|
56
56
|
const isNewSchema = !formUuid;
|
|
57
57
|
const [schema, setSchema] = useState<Schema>();
|
|
58
58
|
const [showDraftSchemaModal, setShowDraftSchemaModal] = useState(false);
|
|
59
59
|
const { form, formError, isLoadingForm } = useForm(formUuid);
|
|
60
60
|
const { clobdata, clobdataError, isLoadingClobdata } = useClobdata(form);
|
|
61
|
-
const [status, setStatus] = useState<Status>(
|
|
62
|
-
const [stringifiedSchema, setStringifiedSchema] = useState(
|
|
63
|
-
schema ? JSON.stringify(schema, null, 2) : ""
|
|
64
|
-
);
|
|
65
|
-
const [copied, setCopied] = useState(false);
|
|
61
|
+
const [status, setStatus] = useState<Status>('idle');
|
|
62
|
+
const [stringifiedSchema, setStringifiedSchema] = useState(schema ? JSON.stringify(schema, null, 2) : '');
|
|
66
63
|
|
|
67
|
-
const isLoadingFormOrSchema =
|
|
68
|
-
formUuid && (isLoadingClobdata || isLoadingForm);
|
|
64
|
+
const isLoadingFormOrSchema = Boolean(formUuid) && (isLoadingClobdata || isLoadingForm);
|
|
69
65
|
|
|
70
66
|
useEffect(() => {
|
|
71
67
|
if (formUuid) {
|
|
72
68
|
if (form && Object.keys(form).length > 0) {
|
|
73
|
-
setStatus(
|
|
69
|
+
setStatus('formLoaded');
|
|
74
70
|
}
|
|
75
71
|
|
|
76
|
-
if (
|
|
77
|
-
status === "formLoaded" &&
|
|
78
|
-
!isLoadingClobdata &&
|
|
79
|
-
clobdata === undefined
|
|
80
|
-
) {
|
|
72
|
+
if (status === 'formLoaded' && !isLoadingClobdata && clobdata === undefined) {
|
|
81
73
|
setShowDraftSchemaModal(true);
|
|
82
74
|
}
|
|
83
75
|
|
|
84
76
|
if (clobdata && Object.keys(clobdata).length > 0) {
|
|
85
|
-
setStatus(
|
|
77
|
+
setStatus('schemaLoaded');
|
|
86
78
|
setSchema(clobdata);
|
|
87
|
-
localStorage.setItem(
|
|
79
|
+
localStorage.setItem('formJSON', JSON.stringify(clobdata));
|
|
88
80
|
}
|
|
89
81
|
}
|
|
90
|
-
}, [
|
|
91
|
-
clobdata,
|
|
92
|
-
form,
|
|
93
|
-
formUuid,
|
|
94
|
-
isLoadingClobdata,
|
|
95
|
-
isLoadingFormOrSchema,
|
|
96
|
-
status,
|
|
97
|
-
]);
|
|
82
|
+
}, [clobdata, form, formUuid, isLoadingClobdata, isLoadingFormOrSchema, status]);
|
|
98
83
|
|
|
99
84
|
useEffect(() => {
|
|
100
85
|
setStringifiedSchema(JSON.stringify(schema, null, 2));
|
|
@@ -102,76 +87,82 @@ const FormEditor: React.FC = () => {
|
|
|
102
87
|
|
|
103
88
|
const handleLoadDraftSchema = useCallback(() => {
|
|
104
89
|
setShowDraftSchemaModal(false);
|
|
105
|
-
|
|
106
|
-
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const draftSchema = localStorage.getItem('formJSON');
|
|
93
|
+
if (draftSchema) {
|
|
94
|
+
setSchema(JSON.parse(draftSchema) as Schema);
|
|
95
|
+
}
|
|
96
|
+
} catch (e) {
|
|
97
|
+
console.error('Error fetching draft schema from localStorage: ', e?.message);
|
|
98
|
+
}
|
|
107
99
|
}, []);
|
|
108
100
|
|
|
109
101
|
const handleSchemaChange = useCallback((updatedSchema: string) => {
|
|
110
102
|
setStringifiedSchema(updatedSchema);
|
|
111
103
|
}, []);
|
|
112
104
|
|
|
113
|
-
const updateSchema = useCallback((updatedSchema) => {
|
|
105
|
+
const updateSchema = useCallback((updatedSchema: Schema) => {
|
|
114
106
|
setSchema(updatedSchema);
|
|
115
|
-
localStorage.setItem(
|
|
107
|
+
localStorage.setItem('formJSON', JSON.stringify(updatedSchema));
|
|
116
108
|
}, []);
|
|
117
109
|
|
|
118
110
|
const inputDummySchema = useCallback(() => {
|
|
119
111
|
const dummySchema: OHRIFormSchema = {
|
|
120
|
-
encounterType:
|
|
121
|
-
name:
|
|
122
|
-
processor:
|
|
112
|
+
encounterType: '',
|
|
113
|
+
name: 'Sample Form',
|
|
114
|
+
processor: 'EncounterFormProcessor',
|
|
123
115
|
referencedForms: [],
|
|
124
|
-
uuid:
|
|
125
|
-
version:
|
|
116
|
+
uuid: '',
|
|
117
|
+
version: '1.0',
|
|
126
118
|
pages: [
|
|
127
119
|
{
|
|
128
|
-
label:
|
|
120
|
+
label: 'First Page',
|
|
129
121
|
sections: [
|
|
130
122
|
{
|
|
131
|
-
label:
|
|
132
|
-
isExpanded:
|
|
123
|
+
label: 'A Section',
|
|
124
|
+
isExpanded: 'true',
|
|
133
125
|
questions: [
|
|
134
126
|
{
|
|
135
|
-
|
|
136
|
-
|
|
127
|
+
id: 'sampleQuestion',
|
|
128
|
+
label: 'A Question of type obs that renders a text input',
|
|
129
|
+
type: 'obs',
|
|
137
130
|
questionOptions: {
|
|
138
|
-
rendering:
|
|
139
|
-
concept:
|
|
131
|
+
rendering: 'text',
|
|
132
|
+
concept: 'a-system-defined-concept-uuid',
|
|
140
133
|
},
|
|
141
|
-
id: "sampleQuestion",
|
|
142
134
|
},
|
|
143
135
|
],
|
|
144
136
|
},
|
|
145
137
|
{
|
|
146
|
-
label:
|
|
147
|
-
isExpanded:
|
|
138
|
+
label: 'Another Section',
|
|
139
|
+
isExpanded: 'true',
|
|
148
140
|
questions: [
|
|
149
141
|
{
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
type:
|
|
142
|
+
id: 'anotherSampleQuestion',
|
|
143
|
+
label: 'Another Question of type obs whose answers get rendered as radio inputs',
|
|
144
|
+
type: 'obs',
|
|
153
145
|
questionOptions: {
|
|
154
|
-
rendering:
|
|
155
|
-
concept:
|
|
146
|
+
rendering: 'radio',
|
|
147
|
+
concept: 'system-defined-concept-uuid',
|
|
156
148
|
answers: [
|
|
157
149
|
{
|
|
158
|
-
concept:
|
|
159
|
-
label:
|
|
150
|
+
concept: 'another-system-defined-concept-uuid',
|
|
151
|
+
label: 'Choice 1',
|
|
160
152
|
conceptMappings: [],
|
|
161
153
|
},
|
|
162
154
|
{
|
|
163
|
-
concept:
|
|
164
|
-
label:
|
|
155
|
+
concept: 'yet-another-system-defined-concept-uuid',
|
|
156
|
+
label: 'Choice 2',
|
|
165
157
|
conceptMappings: [],
|
|
166
158
|
},
|
|
167
159
|
{
|
|
168
|
-
concept:
|
|
169
|
-
label:
|
|
160
|
+
concept: 'yet-one-more-system-defined-concept-uuid',
|
|
161
|
+
label: 'Choice 3',
|
|
170
162
|
conceptMappings: [],
|
|
171
163
|
},
|
|
172
164
|
],
|
|
173
165
|
},
|
|
174
|
-
id: "anotherSampleQuestion",
|
|
175
166
|
},
|
|
176
167
|
],
|
|
177
168
|
},
|
|
@@ -184,10 +175,10 @@ const FormEditor: React.FC = () => {
|
|
|
184
175
|
updateSchema({ ...dummySchema });
|
|
185
176
|
}, [updateSchema]);
|
|
186
177
|
|
|
187
|
-
const [invalidJsonErrorMessage, setInvalidJsonErrorMessage] = useState(
|
|
178
|
+
const [invalidJsonErrorMessage, setInvalidJsonErrorMessage] = useState('');
|
|
188
179
|
|
|
189
180
|
const resetErrorMessage = useCallback(() => {
|
|
190
|
-
setInvalidJsonErrorMessage(
|
|
181
|
+
setInvalidJsonErrorMessage('');
|
|
191
182
|
}, []);
|
|
192
183
|
|
|
193
184
|
const renderSchemaChanges = useCallback(() => {
|
|
@@ -197,8 +188,10 @@ const FormEditor: React.FC = () => {
|
|
|
197
188
|
const parsedJson: Schema = JSON.parse(stringifiedSchema);
|
|
198
189
|
updateSchema(parsedJson);
|
|
199
190
|
setStringifiedSchema(JSON.stringify(parsedJson, null, 2));
|
|
200
|
-
} catch (
|
|
201
|
-
|
|
191
|
+
} catch (e) {
|
|
192
|
+
if (e instanceof Error) {
|
|
193
|
+
setInvalidJsonErrorMessage(e.message);
|
|
194
|
+
}
|
|
202
195
|
}
|
|
203
196
|
}, [stringifiedSchema, updateSchema, resetErrorMessage]);
|
|
204
197
|
|
|
@@ -209,26 +202,23 @@ const FormEditor: React.FC = () => {
|
|
|
209
202
|
onClose={() => setShowDraftSchemaModal(false)}
|
|
210
203
|
preventCloseOnClickOutside
|
|
211
204
|
>
|
|
212
|
-
<ModalHeader title={t(
|
|
213
|
-
<Form onSubmit={(event) => event.preventDefault()}>
|
|
205
|
+
<ModalHeader title={t('schemaNotFound', 'Schema not found')} />
|
|
206
|
+
<Form onSubmit={(event: React.SyntheticEvent) => event.preventDefault()}>
|
|
214
207
|
<ModalBody>
|
|
215
208
|
<p>
|
|
216
209
|
{t(
|
|
217
|
-
|
|
218
|
-
"The schema originally associated with this form could not be found. A draft schema was found saved in your browser's local storage. Would you like to load it instead?"
|
|
210
|
+
'schemaNotFoundText',
|
|
211
|
+
"The schema originally associated with this form could not be found. A draft schema was found saved in your browser's local storage. Would you like to load it instead?",
|
|
219
212
|
)}
|
|
220
213
|
</p>
|
|
221
214
|
</ModalBody>
|
|
222
215
|
</Form>
|
|
223
216
|
<ModalFooter>
|
|
224
|
-
<Button
|
|
225
|
-
|
|
226
|
-
kind="secondary"
|
|
227
|
-
>
|
|
228
|
-
{t("cancel", "Cancel")}
|
|
217
|
+
<Button onClick={() => setShowDraftSchemaModal(false)} kind="secondary">
|
|
218
|
+
{t('cancel', 'Cancel')}
|
|
229
219
|
</Button>
|
|
230
220
|
<Button onClick={handleLoadDraftSchema}>
|
|
231
|
-
<span>{t(
|
|
221
|
+
<span>{t('loadDraft', 'Load draft')}</span>
|
|
232
222
|
</Button>
|
|
233
223
|
</ModalFooter>
|
|
234
224
|
</ComposedModal>
|
|
@@ -238,14 +228,13 @@ const FormEditor: React.FC = () => {
|
|
|
238
228
|
const downloadableSchema = useMemo(
|
|
239
229
|
() =>
|
|
240
230
|
new Blob([JSON.stringify(schema, null, 2)], {
|
|
241
|
-
type:
|
|
231
|
+
type: 'application/json',
|
|
242
232
|
}),
|
|
243
|
-
[schema]
|
|
233
|
+
[schema],
|
|
244
234
|
);
|
|
245
235
|
|
|
246
|
-
const handleCopySchema = useCallback(() => {
|
|
247
|
-
navigator.clipboard.writeText(stringifiedSchema);
|
|
248
|
-
setCopied(true);
|
|
236
|
+
const handleCopySchema = useCallback(async () => {
|
|
237
|
+
await navigator.clipboard.writeText(stringifiedSchema);
|
|
249
238
|
}, [stringifiedSchema]);
|
|
250
239
|
|
|
251
240
|
return (
|
|
@@ -259,45 +248,38 @@ const FormEditor: React.FC = () => {
|
|
|
259
248
|
<Column lg={8} md={8} className={styles.column}>
|
|
260
249
|
<div className={styles.actionButtons}>
|
|
261
250
|
{isLoadingFormOrSchema ? (
|
|
262
|
-
<InlineLoading
|
|
263
|
-
description={t("loadingSchema", "Loading schema") + "..."}
|
|
264
|
-
/>
|
|
251
|
+
<InlineLoading description={t('loadingSchema', 'Loading schema') + '...'} />
|
|
265
252
|
) : null}
|
|
266
253
|
|
|
267
254
|
{isNewSchema && !schema ? (
|
|
268
255
|
<Button kind="ghost" onClick={inputDummySchema}>
|
|
269
|
-
{t(
|
|
256
|
+
{t('inputDummySchema', 'Input dummy schema')}
|
|
270
257
|
</Button>
|
|
271
258
|
) : null}
|
|
272
259
|
|
|
273
260
|
<Button kind="ghost" onClick={renderSchemaChanges}>
|
|
274
|
-
<span>{t(
|
|
261
|
+
<span>{t('renderChanges', 'Render changes')}</span>
|
|
275
262
|
</Button>
|
|
276
263
|
</div>
|
|
277
264
|
<div>
|
|
278
265
|
<div className={styles.heading}>
|
|
279
|
-
<span className={styles.tabHeading}>
|
|
280
|
-
{t("schemaEditor", "Schema editor")}
|
|
281
|
-
</span>
|
|
266
|
+
<span className={styles.tabHeading}>{t('schemaEditor', 'Schema editor')}</span>
|
|
282
267
|
{schema ? (
|
|
283
268
|
<>
|
|
284
269
|
<CopyButton
|
|
285
270
|
align="top"
|
|
286
271
|
className="cds--btn--md"
|
|
287
272
|
enterDelayMs={300}
|
|
288
|
-
iconDescription={t(
|
|
273
|
+
iconDescription={t('copySchema', 'Copy schema')}
|
|
289
274
|
kind="ghost"
|
|
290
275
|
onClick={handleCopySchema}
|
|
291
276
|
/>
|
|
292
|
-
<a
|
|
293
|
-
download={`${form?.name}.json`}
|
|
294
|
-
href={window.URL.createObjectURL(downloadableSchema)}
|
|
295
|
-
>
|
|
277
|
+
<a download={`${form?.name}.json`} href={window.URL.createObjectURL(downloadableSchema)}>
|
|
296
278
|
<Button
|
|
297
279
|
enterDelayMs={300}
|
|
298
280
|
renderIcon={Download}
|
|
299
|
-
kind={
|
|
300
|
-
iconDescription={t(
|
|
281
|
+
kind={'ghost'}
|
|
282
|
+
iconDescription={t('downloadSchema', 'Download schema')}
|
|
301
283
|
hasIconOnly
|
|
302
284
|
size="md"
|
|
303
285
|
tooltipAlignment="start"
|
|
@@ -307,16 +289,10 @@ const FormEditor: React.FC = () => {
|
|
|
307
289
|
) : null}
|
|
308
290
|
</div>
|
|
309
291
|
{formError ? (
|
|
310
|
-
<Error
|
|
311
|
-
error={formError}
|
|
312
|
-
title={t("formError", "Error loading form metadata")}
|
|
313
|
-
/>
|
|
292
|
+
<ErrorNotification error={formError} title={t('formError', 'Error loading form metadata')} />
|
|
314
293
|
) : null}
|
|
315
294
|
{clobdataError ? (
|
|
316
|
-
<Error
|
|
317
|
-
error={clobdataError}
|
|
318
|
-
title={t("schemaLoadError", "Error loading schema")}
|
|
319
|
-
/>
|
|
295
|
+
<ErrorNotification error={clobdataError} title={t('schemaLoadError', 'Error loading schema')} />
|
|
320
296
|
) : null}
|
|
321
297
|
<div className={styles.editorContainer}>
|
|
322
298
|
<SchemaEditor
|
|
@@ -332,22 +308,15 @@ const FormEditor: React.FC = () => {
|
|
|
332
308
|
<ActionButtons schema={schema} t={t} />
|
|
333
309
|
<Tabs>
|
|
334
310
|
<TabList aria-label="Form previews">
|
|
335
|
-
<Tab>{t(
|
|
336
|
-
<Tab>{t(
|
|
311
|
+
<Tab>{t('preview', 'Preview')}</Tab>
|
|
312
|
+
<Tab>{t('interactiveBuilder', 'Interactive Builder')}</Tab>
|
|
337
313
|
</TabList>
|
|
338
314
|
<TabPanels>
|
|
339
315
|
<TabPanel>
|
|
340
|
-
<FormRenderer
|
|
341
|
-
schema={schema}
|
|
342
|
-
isLoading={isLoadingFormOrSchema}
|
|
343
|
-
/>
|
|
316
|
+
<FormRenderer schema={schema} isLoading={isLoadingFormOrSchema} />
|
|
344
317
|
</TabPanel>
|
|
345
318
|
<TabPanel>
|
|
346
|
-
<InteractiveBuilder
|
|
347
|
-
schema={schema}
|
|
348
|
-
onSchemaChange={updateSchema}
|
|
349
|
-
isLoading={isLoadingFormOrSchema}
|
|
350
|
-
/>
|
|
319
|
+
<InteractiveBuilder schema={schema} onSchemaChange={updateSchema} isLoading={isLoadingFormOrSchema} />
|
|
351
320
|
</TabPanel>
|
|
352
321
|
</TabPanels>
|
|
353
322
|
</Tabs>
|
|
@@ -1,56 +1,55 @@
|
|
|
1
|
-
import React, { useEffect, useState } from
|
|
2
|
-
import { ErrorBoundary } from
|
|
3
|
-
import { useTranslation } from
|
|
4
|
-
import { Button, InlineLoading, Tile } from
|
|
5
|
-
import { OHRIFormSchema, OHRIForm } from
|
|
6
|
-
import styles from
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { ErrorBoundary } from 'react-error-boundary';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Button, InlineLoading, Tile } from '@carbon/react';
|
|
5
|
+
import { type OHRIFormSchema, OHRIForm } from '@openmrs/openmrs-form-engine-lib';
|
|
6
|
+
import styles from './form-renderer.scss';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
interface ErrorFallbackProps {
|
|
9
9
|
error: Error;
|
|
10
10
|
resetErrorBoundary: () => void;
|
|
11
|
-
}
|
|
11
|
+
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
interface FormRendererProps {
|
|
14
14
|
isLoading: boolean;
|
|
15
15
|
onSchemaChange?: (schema: OHRIFormSchema) => void;
|
|
16
16
|
schema: OHRIFormSchema;
|
|
17
|
-
}
|
|
17
|
+
}
|
|
18
18
|
|
|
19
19
|
const FormRenderer: React.FC<FormRendererProps> = ({ isLoading, schema }) => {
|
|
20
20
|
const { t } = useTranslation();
|
|
21
21
|
|
|
22
22
|
const dummySchema: OHRIFormSchema = {
|
|
23
|
-
encounterType:
|
|
24
|
-
name:
|
|
23
|
+
encounterType: '',
|
|
24
|
+
name: 'Test Form',
|
|
25
25
|
pages: [
|
|
26
26
|
{
|
|
27
|
-
label:
|
|
27
|
+
label: 'Test Page',
|
|
28
28
|
sections: [
|
|
29
29
|
{
|
|
30
|
-
label:
|
|
31
|
-
isExpanded:
|
|
30
|
+
label: 'Test Section',
|
|
31
|
+
isExpanded: 'true',
|
|
32
32
|
questions: [
|
|
33
33
|
{
|
|
34
|
-
label:
|
|
35
|
-
type:
|
|
34
|
+
label: 'Test Question',
|
|
35
|
+
type: 'obs',
|
|
36
36
|
questionOptions: {
|
|
37
|
-
rendering:
|
|
38
|
-
concept:
|
|
37
|
+
rendering: 'text',
|
|
38
|
+
concept: 'xxxx',
|
|
39
39
|
},
|
|
40
|
-
id:
|
|
40
|
+
id: 'testQuestion',
|
|
41
41
|
},
|
|
42
42
|
],
|
|
43
43
|
},
|
|
44
44
|
],
|
|
45
45
|
},
|
|
46
46
|
],
|
|
47
|
-
processor:
|
|
47
|
+
processor: 'EncounterFormProcessor',
|
|
48
48
|
referencedForms: [],
|
|
49
|
-
uuid:
|
|
49
|
+
uuid: 'xxx',
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
const [schemaToRender, setSchemaToRender] =
|
|
53
|
-
useState<OHRIFormSchema>(dummySchema);
|
|
52
|
+
const [schemaToRender, setSchemaToRender] = useState<OHRIFormSchema>(dummySchema);
|
|
54
53
|
|
|
55
54
|
useEffect(() => {
|
|
56
55
|
if (schema) {
|
|
@@ -61,10 +60,7 @@ const FormRenderer: React.FC<FormRendererProps> = ({ isLoading, schema }) => {
|
|
|
61
60
|
if (isLoading) {
|
|
62
61
|
return (
|
|
63
62
|
<div className={styles.loadingContainer}>
|
|
64
|
-
<InlineLoading
|
|
65
|
-
className={styles.loader}
|
|
66
|
-
description={t("loading", "Loading") + "..."}
|
|
67
|
-
/>
|
|
63
|
+
<InlineLoading className={styles.loader} description={t('loading', 'Loading') + '...'} />
|
|
68
64
|
</div>
|
|
69
65
|
);
|
|
70
66
|
}
|
|
@@ -73,20 +69,18 @@ const FormRenderer: React.FC<FormRendererProps> = ({ isLoading, schema }) => {
|
|
|
73
69
|
<div className={styles.container}>
|
|
74
70
|
{!schema && (
|
|
75
71
|
<Tile className={styles.emptyStateTile}>
|
|
76
|
-
<h4 className={styles.heading}>
|
|
77
|
-
{t("noSchemaLoaded", "No schema loaded")}
|
|
78
|
-
</h4>
|
|
72
|
+
<h4 className={styles.heading}>{t('noSchemaLoaded', 'No schema loaded')}</h4>
|
|
79
73
|
<p className={styles.helperText}>
|
|
80
74
|
{t(
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
'formRendererHelperText',
|
|
76
|
+
'Load a form schema in the Schema Editor to the left to see it rendered here by the Form Engine.',
|
|
83
77
|
)}
|
|
84
78
|
</p>
|
|
85
79
|
</Tile>
|
|
86
80
|
)}
|
|
87
81
|
{schema === schemaToRender && (
|
|
88
82
|
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
|
89
|
-
<OHRIForm formJson={schemaToRender} mode={
|
|
83
|
+
<OHRIForm formJson={schemaToRender} mode={'enter'} patientUUID={''} />
|
|
90
84
|
</ErrorBoundary>
|
|
91
85
|
)}
|
|
92
86
|
</div>
|
|
@@ -97,17 +91,12 @@ function ErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) {
|
|
|
97
91
|
const { t } = useTranslation();
|
|
98
92
|
return (
|
|
99
93
|
<Tile className={styles.errorStateTile}>
|
|
100
|
-
<h4 className={styles.heading}>
|
|
101
|
-
{t(
|
|
102
|
-
"problemLoadingPreview",
|
|
103
|
-
"There was a problem loading the schema preview"
|
|
104
|
-
)}
|
|
105
|
-
</h4>
|
|
94
|
+
<h4 className={styles.heading}>{t('problemLoadingPreview', 'There was a problem loading the form preview')}</h4>
|
|
106
95
|
<p className={styles.helperText}>
|
|
107
96
|
<pre>{error.message}</pre>
|
|
108
97
|
</p>
|
|
109
98
|
<Button kind="primary" onClick={resetErrorBoundary}>
|
|
110
|
-
{t(
|
|
99
|
+
{t('tryAgain', 'Try again')}
|
|
111
100
|
</Button>
|
|
112
101
|
</Tile>
|
|
113
102
|
);
|