From f7dfd51c643f98b25d5948f65e4abcc7e10c082b Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Sun, 8 Apr 2018 18:21:32 +0200 Subject: [PATCH 1/6] Small reorganization of GroupInfo page. --- src/pages/GroupInfo/GroupInfo.js | 68 ++++++++++++++++---------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/pages/GroupInfo/GroupInfo.js b/src/pages/GroupInfo/GroupInfo.js index ed41f31e3..60e1c143e 100644 --- a/src/pages/GroupInfo/GroupInfo.js +++ b/src/pages/GroupInfo/GroupInfo.js @@ -153,38 +153,6 @@ class GroupInfo extends Component { /> - {data.childGroups.all.length > 0 && - - - } - unlimitedHeight - > - - - - {isAdmin && - } - } - - + + {isAdmin && } + + {data.childGroups.all.length > 0 && + + } + unlimitedHeight + > + + } + + {isAdmin && + } + } From b7bf045f6d8bf3d317931a6dc2a01eea6a950bc0 Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Sun, 8 Apr 2018 18:39:21 +0200 Subject: [PATCH 2/6] Fixing bug in EditGroupFrom (each instance must have its own redux-form id). --- .../forms/EditGroupForm/EditGroupForm.js | 1 - src/pages/EditGroup/EditGroup.js | 48 +++++++++++-------- src/pages/GroupInfo/GroupInfo.js | 3 +- src/pages/Instance/Instance.js | 3 +- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/components/forms/EditGroupForm/EditGroupForm.js b/src/components/forms/EditGroupForm/EditGroupForm.js index e284f7726..4549c715a 100644 --- a/src/components/forms/EditGroupForm/EditGroupForm.js +++ b/src/components/forms/EditGroupForm/EditGroupForm.js @@ -283,7 +283,6 @@ const validate = ({ localizedTexts = [], hasThreshold, threshold }) => { }; export default reduxForm({ - form: 'editGroup', enableReinitialize: true, keepDirtyOnReinitialize: false, validate diff --git a/src/pages/EditGroup/EditGroup.js b/src/pages/EditGroup/EditGroup.js index bd06c86fe..8ed84c898 100644 --- a/src/pages/EditGroup/EditGroup.js +++ b/src/pages/EditGroup/EditGroup.js @@ -6,6 +6,7 @@ import { HelpBlock } from 'react-bootstrap'; import { connect } from 'react-redux'; import { push } from 'react-router-redux'; import { reset, formValueSelector } from 'redux-form'; +import { defaultMemoize } from 'reselect'; import Page from '../../components/layout/Page'; import EditGroupForm from '../../components/forms/EditGroupForm'; @@ -25,29 +26,33 @@ import { getLocalizedTextsLocales } from '../../helpers/getLocalizedData'; import withLinks from '../../helpers/withLinks'; class EditGroup extends Component { - componentWillMount = () => this.props.loadAsync(); - componentWillReceiveProps = props => { - if (this.props.params.groupId !== props.params.groupId) { - props.reset(); - props.loadAsync(); + componentWillMount() { + this.props.loadAsync(); + } + componentWillReceiveProps(nextProps) { + if (this.props.params.groupId !== nextProps.params.groupId) { + nextProps.reset(); + nextProps.loadAsync(); } - }; + } - getInitialValues = ({ - localizedTexts, - externalId, - privateData: { isPublic, publicStats, threshold } - }) => ({ - localizedTexts, - externalId, - isPublic, - publicStats, - hasThreshold: threshold !== null && threshold !== undefined, - threshold: - threshold !== null && threshold !== undefined - ? String(Number(threshold) * 100) - : '0' - }); + getInitialValues = defaultMemoize( + ({ + localizedTexts, + externalId, + privateData: { isPublic, publicStats, threshold } + }) => ({ + localizedTexts, + externalId, + isPublic, + publicStats, + hasThreshold: threshold !== null && threshold !== undefined, + threshold: + threshold !== null && threshold !== undefined + ? String(Number(threshold) * 100) + : '0' + }) + ); render() { const { @@ -101,6 +106,7 @@ class EditGroup extends Component { {group =>
{ isAdmin: isAdminOf(userId, groupId)(state), isSuperAdmin: isLoggedAsSuperAdmin(state), isStudent: isStudentOf(userId, groupId)(state), - formValues: getFormValues('editGroup')(state) + formValues: getFormValues('addSubgroup')(state) }; }; diff --git a/src/pages/Instance/Instance.js b/src/pages/Instance/Instance.js index d63c58215..071da342a 100644 --- a/src/pages/Instance/Instance.js +++ b/src/pages/Instance/Instance.js @@ -110,6 +110,7 @@ class Instance extends Component { ({ From 10b03dbd03f9cf1cb05bce1cfe7504a6d40daed5 Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Mon, 9 Apr 2018 01:30:12 +0200 Subject: [PATCH 3/6] Button that toggles between regular and organizational groups added to EditGroup page. --- .../OrganizationalGroupButton.js | 38 +++++++++++++++ .../OrganizationalGroupButton/index.js | 1 + src/components/icons/GroupIcon.js | 8 +++- .../OrganizationalGroupButtonContainer.js | 47 +++++++++++++++++++ .../index.js | 1 + src/locales/cs.json | 3 ++ src/locales/en.json | 3 ++ src/pages/EditGroup/EditGroup.js | 21 ++++++++- src/redux/modules/groups.js | 37 +++++++++++++-- src/redux/selectors/groups.js | 6 +++ 10 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 src/components/buttons/OrganizationalGroupButton/OrganizationalGroupButton.js create mode 100644 src/components/buttons/OrganizationalGroupButton/index.js create mode 100644 src/containers/OrganizationalGroupButtonContainer/OrganizationalGroupButtonContainer.js create mode 100644 src/containers/OrganizationalGroupButtonContainer/index.js diff --git a/src/components/buttons/OrganizationalGroupButton/OrganizationalGroupButton.js b/src/components/buttons/OrganizationalGroupButton/OrganizationalGroupButton.js new file mode 100644 index 000000000..93c2306ea --- /dev/null +++ b/src/components/buttons/OrganizationalGroupButton/OrganizationalGroupButton.js @@ -0,0 +1,38 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import GroupIcon from '../../icons/GroupIcon'; +import LoadingIcon from '../../icons/LoadingIcon'; +import Button from '../../widgets/FlatButton'; + +const OrganizationalGroupButton = ({ + organizational, + pending, + setOrganizational +}) => + ; + +OrganizationalGroupButton.propTypes = { + organizational: PropTypes.bool.isRequired, + pending: PropTypes.bool.isRequired, + setOrganizational: PropTypes.func.isRequired +}; + +export default OrganizationalGroupButton; diff --git a/src/components/buttons/OrganizationalGroupButton/index.js b/src/components/buttons/OrganizationalGroupButton/index.js new file mode 100644 index 000000000..5b9433cd3 --- /dev/null +++ b/src/components/buttons/OrganizationalGroupButton/index.js @@ -0,0 +1 @@ +export default from './OrganizationalGroupButton'; diff --git a/src/components/icons/GroupIcon.js b/src/components/icons/GroupIcon.js index 7e433e070..cdd288415 100644 --- a/src/components/icons/GroupIcon.js +++ b/src/components/icons/GroupIcon.js @@ -1,6 +1,12 @@ import React from 'react'; +import PropTypes from 'prop-types'; import Icon from 'react-fontawesome'; -const GroupIcon = props => ; +const GroupIcon = ({ organizational = false, ...props }) => + ; + +GroupIcon.propTypes = { + organizational: PropTypes.bool +}; export default GroupIcon; diff --git a/src/containers/OrganizationalGroupButtonContainer/OrganizationalGroupButtonContainer.js b/src/containers/OrganizationalGroupButtonContainer/OrganizationalGroupButtonContainer.js new file mode 100644 index 000000000..0843508d0 --- /dev/null +++ b/src/containers/OrganizationalGroupButtonContainer/OrganizationalGroupButtonContainer.js @@ -0,0 +1,47 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { connect } from 'react-redux'; + +import OrganizationalGroupButton from '../../components/buttons/OrganizationalGroupButton'; +import { setOrganizational } from '../../redux/modules/groups'; +import { + groupSelector, + groupOrganizationalPendingChange +} from '../../redux/selectors/groups'; +import ResourceRenderer from '../../components/helpers/ResourceRenderer'; + +const OrganizationalGroupButtonContainer = ({ + group, + pending, + setOrganizational +}) => + + {({ organizational }) => + } + ; + +OrganizationalGroupButtonContainer.propTypes = { + id: PropTypes.string.isRequired, + group: ImmutablePropTypes.map, + pending: PropTypes.bool.isRequired, + setOrganizational: PropTypes.func.isRequired +}; + +const mapStateToProps = (state, { id }) => ({ + group: groupSelector(id)(state), + pending: groupOrganizationalPendingChange(id)(state) +}); + +const mapDispatchToProps = (dispatch, { id }) => ({ + setOrganizational: organizational => () => + dispatch(setOrganizational(id, organizational)) +}); + +export default connect(mapStateToProps, mapDispatchToProps)( + OrganizationalGroupButtonContainer +); diff --git a/src/containers/OrganizationalGroupButtonContainer/index.js b/src/containers/OrganizationalGroupButtonContainer/index.js new file mode 100644 index 000000000..63bafaacb --- /dev/null +++ b/src/containers/OrganizationalGroupButtonContainer/index.js @@ -0,0 +1 @@ +export default from './OrganizationalGroupButtonContainer'; diff --git a/src/locales/cs.json b/src/locales/cs.json index 2a8deee00..bcf65c95b 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -289,6 +289,7 @@ "app.editGroup.deleteGroup": "Smazat skupinu", "app.editGroup.deleteGroupWarning": "Smazání skupiny odstraní také všechny podskupiny, skupinové úlohy a zadané úlohy včetně studentských řešení.", "app.editGroup.description": "Změnit nastavení skupiny", + "app.editGroup.organizationalExplain": "Běžné skupiny jsou platformou spojující studenty a zadané úlohy. Organizační skupiny slouží pouze k vytváření hierarchie, takže v nich nesmí být přihlášení studenti a nesmí obsahovat zadané úlohy.", "app.editGroup.title": "Upravit skupinu", "app.editGroupForm.createGroup": "Vytvořit skupinu", "app.editGroupForm.description": "Popis skupiny:", @@ -761,6 +762,8 @@ "app.notifications.hideAll": "Pouze nové notifikace", "app.notifications.showAll": "Zobrazit {count, plural, one {jednu starou notifikaci} two {dvě staré notifikace} other {# starých notifikací}}", "app.notifications.title": "{count, plural, =0 {Nemáte žádnou novou notifikaci} one {Máte novou notifikaci} =2 {Máte dvě nové notifikace} =3 {Máte tři nové notifikace} =4 {Máte čtyři nové notifikace} other {Máte # nových notifikací}}", + "app.organizationalGroupButton.set": "Změnit na organizační skupinu", + "app.organizationalGroupButton.unset": "Změnit na běžnou skupinu", "app.page.exercises.failed": "Načítání seznamu úloh selhalo", "app.page.exercises.failedDescription": "Omlouváme se za nepříjemnosti, zkuste to prosím později.", "app.page.exercises.loading": "Načítání seznamu úloh ...", diff --git a/src/locales/en.json b/src/locales/en.json index cf58d350f..3157e2e46 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -289,6 +289,7 @@ "app.editGroup.deleteGroup": "Delete the group", "app.editGroup.deleteGroupWarning": "Deleting a group will remove all the subgroups, the students submissions and all the assignments and the submissions of the students.", "app.editGroup.description": "Change group settings", + "app.editGroup.organizationalExplain": "Regular groups are containers for students and assignments. Organizational groups are intended to create hierarchy, so they are forbidden to hold any students or assignments.", "app.editGroup.title": "Edit Group", "app.editGroupForm.createGroup": "Create Group", "app.editGroupForm.description": "Group description:", @@ -761,6 +762,8 @@ "app.notifications.hideAll": "Only new notifications", "app.notifications.showAll": "Show {count, plural, one {old notification} two {two old notifications} other {all # notifications}}", "app.notifications.title": "You have {count, number} new {count, plural, one {notification} two {notifications} other {notifications}}", + "app.organizationalGroupButton.set": "Change to Organizational Group", + "app.organizationalGroupButton.unset": "Change to Regular Group", "app.page.exercises.failed": "Cannot load the list of exercises", "app.page.exercises.failedDescription": "We are sorry for the inconvenience, please try again later.", "app.page.exercises.loading": "Loading list of exercises ...", diff --git a/src/pages/EditGroup/EditGroup.js b/src/pages/EditGroup/EditGroup.js index 8ed84c898..91edc27ef 100644 --- a/src/pages/EditGroup/EditGroup.js +++ b/src/pages/EditGroup/EditGroup.js @@ -2,14 +2,16 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { FormattedMessage } from 'react-intl'; -import { HelpBlock } from 'react-bootstrap'; +import { HelpBlock, Row, Col } from 'react-bootstrap'; import { connect } from 'react-redux'; import { push } from 'react-router-redux'; import { reset, formValueSelector } from 'redux-form'; import { defaultMemoize } from 'reselect'; +import Icon from 'react-fontawesome'; import Page from '../../components/layout/Page'; import EditGroupForm from '../../components/forms/EditGroupForm'; +import OrganizationalGroupButtonContainer from '../../containers/OrganizationalGroupButtonContainer'; import DeleteGroupButtonContainer from '../../containers/DeleteGroupButtonContainer'; import Box from '../../components/widgets/Box'; import { LocalizedGroupName } from '../../components/helpers/LocalizedNames'; @@ -105,6 +107,23 @@ class EditGroup extends Component { > {group =>
+ + +

+ +

+ + +

+    + +

+ +
+ dispatch => ) ); // @todo: Make translatable +export const setOrganizational = (groupId, organizational) => + createApiAction({ + type: additionalActionTypes.SET_ORGANIZATIONAL, + method: 'POST', + endpoint: `/groups/${groupId}/organizational`, + body: { value: organizational }, + meta: { groupId } + }); + /** * Reducer */ @@ -277,7 +290,7 @@ const reducer = handleActions( admins => admins.filter(id => id !== userId).concat([userId]) ), - [additionalActionTypes.ADD_ADMIN_FAILED]: ( + [additionalActionTypes.ADD_ADMIN_REJECTED]: ( state, { payload, meta: { groupId, userId } } ) => @@ -300,7 +313,7 @@ const reducer = handleActions( admins => admins.filter(id => id !== userId) ), - [additionalActionTypes.REMOVE_ADMIN_FAILED]: ( + [additionalActionTypes.REMOVE_ADMIN_REJECTED]: ( state, { payload, meta: { groupId, userId } } ) => @@ -314,6 +327,24 @@ const reducer = handleActions( { payload, meta: { groupId } } ) => state.setIn(['resources', groupId, 'data'], fromJS(payload)), + [additionalActionTypes.SET_ORGANIZATIONAL_PENDING]: ( + state, + { payload, meta: { groupId } } + ) => state.setIn(['resources', groupId, 'pending-organizational'], true), + + [additionalActionTypes.SET_ORGANIZATIONAL_FULFILLED]: ( + state, + { payload, meta: { groupId } } + ) => + state + .deleteIn(['resources', groupId, 'pending-organizational']) + .setIn(['resources', groupId, 'data'], fromJS(payload)), + + [additionalActionTypes.SET_ORGANIZATIONAL_REJECTED]: ( + state, + { payload, meta: { groupId } } + ) => state.deleteIn(['resources', groupId, 'pending-organizational']), + [additionalActionTypes.LOAD_USERS_GROUPS_FULFILLED]: ( state, { payload, ...rest } diff --git a/src/redux/selectors/groups.js b/src/redux/selectors/groups.js index cae373106..65fbcd3b8 100644 --- a/src/redux/selectors/groups.js +++ b/src/redux/selectors/groups.js @@ -153,3 +153,9 @@ const getGroupParentIds = (id, groups) => { export const allParentIdsForGroup = id => createSelector(groupsSelector, groups => getGroupParentIds(id, groups)); + +export const groupOrganizationalPendingChange = id => + createSelector( + groupsSelector, + groups => groups && groups.getIn([id, 'pending-organizational'], false) + ); From 11ca192db3360399670f74f1b04075b0a18387bb Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Mon, 9 Apr 2018 02:07:13 +0200 Subject: [PATCH 4/6] Structure of GroupDetail page modified to better reflect the needs of organizational groups. --- .../Groups/GroupTopButtons/GroupTopButtons.js | 22 +-- src/locales/cs.json | 5 +- src/locales/en.json | 1 + src/pages/GroupDetail/GroupDetail.js | 130 ++++++++++-------- 4 files changed, 90 insertions(+), 68 deletions(-) diff --git a/src/components/Groups/GroupTopButtons/GroupTopButtons.js b/src/components/Groups/GroupTopButtons/GroupTopButtons.js index 90902e089..b2a81dd33 100644 --- a/src/components/Groups/GroupTopButtons/GroupTopButtons.js +++ b/src/components/Groups/GroupTopButtons/GroupTopButtons.js @@ -29,22 +29,24 @@ const GroupTopButtons = ({ /> } + - {!group.organizational && - - - } + + + + + {canLeaveJoin && - - - - + +

- } - noPadding - unlimitedHeight - > - - {groupStats => - item.userId === userId)} - isAdmin={isAdmin || isSupervisor} - />} - - - - +

+ +
} + + {!data.organizational && + + + + } + noPadding + unlimitedHeight + > + + {groupStats => + item.userId === userId + )} + isAdmin={isAdmin || isSupervisor} + />} + + + + } + + {!data.organizational && + + + + } + unlimitedHeight + noPadding + > + + {(groupStats, ...pubAssignments) => + + } + />} + + + + } - - - - } - unlimitedHeight - noPadding - > - - {(groupStats, ...pubAssignments) => - - } - />} - - - - {(isSupervisor || isAdmin) && + !data.organizational && } + {(isSupervisor || isAdmin) && From 5d0146d34d29ec14dcfdcbf7b5e105a3922b0e7f Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Mon, 9 Apr 2018 02:07:39 +0200 Subject: [PATCH 5/6] Visibility switch moved to the bottom of EditAssignment form. --- .../EditAssignmentForm/EditAssignmentForm.js | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/components/forms/EditAssignmentForm/EditAssignmentForm.js b/src/components/forms/EditAssignmentForm/EditAssignmentForm.js index fa5cdcfb9..74b750e33 100644 --- a/src/components/forms/EditAssignmentForm/EditAssignmentForm.js +++ b/src/components/forms/EditAssignmentForm/EditAssignmentForm.js @@ -206,18 +206,6 @@ const EditAssignmentForm = ({ } /> - - } - /> -

@@ -245,6 +233,20 @@ const EditAssignmentForm = ({ )} + +
+ + + } + />

