Skip to content

Commit b3e7ae8

Browse files
committed
Better error reporting and group highlighting during term planting.
1 parent b11b07d commit b3e7ae8

File tree

4 files changed

+73
-25
lines changed

4 files changed

+73
-25
lines changed

src/components/Groups/GroupsTreeView/GroupsTreeList.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,18 @@ import PropTypes from 'prop-types';
44
import GroupsTreeNode from './GroupsTreeNode.js';
55

66
const GroupsTreeList = React.memo(
7-
({ groups, checkboxes, checked, setChecked, isExpanded = false, addAttribute, removeAttribute, locale }) => (
7+
({
8+
groups,
9+
checkboxes,
10+
highlight,
11+
checked,
12+
errors,
13+
setChecked,
14+
isExpanded = false,
15+
addAttribute,
16+
removeAttribute,
17+
locale,
18+
}) => (
819
<ul className="groupTree nav flex-column">
920
{groups.map(group => (
1021
<GroupsTreeNode
@@ -15,8 +26,10 @@ const GroupsTreeList = React.memo(
1526
removeAttribute={removeAttribute}
1627
locale={locale}
1728
checkboxes={checkboxes}
29+
highlight={highlight}
1830
checked={checked}
1931
setChecked={setChecked}
32+
errors={errors}
2033
/>
2134
))}
2235
</ul>
@@ -26,7 +39,9 @@ const GroupsTreeList = React.memo(
2639
GroupsTreeList.propTypes = {
2740
groups: PropTypes.array.isRequired,
2841
checkboxes: PropTypes.func,
42+
highlight: PropTypes.func,
2943
checked: PropTypes.object,
44+
errors: PropTypes.object,
3045
setChecked: PropTypes.func,
3146
isExpanded: PropTypes.bool,
3247
addAttribute: PropTypes.func,

src/components/Groups/GroupsTreeView/GroupsTreeNode.js

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Button from '../../widgets/TheButton';
88
import Confirm from '../../widgets/Confirm';
99
import GroupsTreeList from './GroupsTreeList.js';
1010
import GroupMembershipIcon from '../GroupMembershipIcon';
11-
import Icon, { AddIcon, CloseIcon, GroupIcon, LectureIcon, LoadingIcon, TermIcon } from '../../icons';
11+
import Icon, { AddIcon, CloseIcon, GroupIcon, LectureIcon, LoadingIcon, TermIcon, WarningIcon } from '../../icons';
1212
import { EMPTY_OBJ } from '../../../helpers/common.js';
1313

1414
const DEFAULT_ICON = ['far', 'square'];
@@ -48,7 +48,9 @@ const GroupsTreeNode = React.memo(
4848
({
4949
group,
5050
checkboxes = null,
51+
highlight = null,
5152
checked = null,
53+
errors = null,
5254
setChecked = null,
5355
isExpanded = false,
5456
addAttribute,
@@ -73,29 +75,38 @@ const GroupsTreeNode = React.memo(
7375
const hasCheckbox = checkboxes ? checkboxes(group) : false;
7476
const isChecked = checked ? Boolean(checked[id]) : false;
7577
const clickHandler = hasCheckbox ? () => setChecked(id, !isChecked) : () => setOpen(!isOpen);
78+
const error = (errors && errors[id]) || null;
79+
const highlightClass = highlight ? highlight(group) : '';
80+
const highlightIconClass = highlight ? highlight(group) : 'text-body-tertiary';
7681

7782
return (
7883
<li>
7984
<span onClick={clickHandler} className="clearfix">
80-
{hasCheckbox ? (
81-
<Icon
82-
icon={isChecked ? 'square-check' : DEFAULT_ICON}
83-
className="text-success clickable"
84-
gapRight={2}
85-
fixedWidth
86-
/>
87-
) : checkboxes ? (
88-
<Icon icon="square" className="text-body-secondary opacity-25" gapRight={2} fixedWidth />
89-
) : (
90-
<Icon
91-
icon={leafNode ? DEFAULT_ICON : isOpen ? 'minus-square' : 'plus-square'}
92-
className="text-body-secondary clickable"
93-
gapRight={2}
94-
fixedWidth
95-
/>
96-
)}
85+
<span className={error ? 'text-danger fw-bold' : highlightClass}>
86+
{hasCheckbox ? (
87+
<Icon
88+
icon={isChecked ? 'square-check' : DEFAULT_ICON}
89+
className="text-success clickable"
90+
gapRight={2}
91+
fixedWidth
92+
/>
93+
) : checkboxes ? (
94+
<Icon icon="square" className={`opacity-50 ${highlightIconClass}`} gapRight={2} fixedWidth />
95+
) : (
96+
<Icon
97+
icon={leafNode ? DEFAULT_ICON : isOpen ? 'minus-square' : 'plus-square'}
98+
className={`clickable ${highlightIconClass}`}
99+
gapRight={2}
100+
fixedWidth
101+
/>
102+
)}
103+
104+
{error && (
105+
<WarningIcon gapRight className="text-danger" tooltip={error} tooltipId={`error-tooltip-${id}`} />
106+
)}
97107

98-
{getLocalizedName(name, id, locale)}
108+
{getLocalizedName(name, id, locale)}
109+
</span>
99110

100111
{admins && admins.length > 0 && (
101112
<span className="ps-2">
@@ -216,8 +227,10 @@ const GroupsTreeNode = React.memo(
216227
removeAttribute={removeAttribute}
217228
locale={locale}
218229
checkboxes={checkboxes}
230+
highlight={highlight}
219231
checked={checked}
220232
setChecked={setChecked}
233+
errors={errors}
221234
/>
222235
</Collapse>
223236
)}
@@ -239,7 +252,9 @@ GroupsTreeNode.propTypes = {
239252
pending: PropTypes.bool,
240253
}),
241254
checkboxes: PropTypes.func,
255+
highlight: PropTypes.func,
242256
checked: PropTypes.object,
257+
errors: PropTypes.object,
243258
setChecked: PropTypes.func,
244259
isExpanded: PropTypes.bool,
245260
addAttribute: PropTypes.func,

src/components/Groups/GroupsTreeView/GroupsTreeView.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ const GroupsTreeView = ({
1414
groups,
1515
filter = null,
1616
checkboxes = null,
17+
highlight = null,
1718
checked = null,
1819
setChecked = null,
1920
isExpanded = false,
2021
addAttribute = null,
2122
removeAttribute = null,
23+
errors = null,
2224
intl: { locale },
2325
}) => {
2426
const topLevelGroups = getTopLevelGroups(groups, locale, filter);
@@ -33,8 +35,10 @@ const GroupsTreeView = ({
3335
addAttribute={addAttribute}
3436
removeAttribute={removeAttribute}
3537
checkboxes={checkboxes}
38+
highlight={highlight}
3639
checked={checked}
3740
setChecked={setChecked}
41+
errors={errors}
3842
/>
3943
);
4044
};
@@ -43,7 +47,9 @@ GroupsTreeView.propTypes = {
4347
groups: PropTypes.object.isRequired, // plain object with groupId -> group mappings
4448
filter: PropTypes.func,
4549
checkboxes: PropTypes.func,
50+
highlight: PropTypes.func,
4651
checked: PropTypes.object,
52+
errors: PropTypes.object,
4753
setChecked: PropTypes.func,
4854
isExpanded: PropTypes.bool,
4955
addAttribute: PropTypes.func,

src/pages/GroupsSuperadmin/GroupsSuperadmin.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
33
import ImmutablePropTypes from 'react-immutable-proptypes';
44
import { connect } from 'react-redux';
55
import { Modal, Badge, Dropdown, ButtonGroup, Row, Col } from 'react-bootstrap';
6-
import { FormattedMessage } from 'react-intl';
6+
import { FormattedMessage, injectIntl } from 'react-intl';
77
import { FORM_ERROR } from 'final-form';
88
import { lruMemoize } from 'reselect';
99

@@ -49,6 +49,10 @@ const plantingCheckboxSelector = lruMemoize(
4949
!group.children.some(g => g.attributes?.term?.includes(`${term.year}-${term.term}`))
5050
);
5151

52+
const highlightClassGenerator = lruMemoize(
53+
term => group => (group.attributes?.term?.includes(`${term.year}-${term.term}`) ? 'text-success fw-bold' : '')
54+
);
55+
5256
class GroupsSuperadmin extends Component {
5357
state = {
5458
modalGroup: null,
@@ -61,7 +65,7 @@ class GroupsSuperadmin extends Component {
6165
plantGroupsCount: 0,
6266
plantGroupsPending: false,
6367
plantGroupsErrors: null,
64-
plantedGroups: 5,
68+
plantedGroups: 0,
6569
};
6670

6771
openModalGroup = modalGroup =>
@@ -142,7 +146,11 @@ class GroupsSuperadmin extends Component {
142146
};
143147

144148
plantGroups = async term => {
145-
const { createTermGroup, reloadGroups } = this.props;
149+
const {
150+
createTermGroup,
151+
reloadGroups,
152+
intl: { formatMessage },
153+
} = this.props;
146154
if (this.state.plantGroupsCount === 0) {
147155
return;
148156
}
@@ -166,7 +174,7 @@ class GroupsSuperadmin extends Component {
166174
await promises[id];
167175
++plantedGroups;
168176
} catch (err) {
169-
plantGroupsErrors[id] = getErrorMessage(err);
177+
plantGroupsErrors[id] = getErrorMessage(formatMessage)(err);
170178
}
171179
}
172180

@@ -320,6 +328,9 @@ class GroupsSuperadmin extends Component {
320328
checkboxes={
321329
this.state.plantTexts ? plantingCheckboxSelector(this.state.plantTerm || terms[0]) : null
322330
}
331+
highlight={
332+
this.state.plantTexts ? highlightClassGenerator(this.state.plantTerm || terms[0]) : null
333+
}
323334
checked={this.state.plantGroups}
324335
addAttribute={!this.state.plantTexts ? this.openModalGroup : null}
325336
removeAttribute={!this.state.plantTexts ? removeAttribute : null}
@@ -493,6 +504,7 @@ GroupsSuperadmin.propTypes = {
493504
removeAttribute: PropTypes.func.isRequired,
494505
createTermGroup: PropTypes.func.isRequired,
495506
reloadGroups: PropTypes.func.isRequired,
507+
intl: PropTypes.object,
496508
};
497509

498510
export default connect(
@@ -513,4 +525,4 @@ export default connect(
513525
createTermGroup: (parentId, term, texts) => dispatch(createTermGroup(parentId, term, texts)),
514526
reloadGroups: () => dispatch(fetchAllGroups()),
515527
})
516-
)(GroupsSuperadmin);
528+
)(injectIntl(GroupsSuperadmin));

0 commit comments

Comments
 (0)