Skip to content

Commit 1075a77

Browse files
committed
Attaching operations add/remove attribute to group tree view.
1 parent 89bcb58 commit 1075a77

File tree

4 files changed

+58
-26
lines changed

4 files changed

+58
-26
lines changed

src/components/Groups/GroupsTreeView/GroupsTreeNode.js

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { Badge, OverlayTrigger, Popover } from 'react-bootstrap';
55
import Collapse from 'react-collapse';
66

77
import Button from '../../widgets/TheButton';
8+
import Confirm from '../../widgets/Confirm';
89
import GroupsTreeList from './GroupsTreeList.js';
910
import GroupMembershipIcon from '../GroupMembershipIcon';
10-
import Icon, { CloseIcon, GroupIcon, LectureIcon, LoadingIcon, TermIcon } from '../../icons';
11+
import Icon, { AddIcon, CloseIcon, GroupIcon, LectureIcon, LoadingIcon, TermIcon } from '../../icons';
1112
import withLinks from '../../../helpers/withLinks.js';
1213
import { EMPTY_OBJ } from '../../../helpers/common.js';
1314

@@ -136,31 +137,46 @@ const GroupsTreeNode = React.memo(({ group, isExpanded = false, addAttribute, re
136137
<GroupMembershipIcon id={id} membership={membership} gapLeft={2} />
137138
{pending && <LoadingIcon gapLeft={2} />}
138139

140+
{addAttribute && (
141+
<span className="float-end" onClick={clickEventDissipator}>
142+
<Button size="xs" variant="success" className="ms-3" disabled={pending} onClick={() => addAttribute(group)}>
143+
<AddIcon gapRight />
144+
<FormattedMessage id="app.groupsTreeView.addAttribute" defaultMessage="Add" />
145+
</Button>
146+
</span>
147+
)}
148+
139149
{attributes && Object.keys(attributes).length > 0 && (
140150
<span className="float-end" onClick={clickEventDissipator}>
141151
{Object.keys(attributes).map(key =>
142152
attributes[key].map(value => (
143-
<Badge key={value} bg={KNOWN_ATTR_KEYS[key] || 'secondary'} className="ms-1">
153+
<Badge
154+
key={value}
155+
bg={KNOWN_ATTR_KEYS[key] || 'secondary'}
156+
className={'ms-1' + (pending ? ' opacity-25' : '')}>
144157
{ATTR_ICONS[key]}
145158
{!KNOWN_ATTR_KEYS[key] && `${key}: `}
146159
{value}
147-
{removeAttribute && <CloseIcon gapLeft onClick={() => removeAttribute(id, key, value)} />}
160+
161+
{removeAttribute && !pending && (
162+
<Confirm
163+
id={id}
164+
onConfirmed={() => removeAttribute(id, key, value)}
165+
question={
166+
<FormattedMessage
167+
id="app.groupsTreeView.removeAttributeConfirm"
168+
defaultMessage="Are you sure you want to remove this attribute?"
169+
/>
170+
}>
171+
<CloseIcon gapLeft className="clickable" />
172+
</Confirm>
173+
)}
174+
{pending && <LoadingIcon gapLeft />}
148175
</Badge>
149176
))
150177
)}
151178
</span>
152179
)}
153-
154-
{addAttribute && (
155-
<span className="float-end" onClick={clickEventDissipator}>
156-
<Button size="xs" variant="success" onClick={() => addAttribute(id)}>
157-
<Icon icon="plus" fixedWidth />
158-
<span className="d-none d-sm-inline">
159-
<FormattedMessage id="app.groupsTreeView.addAttribute" defaultMessage="Add attribute" />
160-
</span>
161-
</Button>
162-
</span>
163-
)}
164180
</span>
165181

166182
{!leafNode && (

src/locales/cs.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,13 @@
111111
"app.groupsTeacher.selectParentGroupForCreating": "Vyberte rodičovskou skupinu pro nově vytvářenou skupinu",
112112
"app.groupsTeacher.selectedEvent": "Vybraná událost",
113113
"app.groupsTeacher.title": "Vytvořit skupiny pro předměty ze SISu",
114-
"app.groupsTreeView.addAttribute": "Přidat atribut",
114+
"app.groupsTreeView.addAttribute": "Přidat",
115115
"app.groupsTreeView.adminPopover.title": "Administrátoři skupiny",
116116
"app.groupsTreeView.adminsCount": "{count} {count, plural, one {administrátor} =2 {administrátoři} =3 {administrátoři} =4 {administrátoři} other {administrátorů}}",
117117
"app.groupsTreeView.empty": "Nejsou dostupné žádné skupiny.",
118118
"app.groupsTreeView.examTooltip": "Skupina pro zkoušky",
119119
"app.groupsTreeView.organizationalTooltip": "Skupina je organizační (nemá žádné studenty ani zadané úlohy)",
120+
"app.groupsTreeView.removeAttributeConfirm": "Jste si jisti, že chcete odstranit tento atribut?",
120121
"app.header.languageSwitching.translationTitle": "Jazyková verze",
121122
"app.headerNotification.copiedToClipboard": "Zkopírováno do schránky.",
122123
"app.homepage.about": "Toto rozšíření ReCodExu má na starost datovou integraci mezi ReCodExem a Studijním Informačním Systémem (SIS) Karlovy Univerzity. Prosíme vyberte si jednu ze stránek níže.",
@@ -256,4 +257,4 @@
256257
"generic.reset": "Resetovat",
257258
"generic.save": "Uložit",
258259
"generic.search": "Vyhledat"
259-
}
260+
}

src/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,13 @@
111111
"app.groupsTeacher.selectParentGroupForCreating": "Select Parent Group for a New Group",
112112
"app.groupsTeacher.selectedEvent": "Selected event",
113113
"app.groupsTeacher.title": "Create Groups for SIS Courses",
114-
"app.groupsTreeView.addAttribute": "Add attribute",
114+
"app.groupsTreeView.addAttribute": "Add",
115115
"app.groupsTreeView.adminPopover.title": "Group administrators",
116116
"app.groupsTreeView.adminsCount": "{count} {count, plural, one {admin} other {admins}}",
117117
"app.groupsTreeView.empty": "No groups available",
118118
"app.groupsTreeView.examTooltip": "Exam group",
119119
"app.groupsTreeView.organizationalTooltip": "The group is organizational (it does not have any students or assignments)",
120+
"app.groupsTreeView.removeAttributeConfirm": "Are you sure you want to remove this attribute?",
120121
"app.header.languageSwitching.translationTitle": "Translation",
121122
"app.headerNotification.copiedToClipboard": "Copied to clipboard.",
122123
"app.homepage.about": "This ReCodEx extension handles data integration and exchange between ReCodEx and Charles University Student Information System (SIS). Please choose one of the pages below.",

src/pages/GroupsSuperadmin/GroupsSuperadmin.js

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import Page from '../../components/layout/Page';
99
import Box from '../../components/widgets/Box';
1010
import GroupsTreeView from '../../components/Groups/GroupsTreeView';
1111
import Button, { TheButtonGroup } from '../../components/widgets/TheButton';
12-
import { CloseIcon, ManagementIcon } from '../../components/icons';
12+
import { CloseIcon, GroupIcon, ManagementIcon } from '../../components/icons';
1313
// import ResourceRenderer from '../../components/helpers/ResourceRenderer';
1414

15-
import { fetchAllGroups } from '../../redux/modules/groups.js';
15+
import { fetchAllGroups, addGroupAttribute, removeGroupAttribute } from '../../redux/modules/groups.js';
1616
import { fetchUserIfNeeded } from '../../redux/modules/users.js';
1717
import { fetchAllTerms } from '../../redux/modules/terms.js';
1818
import { loggedInUserIdSelector } from '../../redux/selectors/auth.js';
@@ -43,18 +43,18 @@ const getGroupAdmins = group => {
4343

4444
class GroupsSuperadmin extends Component {
4545
state = {
46+
modalGroup: null,
4647
modalPending: false,
4748
modalError: null,
48-
selectGroups: null,
49-
selectedGroupId: '',
5049
};
5150

51+
openModal = modalGroup => this.setState({ modalGroup, modalPending: false, modalError: null });
52+
5253
closeModal = () =>
5354
this.setState({
55+
modalGroup: null,
5456
modalPending: false,
5557
modalError: null,
56-
selectGroups: null,
57-
selectedGroupId: '',
5858
});
5959

6060
// handleGroupChange = ev => this.setState({ selectedGroupId: ev.target.value });
@@ -83,7 +83,7 @@ class GroupsSuperadmin extends Component {
8383
]);
8484

8585
render() {
86-
const { loggedInUser, groups } = this.props;
86+
const { loggedInUser, groups, removeAttribute } = this.props;
8787

8888
return (
8989
<Page
@@ -98,10 +98,15 @@ class GroupsSuperadmin extends Component {
9898
<Box
9999
unlimitedHeight
100100
title={<FormattedMessage id="app.groupsSupervisor.currentlyManagedGroups" defaultMessage="Groups" />}>
101-
<GroupsTreeView groups={groups} />
101+
<GroupsTreeView groups={groups} addAttribute={this.openModal} removeAttribute={removeAttribute} />
102102
</Box>
103103

104-
<Modal show={false} backdrop="static" size="xl" fullscreen="xl-down" onHide={this.closeModal}>
104+
<Modal
105+
show={this.state.modalGroup !== null}
106+
backdrop="static"
107+
size="xl"
108+
fullscreen="xl-down"
109+
onHide={this.closeModal}>
105110
<Modal.Header closeButton={!this.state.modalPending}>
106111
<Modal.Title>
107112
<FormattedMessage
@@ -112,6 +117,11 @@ class GroupsSuperadmin extends Component {
112117
</Modal.Header>
113118

114119
<Modal.Body>
120+
<h5>
121+
<GroupIcon gapRight={3} className="text-muted" />
122+
{this.state.modalGroup?.fullName}
123+
</h5>
124+
115125
{this.state.modalError && (
116126
<Callout variant="danger" className="mt-3">
117127
<p>
@@ -154,6 +164,8 @@ GroupsSuperadmin.propTypes = {
154164
terms: ImmutablePropTypes.list,
155165
groups: ImmutablePropTypes.map,
156166
loadAsync: PropTypes.func.isRequired,
167+
addAttribute: PropTypes.func.isRequired,
168+
removeAttribute: PropTypes.func.isRequired,
157169
};
158170

159171
export default connect(
@@ -166,5 +178,7 @@ export default connect(
166178
dispatch => ({
167179
loadAsync: (userId, expiration = DEFAULT_EXPIRATION) =>
168180
GroupsSuperadmin.loadAsync({ userId }, dispatch, expiration),
181+
addAttribute: (groupId, key, value) => dispatch(addGroupAttribute(groupId, key, value)),
182+
removeAttribute: (groupId, key, value) => dispatch(removeGroupAttribute(groupId, key, value)),
169183
})
170184
)(GroupsSuperadmin);

0 commit comments

Comments
 (0)