Skip to content

Commit dfda8d0

Browse files
committed
Transitively archived groups will show links to directly archived ancestor groups.
1 parent 6290338 commit dfda8d0

File tree

6 files changed

+79
-23
lines changed

6 files changed

+79
-23
lines changed

src/components/Groups/GroupArchivedWarning/GroupArchivedWarning.js

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,25 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import { Row, Col } from 'react-bootstrap';
44
import { FormattedMessage } from 'react-intl';
5+
import { Link } from 'react-router-dom';
6+
7+
import GroupsNameContainer from '../../../containers/GroupsNameContainer';
58
import Callout from '../../widgets/Callout';
69

7-
const GroupArchivedWarning = ({ archived, directlyArchived }) =>
8-
archived && (
10+
const GroupArchivedWarning = ({
11+
archived = false,
12+
directlyArchived = false,
13+
parentGroupsIds = [],
14+
groupsDataAccessor = null,
15+
linkFactory = null,
16+
}) => {
17+
const directlyArchivedAncestors =
18+
archived &&
19+
!directlyArchived &&
20+
groupsDataAccessor &&
21+
parentGroupsIds.filter(id => groupsDataAccessor(id) && groupsDataAccessor(id).get('directlyArchived'));
22+
23+
return archived ? (
924
<Row>
1025
<Col lg={12}>
1126
<Callout variant="warning">
@@ -15,19 +30,40 @@ const GroupArchivedWarning = ({ archived, directlyArchived }) =>
1530
/>
1631
<br />
1732
{!directlyArchived && (
18-
<FormattedMessage
19-
id="app.group.notDirectlyArchived"
20-
defaultMessage="This group inherits the archived flag from one of its parent groups."
21-
/>
33+
<>
34+
<FormattedMessage
35+
id="app.group.notDirectlyArchived"
36+
defaultMessage="This group inherits the archived flag from one of its parent groups."
37+
/>
38+
{directlyArchivedAncestors && directlyArchivedAncestors.length > 0 && (
39+
<ul>
40+
{directlyArchivedAncestors.map(id => (
41+
<li key={id}>
42+
{linkFactory ? (
43+
<Link to={linkFactory(id)}>
44+
<GroupsNameContainer groupId={id} fullName />
45+
</Link>
46+
) : (
47+
<GroupsNameContainer groupId={id} fullName links />
48+
)}
49+
</li>
50+
))}
51+
</ul>
52+
)}
53+
</>
2254
)}
2355
</Callout>
2456
</Col>
2557
</Row>
26-
);
58+
) : null;
59+
};
2760

2861
GroupArchivedWarning.propTypes = {
29-
archived: PropTypes.bool.isRequired,
30-
directlyArchived: PropTypes.bool.isRequired,
62+
archived: PropTypes.bool,
63+
directlyArchived: PropTypes.bool,
64+
parentGroupsIds: PropTypes.array,
65+
groupsDataAccessor: PropTypes.func,
66+
linkFactory: PropTypes.func,
3167
};
3268

3369
export default GroupArchivedWarning;

src/locales/cs.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@
894894
"app.group.info": "Info skupiny",
895895
"app.group.isPublicWarning": "Skupina je veřejná, což znamená, že ji vidí všichni a kdokoli se do ní může přihlásit.",
896896
"app.group.mailtoAll": "Mail všem studentům",
897-
"app.group.notDirectlyArchived": "Tato skupina zdědila příznak archivace od svých rodičů.",
897+
"app.group.notDirectlyArchived": "Tato skupina zdědila příznak archivace od jednoho ze svých rodičů.",
898898
"app.group.organizationalExplain": "Tato skupina je organizační, takže nemůže mít žádné studenty ani zadané úlohy. Nicméně úlohy spojené s touto skupinou je možné zadat v podskupinách.",
899899
"app.group.setRoot": "Vybrat",
900900
"app.group.spervisorsView.addStudent": "Přidat studenta",