; From 3afd5afbc8798b3fbd5f6665bb707db785daa833 Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Mon, 9 Apr 2018 13:32:46 +0200 Subject: [PATCH 6/6] Fixing group links and icons (to reflect organizational flags) on various pages. --- .../Groups/GroupTopButtons/GroupTopButtons.js | 5 +-- src/components/Groups/GroupTree/GroupTree.js | 36 +++++++++++++------ .../widgets/TreeView/TreeViewLeaf.js | 22 +++++++++++- .../SisIntegrationContainer.js | 2 +- .../SisSupervisorGroupsContainer.js | 2 +- src/locales/cs.json | 3 +- src/locales/en.json | 3 +- src/pages/Dashboard/Dashboard.js | 32 ++++++++++++++--- src/pages/User/User.js | 2 +- 9 files changed, 82 insertions(+), 25 deletions(-) diff --git a/src/components/Groups/GroupTopButtons/GroupTopButtons.js b/src/components/Groups/GroupTopButtons/GroupTopButtons.js index b2a81dd33..16b071197 100644 --- a/src/components/Groups/GroupTopButtons/GroupTopButtons.js +++ b/src/components/Groups/GroupTopButtons/GroupTopButtons.js @@ -40,10 +40,7 @@ const GroupTopButtons = ({ diff --git a/src/components/Groups/GroupTree/GroupTree.js b/src/components/Groups/GroupTree/GroupTree.js index 52ee8aa6f..0201fab01 100644 --- a/src/components/Groups/GroupTree/GroupTree.js +++ b/src/components/Groups/GroupTree/GroupTree.js @@ -28,17 +28,30 @@ class GroupTree extends Component { /> ; - renderButtons = groupId => { - const { links: { GROUP_INFO_URI_FACTORY } } = this.props; + renderButtons = (groupId, organizational) => { + const { + links: { GROUP_INFO_URI_FACTORY, GROUP_DETAIL_URI_FACTORY } + } = this.props; return ( - + @@ -80,8 +93,7 @@ class GroupTree extends Component { isPublic = false, groups, currentGroupId = null, - visibleGroupsMap = null, - links: { GROUP_INFO_URI_FACTORY } + visibleGroupsMap = null } = this.props; const group = groups.get(id); @@ -94,7 +106,8 @@ class GroupTree extends Component { localizedTexts, canView, childGroups, - primaryAdminsIds + primaryAdminsIds, + organizational } = getJsData(group); const actualVisibleGroupsMap = @@ -117,11 +130,12 @@ class GroupTree extends Component { id={id} level={level} admins={primaryAdminsIds} + organizational={organizational} isPublic={isPublic} isOpen={currentGroupId === id || isOpen} actions={ currentGroupId !== id && canView - ? this.renderButtons(id, GROUP_INFO_URI_FACTORY(id)) + ? this.renderButtons(id, organizational) : undefined } > diff --git a/src/components/widgets/TreeView/TreeViewLeaf.js b/src/components/widgets/TreeView/TreeViewLeaf.js index 1e35b3643..0341063aa 100644 --- a/src/components/widgets/TreeView/TreeViewLeaf.js +++ b/src/components/widgets/TreeView/TreeViewLeaf.js @@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl'; import { OverlayTrigger, Tooltip } from 'react-bootstrap'; import Icon from 'react-fontawesome'; -import { LoadingIcon } from '../../icons'; +import { LoadingIcon, GroupIcon } from '../../icons'; import LevelGap from './LevelGap'; import GroupsName from '../../../components/Groups/GroupsName'; import UsersNameContainer from '../../../containers/UsersNameContainer'; @@ -14,6 +14,7 @@ const TreeViewLeaf = ({ loading = false, title, admins, + organizational, isPublic, icon = 'square-o', onClick, @@ -43,6 +44,24 @@ const TreeViewLeaf = ({ ) } + {organizational && + + + + } + > + + } {isPublic && {' '} diff --git a/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js b/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js index 94bcc4c04..72c1b6c16 100644 --- a/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js +++ b/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js @@ -242,7 +242,7 @@ class SisSupervisorGroupsContainer extends Component { > {' '} diff --git a/src/locales/cs.json b/src/locales/cs.json index bd6d4c6a0..ac06730dd 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -639,7 +639,7 @@ "app.group.adminsView.addSupervisor": "Přidat vedoucího", "app.group.createExercise": "Přidat skupinovou úlohu", "app.group.description": "Přehled detailů skupiny a zadaných úloh", - "app.group.detailButton": "Detail skupiny", + "app.group.detail": "Detail skupiny", "app.group.info": "Group Info", "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.", "app.group.spervisorsView.addStudent": "Přidat studenta", @@ -655,6 +655,7 @@ "app.groupDetail.threshold": "Minimální procentuální počet bodů potřebných ke splnění tohoto kurzu:", "app.groupResultsTableRow.noResults": "Nyní zde nejsou žádné výsledky k zobrazení.", "app.groupTree.loading": "Načítání...", + "app.groupTree.treeViewLeaf.organizationalTooltip": "Skupina je organizační (nemá žádné studenty ani zadané úlohy)", "app.groupTree.treeViewLeaf.publicTooltip": "Skupina je veřejná", "app.groups.joinGroupButton": "Stát se členem", "app.groups.leaveGroupButton": "Opustit skupinu", diff --git a/src/locales/en.json b/src/locales/en.json index 1d6d0bf23..30b0caaaa 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -639,7 +639,7 @@ "app.group.adminsView.addSupervisor": "Add supervisor", "app.group.createExercise": "Add group exercise", "app.group.description": "Group overview and assignments", - "app.group.detailButton": "Group Detail", + "app.group.detail": "Group Detail", "app.group.info": "Group Info", "app.group.organizationalExplain": "This group is organizational, so it cannot have any students nor assignments. However, it may have attached exercises which can be assigned in sub-groups.", "app.group.spervisorsView.addStudent": "Add Student", @@ -655,6 +655,7 @@ "app.groupDetail.threshold": "Minimum percent of the total points count needed to complete the course:", "app.groupResultsTableRow.noResults": "There are currently no results available.", "app.groupTree.loading": "Loading ...", + "app.groupTree.treeViewLeaf.organizationalTooltip": "The group is organizational (it does not have any students or assignments)", "app.groupTree.treeViewLeaf.publicTooltip": "The group is public", "app.groups.joinGroupButton": "Join group", "app.groups.leaveGroupButton": "Leave group", diff --git a/src/pages/Dashboard/Dashboard.js b/src/pages/Dashboard/Dashboard.js index c02a19cb3..fb3773bf8 100644 --- a/src/pages/Dashboard/Dashboard.js +++ b/src/pages/Dashboard/Dashboard.js @@ -44,7 +44,7 @@ import { } from '../../redux/selectors/usersGroups'; import { assignmentEnvironmentsSelector } from '../../redux/selectors/assignments'; -import { InfoIcon } from '../../components/icons'; +import { InfoIcon, GroupIcon } from '../../components/icons'; import { getJsData } from '../../redux/helpers/resourceManager'; import SisIntegrationContainer from '../../containers/SisIntegrationContainer'; import SisSupervisorGroupsContainer from '../../containers/SisSupervisorGroupsContainer'; @@ -120,7 +120,7 @@ class Dashboard extends Component { assignmentEnvironmentsSelector, statistics, allGroups, - links: { GROUP_DETAIL_URI_FACTORY }, + links: { GROUP_INFO_URI_FACTORY, GROUP_DETAIL_URI_FACTORY }, intl: { locale } } = this.props; @@ -251,14 +251,26 @@ class Dashboard extends Component { isOpen footer={

+ + + @@ -331,14 +343,26 @@ class Dashboard extends Component { isOpen footer={

+ + + diff --git a/src/pages/User/User.js b/src/pages/User/User.js index 58778de35..9d59a502e 100644 --- a/src/pages/User/User.js +++ b/src/pages/User/User.js @@ -207,7 +207,7 @@ class User extends Component { >