Skip to content

Commit

Permalink
Merge pull request #171 from ReCodEx/group-treeview-improve
Browse files Browse the repository at this point in the history
Group tree view updated ...
  • Loading branch information
Martin Kruliš committed Jan 24, 2018
2 parents 5f9c4a4 + 27cf7f7 commit 591e2a7
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 30 deletions.
71 changes: 47 additions & 24 deletions src/components/Groups/GroupTree/GroupTree.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage, injectIntl } from 'react-intl';
import { LinkContainer } from 'react-router-bootstrap';
import Icon from 'react-fontawesome';

import Button from '../../widgets/FlatButton';
import { LinkContainer } from 'react-router-bootstrap';
import { TreeView, TreeViewItem } from '../../widgets/TreeView';
import { isReady, getJsData } from '../../../redux/helpers/resourceManager';
import GroupsName from '../GroupsName';
import { computeVisibleGroupsMap } from '../../helpers/group.js';
import { getLocalizedResourceName } from '../../../helpers/getLocalizedData';

import withLinks from '../../../hoc/withLinks';

Expand Down Expand Up @@ -42,6 +45,33 @@ class GroupTree extends Component {
);
};

renderChildGroups = (
{ all: allChildGroups, public: publicChildGroups },
visibleGroupsMap
) => {
const { level = 0, isOpen = false, groups, intl: { locale } } = this.props;
return allChildGroups
.filter(id => visibleGroupsMap[id])
.sort((id1, id2) => {
const name1 = getLocalizedResourceName(groups.get(id1), locale);
const name2 = getLocalizedResourceName(groups.get(id2), locale);
return name1 !== undefined && name2 !== undefined
? name1.localeCompare(name2, locale)
: 0;
})
.map(id =>
<GroupTree
{...this.props}
key={id}
id={id}
isOpen={level !== 0 || isOpen}
level={level + 1}
isPublic={publicChildGroups.indexOf(id) >= 0}
visibleGroupsMap={visibleGroupsMap}
/>
);
};

render() {
const {
id,
Expand All @@ -50,6 +80,7 @@ class GroupTree extends Component {
isPublic = true,
groups,
currentGroupId = null,
visibleGroupsMap = null,
links: { GROUP_URI_FACTORY }
} = this.props;

Expand All @@ -62,10 +93,15 @@ class GroupTree extends Component {
name,
localizedTexts,
canView,
childGroups: { all: allChildGroups, public: publicChildGroups },
childGroups,
primaryAdminsIds
} = getJsData(group);

const actualVisibleGroupsMap =
visibleGroupsMap !== null
? visibleGroupsMap
: computeVisibleGroupsMap(groups);

return (
<TreeView>
{level !== 0 &&
Expand All @@ -78,36 +114,21 @@ class GroupTree extends Component {
noLink
/>
}
id={id}
level={level}
admins={primaryAdminsIds}
isPublic={isPublic}
isOpen={currentGroupId === id || isOpen}
actions={
currentGroupId !== id && canView
? this.renderButtons(id, GROUP_URI_FACTORY(id))
: undefined
}
>
{allChildGroups.map(id =>
<GroupTree
{...this.props}
key={id}
id={id}
isOpen={true}
level={level + 1}
isPublic={publicChildGroups.indexOf(id) >= 0}
/>
)}
{this.renderChildGroups(childGroups, actualVisibleGroupsMap)}
</TreeViewItem>}
{level === 0 &&
allChildGroups.map(id =>
<GroupTree
{...this.props}
key={id}
id={id}
level={level + 1}
isPublic={publicChildGroups.indexOf(id) >= 0}
/>
)}
this.renderChildGroups(childGroups, actualVisibleGroupsMap)}
</TreeView>
);
}
Expand All @@ -120,7 +141,9 @@ GroupTree.propTypes = {
isOpen: PropTypes.bool,
isPublic: PropTypes.bool,
currentGroupId: PropTypes.string,
links: PropTypes.object
visibleGroupsMap: PropTypes.object,
links: PropTypes.object,
intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired
};

export default withLinks(GroupTree);
export default withLinks(injectIntl(GroupTree));
2 changes: 1 addition & 1 deletion src/components/forms/EditGroupForm/EditGroupForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ const EditGroupForm = ({
label={
<FormattedMessage
id="app.createGroup.isPublic"
defaultMessage="Students can join the group themselves"
defaultMessage="Public (everyone can see and join this group)"
/>
}
required
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const LocalizedExerciseName = ({ entity, intl: { locale } }) => {
</Tooltip>
}
>
<Icon name="flag-o" className="text-black" />
<Icon name="flag-o" className="text-muted" />
</OverlayTrigger>&nbsp;
</span>}
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const LocalizedGroupName = ({ entity, intl: { locale } }) => {
</Tooltip>
}
>
<Icon name="flag-o" className="text-black" />
<Icon name="flag-o" className="text-muted" />
</OverlayTrigger>&nbsp;
</span>}
</span>
Expand Down
15 changes: 15 additions & 0 deletions src/components/helpers/group.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defaultMemoize } from 'reselect';

