@performant-software/user-defined-fields 0.6.2-beta.3
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/LICENSE +21 -0
- package/README.md +0 -0
- package/build/index.js +2 -0
- package/build/index.js.map +1 -0
- package/build/main.css +5 -0
- package/index.js +1 -0
- package/package.json +27 -0
- package/src/components/UserDefinedFieldModal.js +114 -0
- package/src/components/UserDefinedFieldOptions.css +4 -0
- package/src/components/UserDefinedFieldOptions.js +112 -0
- package/src/components/UserDefinedFieldsEmbeddedList.js +44 -0
- package/src/components/UserDefinedFieldsForm.js +209 -0
- package/src/components/UserDefinedFieldsList.js +40 -0
- package/src/i18n/en.json +31 -0
- package/src/i18n/i18n.js +24 -0
- package/src/index.js +18 -0
- package/src/services/UserDefinedFields.js +48 -0
- package/src/transforms/UserDefinedField.js +36 -0
- package/src/transforms/UserDefinedFields.js +55 -0
- package/src/utils/UserDefinedFields.js +61 -0
- package/types/components/UserDefinedFieldModal.js.flow +114 -0
- package/types/components/UserDefinedFieldOptions.js.flow +112 -0
- package/types/components/UserDefinedFieldsEmbeddedList.js.flow +44 -0
- package/types/components/UserDefinedFieldsForm.js.flow +209 -0
- package/types/components/UserDefinedFieldsList.js.flow +40 -0
- package/types/i18n/i18n.js.flow +24 -0
- package/types/index.js.flow +18 -0
- package/types/services/UserDefinedFields.js.flow +48 -0
- package/types/transforms/UserDefinedField.js.flow +36 -0
- package/types/transforms/UserDefinedFields.js.flow +55 -0
- package/types/utils/UserDefinedFields.js.flow +61 -0
- package/webpack.config.js +3 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import { DatePicker } from '@performant-software/semantic-components';
|
|
4
|
+
import { RichTextArea, UserDefinedFieldsService } from '@performant-software/shared-components';
|
|
5
|
+
import React, {
|
|
6
|
+
useCallback,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useState,
|
|
10
|
+
type ComponentType
|
|
11
|
+
} from 'react';
|
|
12
|
+
import { Form } from 'semantic-ui-react';
|
|
13
|
+
import _ from 'underscore';
|
|
14
|
+
|
|
15
|
+
type Props = {
|
|
16
|
+
defineableId?: number,
|
|
17
|
+
defineableType?: string,
|
|
18
|
+
isError: (key: string) => boolean,
|
|
19
|
+
item: any,
|
|
20
|
+
onChange: (obj: any) => void,
|
|
21
|
+
onClearValidationError: (...keys: Array<string>) => void
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const DataTypes = {
|
|
25
|
+
boolean: 'Boolean',
|
|
26
|
+
date: 'Date',
|
|
27
|
+
number: 'Number',
|
|
28
|
+
richText: 'RichText',
|
|
29
|
+
select: 'Select',
|
|
30
|
+
string: 'String',
|
|
31
|
+
text: 'Text'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const UserDefinedFieldsForm: ComponentType<any> = (props: Props) => {
|
|
35
|
+
const [fields, setFields] = useState([]);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sets the parsed value by parsing the JSON stored in the "user_defined" prop of the item.
|
|
39
|
+
*
|
|
40
|
+
* @type {unknown}
|
|
41
|
+
*/
|
|
42
|
+
const parsedValue = useMemo(() => {
|
|
43
|
+
let value = {};
|
|
44
|
+
|
|
45
|
+
if (_.isString(props.item.user_defined)) {
|
|
46
|
+
value = JSON.parse(props.item.user_defined);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return value;
|
|
50
|
+
}, [props.item.user_defined]);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns the key for the passed field.
|
|
54
|
+
*
|
|
55
|
+
* @type {function(*): string}
|
|
56
|
+
*/
|
|
57
|
+
const getFieldKey = useCallback((field) => `user_defined[${field.column_name}]`, []);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns true if an error exists on the state for the passed field.
|
|
61
|
+
*
|
|
62
|
+
* @type {function(*): *}
|
|
63
|
+
*/
|
|
64
|
+
const isError = useCallback((field) => props.isError(getFieldKey(field)), [getFieldKey, props.isError]);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Changes the value for the passed item.
|
|
68
|
+
*
|
|
69
|
+
* @type {(function(*, *): void)|*}
|
|
70
|
+
*/
|
|
71
|
+
const onChange = useCallback((field, value) => {
|
|
72
|
+
props.onChange(JSON.stringify({ ...parsedValue, [field.column_name]: value }));
|
|
73
|
+
|
|
74
|
+
// Clear the validation error if one exists
|
|
75
|
+
if (props.onClearValidationError) {
|
|
76
|
+
props.onClearValidationError(getFieldKey(field));
|
|
77
|
+
}
|
|
78
|
+
}, [getFieldKey, parsedValue, props.onChange, props.onClearValidationError]);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Renders the passed item.
|
|
82
|
+
*
|
|
83
|
+
* @type {function(*): *}
|
|
84
|
+
*/
|
|
85
|
+
const renderItem = useCallback((field) => {
|
|
86
|
+
let rendered;
|
|
87
|
+
|
|
88
|
+
const fieldValue = parsedValue && parsedValue[field.column_name];
|
|
89
|
+
|
|
90
|
+
if (field.data_type === DataTypes.string) {
|
|
91
|
+
rendered = (
|
|
92
|
+
<Form.Input
|
|
93
|
+
error={isError(field)}
|
|
94
|
+
label={field.column_name}
|
|
95
|
+
required={field.required}
|
|
96
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
97
|
+
value={fieldValue}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (field.data_type === DataTypes.number) {
|
|
103
|
+
rendered = (
|
|
104
|
+
<Form.Input
|
|
105
|
+
error={isError(field)}
|
|
106
|
+
label={field.column_name}
|
|
107
|
+
required={field.required}
|
|
108
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
109
|
+
value={fieldValue}
|
|
110
|
+
type='number'
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (field.data_type === DataTypes.select) {
|
|
116
|
+
rendered = (
|
|
117
|
+
<Form.Dropdown
|
|
118
|
+
clearable
|
|
119
|
+
error={isError(field)}
|
|
120
|
+
label={field.column_name}
|
|
121
|
+
multiple={field.allow_multiple}
|
|
122
|
+
required={field.required}
|
|
123
|
+
options={_.map(field.options, (option) => ({ key: option, value: option, text: option }))}
|
|
124
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
125
|
+
selectOnBlur={false}
|
|
126
|
+
selection
|
|
127
|
+
value={fieldValue}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (field.data_type === DataTypes.text) {
|
|
133
|
+
rendered = (
|
|
134
|
+
<Form.TextArea
|
|
135
|
+
error={isError(field)}
|
|
136
|
+
label={field.column_name}
|
|
137
|
+
required={field.required}
|
|
138
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
139
|
+
value={fieldValue}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (field.data_type === DataTypes.date) {
|
|
145
|
+
rendered = (
|
|
146
|
+
<Form.Input
|
|
147
|
+
error={isError(field)}
|
|
148
|
+
label={field.column_name}
|
|
149
|
+
required={field.required}
|
|
150
|
+
>
|
|
151
|
+
<DatePicker
|
|
152
|
+
onChange={(date) => onChange(field, date && date.toString())}
|
|
153
|
+
value={fieldValue && new Date(fieldValue)}
|
|
154
|
+
/>
|
|
155
|
+
</Form.Input>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (field.data_type === DataTypes.boolean) {
|
|
160
|
+
rendered = (
|
|
161
|
+
<Form.Checkbox
|
|
162
|
+
checked={!!fieldValue}
|
|
163
|
+
error={isError(field)}
|
|
164
|
+
label={field.column_name}
|
|
165
|
+
onChange={(e, { checked }) => onChange(field, checked)}
|
|
166
|
+
/>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (field.data_type === DataTypes.richText) {
|
|
171
|
+
rendered = (
|
|
172
|
+
<Form.Input
|
|
173
|
+
error={isError(field)}
|
|
174
|
+
label={field.column_name}
|
|
175
|
+
required={field.required}
|
|
176
|
+
>
|
|
177
|
+
<RichTextArea
|
|
178
|
+
onChange={(value) => onChange(field, value)}
|
|
179
|
+
value={fieldValue}
|
|
180
|
+
/>
|
|
181
|
+
</Form.Input>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return rendered;
|
|
186
|
+
}, [parsedValue, isError, onChange]);
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Fetches the user defined fields when the component mounts.
|
|
190
|
+
*/
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
const params = {
|
|
193
|
+
defineable_id: props.defineableId,
|
|
194
|
+
defineable_type: props.defineableType
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
UserDefinedFieldsService
|
|
198
|
+
.fetchAll(params)
|
|
199
|
+
.then(({ data }) => setFields(data.user_defined_fields));
|
|
200
|
+
}, []);
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<>
|
|
204
|
+
{ _.map(fields, renderItem.bind(this)) }
|
|
205
|
+
</>
|
|
206
|
+
);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export default UserDefinedFieldsForm;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import { BooleanIcon, ListTable } from '@performant-software/semantic-components';
|
|
4
|
+
import { UserDefinedFieldsService } from '@performant-software/shared-components';
|
|
5
|
+
import React, { type ComponentType } from 'react';
|
|
6
|
+
import i18n from '../i18n/i18n';
|
|
7
|
+
import UserDefinedFieldModal from './UserDefinedFieldModal';
|
|
8
|
+
|
|
9
|
+
const UserDefinedFieldsList: ComponentType<any> = () => (
|
|
10
|
+
<ListTable
|
|
11
|
+
actions={[{
|
|
12
|
+
name: 'edit'
|
|
13
|
+
}, {
|
|
14
|
+
name: 'delete'
|
|
15
|
+
}]}
|
|
16
|
+
columns={[{
|
|
17
|
+
name: 'table_name',
|
|
18
|
+
label: i18n.t('UserDefinedFieldsList.columns.table')
|
|
19
|
+
}, {
|
|
20
|
+
name: 'column_name',
|
|
21
|
+
label: i18n.t('UserDefinedFieldsList.columns.name')
|
|
22
|
+
}, {
|
|
23
|
+
name: 'data_type',
|
|
24
|
+
label: i18n.t('UserDefinedFieldsList.columns.dataType')
|
|
25
|
+
}, {
|
|
26
|
+
name: 'required',
|
|
27
|
+
label: i18n.t('UserDefinedFieldsList.columns.required'),
|
|
28
|
+
render: (udf) => <BooleanIcon value={udf.required} />
|
|
29
|
+
}]}
|
|
30
|
+
collectionName='user_defined_fields'
|
|
31
|
+
modal={{
|
|
32
|
+
component: UserDefinedFieldModal
|
|
33
|
+
}}
|
|
34
|
+
onLoad={(params) => UserDefinedFieldsService.fetchAll(params)}
|
|
35
|
+
onSave={(udf) => UserDefinedFieldsService.save(udf)}
|
|
36
|
+
onDelete={(udf) => UserDefinedFieldsService.delete(udf)}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export default UserDefinedFieldsList;
|
package/src/i18n/en.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"UserDefinedFieldModal": {
|
|
3
|
+
"labels": {
|
|
4
|
+
"allowMultiple": "Allow multiple",
|
|
5
|
+
"dataType": "Data type",
|
|
6
|
+
"name": "Name",
|
|
7
|
+
"required": "Required",
|
|
8
|
+
"table": "Table"
|
|
9
|
+
},
|
|
10
|
+
"title": {
|
|
11
|
+
"add": "Add User Defined Field",
|
|
12
|
+
"edit": "Edit User Defined Field"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"UserDefinedFieldsEmbeddedList": {
|
|
16
|
+
"columns": {
|
|
17
|
+
"dataType": "Data type",
|
|
18
|
+
"name": "Name",
|
|
19
|
+
"required": "Required",
|
|
20
|
+
"table": "Table"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"UserDefinedFieldsList": {
|
|
24
|
+
"columns": {
|
|
25
|
+
"dataType": "Data type",
|
|
26
|
+
"name": "Name",
|
|
27
|
+
"required": "Required",
|
|
28
|
+
"table": "Table"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/i18n/i18n.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import i18next from 'i18next';
|
|
2
|
+
|
|
3
|
+
import en from './en.json';
|
|
4
|
+
|
|
5
|
+
const resources = {
|
|
6
|
+
en: {
|
|
7
|
+
translation: en
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const i18n = i18next.createInstance();
|
|
12
|
+
|
|
13
|
+
i18n
|
|
14
|
+
.init({
|
|
15
|
+
debug: true,
|
|
16
|
+
fallbackLng: 'en',
|
|
17
|
+
lng: 'en',
|
|
18
|
+
interpolation: {
|
|
19
|
+
escapeValue: false,
|
|
20
|
+
},
|
|
21
|
+
resources
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export default i18n;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
// Components
|
|
4
|
+
export { default as UserDefinedFieldModal } from './components/UserDefinedFieldModal';
|
|
5
|
+
export { default as UserDefinedFieldOptions } from './components/UserDefinedFieldOptions';
|
|
6
|
+
export { default as UserDefinedFieldsEmbeddedList } from './components/UserDefinedFieldsEmbeddedList';
|
|
7
|
+
export { default as UserDefinedFieldsForm } from './components/UserDefinedFieldsForm';
|
|
8
|
+
export { default as UserDefinedFieldsList } from './components/UserDefinedFieldsList';
|
|
9
|
+
|
|
10
|
+
// Services
|
|
11
|
+
export { default as UserDefinedFieldsService } from './services/UserDefinedFields';
|
|
12
|
+
|
|
13
|
+
// Transforms
|
|
14
|
+
export { default as UserDefinedFieldTransform } from './transforms/UserDefinedField';
|
|
15
|
+
export { default as UserDefinedFieldsTransform } from './transforms/UserDefinedFields';
|
|
16
|
+
|
|
17
|
+
// Utils
|
|
18
|
+
export { default as UserDefinedFields } from './utils/UserDefinedFields';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import { BaseService } from '@performant-software/shared-components';
|
|
4
|
+
import UserDefinedField from '../transforms/UserDefinedField';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Class responsible for handling all user defined field API requests.
|
|
8
|
+
*/
|
|
9
|
+
class UserDefinedFields extends BaseService {
|
|
10
|
+
/**
|
|
11
|
+
* Calls the /api/user_defined_fields/data_types API endpoint.
|
|
12
|
+
*
|
|
13
|
+
* @returns {Promise<AxiosResponse<any>>}
|
|
14
|
+
*/
|
|
15
|
+
fetchDataTypes(): Promise<any> {
|
|
16
|
+
return this.getAxios().get('/user_defined_fields/data_types');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Calls the /api/user_defined_fields/tables API endpoint.
|
|
21
|
+
*
|
|
22
|
+
* @returns {Promise<AxiosResponse<any>>}
|
|
23
|
+
*/
|
|
24
|
+
fetchTables(): Promise<any> {
|
|
25
|
+
return this.getAxios().get('/user_defined_fields/tables');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns the user defined fields base URL.
|
|
30
|
+
*
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
getBaseUrl(): string {
|
|
34
|
+
return '/user_defined_fields/user_defined_fields';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Returns the user defined fields transform object.
|
|
39
|
+
*
|
|
40
|
+
* @returns {UserDefinedField}
|
|
41
|
+
*/
|
|
42
|
+
getTransform(): any {
|
|
43
|
+
return UserDefinedField;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const UserDefinedFieldsService: UserDefinedFields = new UserDefinedFields();
|
|
48
|
+
export default UserDefinedFieldsService;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import { BaseTransform } from '@performant-software/shared-components';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Class for transforming user defined field objects for PUT/POST requests.
|
|
7
|
+
*/
|
|
8
|
+
class UserDefinedField extends BaseTransform {
|
|
9
|
+
/**
|
|
10
|
+
* Returns the user defined field payload keys.
|
|
11
|
+
*
|
|
12
|
+
* @returns {string[]}
|
|
13
|
+
*/
|
|
14
|
+
getPayloadKeys(): Array<string> {
|
|
15
|
+
return [
|
|
16
|
+
'table_name',
|
|
17
|
+
'column_name',
|
|
18
|
+
'data_type',
|
|
19
|
+
'required',
|
|
20
|
+
'allow_multiple',
|
|
21
|
+
'options'
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Returns the user defined field parameter name.
|
|
27
|
+
*
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
getParameterName(): string {
|
|
31
|
+
return 'user_defined_field';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const UserDefinedFieldTransform: UserDefinedField = new UserDefinedField();
|
|
36
|
+
export default UserDefinedFieldTransform;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import { NestedAttributesTransform } from '@performant-software/shared-components';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Class for transforming user defined field nested objects for PUT/POST requests.
|
|
7
|
+
*/
|
|
8
|
+
class UserDefinedFields extends NestedAttributesTransform {
|
|
9
|
+
/**
|
|
10
|
+
* Returns the user defined field payload keys.
|
|
11
|
+
*
|
|
12
|
+
* @returns {string[]}
|
|
13
|
+
*/
|
|
14
|
+
getPayloadKeys(): Array<string> {
|
|
15
|
+
return [
|
|
16
|
+
'id',
|
|
17
|
+
'table_name',
|
|
18
|
+
'column_name',
|
|
19
|
+
'data_type',
|
|
20
|
+
'required',
|
|
21
|
+
'allow_multiple',
|
|
22
|
+
'options',
|
|
23
|
+
'_destroy'
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Converts the user defined field collection for the passed object to form data to be sent on
|
|
29
|
+
* the request as nested attributes.
|
|
30
|
+
*
|
|
31
|
+
* @param formData
|
|
32
|
+
* @param prefix
|
|
33
|
+
* @param record
|
|
34
|
+
* @param collection
|
|
35
|
+
*/
|
|
36
|
+
toFormData(formData: FormData, prefix: string, record: any, collection: string = 'user_defined_fields'): void {
|
|
37
|
+
return super.toFormData(formData, prefix, record, collection);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Converts the user defined field collection for the passed object to JSON to be sent on
|
|
42
|
+
* the request as nested attributes.
|
|
43
|
+
*
|
|
44
|
+
* @param record
|
|
45
|
+
* @param collectionName
|
|
46
|
+
*
|
|
47
|
+
* @returns {{[p: string]: *}}
|
|
48
|
+
*/
|
|
49
|
+
toPayload(record: any, collectionName: string = 'user_defined_fields'): any {
|
|
50
|
+
return super.toPayload(record, collectionName);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const UserDefinedFieldsTransform: UserDefinedFields = new UserDefinedFields();
|
|
55
|
+
export default UserDefinedFieldsTransform;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import _ from 'underscore';
|
|
4
|
+
|
|
5
|
+
type Item = {
|
|
6
|
+
user_defined: ?string
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type ErrorProps = {
|
|
10
|
+
key: string,
|
|
11
|
+
error: Array<string>
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type ErrorReturn = {
|
|
15
|
+
[key: string]: string
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const KEY_USER_DEFINED = 'user_defined';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns the error for the passed user defined field.
|
|
22
|
+
*
|
|
23
|
+
* @param key
|
|
24
|
+
* @param error
|
|
25
|
+
*
|
|
26
|
+
* @returns {{}|{[p: string]: *}}
|
|
27
|
+
*/
|
|
28
|
+
const resolveError = ({ key, error }: ErrorProps): ErrorReturn => {
|
|
29
|
+
if (key !== KEY_USER_DEFINED || _.isEmpty(error)) {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const [field, message] = error;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
[`${KEY_USER_DEFINED}[${field}]`]: message
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Returns the value for the user defined field at the passed key for the passed item.
|
|
42
|
+
*
|
|
43
|
+
* @param item
|
|
44
|
+
* @param key
|
|
45
|
+
*
|
|
46
|
+
* @returns {*}
|
|
47
|
+
*/
|
|
48
|
+
const resolveValue = (item: Item, key: string): string => {
|
|
49
|
+
let userDefined = {};
|
|
50
|
+
|
|
51
|
+
if (_.isString(item.user_defined)) {
|
|
52
|
+
userDefined = JSON.parse(item.user_defined || '{}');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return userDefined[key];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default {
|
|
59
|
+
resolveError,
|
|
60
|
+
resolveValue
|
|
61
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import { UserDefinedFieldsService } from '@performant-software/shared-components';
|
|
4
|
+
import React, {
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useState,
|
|
8
|
+
type ComponentType
|
|
9
|
+
} from 'react';
|
|
10
|
+
import { Form, Modal } from 'semantic-ui-react';
|
|
11
|
+
import _ from 'underscore';
|
|
12
|
+
import i18n from '../i18n/i18n';
|
|
13
|
+
import UserDefinedFieldOptions from './UserDefinedFieldOptions';
|
|
14
|
+
|
|
15
|
+
const DataTypes = {
|
|
16
|
+
select: 'Select'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const UserDefinedFieldModal: ComponentType<any> = (props) => {
|
|
20
|
+
const [dataTypeOptions, setDataTypeOptions] = useState([]);
|
|
21
|
+
const [tableOptions, setTableOptions] = useState([]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Transforms the passed array of items into a Semantic UI option object.
|
|
25
|
+
*
|
|
26
|
+
* @type {function(*): *}
|
|
27
|
+
*/
|
|
28
|
+
const transformOptions = useCallback((items) => (
|
|
29
|
+
_.map(items, (item) => ({ key: item, value: item, text: item }))
|
|
30
|
+
), []);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Fetch the available tables and data types.
|
|
34
|
+
*/
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
UserDefinedFieldsService
|
|
37
|
+
.fetchTables()
|
|
38
|
+
.then(({ data }) => setTableOptions(transformOptions(data.tables)));
|
|
39
|
+
|
|
40
|
+
UserDefinedFieldsService
|
|
41
|
+
.fetchDataTypes()
|
|
42
|
+
.then(({ data }) => setDataTypeOptions(transformOptions(data.data_types)));
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Modal
|
|
47
|
+
as={Form}
|
|
48
|
+
centered={false}
|
|
49
|
+
open
|
|
50
|
+
>
|
|
51
|
+
<Modal.Header
|
|
52
|
+
content={props.item.id
|
|
53
|
+
? i18n.t('UserDefinedFieldModal.title.edit')
|
|
54
|
+
: i18n.t('UserDefinedFieldModal.title.add')}
|
|
55
|
+
/>
|
|
56
|
+
<Modal.Content>
|
|
57
|
+
<Form.Dropdown
|
|
58
|
+
clearable
|
|
59
|
+
error={props.isError('table_name')}
|
|
60
|
+
label={i18n.t('UserDefinedFieldModal.labels.table')}
|
|
61
|
+
onChange={props.onTextInputChange.bind(this, 'table_name')}
|
|
62
|
+
options={tableOptions}
|
|
63
|
+
required={props.isRequired('table_name')}
|
|
64
|
+
selection
|
|
65
|
+
selectOnBlur={false}
|
|
66
|
+
value={props.item.table_name || ''}
|
|
67
|
+
/>
|
|
68
|
+
<Form.Input
|
|
69
|
+
error={props.isError('column_name')}
|
|
70
|
+
label={i18n.t('UserDefinedFieldModal.labels.name')}
|
|
71
|
+
onChange={props.onTextInputChange.bind(this, 'column_name')}
|
|
72
|
+
required={props.isRequired('column_name')}
|
|
73
|
+
value={props.item.column_name || ''}
|
|
74
|
+
/>
|
|
75
|
+
<Form.Dropdown
|
|
76
|
+
clearable
|
|
77
|
+
error={props.isError('data_type')}
|
|
78
|
+
label={i18n.t('UserDefinedFieldModal.labels.dataType')}
|
|
79
|
+
onChange={props.onTextInputChange.bind(this, 'data_type')}
|
|
80
|
+
options={dataTypeOptions}
|
|
81
|
+
required={props.isRequired('data_type')}
|
|
82
|
+
selection
|
|
83
|
+
selectOnBlur={false}
|
|
84
|
+
value={props.item.data_type || ''}
|
|
85
|
+
/>
|
|
86
|
+
<Form.Group>
|
|
87
|
+
<Form.Checkbox
|
|
88
|
+
error={props.isError('required')}
|
|
89
|
+
checked={props.item.required}
|
|
90
|
+
label={i18n.t('UserDefinedFieldModal.labels.required')}
|
|
91
|
+
onChange={props.onCheckboxInputChange.bind(this, 'required')}
|
|
92
|
+
/>
|
|
93
|
+
{ props.item.data_type === DataTypes.select && (
|
|
94
|
+
<Form.Checkbox
|
|
95
|
+
error={props.isError('allow_multiple')}
|
|
96
|
+
checked={props.item.allow_multiple}
|
|
97
|
+
label={i18n.t('UserDefinedFieldModal.labels.allowMultiple')}
|
|
98
|
+
onChange={props.onCheckboxInputChange.bind(this, 'allow_multiple')}
|
|
99
|
+
/>
|
|
100
|
+
)}
|
|
101
|
+
</Form.Group>
|
|
102
|
+
{ props.item.data_type === DataTypes.select && (
|
|
103
|
+
<UserDefinedFieldOptions
|
|
104
|
+
options={props.item.options}
|
|
105
|
+
onChange={(options) => props.onSetState({ options })}
|
|
106
|
+
/>
|
|
107
|
+
)}
|
|
108
|
+
</Modal.Content>
|
|
109
|
+
{ props.children }
|
|
110
|
+
</Modal>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export default UserDefinedFieldModal;
|