src/pages/EditGroup/EditGroup.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import ArchiveGroupButtonContainer from '../../containers/ArchiveGroupButtonCont
1717
import DeleteGroupButtonContainer from '../../containers/DeleteGroupButtonContainer';
1818
import Box from '../../components/widgets/Box';
1919
import Callout from '../../components/widgets/Callout';
20+
import GroupArchivedWarning from '../../components/Groups/GroupArchivedWarning/GroupArchivedWarning';
2021
import { BanIcon, InfoIcon, EditGroupIcon } from '../../components/icons';
2122

2223
import { fetchGroup, fetchGroupIfNeeded, editGroup, relocateGroup } from '../../redux/modules/groups';
@@ -32,7 +33,6 @@ import { getLocalizedTextsInitialValues, transformLocalizedTextsFormData } from
3233

3334
import withLinks from '../../helpers/withLinks';
3435
import { hasPermissions } from '../../helpers/common';
35-
import GroupArchivedWarning from '../../components/Groups/GroupArchivedWarning/GroupArchivedWarning';
3636

3737
const canRelocate = group => hasPermissions(group, 'relocate') && !group.archived;
3838

@@ -67,13 +67,13 @@ class EditGroup extends Component {
6767
groups,
6868
groupsAccessor,
6969
isSuperAdmin,
70-
links: { GROUP_INFO_URI_FACTORY, INSTANCE_URI_FACTORY },
7170
editGroup,
7271
relocateGroup,
7372
hasThreshold,
7473
canViewParentDetail,
7574
instanceId,
7675
reload,
76+
links: { GROUP_INFO_URI_FACTORY, INSTANCE_URI_FACTORY, GROUP_EDIT_URI_FACTORY },
7777
intl: { locale },
7878
} = this.props;
7979

@@ -103,7 +103,7 @@ class EditGroup extends Component {
103103
</Row>
104104
)}
105105

106-
<GroupArchivedWarning archived={group.archived} directlyArchived={group.directlyArchived} />
106+
<GroupArchivedWarning {...group} groupsDataAccessor={groupsAccessor} linkFactory={GROUP_EDIT_URI_FACTORY} />
107107

