Skip to content

Commit bec4c29

Browse files
committed
composeFields: improve user experience
* chore: code cleanup
1 parent e27a4d1 commit bec4c29

File tree

4 files changed

+150
-75
lines changed

4 files changed

+150
-75
lines changed

package-lock.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib/forms/widgets/custom_fields/ComposeFields.js

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import _isEmpty from "lodash/isEmpty";
12
import React, { Component } from "react";
23
import PropTypes from "prop-types";
34
import { Divider } from "semantic-ui-react";
@@ -8,9 +9,11 @@ import { Extensions } from "./Extensions";
89
export class ComposeFields extends Component {
910
constructor(props) {
1011
super(props);
11-
const { composeSections } = this.props;
12-
13-
this.state = { sections: composeSections, tempFields: [] };
12+
const { composeSections, record } = props;
13+
const filled = Object.keys(record.custom_fields).map(
14+
(key) => `custom_fields.${key}`
15+
);
16+
this.state = { sections: composeSections, tempFields: [], recordFields: filled };
1417
this.fieldsCfg = this.getFieldsConfig(composeSections);
1518
this.sectionsList = composeSections.map((section) => section.section);
1619
}
@@ -29,15 +32,13 @@ export class ComposeFields extends Component {
2932

3033
getFieldsWithValues = (sectionFields) => {
3134
const { record } = this.props;
32-
const { tempFields } = this.state;
35+
const { tempFields, recordFields } = this.state;
3336
const filledFields = [];
37+
if (!record.custom_fields) {
38+
return [];
39+
}
3440
for (const field of sectionFields) {
35-
if (
36-
Object.keys(record.custom_fields).includes(
37-
field.key.replace("custom_fields.", "")
38-
) ||
39-
tempFields.includes(field)
40-
) {
41+
if (recordFields.includes(field.key) || tempFields.includes(field)) {
4142
filledFields.push(field);
4243
}
4344
}
@@ -53,44 +54,61 @@ export class ComposeFields extends Component {
5354
}
5455
};
5556

56-
addFieldCallback = (field) => {
57+
addFieldCallback = (fields) => {
5758
const { sections: prevSections, tempFields: prevTempFields } = this.state;
59+
5860
const sections = [...prevSections];
59-
const sectionToUpdate = this.getSectionOfField(field);
60-
for (const section of sections) {
61-
if (section.section === sectionToUpdate) {
62-
section["fields"] = [...section.fields, field].sort((a, b) =>
63-
a.key.localeCompare(b.key)
64-
);
61+
for (const field of fields) {
62+
const sectionToUpdate = this.getSectionOfField(field);
63+
for (const section of sections) {
64+
if (section.section === sectionToUpdate) {
65+
section["fields"] = [...section.fields, field].sort((a, b) =>
66+
a.key.localeCompare(b.key)
67+
);
68+
}
6569
}
6670
}
67-
this.setState({ sections: [...sections], tempFields: [...prevTempFields, field] });
71+
this.setState({
72+
sections: [...sections],
73+
tempFields: [...prevTempFields, ...fields],
74+
});
6875
};
6976

7077
render() {
7178
const { templateLoaders, record } = this.props;
72-
const { sections } = this.state;
79+
const { sections, tempFields, recordFields } = this.state;
80+
const existingFields = [
81+
...Object.entries(tempFields).map(([key, value]) => value.key),
82+
...recordFields,
83+
];
7384

7485
return (
7586
<AccordionField key="compose fields" label="Domain specific fields" active>
76-
{sections.map(({ fields, paths, ...sectionConfig }) => (
77-
<div key={sectionConfig.section} className="rel-mb-2">
78-
<FieldLabel
79-
htmlFor={sectionConfig.section}
80-
icon={sectionConfig.icon}
81-
label={sectionConfig.section}
82-
/>
83-
<Divider fitted className="rel-mb-1" />
84-
<div className="rel-ml-1">{this.getFieldsWithValues(fields)}</div>
85-
</div>
86-
))}
87+
{sections.map(({ fields, paths, ...sectionConfig }) => {
88+
const recordCustomFields = this.getFieldsWithValues(fields);
89+
if (_isEmpty(recordCustomFields)) {
90+
return undefined;
91+
}
92+
return (
93+
<div key={sectionConfig.section} className="rel-mb-2">
94+
<FieldLabel
95+
htmlFor={sectionConfig.section}
96+
icon={sectionConfig.icon}
97+
label={sectionConfig.section}
98+
/>
99+
<Divider fitted className="rel-mb-1" />
100+
<div className="rel-ml-1">{recordCustomFields}</div>
101+
</div>
102+
);
103+
})}
87104
<Extensions
88105
fieldPath="custom_fields"
89106
{...this.fieldsCfg}
90107
templateLoaders={templateLoaders}
91108
addFieldCallback={this.addFieldCallback}
92109
sections={this.sectionsList}
93110
record={record}
111+
existingFields={existingFields}
94112
/>
95113
</AccordionField>
96114
);

src/lib/forms/widgets/custom_fields/Extensions.js

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@ import PropTypes from "prop-types";
1616
export class Extensions extends Component {
1717
constructor(props) {
1818
super(props);
19+
const { existingFields } = props;
1920
this.state = {
2021
modalOpen: false,
2122
selectedField: undefined,
2223
selectedFieldTarget: undefined,
23-
fields: [],
24+
addFields: [],
25+
existingFields: [...existingFields],
26+
loading: false,
2427
};
2528
}
29+
2630
handleModalOpen = () => {
2731
this.setState({ modalOpen: true });
2832
};
@@ -36,6 +40,7 @@ export class Extensions extends Component {
3640
if (previousSelected) {
3741
previousSelected.classList.toggle("selected-background");
3842
}
43+
e.currentTarget.classList.toggle("selected-background");
3944
const newField = {
4045
field: fieldName,
4146
props: { ...field },
@@ -45,23 +50,50 @@ export class Extensions extends Component {
4550
selectedField: { ...newField },
4651
selectedFieldTarget: e.currentTarget,
4752
});
48-
e.currentTarget.classList.toggle("selected-background");
4953
};
5054

5155
handleAddField = async (withClose = false) => {
52-
const { selectedField } = this.state;
56+
const {
57+
selectedField,
58+
addFields: prevFields,
59+
selectedFieldTarget,
60+
existingFields: prevExisting,
61+
} = this.state;
5362
const { fieldPath, templateLoaders, addFieldCallback } = this.props;
63+
this.setState({ loading: true });
5464
const field = await importWidget(templateLoaders, {
5565
...selectedField,
5666
fieldPath: `${fieldPath}.${selectedField.field}`,
5767
});
58-
const { fields: prevFields } = this.state;
59-
this.setState({ fields: [...prevFields, field] });
60-
if (withClose) {
61-
this.handleModalClosed();
62-
}
63-
this.setState({ selectedField: undefined, selectedFieldTarget: undefined });
64-
addFieldCallback(field);
68+
69+
const performCallback = (selectedFieldTarget) => {
70+
const { addFields } = this.state;
71+
72+
if (withClose) {
73+
addFieldCallback(addFields);
74+
this.setState({ addFields: [], existingFields: [] });
75+
this.handleModalClosed();
76+
}
77+
};
78+
selectedFieldTarget.classList.toggle("selected-background");
79+
this.setState(
80+
{
81+
addFields: [...prevFields, field],
82+
existingFields: [...prevExisting, field.key],
83+
selectedField: undefined,
84+
selectedFieldTarget: undefined,
85+
loading: false,
86+
},
87+
() => performCallback(selectedFieldTarget)
88+
);
89+
};
90+
91+
handleCancel = () => {
92+
const { addFields } = this.state;
93+
const { addFieldCallback } = this.props;
94+
addFieldCallback(addFields);
95+
this.setState({ addFields: [] });
96+
this.handleModalClosed();
6597
};
6698

6799
render() {
@@ -73,10 +105,10 @@ export class Extensions extends Component {
73105
templateLoaders,
74106
addFieldCallback,
75107
sections,
108+
existingFields: selected,
76109
...fieldsList
77110
} = this.props;
78-
const { modalOpen, fields } = this.state;
79-
111+
const { modalOpen, existingFields, loading } = this.state;
80112
return (
81113
<>
82114
<Divider />
@@ -89,23 +121,35 @@ export class Extensions extends Component {
89121
<ListAndFilterCustomFields
90122
fieldPath={fieldPath}
91123
handleSelectField={this.handleSelectField}
92-
alreadyAddedFields={fields}
124+
alreadyAddedFields={existingFields}
93125
fieldsList={fieldsList}
94126
sections={sections}
95127
/>
96128
<Modal.Actions>
97129
<Button
98-
onClick={this.handleModalClosed}
130+
onClick={this.handleCancel}
99131
floated="left"
100132
icon="cancel"
101133
labelPosition="left"
102134
content="Cancel"
103135
/>
104-
<Button icon labelPosition="left" onClick={this.handleAddField}>
136+
<Button
137+
icon
138+
labelPosition="left"
139+
onClick={() => this.handleAddField(false)}
140+
disabled={loading}
141+
loading={loading}
142+
>
105143
<Icon name="plus" />
106144
Add field and continue
107145
</Button>
108-
<Button icon labelPosition="left" onClick={() => this.handleAddField(true)}>
146+
<Button
147+
icon
148+
labelPosition="left"
149+
onClick={() => this.handleAddField(true)}
150+
disabled={loading}
151+
loading={loading}
152+
>
109153
<Icon name="plus" />
110154
Add field and close
111155
</Button>
@@ -124,6 +168,7 @@ Extensions.propTypes = {
124168
templateLoaders: PropTypes.array.isRequired,
125169
addFieldCallback: PropTypes.func.isRequired,
126170
sections: PropTypes.array,
171+
existingFields: PropTypes.array.isRequired,
127172
};
128173

129174
Extensions.defaultProps = {

0 commit comments

Comments
 (0)