export const computeVisibleGroupsMap = defaultMemoize(groups => {
const res = {};
groups
.filter(group => group.getIn(['data', 'canView']))
.forEach((group, id) => {
res[id] = true;
group.getIn(['data', 'parentGroupsIds'], []).forEach(parentId => {
res[parentId] = true;
});
});

return res;
});
24 changes: 24 additions & 0 deletions src/components/widgets/TreeView/TreeViewLeaf.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
import Icon from 'react-fontawesome';

import { LoadingIcon } from '../../icons';
import LevelGap from './LevelGap';
import GroupsName from '../../../components/Groups/GroupsName';
import UsersNameContainer from '../../../containers/UsersNameContainer';

const TreeViewLeaf = ({
id,
loading = false,
title,
admins,
isPublic,
icon = 'square-o',
onClick,
level,
Expand Down Expand Up @@ -39,16 +43,36 @@ const TreeViewLeaf = ({
</em>
</small>)
</span>}
{isPublic &&
<OverlayTrigger
placement="bottom"
overlay={
<Tooltip id={`${id}-public-tooltip`}>
<FormattedMessage
id="app.groupTree.treeViewLeaf.publicTooltip"
defaultMessage="The group is public"
/>
</Tooltip>
}
>
<Icon
name="eye"
className="text-muted"
style={{ marginLeft: '0.5em' }}
/>
</OverlayTrigger>}
<span className="pull-right">{actions}</span>
</li>;

TreeViewLeaf.propTypes = {
id: PropTypes.string,
loading: PropTypes.bool,
title: PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({ type: PropTypes.oneOf([FormattedMessage, GroupsName]) })
]).isRequired,
admins: PropTypes.array,
isPublic: PropTypes.bool,
icon: PropTypes.string,
onClick: PropTypes.func,
level: PropTypes.number.isRequired,
Expand Down
12 changes: 12 additions & 0 deletions src/helpers/getLocalizedData.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@ const getLocalizedX = field => (entity, locale) => {
return localizedText ? localizedText[field] : entity[field];
};

const getLocalizedResourceX = field => (resource, locale) => {
const localizedTexts = resource && resource.getIn(['data', 'localizedTexts']);
const localizedText =
localizedTexts &&
localizedTexts.find(text => text.get('locale') === locale);
return localizedText
? localizedText.get(field)
: resource ? resource.getIn(['data', field]) : undefined;
};

export const getLocalizedName = getLocalizedX('name');
export const getLocalizedDescription = getLocalizedX('description');

export const getLocalizedResourceName = getLocalizedResourceX('name');

export const getOtherLocalizedNames = (entity, locale) => {
const name = getLocalizedName(entity, locale);
return entity.localizedTexts
Expand Down
5 changes: 3 additions & 2 deletions src/locales/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@
"app.confirm.no": "Ne",
"app.confirm.yes": "Ano",
"app.createGroup.externalId": "Externí identifikátor skupiny (například ID ze školního informačního systému):",
"app.createGroup.hasThreshold": "Students require cetrain number of points to complete the course",
"app.createGroup.isPublic": "Studenti se mohou sami přidávat k této skupině",
"app.createGroup.hasThreshold": "Studenti potřebují určitý počet bodů pro splnění kurzu",
"app.createGroup.isPublic": "Veřejná (skupinu vidí všichni uživatelé a můžou se do ní přidat)",
"app.createGroup.publicStats": "Studenti mohou vidět dosažené body ostatních",
"app.createGroup.threshold": "Minimální procentuální hranice potřebná ke splnění tohoto kurzu:",
"app.createGroup.validation.thresholdBetweenZeroHundred": "Procentuální hranice musí být celé číslo od 0 do 100.",
Expand Down Expand Up @@ -685,6 +685,7 @@
"app.groupResultsTableRow.noResults": "Nyní zde nejsou žádné výsledky k zobrazení.",
"app.groupTree.detailButton": "Zobrazit stránku skupiny",
"app.groupTree.loading": "Načítání...",
"app.groupTree.treeViewLeaf.publicTooltip": "The group is public",
"app.groups.joinGroupButton": "Stát se členem",
"app.groups.leaveGroupButton": "Opustit skupinu",
"app.groups.makeGroupAdminButton": "Změnit na správce skupiny",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
"app.confirm.yes": "Yes",
"app.createGroup.externalId": "External ID of the group (e. g. ID of the group in the school IS):",
"app.createGroup.hasThreshold": "Students require cetrain number of points to complete the course",
"app.createGroup.isPublic": "Students can join the group themselves",
"app.createGroup.isPublic": "Public (everyone can see and join this group)",
"app.createGroup.publicStats": "Students can see statistics of each other",
"app.createGroup.threshold": "Minimum percent of the total points count needed to complete the course:",
"app.createGroup.validation.thresholdBetweenZeroHundred": "Threshold must be an integer in between 0 and 100.",
Expand Down Expand Up @@ -685,6 +685,7 @@
"app.groupResultsTableRow.noResults": "There are currently no results available.",
"app.groupTree.detailButton": "See group's page",
"app.groupTree.loading": "Loading ...",
"app.groupTree.treeViewLeaf.publicTooltip": "The group is public",
"app.groups.joinGroupButton": "Join group",
"app.groups.leaveGroupButton": "Leave group",
"app.groups.makeGroupAdminButton": "Make group admin",
Expand Down

0 comments on commit 591e2a7

Please sign in to comment.