@openmrs/esm-implementer-tools-app 6.3.1-pre.3218 → 6.3.1-pre.3236
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/.turbo/turbo-build.log +7 -7
- package/dist/4300.js +1 -1
- package/dist/5563.js +1 -1
- package/dist/6132.js +1 -0
- package/dist/6132.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/openmrs-esm-implementer-tools-app.js +1 -1
- package/dist/openmrs-esm-implementer-tools-app.js.buildmanifest.json +30 -30
- package/dist/routes.json +1 -1
- package/package.json +4 -4
- package/src/configuration/configuration.styles.scss +0 -18
- package/src/configuration/interactive-editor/editable-value.component.tsx +1 -1
- package/src/configuration/interactive-editor/validators.resource.ts +147 -0
- package/src/configuration/interactive-editor/{value-editor.scss → value-editor.styles.scss} +7 -0
- package/src/configuration/interactive-editor/value-editor.tsx +46 -18
- package/src/configuration/interactive-editor/value-editors/value-editor-field.tsx +21 -3
- package/src/popup/popup.component.tsx +39 -37
- package/src/popup/popup.styles.scss +0 -3
- package/translations/en.json +7 -0
- package/dist/3150.js +0 -1
- package/dist/3150.js.map +0 -1
|
@@ -391,30 +391,6 @@
|
|
|
391
391
|
"hash": "ad8873304325ee50",
|
|
392
392
|
"childrenByOrder": {}
|
|
393
393
|
},
|
|
394
|
-
{
|
|
395
|
-
"rendered": true,
|
|
396
|
-
"initial": false,
|
|
397
|
-
"entry": false,
|
|
398
|
-
"recorded": false,
|
|
399
|
-
"size": 830316,
|
|
400
|
-
"sizes": {
|
|
401
|
-
"javascript": 830316
|
|
402
|
-
},
|
|
403
|
-
"names": [],
|
|
404
|
-
"idHints": [],
|
|
405
|
-
"runtime": [
|
|
406
|
-
"@openmrs/esm-implementer-tools-app",
|
|
407
|
-
"main"
|
|
408
|
-
],
|
|
409
|
-
"files": [
|
|
410
|
-
"3150.js"
|
|
411
|
-
],
|
|
412
|
-
"auxiliaryFiles": [
|
|
413
|
-
"3150.js.map"
|
|
414
|
-
],
|
|
415
|
-
"hash": "34108f9dab7ad3ab",
|
|
416
|
-
"childrenByOrder": {}
|
|
417
|
-
},
|
|
418
394
|
{
|
|
419
395
|
"rendered": true,
|
|
420
396
|
"initial": false,
|
|
@@ -536,9 +512,9 @@
|
|
|
536
512
|
"initial": false,
|
|
537
513
|
"entry": false,
|
|
538
514
|
"recorded": false,
|
|
539
|
-
"size":
|
|
515
|
+
"size": 1500,
|
|
540
516
|
"sizes": {
|
|
541
|
-
"javascript":
|
|
517
|
+
"javascript": 1500
|
|
542
518
|
},
|
|
543
519
|
"names": [],
|
|
544
520
|
"idHints": [],
|
|
@@ -550,7 +526,7 @@
|
|
|
550
526
|
"4300.js"
|
|
551
527
|
],
|
|
552
528
|
"auxiliaryFiles": [],
|
|
553
|
-
"hash": "
|
|
529
|
+
"hash": "e5ec50abd2006d8c",
|
|
554
530
|
"childrenByOrder": {}
|
|
555
531
|
},
|
|
556
532
|
{
|
|
@@ -776,7 +752,7 @@
|
|
|
776
752
|
"auxiliaryFiles": [
|
|
777
753
|
"5563.js.map"
|
|
778
754
|
],
|
|
779
|
-
"hash": "
|
|
755
|
+
"hash": "d3d48bbe9d564559",
|
|
780
756
|
"childrenByOrder": {}
|
|
781
757
|
},
|
|
782
758
|
{
|
|
@@ -901,6 +877,30 @@
|
|
|
901
877
|
"hash": "7e9ab64623e07a93",
|
|
902
878
|
"childrenByOrder": {}
|
|
903
879
|
},
|
|
880
|
+
{
|
|
881
|
+
"rendered": true,
|
|
882
|
+
"initial": false,
|
|
883
|
+
"entry": false,
|
|
884
|
+
"recorded": false,
|
|
885
|
+
"size": 847661,
|
|
886
|
+
"sizes": {
|
|
887
|
+
"javascript": 847661
|
|
888
|
+
},
|
|
889
|
+
"names": [],
|
|
890
|
+
"idHints": [],
|
|
891
|
+
"runtime": [
|
|
892
|
+
"@openmrs/esm-implementer-tools-app",
|
|
893
|
+
"main"
|
|
894
|
+
],
|
|
895
|
+
"files": [
|
|
896
|
+
"6132.js"
|
|
897
|
+
],
|
|
898
|
+
"auxiliaryFiles": [
|
|
899
|
+
"6132.js.map"
|
|
900
|
+
],
|
|
901
|
+
"hash": "fa1951d76a17d7ff",
|
|
902
|
+
"childrenByOrder": {}
|
|
903
|
+
},
|
|
904
904
|
{
|
|
905
905
|
"rendered": true,
|
|
906
906
|
"initial": false,
|
|
@@ -1325,7 +1325,7 @@
|
|
|
1325
1325
|
"auxiliaryFiles": [
|
|
1326
1326
|
"main.js.map"
|
|
1327
1327
|
],
|
|
1328
|
-
"hash": "
|
|
1328
|
+
"hash": "8e276b02ed1ca12e",
|
|
1329
1329
|
"childrenByOrder": {}
|
|
1330
1330
|
},
|
|
1331
1331
|
{
|
|
@@ -1578,7 +1578,7 @@
|
|
|
1578
1578
|
"auxiliaryFiles": [
|
|
1579
1579
|
"openmrs-esm-implementer-tools-app.js.map"
|
|
1580
1580
|
],
|
|
1581
|
-
"hash": "
|
|
1581
|
+
"hash": "bf3a56f61f240a22",
|
|
1582
1582
|
"childrenByOrder": {}
|
|
1583
1583
|
}
|
|
1584
1584
|
]
|
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","pages":[{"component":"implementerTools","route":true},{"component":"globalImplementerTools","route":true}],"extensions":[{"name":"implementer-tools-button","slot":"top-nav-actions-slot","component":"implementerToolsButton","order":20}],"version":"6.3.1-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","pages":[{"component":"implementerTools","route":true},{"component":"globalImplementerTools","route":true}],"extensions":[{"name":"implementer-tools-button","slot":"top-nav-actions-slot","component":"implementerToolsButton","order":20}],"version":"6.3.1-pre.3236"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-implementer-tools-app",
|
|
3
|
-
"version": "6.3.1-pre.
|
|
3
|
+
"version": "6.3.1-pre.3236",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "The admin interface for OpenMRS Frontend",
|
|
6
6
|
"browser": "dist/openmrs-esm-implementer-tools-app.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"analyze": "webpack --mode=production --env analyze=true",
|
|
18
18
|
"typescript": "tsc",
|
|
19
19
|
"lint": "eslint src --ext ts,tsx",
|
|
20
|
-
"extract-translations": "i18next 'src/**/*.component.tsx' 'src/**/*.button.tsx' 'src/**/*.extension.tsx' 'src/**/*.modal.tsx' --config='../../../tools/i18next-parser.config.js'"
|
|
20
|
+
"extract-translations": "i18next 'src/**/*.component.tsx' 'src/**/*.button.tsx' 'src/**/*.extension.tsx' 'src/**/*.modal.tsx' 'src/**/*.resource.ts' --config='../../../tools/i18next-parser.config.js'"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"openmrs",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"swr": "2.x"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@openmrs/esm-framework": "6.3.1-pre.
|
|
55
|
-
"@openmrs/webpack-config": "6.3.1-pre.
|
|
54
|
+
"@openmrs/esm-framework": "6.3.1-pre.3236",
|
|
55
|
+
"@openmrs/webpack-config": "6.3.1-pre.3236",
|
|
56
56
|
"ace-builds": "^1.4.14",
|
|
57
57
|
"react": "^18.1.0",
|
|
58
58
|
"react-ace": "^9.5.0",
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
.tools {
|
|
7
7
|
width: 100%;
|
|
8
|
-
background-color: theme.$background;
|
|
9
8
|
border-bottom: 1px solid theme.$border-subtle-00;
|
|
10
9
|
border-top: 1px solid theme.$border-subtle-00;
|
|
11
10
|
z-index: 2;
|
|
@@ -45,23 +44,6 @@
|
|
|
45
44
|
width: 100%;
|
|
46
45
|
display: flex;
|
|
47
46
|
overflow-y: auto;
|
|
48
|
-
|
|
49
|
-
:global(.ace_scroller) {
|
|
50
|
-
background-color: theme.$background;
|
|
51
|
-
color: theme.$text-secondary;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
:global(.ace_gutter) {
|
|
55
|
-
background-color: theme.$background;
|
|
56
|
-
color: theme.$text-secondary;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
:global(.ace_active-line) {
|
|
60
|
-
background-color: theme.$layer-accent !important;
|
|
61
|
-
}
|
|
62
|
-
:global(.ace_cursor) {
|
|
63
|
-
color: theme.$text-secondary;
|
|
64
|
-
}
|
|
65
47
|
}
|
|
66
48
|
|
|
67
49
|
.configTreePane {
|
|
@@ -89,7 +89,7 @@ export default function EditableValue({ path, element, customType }: EditableVal
|
|
|
89
89
|
customType={customType}
|
|
90
90
|
path={path}
|
|
91
91
|
handleClose={closeEditor}
|
|
92
|
-
|
|
92
|
+
handleSaveToConfiguration={(val) => {
|
|
93
93
|
try {
|
|
94
94
|
const result = JSON.parse(val);
|
|
95
95
|
const tempConfigUpdate = set(cloneDeep(temporaryConfigStore.getState()), ['config', ...path], result);
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type TOptions } from 'i18next';
|
|
2
|
+
import { Type, type ConfigValue, type ConfigSchema, type Validator, translateFrom } from '@openmrs/esm-framework';
|
|
3
|
+
import type { CustomValueType } from './value-editor';
|
|
4
|
+
|
|
5
|
+
const moduleName = '@openmrs/esm-implementer-tools-app';
|
|
6
|
+
const t = (key: string, fallback?: string, options?: Omit<TOptions, 'ns' | 'defaultValue'>) =>
|
|
7
|
+
translateFrom(moduleName, key, fallback, options);
|
|
8
|
+
|
|
9
|
+
const validateString = (value: ConfigValue, validators: Array<Validator>) => {
|
|
10
|
+
if (typeof value !== 'string') {
|
|
11
|
+
return t('stringValidationMessage', 'Value must be a string');
|
|
12
|
+
}
|
|
13
|
+
for (const validator of validators) {
|
|
14
|
+
const error = validator(value);
|
|
15
|
+
if (error) return error;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const validateNumber = (value: ConfigValue, validators: Array<Validator>) => {
|
|
21
|
+
if (typeof value !== 'number') {
|
|
22
|
+
return t('numberValidationMessage', 'Value must be a number');
|
|
23
|
+
}
|
|
24
|
+
for (const validator of validators) {
|
|
25
|
+
const error = validator(value);
|
|
26
|
+
if (error) return error;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const validateBoolean = (value: ConfigValue, validators: Array<Validator>) => {
|
|
32
|
+
if (typeof value !== 'boolean') {
|
|
33
|
+
return t('booleanValidationMessage', 'Value must be a boolean');
|
|
34
|
+
}
|
|
35
|
+
for (const validator of validators) {
|
|
36
|
+
const error = validator(value);
|
|
37
|
+
if (error) return error;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const validateArray = (value: ConfigValue, validators: Array<Validator>, elementSchema?: ConfigSchema) => {
|
|
43
|
+
if (!Array.isArray(value)) {
|
|
44
|
+
return t('arrayValidationMessage', 'Value must be an array');
|
|
45
|
+
}
|
|
46
|
+
for (const validator of validators) {
|
|
47
|
+
const error = validator(value);
|
|
48
|
+
if (error) return error;
|
|
49
|
+
}
|
|
50
|
+
if (elementSchema) {
|
|
51
|
+
for (let i = 0; i < value.length; i++) {
|
|
52
|
+
const element = value[i];
|
|
53
|
+
if (elementSchema._type === Type.Object) {
|
|
54
|
+
const error = validateObject(element, [], elementSchema);
|
|
55
|
+
if (error) return error;
|
|
56
|
+
} else {
|
|
57
|
+
const elementType = elementSchema._type;
|
|
58
|
+
const elementValidators = elementSchema._validators ?? [];
|
|
59
|
+
const error = validateValue(element, elementType, elementValidators, elementSchema._elements);
|
|
60
|
+
if (error) return error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const validateObject = (value: ConfigValue, validators: Array<Validator>, elementSchema?: ConfigSchema) => {
|
|
68
|
+
if (!elementSchema || typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
69
|
+
return t('objectValidationMessage', 'Value must be an object');
|
|
70
|
+
}
|
|
71
|
+
for (const validator of validators) {
|
|
72
|
+
const error = validator(value);
|
|
73
|
+
if (error) return error;
|
|
74
|
+
}
|
|
75
|
+
for (const key of Object.keys(elementSchema)) {
|
|
76
|
+
if (key.startsWith('_')) continue;
|
|
77
|
+
const propSchema = elementSchema[key];
|
|
78
|
+
const propValue = value[key];
|
|
79
|
+
if (typeof propSchema === 'object' && propSchema !== null && '_type' in propSchema) {
|
|
80
|
+
if (propSchema._type === Type.Array) {
|
|
81
|
+
const error = validateArray(propValue, propSchema._validators ?? [], propSchema._elements);
|
|
82
|
+
if (error)
|
|
83
|
+
return t('objectPropertyValidationMessage', 'Property {{key}}: {{error}}', {
|
|
84
|
+
key,
|
|
85
|
+
error,
|
|
86
|
+
});
|
|
87
|
+
} else {
|
|
88
|
+
const propType = propSchema._type;
|
|
89
|
+
const propValidators = propSchema._validators ?? [];
|
|
90
|
+
const error = validateValue(propValue, propType, propValidators, propSchema._elements);
|
|
91
|
+
if (error)
|
|
92
|
+
return t('objectPropertyValidationMessage', 'Property {{key}}: {{error}}', {
|
|
93
|
+
key,
|
|
94
|
+
error,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
} else if (typeof propSchema === 'object' && propSchema !== null) {
|
|
98
|
+
const error = validateObject(propValue, [], propSchema as ConfigSchema);
|
|
99
|
+
if (error)
|
|
100
|
+
return t('objectPropertyValidationMessage', 'Property {{key}}: {{error}}', {
|
|
101
|
+
key,
|
|
102
|
+
error,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const validateUuid = (value: ConfigValue, validators: Array<Validator>) => {
|
|
110
|
+
if (
|
|
111
|
+
typeof value !== 'string' ||
|
|
112
|
+
!/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|[0-9A-Za-z]{11,36})$/i.test(
|
|
113
|
+
value,
|
|
114
|
+
)
|
|
115
|
+
) {
|
|
116
|
+
return t('uuidValidationMessage', 'Value must be a valid UUID string');
|
|
117
|
+
}
|
|
118
|
+
for (const validator of validators) {
|
|
119
|
+
const error = validator(value);
|
|
120
|
+
if (error) return error;
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const validateValue = (
|
|
126
|
+
value: ConfigValue,
|
|
127
|
+
fieldType: Type | CustomValueType | undefined,
|
|
128
|
+
validators: Array<Validator>,
|
|
129
|
+
elementSchema?: ConfigSchema,
|
|
130
|
+
) => {
|
|
131
|
+
switch (fieldType) {
|
|
132
|
+
case Type.String:
|
|
133
|
+
return validateString(value, validators);
|
|
134
|
+
case Type.Number:
|
|
135
|
+
return validateNumber(value, validators);
|
|
136
|
+
case Type.Boolean:
|
|
137
|
+
return validateBoolean(value, validators);
|
|
138
|
+
case Type.Array:
|
|
139
|
+
return validateArray(value, validators, elementSchema);
|
|
140
|
+
case Type.Object:
|
|
141
|
+
return validateObject(value, validators, elementSchema);
|
|
142
|
+
case Type.UUID || Type.ConceptUuid || Type.PersonAttributeTypeUuid || Type.PatientIdentifierTypeUuid:
|
|
143
|
+
return validateUuid(value, validators);
|
|
144
|
+
default:
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
@use '@carbon/layout';
|
|
2
2
|
@use '@carbon/styles/scss/components/button';
|
|
3
|
+
@use '@carbon/styles/scss/theme';
|
|
4
|
+
|
|
5
|
+
.errorMessage {
|
|
6
|
+
color: theme.$text-error;
|
|
7
|
+
font-size: layout.$spacing-04;
|
|
8
|
+
margin-top: layout.$spacing-02;
|
|
9
|
+
}
|
|
3
10
|
|
|
4
11
|
.valueEditorButtons {
|
|
5
12
|
margin-top: layout.$spacing-03;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import React, { useEffect, useState, useRef } from 'react';
|
|
2
|
-
import { useTranslation } from 'react-i18next';
|
|
1
|
+
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
|
3
2
|
import { Button } from '@carbon/react';
|
|
4
|
-
import { CloseIcon,
|
|
3
|
+
import { CloseIcon, type ConfigSchema, type Config, getCoreTranslation, SaveIcon, Type } from '@openmrs/esm-framework';
|
|
5
4
|
import type { ConfigValueDescriptor } from './editable-value.component';
|
|
6
5
|
import { ValueEditorField } from './value-editors/value-editor-field';
|
|
7
|
-
import styles from './value-editor.scss';
|
|
6
|
+
import styles from './value-editor.styles.scss';
|
|
7
|
+
import { validateValue } from './validators.resource';
|
|
8
8
|
|
|
9
9
|
export type CustomValueType = 'add' | 'remove' | 'order' | 'configure';
|
|
10
10
|
|
|
@@ -14,23 +14,41 @@ interface ValueEditorProps {
|
|
|
14
14
|
element: ConfigValueDescriptor;
|
|
15
15
|
customType?: CustomValueType;
|
|
16
16
|
path: Array<string>;
|
|
17
|
-
|
|
17
|
+
handleSaveToConfiguration: (val: string) => void;
|
|
18
18
|
handleClose: () => void;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function ValueEditor({ element, customType, path,
|
|
21
|
+
export function ValueEditor({ element, customType, path, handleSaveToConfiguration, handleClose }: ValueEditorProps) {
|
|
22
22
|
const ref = useRef<HTMLDivElement>(null);
|
|
23
23
|
const [tmpValue, setTmpValue] = useState(element._value);
|
|
24
|
-
const
|
|
24
|
+
const [error, setError] = useState<string | null>(null);
|
|
25
25
|
|
|
26
26
|
const valueType = customType ?? element._type;
|
|
27
|
+
const validators = element._validators ?? [];
|
|
28
|
+
|
|
29
|
+
let elementSchema: ConfigValueDescriptor | Config | undefined = undefined;
|
|
30
|
+
if (valueType === Type.Object) {
|
|
31
|
+
elementSchema = element;
|
|
32
|
+
} else if (valueType === Type.Array) {
|
|
33
|
+
elementSchema = element._elements;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const handleSave = useCallback(() => {
|
|
37
|
+
const errorMessage = validateValue(tmpValue, valueType, validators, elementSchema as ConfigSchema);
|
|
38
|
+
if (errorMessage) {
|
|
39
|
+
setError(errorMessage);
|
|
40
|
+
} else {
|
|
41
|
+
setError(null);
|
|
42
|
+
handleSaveToConfiguration(JSON.stringify(tmpValue));
|
|
43
|
+
}
|
|
44
|
+
}, [tmpValue, valueType, validators, elementSchema, handleSaveToConfiguration]);
|
|
27
45
|
|
|
28
46
|
useEffect(() => {
|
|
29
47
|
const keyListener = (e: KeyboardEvent) => {
|
|
30
48
|
if (e.key === 'Escape') {
|
|
31
49
|
handleClose();
|
|
32
50
|
} else if (e.key === 'Enter') {
|
|
33
|
-
handleSave(
|
|
51
|
+
handleSave();
|
|
34
52
|
}
|
|
35
53
|
};
|
|
36
54
|
|
|
@@ -38,21 +56,31 @@ export function ValueEditor({ element, customType, path, handleSave, handleClose
|
|
|
38
56
|
return () => {
|
|
39
57
|
document.removeEventListener('keyup', keyListener);
|
|
40
58
|
};
|
|
41
|
-
}, [handleSave, handleClose
|
|
59
|
+
}, [handleSave, handleClose]);
|
|
42
60
|
|
|
43
61
|
return (
|
|
44
|
-
<div ref={ref}
|
|
45
|
-
<ValueEditorField
|
|
62
|
+
<div ref={ref}>
|
|
63
|
+
<ValueEditorField
|
|
64
|
+
element={element}
|
|
65
|
+
path={path}
|
|
66
|
+
value={tmpValue}
|
|
67
|
+
onChange={setTmpValue}
|
|
68
|
+
valueType={valueType}
|
|
69
|
+
error={error}
|
|
70
|
+
/>
|
|
71
|
+
<div className={styles.errorMessage}>
|
|
72
|
+
{valueType !== Type.Number &&
|
|
73
|
+
valueType !== Type.String &&
|
|
74
|
+
valueType !== Type.UUID &&
|
|
75
|
+
valueType !== Type.Boolean &&
|
|
76
|
+
error}
|
|
77
|
+
</div>
|
|
46
78
|
<div className={styles.valueEditorButtons}>
|
|
47
|
-
<Button
|
|
48
|
-
|
|
49
|
-
kind="primary"
|
|
50
|
-
onClick={() => handleSave(JSON.stringify(tmpValue))}
|
|
51
|
-
>
|
|
52
|
-
{t('saveValueButtonText', 'Save')}
|
|
79
|
+
<Button renderIcon={(props) => <SaveIcon {...props} size={16} />} kind="primary" onClick={handleSave}>
|
|
80
|
+
{getCoreTranslation('save')}
|
|
53
81
|
</Button>
|
|
54
82
|
<Button renderIcon={(props) => <CloseIcon {...props} size={16} />} kind="secondary" onClick={handleClose}>
|
|
55
|
-
{
|
|
83
|
+
{getCoreTranslation('cancel')}
|
|
56
84
|
</Button>
|
|
57
85
|
</div>
|
|
58
86
|
</div>
|
|
@@ -19,9 +19,10 @@ export interface ValueEditorFieldProps {
|
|
|
19
19
|
valueType?: ValueType;
|
|
20
20
|
value: any;
|
|
21
21
|
onChange: (value: any) => void;
|
|
22
|
+
error?: string | null;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
export function ValueEditorField({ element, path, valueType, value, onChange }: ValueEditorFieldProps) {
|
|
25
|
+
export function ValueEditorField({ element, path, valueType, value, onChange, error }: ValueEditorFieldProps) {
|
|
25
26
|
const [id] = useState(uniqueId('value-editor-'));
|
|
26
27
|
|
|
27
28
|
if (valueType === 'remove' && !path) {
|
|
@@ -31,7 +32,15 @@ export function ValueEditorField({ element, path, valueType, value, onChange }:
|
|
|
31
32
|
return valueType === Type.Array ? (
|
|
32
33
|
<ArrayEditor element={element} valueArray={value} setValue={onChange} />
|
|
33
34
|
) : valueType === Type.Boolean ? (
|
|
34
|
-
<Checkbox
|
|
35
|
+
<Checkbox
|
|
36
|
+
id={id}
|
|
37
|
+
checked={value}
|
|
38
|
+
hideLabel
|
|
39
|
+
labelText=""
|
|
40
|
+
onChange={(event, { checked, id }) => onChange(checked)}
|
|
41
|
+
invalid={Boolean(error)}
|
|
42
|
+
invalidText={error}
|
|
43
|
+
/>
|
|
35
44
|
) : valueType === Type.ConceptUuid ? (
|
|
36
45
|
<ConceptSearchBox value={value} setConcept={(concept) => onChange(concept.uuid)} />
|
|
37
46
|
) : valueType === Type.PersonAttributeTypeUuid ? (
|
|
@@ -47,9 +56,18 @@ export function ValueEditorField({ element, path, valueType, value, onChange }:
|
|
|
47
56
|
value={value}
|
|
48
57
|
onChange={(_, { value }) => onChange(value ? (typeof value === 'string' ? parseInt(value) : value) : 0)}
|
|
49
58
|
hideSteppers
|
|
59
|
+
invalid={Boolean(error)}
|
|
60
|
+
invalidText={error}
|
|
50
61
|
/>
|
|
51
62
|
) : valueType === Type.String || valueType === Type.UUID ? (
|
|
52
|
-
<TextInput
|
|
63
|
+
<TextInput
|
|
64
|
+
id={id}
|
|
65
|
+
value={value}
|
|
66
|
+
labelText=""
|
|
67
|
+
onChange={(e) => onChange(e.target.value)}
|
|
68
|
+
invalid={Boolean(error)}
|
|
69
|
+
invalidText={error}
|
|
70
|
+
/>
|
|
53
71
|
) : valueType === 'add' ? (
|
|
54
72
|
<ExtensionSlotAdd value={value ?? element._value} setValue={onChange} />
|
|
55
73
|
) : valueType === 'remove' && path ? (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useMemo, useState } from 'react';
|
|
2
|
-
import { ContentSwitcher, IconButton, Switch } from '@carbon/react';
|
|
2
|
+
import { ContentSwitcher, IconButton, Switch, Theme } from '@carbon/react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { CloseIcon } from '@openmrs/esm-framework';
|
|
5
5
|
import { Configuration } from '../configuration/configuration.component';
|
|
@@ -45,43 +45,45 @@ export default function Popup({
|
|
|
45
45
|
|
|
46
46
|
return (
|
|
47
47
|
<div className={styles.popup}>
|
|
48
|
-
<
|
|
49
|
-
<div className={styles.
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
48
|
+
<Theme theme="g90">
|
|
49
|
+
<div className={styles.topBar}>
|
|
50
|
+
<div className={styles.tabs}>
|
|
51
|
+
<ContentSwitcher
|
|
52
|
+
className={styles.contentSwitcher}
|
|
53
|
+
selectedIndex={activeTab}
|
|
54
|
+
onChange={(switcherItem: SwitcherItem) => {
|
|
55
|
+
setActiveTab(switcherItem.index);
|
|
56
|
+
}}
|
|
57
|
+
size="lg"
|
|
58
|
+
>
|
|
59
|
+
<Switch name="configuration-tab" text={t('configuration', 'Configuration')} className="darkThemeSwitch" />
|
|
60
|
+
<Switch
|
|
61
|
+
name="frontend-modules-tab"
|
|
62
|
+
text={t('frontendModules', 'Frontend modules')}
|
|
63
|
+
className="darkThemeSwitch"
|
|
64
|
+
/>
|
|
65
|
+
<Switch
|
|
66
|
+
name="backend-modules-tab"
|
|
67
|
+
text={t('backendModules', 'Backend modules')}
|
|
68
|
+
className="darkThemeSwitch"
|
|
69
|
+
/>
|
|
70
|
+
<Switch name="feature-flags-tab" text={t('featureFlags', 'Feature flags')} className="darkThemeSwitch" />
|
|
71
|
+
</ContentSwitcher>
|
|
72
|
+
</div>
|
|
73
|
+
<div>
|
|
74
|
+
<IconButton
|
|
75
|
+
align="left"
|
|
76
|
+
className={styles.closeButton}
|
|
77
|
+
kind="secondary"
|
|
78
|
+
label={t('close', 'Close')}
|
|
79
|
+
onClick={close}
|
|
80
|
+
>
|
|
81
|
+
<CloseIcon />
|
|
82
|
+
</IconButton>
|
|
83
|
+
</div>
|
|
71
84
|
</div>
|
|
72
|
-
<div>
|
|
73
|
-
|
|
74
|
-
align="left"
|
|
75
|
-
className={styles.closeButton}
|
|
76
|
-
kind="secondary"
|
|
77
|
-
label={t('close', 'Close')}
|
|
78
|
-
onClick={close}
|
|
79
|
-
>
|
|
80
|
-
<CloseIcon />
|
|
81
|
-
</IconButton>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
<div className={styles.content}>{tabContent}</div>
|
|
85
|
+
<div className={styles.content}>{tabContent}</div>
|
|
86
|
+
</Theme>
|
|
85
87
|
</div>
|
|
86
88
|
);
|
|
87
89
|
}
|
|
@@ -9,9 +9,7 @@
|
|
|
9
9
|
left: 0;
|
|
10
10
|
width: 100%;
|
|
11
11
|
z-index: 100000;
|
|
12
|
-
background-color: theme.$background;
|
|
13
12
|
border-top: 2px solid $ui-05;
|
|
14
|
-
color: $color-gray-100;
|
|
15
13
|
box-sizing: border-box;
|
|
16
14
|
|
|
17
15
|
tr {
|
|
@@ -62,7 +60,6 @@
|
|
|
62
60
|
justify-content: space-between;
|
|
63
61
|
align-items: center;
|
|
64
62
|
z-index: 2;
|
|
65
|
-
background-color: theme.$background;
|
|
66
63
|
}
|
|
67
64
|
|
|
68
65
|
.closeButton {
|
package/translations/en.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"activeItemSourceText": "The current value comes from ",
|
|
3
|
+
"arrayValidationMessage": "Value must be an array",
|
|
3
4
|
"backendModules": "Backend modules",
|
|
5
|
+
"booleanValidationMessage": "Value must be a boolean",
|
|
4
6
|
"checkImplementerToolsMessage": "Check the Backend Modules tab in the Implementer Tools for more details",
|
|
5
7
|
"clearConfig": "Clear local config",
|
|
6
8
|
"close": "Close",
|
|
@@ -21,12 +23,17 @@
|
|
|
21
23
|
"missing": "Missing",
|
|
22
24
|
"moduleName": "Module name",
|
|
23
25
|
"modulesWithMissingDependenciesWarning": "Some modules have unresolved backend dependencies",
|
|
26
|
+
"numberValidationMessage": "Value must be a number",
|
|
27
|
+
"objectPropertyValidationMessage": "Property {{key}}: {{error}}",
|
|
28
|
+
"objectValidationMessage": "Value must be an object",
|
|
24
29
|
"requiredVersion": "Required Version",
|
|
25
30
|
"resetToDefaultValueButtonText": "Reset to default",
|
|
31
|
+
"stringValidationMessage": "Value must be a string",
|
|
26
32
|
"toggleImplementerTools": "Toggle Implementer Tools",
|
|
27
33
|
"uiEditor": "UI editor",
|
|
28
34
|
"unknownVersion": "unknown",
|
|
29
35
|
"updateConfig": "Update config",
|
|
36
|
+
"uuidValidationMessage": "Value must be a valid UUID string",
|
|
30
37
|
"value": "Value",
|
|
31
38
|
"viewModules": "View modules"
|
|
32
39
|
}
|