108108
{hasPermissions(group, 'update') && (
109109
<>

src/pages/GroupDetail/GroupDetail.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { loggedInUserIdSelector } from '../../redux/selectors/auth';
3838
import { loggedInUserSelector, getLoggedInUserEffectiveRole } from '../../redux/selectors/users';
3939
import {
4040
groupSelector,
41-
groupsSelector,
41+
groupDataAccessorSelector,
4242
groupsAssignmentsSelector,
4343
groupsShadowAssignmentsSelector,
4444
} from '../../redux/selectors/groups';
@@ -130,6 +130,7 @@ class GroupDetail extends Component {
130130
render() {
131131
const {
132132
group,
133+
groupsAccessor,
133134
students,
134135
loggedUser,
135136
effectiveRole,
@@ -149,6 +150,7 @@ class GroupDetail extends Component {
149150
fetchUsersSolutions,
150151
setShadowPoints,
151152
removeShadowPoints,
153+
links: { GROUP_DETAIL_URI_FACTORY },
152154
} = this.props;
153155

154156
return (
@@ -216,7 +218,11 @@ class GroupDetail extends Component {
216218
</Row>
217219
)}
218220

219-
<GroupArchivedWarning archived={data.archived} directlyArchived={data.directlyArchived} />
221+
<GroupArchivedWarning
222+
{...data}
223+
groupsDataAccessor={groupsAccessor}
224+
linkFactory={GROUP_DETAIL_URI_FACTORY}
225+
/>
220226

221227
{!data.organizational && hasPermissions(data, 'viewAssignments') && (
222228
<>
@@ -421,12 +427,12 @@ GroupDetail.propTypes = {
421427
loggedUser: ImmutablePropTypes.map,
422428
effectiveRole: PropTypes.string,
423429
group: ImmutablePropTypes.map,
430+
groupsAccessor: PropTypes.func.isRequired,
424431
instance: ImmutablePropTypes.map,
425432
students: PropTypes.array,
426433
assignments: ImmutablePropTypes.list,
427434
shadowAssignments: ImmutablePropTypes.list,
428435
assignmentEnvironmentsSelector: PropTypes.func,
429-
groups: ImmutablePropTypes.map,
430436
isGroupAdmin: PropTypes.bool,
431437
isGroupSupervisor: PropTypes.bool,
432438
isGroupStudent: PropTypes.bool,
@@ -457,10 +463,10 @@ const mapStateToProps = (
457463

458464
return {
459465
group: groupSelector(state, groupId),
466+
groupsAccessor: groupDataAccessorSelector(state),
460467
userId,
461468
loggedUser: loggedInUserSelector(state),
462469
effectiveRole: getLoggedInUserEffectiveRole(state),
463-
groups: groupsSelector(state),
464470
assignments: groupsAssignmentsSelector(state, groupId),
465471
shadowAssignments: groupsShadowAssignmentsSelector(state, groupId),
466472
assignmentEnvironmentsSelector: assignmentEnvironmentsSelector(state),

src/pages/GroupInfo/GroupInfo.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import { fetchByIds, fetchUser } from '../../redux/modules/users';
1919
import { loggedInUserIdSelector } from '../../redux/selectors/auth';
2020
import { isLoggedAsSuperAdmin } from '../../redux/selectors/users';
21-
import { groupSelector, groupsSelector } from '../../redux/selectors/groups';
21+
import { groupSelector, groupDataAccessorSelector, groupsSelector } from '../../redux/selectors/groups';
2222
import {
2323
primaryAdminsOfGroupSelector,
2424
supervisorsOfGroupSelector,
@@ -46,6 +46,8 @@ import { BanIcon, GroupIcon } from '../../components/icons';
4646
import { hasPermissions, safeGet } from '../../helpers/common';
4747
import GroupArchivedWarning from '../../components/Groups/GroupArchivedWarning/GroupArchivedWarning';
4848

49+
import withLinks from '../../helpers/withLinks';
50+
4951
class GroupInfo extends Component {
5052
static loadAsync = ({ groupId }, dispatch) =>
5153
dispatch(fetchGroupIfNeeded(groupId))
@@ -87,6 +89,7 @@ class GroupInfo extends Component {
8789
group,
8890
userId,
8991
groups,
92+
groupsAccessor,
9093
primaryAdmins = [],
9194
supervisors = [],
9295
observers = [],
@@ -102,6 +105,7 @@ class GroupInfo extends Component {
102105
addSupervisor,
103106
addObserver,
104107
removeMember,
108+
links: { GROUP_INFO_URI_FACTORY },
105109
intl: { locale },
106110
} = this.props;
107111

@@ -131,7 +135,7 @@ class GroupInfo extends Component {
131135
</div>
132136
)}
133137

134-
<GroupArchivedWarning archived={data.archived} directlyArchived={data.directlyArchived} />
138+
<GroupArchivedWarning {...data} groupsDataAccessor={groupsAccessor} linkFactory={GROUP_INFO_URI_FACTORY} />
135139

136140
{!hasPermissions(data, 'viewDetail') && (
137141
<Row>
@@ -262,6 +266,7 @@ GroupInfo.propTypes = {
262266
match: PropTypes.shape({ params: PropTypes.shape({ groupId: PropTypes.string.isRequired }).isRequired }).isRequired,
263267
userId: PropTypes.string.isRequired,
264268
group: ImmutablePropTypes.map,
269+
groupsAccessor: PropTypes.func.isRequired,
265270
instance: ImmutablePropTypes.map,
266271
primaryAdmins: PropTypes.array,
267272
supervisors: PropTypes.array,
@@ -301,6 +306,7 @@ const mapStateToProps = (
301306
group: groupSelector(state, groupId),
302307
userId,
303308
groups: groupsSelector(state),
309+
groupsAccessor: groupDataAccessorSelector(state),
304310
primaryAdmins: primaryAdminsOfGroupSelector(state, groupId),
305311
supervisors: supervisorsOfGroupSelector(state, groupId),
306312
observers: observersOfGroupSelector(state, groupId),
@@ -337,4 +343,4 @@ const mapDispatchToProps = (dispatch, { match: { params } }) => ({
337343
removeMember: (userId, groupId) => dispatch(removeMember(userId, groupId)),
338344
});
339345

340-
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(GroupInfo));
346+
export default withLinks(connect(mapStateToProps, mapDispatchToProps)(injectIntl(GroupInfo)));

src/pages/GroupUserSolutions/GroupUserSolutions.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ import ResourceRenderer from '../../components/helpers/ResourceRenderer';
2626
import FetchManyResourceRenderer from '../../components/helpers/FetchManyResourceRenderer';
2727
import { LocalizedExerciseName } from '../../components/helpers/LocalizedNames';
2828
import EnvironmentsListItem from '../../components/helpers/EnvironmentsList/EnvironmentsListItem';
29+
import GroupArchivedWarning from '../../components/Groups/GroupArchivedWarning/GroupArchivedWarning';
2930

3031
import { fetchUserIfNeeded } from '../../redux/modules/users';
3132
import { fetchAssignmentsForGroup } from '../../redux/modules/assignments';
3233
import { fetchGroupIfNeeded } from '../../redux/modules/groups';
3334
import { fetchRuntimeEnvironments } from '../../redux/modules/runtimeEnvironments';
3435
import { fetchGroupStudentsSolutions } from '../../redux/modules/solutions';
35-
import { groupSelector, groupsAssignmentsSelector } from '../../redux/selectors/groups';
36+
import { groupSelector, groupsAssignmentsSelector, groupDataAccessorSelector } from '../../redux/selectors/groups';
3637
import {
3738
assignmentEnvironmentsSelector,
3839
getUserSolutions,
@@ -291,6 +292,7 @@ class GroupUserSolutions extends Component {
291292
groupId,
292293
userId,
293294
group,
295+
groupsAccessor,
294296
assignments,
295297
assignmentEnvironmentsSelector,
296298
getAssignmentSolutions,
@@ -318,6 +320,12 @@ class GroupUserSolutions extends Component {
318320
canViewDetail={hasPermissions(group, 'viewDetail')}
319321
/>
320322

323+
<GroupArchivedWarning
324+
{...group}
325+
groupsDataAccessor={groupsAccessor}
326+
linkFactory={links.GROUP_EDIT_URI_FACTORY}
327+
/>
328+
321329
<div className="text-right text-nowrap py-2">
322330
<OnOffCheckbox
323331
className="text-left mr-3"
@@ -436,6 +444,7 @@ GroupUserSolutions.propTypes = {
436444
groupId: PropTypes.string.isRequired,
437445
userId: PropTypes.string.isRequired,
438446
group: ImmutablePropTypes.map,
447+
groupsAccessor: PropTypes.func.isRequired,
439448
assignments: ImmutablePropTypes.list,
440449
assignmentEnvironmentsSelector: PropTypes.func,
441450
fetchSolutionsStatus: PropTypes.string,
@@ -462,15 +471,14 @@ export default withLinks(
462471
groupId,
463472
userId,
464473
group: groupSelector(state, groupId),
474+
groupsAccessor: groupDataAccessorSelector(state),
465475
assignments: groupsAssignmentsSelector(state, groupId),
466476
assignmentEnvironmentsSelector: assignmentEnvironmentsSelector(state),
467477
fetchSolutionsStatus: fetchManyGroupStudentsSolutionsStatus(state)(groupId, userId),
468478
fetchRuntimesStatus: fetchRuntimeEnvironmentsStatus(state),
469-
470479
getAssignmentSolutions: assignmentId => getUserSolutions(state)(userId, assignmentId),
471480
getAssignmentSolutionsSorted: assignmentId => getUserSolutionsSortedData(state)(userId, assignmentId),
472481
getRuntime: runtimeEnvironmentSelector(state),
473-
474482
loggedUserId: loggedInUserIdSelector(state),
475483
};
476484
},

0 commit comments

Comments
 (0)