From 4b681c84ef57ab1031cf16dc58042815d8687b0c Mon Sep 17 00:00:00 2001 From: Petr Stefan Date: Fri, 10 Nov 2017 21:29:57 +0100 Subject: [PATCH 1/4] Group edit form localization support --- .../Groups/AdminsView/AdminsView.js | 35 ++-- .../forms/CreateGroupForm/CreateGroupForm.js | 196 ------------------ src/components/forms/CreateGroupForm/index.js | 1 - .../EditExerciseForm/EditExerciseForm.js | 2 +- .../forms/EditGroupForm/EditGroupForm.js | 168 ++++++++++----- src/pages/Instance/Instance.js | 17 +- 6 files changed, 153 insertions(+), 266 deletions(-) delete mode 100644 src/components/forms/CreateGroupForm/CreateGroupForm.js delete mode 100644 src/components/forms/CreateGroupForm/index.js diff --git a/src/components/Groups/AdminsView/AdminsView.js b/src/components/Groups/AdminsView/AdminsView.js index 9823a166b..cf92f5257 100644 --- a/src/components/Groups/AdminsView/AdminsView.js +++ b/src/components/Groups/AdminsView/AdminsView.js @@ -2,12 +2,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; import { Row, Col } from 'react-bootstrap'; +import { getFormValues } from 'redux-form'; +import { connect } from 'react-redux'; import Box from '../../widgets/Box'; import AddSupervisor from '../AddSupervisor'; -import CreateGroupForm from '../../forms/CreateGroupForm'; // @todo replace with it's' container +import EditGroupForm from '../../forms/EditGroupForm'; -const AdminsView = ({ group, addSubgroup }) => ( +const AdminsView = ({ group, addSubgroup, formValues }) =>
@@ -34,26 +36,29 @@ const AdminsView = ({ group, addSubgroup }) => ( - - } + -
-); + ; AdminsView.propTypes = { group: PropTypes.object.isRequired, - addSubgroup: PropTypes.func.isRequired + addSubgroup: PropTypes.func.isRequired, + formValues: PropTypes.object }; -export default AdminsView; +export default connect( + state => { + return { + formValues: getFormValues('editGroup')(state) + }; + }, + dispatch => ({}) +)(AdminsView); diff --git a/src/components/forms/CreateGroupForm/CreateGroupForm.js b/src/components/forms/CreateGroupForm/CreateGroupForm.js deleted file mode 100644 index 0b70ee201..000000000 --- a/src/components/forms/CreateGroupForm/CreateGroupForm.js +++ /dev/null @@ -1,196 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; -import { reduxForm, Field } from 'redux-form'; -import FormBox from '../../widgets/FormBox'; -import { Alert } from 'react-bootstrap'; -import { TextField, MarkdownTextAreaField, CheckboxField } from '../Fields'; -import { validateAddGroup } from '../../../redux/modules/groups'; -import SubmitButton from '../SubmitButton'; - -const CreateGroupForm = ({ - title = ( - - ), - handleSubmit, - submitSucceeded = false, - submitFailed = false, - anyTouched = false, - asyncValidating = false, - invalid = false, - submitting = false, - reset -}) => - - - ), - submit: ( - - ), - submitting: ( - - ) - }} - /> - - } - > - {submitFailed && - - - } - - - } - /> - - - } - /> - - - } - /> - - - } - /> - ; - -CreateGroupForm.propTypes = { - title: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.shape({ type: PropTypes.oneOf([FormattedMessage]) }) - ]), - onSubmit: PropTypes.func.isRequired, - handleSubmit: PropTypes.func.isRequired, - reset: PropTypes.func.isRequired, - submitFailed: PropTypes.bool, - submitSucceeded: PropTypes.bool, - anyTouched: PropTypes.bool, - asyncValidating: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), - invalid: PropTypes.bool, - submitting: PropTypes.bool, - parentGroupId: PropTypes.string -}; - -const validate = ({ name, description }) => { - const errors = {}; - - if (!name || name.length === 0) { - errors['name'] = ( - - ); - } - - return errors; -}; - -const asyncValidate = ( - { name }, - dispatch, - { instanceId, parentGroupId = undefined } -) => - new Promise((resolve, reject) => - dispatch(validateAddGroup(name, instanceId, parentGroupId)) - .then(res => res.value) - .then(({ groupNameIsFree }) => { - var errors = {}; - - if (!groupNameIsFree) { - errors['name'] = ( - - ); - } - - if (Object.keys(errors).length > 0) { - throw errors; - } - }) - .then(resolve()) - .catch(errors => reject(errors)) - ); - -export default reduxForm({ - form: 'createGroup', - validate, - asyncValidate, - asyncBlurFields: ['name'] -})(CreateGroupForm); diff --git a/src/components/forms/CreateGroupForm/index.js b/src/components/forms/CreateGroupForm/index.js deleted file mode 100644 index 81dd95dab..000000000 --- a/src/components/forms/CreateGroupForm/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './CreateGroupForm'; diff --git a/src/components/forms/EditExerciseForm/EditExerciseForm.js b/src/components/forms/EditExerciseForm/EditExerciseForm.js index 0a8afbf6e..611c402e7 100644 --- a/src/components/forms/EditExerciseForm/EditExerciseForm.js +++ b/src/components/forms/EditExerciseForm/EditExerciseForm.js @@ -187,7 +187,7 @@ EditExerciseForm.propTypes = { links: PropTypes.object }; -const validate = ({ name, description, difficulty, localizedTexts }) => { +const validate = ({ difficulty, localizedTexts }) => { const errors = {}; if (!difficulty) { diff --git a/src/components/forms/EditGroupForm/EditGroupForm.js b/src/components/forms/EditGroupForm/EditGroupForm.js index facfd131f..5187e174e 100644 --- a/src/components/forms/EditGroupForm/EditGroupForm.js +++ b/src/components/forms/EditGroupForm/EditGroupForm.js @@ -1,12 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; -import { reduxForm, Field } from 'redux-form'; +import { reduxForm, Field, FieldArray } from 'redux-form'; import { Alert } from 'react-bootstrap'; import FormBox from '../../widgets/FormBox'; import SubmitButton from '../SubmitButton'; +import LocalizedTextsFormField from '../LocalizedTextsFormField'; -import { TextField, CheckboxField, MarkdownTextAreaField } from '../Fields'; +import { TextField, CheckboxField } from '../Fields'; const EditGroupForm = ({ submitting, @@ -14,14 +15,23 @@ const EditGroupForm = ({ anyTouched, submitFailed = false, submitSucceeded = false, - invalid + invalid, + createNew = false, + formValues: { localizedTexts } = {}, + collapsable = false, + isOpen = true }) => + createNew + ? + : } type={submitSucceeded ? 'success' : undefined} footer={ @@ -35,12 +45,15 @@ const EditGroupForm = ({ hasFailed={submitFailed} invalid={invalid} messages={{ - submit: ( - - ), + submit: createNew + ? + : , submitting: ( } + collapsable={collapsable} + isOpen={isOpen} + unlimitedheight > {submitFailed && @@ -66,17 +82,10 @@ const EditGroupForm = ({ /> } - - } + - - } - /> - { +const validate = ({ localizedTexts = [], threshold }) => { const errors = {}; - if (!name || name.length === 0) { - errors['name'] = ( - - ); - } - if (threshold) { const numericThreshold = Number(threshold); if (threshold !== Math.round(numericThreshold).toString()) { @@ -187,10 +180,87 @@ const validate = ({ name, description, threshold }) => { } } + if (localizedTexts.length < 1) { + errors['_error'] = ( + + ); + } + + const localizedTextsErrors = {}; + for (let i = 0; i < localizedTexts.length; ++i) { + const localeErrors = {}; + if (!localizedTexts[i]) { + localeErrors['locale'] = ( + + ); + } else { + if (!localizedTexts[i].name) { + localeErrors['name'] = ( + + ); + } + + if (!localizedTexts[i].locale) { + localeErrors['locale'] = ( + + ); + } + + if (!localizedTexts[i].text) { + localeErrors['text'] = ( + + ); + } + + if (!localizedTexts[i].description) { + localeErrors['description'] = ( + + ); + } + } + + localizedTextsErrors[i] = localeErrors; + } + + const localeArr = localizedTexts + .filter(text => text !== undefined) + .map(text => text.locale); + for (let i = 0; i < localeArr.length; ++i) { + if (localeArr.indexOf(localeArr[i]) !== i) { + if (!localizedTextsErrors[i].locale) { + localizedTextsErrors[i].locale = ( + + ); + } + } + } + errors['localizedTexts'] = localizedTextsErrors; + return errors; }; export default reduxForm({ - form: 'edit-group', + form: 'editGroup', validate })(EditGroupForm); diff --git a/src/pages/Instance/Instance.js b/src/pages/Instance/Instance.js index 767f94223..d1a888f02 100644 --- a/src/pages/Instance/Instance.js +++ b/src/pages/Instance/Instance.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; +import { getFormValues } from 'redux-form'; import { FormattedMessage } from 'react-intl'; import { Row, Col } from 'react-bootstrap'; import Button from '../../components/widgets/FlatButton'; @@ -11,7 +12,7 @@ import Page from '../../components/layout/Page'; import InstanceDetail from '../../components/Instances/InstanceDetail'; import LicencesTableContainer from '../../containers/LicencesTableContainer'; import AddLicenceFormContainer from '../../containers/AddLicenceFormContainer'; -import CreateGroupForm from '../../components/forms/CreateGroupForm'; +import EditGroupForm from '../../components/forms/EditGroupForm'; import { EditIcon } from '../../components/icons'; import { fetchInstanceIfNeeded } from '../../redux/modules/instances'; @@ -52,6 +53,7 @@ class Instance extends Component { createGroup, isAdmin, isSuperAdmin, + formValues, links: { ADMIN_EDIT_INSTANCE_URI_FACTORY } } = this.props; @@ -106,9 +108,14 @@ class Instance extends Component { {(isSuperAdmin || isAdmin) && - @@ -133,7 +140,8 @@ Instance.propTypes = { createGroup: PropTypes.func.isRequired, isAdmin: PropTypes.bool.isRequired, isSuperAdmin: PropTypes.bool.isRequired, - links: PropTypes.object.isRequired + links: PropTypes.object.isRequired, + formValues: PropTypes.object }; export default withLinks( @@ -144,7 +152,8 @@ export default withLinks( instance: instanceSelector(state, instanceId), groups: publicGroupsSelectors(state), isAdmin: isAdminOfInstance(userId, instanceId)(state), - isSuperAdmin: isSuperAdmin(userId)(state) + isSuperAdmin: isSuperAdmin(userId)(state), + formValues: getFormValues('editGroup')(state) }; }, (dispatch, { params: { instanceId } }) => ({ From bf72560c00b6f98a6ce8e1f6051925c23167bbc6 Mon Sep 17 00:00:00 2001 From: Petr Stefan Date: Fri, 10 Nov 2017 23:46:17 +0100 Subject: [PATCH 2/4] Group name is localized --- .../AdminAssignmentsTableRow.js | 2 +- .../AssignmentTableRow/AssignmentTableRow.js | 2 +- .../ExerciseDetail/ExerciseDetail.js | 2 +- .../ExercisesListItem/ExercisesListItem.js | 2 +- .../Exercises/ExercisesName/ExercisesName.js | 2 +- .../ExercisesSimpleListItem.js | 2 +- .../Groups/AdminsView/AdminsView.js | 12 +++--- .../Groups/GroupDetail/GroupDetail.js | 16 ++++--- .../Groups/GroupsList/GroupsList.js | 22 +++++----- .../Groups/GroupsName/GroupsName.js | 25 +++++++---- .../Groups/ResultsTable/ResultsTable.js | 2 +- .../Groups/StudentsView/StudentsView.js | 21 ++++----- .../Groups/SupervisorsView/SupervisorsView.js | 33 +++++++------- .../EditAssignmentForm/EditAssignmentForm.js | 2 +- .../EditExerciseForm/EditExerciseForm.js | 2 +- .../ForkExerciseForm/ForkExerciseForm.js | 9 ++-- .../SisBindGroupForm/SisBindGroupForm.js | 20 ++++++--- .../SisCreateGroupForm/SisCreateGroupForm.js | 19 +++++--- .../helpers/LocalizedExerciseName/index.js | 1 - .../LocalizedExerciseName.js | 4 +- .../LocalizedNames/LocalizedGroupName.js | 43 +++++++++++++++++++ .../helpers/LocalizedNames/index.js | 2 + .../SisIntegrationContainer.js | 13 +++--- .../SisSupervisorGroupsContainer.js | 6 ++- src/pages/Assignment/Assignment.js | 2 +- src/pages/Dashboard/Dashboard.js | 9 ++-- src/pages/EditExercise/EditExercise.js | 2 +- .../EditExerciseConfig/EditExerciseConfig.js | 2 +- src/pages/EditGroup/EditGroup.js | 13 +++--- src/pages/Exercise/Exercise.js | 2 +- src/pages/Group/Group.js | 3 +- src/pages/Submission/Submission.js | 2 +- src/pages/User/User.js | 7 ++- 33 files changed, 196 insertions(+), 110 deletions(-) delete mode 100644 src/components/helpers/LocalizedExerciseName/index.js rename src/components/helpers/{LocalizedExerciseName => LocalizedNames}/LocalizedExerciseName.js (91%) create mode 100644 src/components/helpers/LocalizedNames/LocalizedGroupName.js create mode 100644 src/components/helpers/LocalizedNames/index.js diff --git a/src/components/Assignments/AdminAssignmentsTable/AdminAssignmentsTableRow.js b/src/components/Assignments/AdminAssignmentsTable/AdminAssignmentsTableRow.js index a06368f76..357d3e928 100644 --- a/src/components/Assignments/AdminAssignmentsTable/AdminAssignmentsTableRow.js +++ b/src/components/Assignments/AdminAssignmentsTable/AdminAssignmentsTableRow.js @@ -8,7 +8,7 @@ import { LinkContainer } from 'react-router-bootstrap'; import DeleteAssignmentButtonContainer from '../../../containers/DeleteAssignmentButtonContainer'; import withLinks from '../../../hoc/withLinks'; -import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../helpers/LocalizedNames'; import { EditIcon, MaybePublicIcon, diff --git a/src/components/Assignments/Assignment/AssignmentTableRow/AssignmentTableRow.js b/src/components/Assignments/Assignment/AssignmentTableRow/AssignmentTableRow.js index 3143def0c..5b0eb4c6c 100644 --- a/src/components/Assignments/Assignment/AssignmentTableRow/AssignmentTableRow.js +++ b/src/components/Assignments/Assignment/AssignmentTableRow/AssignmentTableRow.js @@ -5,7 +5,7 @@ import AssignmentStatusIcon from '../AssignmentStatusIcon/AssignmentStatusIcon'; import { FormattedDate, FormattedTime } from 'react-intl'; import withLinks from '../../../../hoc/withLinks'; -import LocalizedExerciseName from '../../../helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../../helpers/LocalizedNames'; import { MaybeBonusAssignmentIcon } from '../../../icons'; const AssignmentTableRow = ({ diff --git a/src/components/Exercises/ExerciseDetail/ExerciseDetail.js b/src/components/Exercises/ExerciseDetail/ExerciseDetail.js index d07387935..cc62254e4 100644 --- a/src/components/Exercises/ExerciseDetail/ExerciseDetail.js +++ b/src/components/Exercises/ExerciseDetail/ExerciseDetail.js @@ -18,7 +18,7 @@ import GroupsNameContainer from '../../../containers/GroupsNameContainer'; import styles from './ExerciseDetail.less'; import { MaybeSucceededIcon } from '../../icons'; import { getLocalizedDescription } from '../../../helpers/getLocalizedData'; -import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../helpers/LocalizedNames'; const ExerciseDetail = ({ id, diff --git a/src/components/Exercises/ExercisesListItem/ExercisesListItem.js b/src/components/Exercises/ExercisesListItem/ExercisesListItem.js index a6cebac50..b675d24a2 100644 --- a/src/components/Exercises/ExercisesListItem/ExercisesListItem.js +++ b/src/components/Exercises/ExercisesListItem/ExercisesListItem.js @@ -8,7 +8,7 @@ import GroupsNameContainer from '../../../containers/GroupsNameContainer'; import { Link } from 'react-router'; import withLinks from '../../../hoc/withLinks'; -import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../helpers/LocalizedNames'; import { MaybeLockedExerciseIcon } from '../../icons'; const ExercisesListItem = ({ diff --git a/src/components/Exercises/ExercisesName/ExercisesName.js b/src/components/Exercises/ExercisesName/ExercisesName.js index 06c64582e..19b00a590 100644 --- a/src/components/Exercises/ExercisesName/ExercisesName.js +++ b/src/components/Exercises/ExercisesName/ExercisesName.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { Link } from 'react-router'; import withLinks from '../../../hoc/withLinks'; -import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../helpers/LocalizedNames'; import { MaybeLockedExerciseIcon } from '../../icons'; diff --git a/src/components/Exercises/ExercisesSimpleListItem/ExercisesSimpleListItem.js b/src/components/Exercises/ExercisesSimpleListItem/ExercisesSimpleListItem.js index 3725bf80c..294c6a32f 100644 --- a/src/components/Exercises/ExercisesSimpleListItem/ExercisesSimpleListItem.js +++ b/src/components/Exercises/ExercisesSimpleListItem/ExercisesSimpleListItem.js @@ -5,7 +5,7 @@ import UsersNameContainer from '../../../containers/UsersNameContainer'; import { Link } from 'react-router'; import withLinks from '../../../hoc/withLinks'; -import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../helpers/LocalizedNames'; import { MaybeLockedExerciseIcon } from '../../icons'; const ExercisesSimpleListItem = ({ diff --git a/src/components/Groups/AdminsView/AdminsView.js b/src/components/Groups/AdminsView/AdminsView.js index cf92f5257..469ca95c9 100644 --- a/src/components/Groups/AdminsView/AdminsView.js +++ b/src/components/Groups/AdminsView/AdminsView.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl } from 'react-intl'; import { Row, Col } from 'react-bootstrap'; import { getFormValues } from 'redux-form'; import { connect } from 'react-redux'; @@ -8,8 +8,9 @@ import { connect } from 'react-redux'; import Box from '../../widgets/Box'; import AddSupervisor from '../AddSupervisor'; import EditGroupForm from '../../forms/EditGroupForm'; +import { getLocalizedName } from '../../../helpers/getLocalizedData'; -const AdminsView = ({ group, addSubgroup, formValues }) => +const AdminsView = ({ group, addSubgroup, formValues, intl: { locale } }) =>
@@ -17,7 +18,7 @@ const AdminsView = ({ group, addSubgroup, formValues }) => @@ -51,7 +52,8 @@ const AdminsView = ({ group, addSubgroup, formValues }) => AdminsView.propTypes = { group: PropTypes.object.isRequired, addSubgroup: PropTypes.func.isRequired, - formValues: PropTypes.object + formValues: PropTypes.object, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; export default connect( @@ -61,4 +63,4 @@ export default connect( }; }, dispatch => ({}) -)(AdminsView); +)(injectIntl(AdminsView)); diff --git a/src/components/Groups/GroupDetail/GroupDetail.js b/src/components/Groups/GroupDetail/GroupDetail.js index ea78b406e..710a3275d 100644 --- a/src/components/Groups/GroupDetail/GroupDetail.js +++ b/src/components/Groups/GroupDetail/GroupDetail.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { FormattedMessage, FormattedNumber } from 'react-intl'; +import { FormattedMessage, FormattedNumber, injectIntl } from 'react-intl'; import { Row, Col, Table } from 'react-bootstrap'; import ReactMarkdown from 'react-remarkable'; @@ -9,12 +9,14 @@ import Box from '../../widgets/Box'; import SupervisorsList from '../../Users/SupervisorsList'; import { MaybeSucceededIcon } from '../../icons'; import GroupTree from '../GroupTree'; +import { getLocalizedName } from '../../../helpers/getLocalizedData'; const GroupDetail = ({ group: { id, externalId, name, + localizedTexts, description, threshold, parentGroupId, @@ -26,7 +28,8 @@ const GroupDetail = ({ groups, publicGroups, supervisors, - isAdmin + isAdmin, + intl: { locale } }) =>
@@ -123,7 +126,9 @@ const GroupDetail = ({ } > @@ -157,7 +162,8 @@ GroupDetail.propTypes = { groups: PropTypes.object.isRequired, publicGroups: ImmutablePropTypes.map.isRequired, supervisors: PropTypes.array.isRequired, - isAdmin: PropTypes.bool + isAdmin: PropTypes.bool, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; -export default GroupDetail; +export default injectIntl(GroupDetail); diff --git a/src/components/Groups/GroupsList/GroupsList.js b/src/components/Groups/GroupsList/GroupsList.js index 1061c3fc7..06ffe34c6 100644 --- a/src/components/Groups/GroupsList/GroupsList.js +++ b/src/components/Groups/GroupsList/GroupsList.js @@ -1,11 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Link } from 'react-router'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { Table } from 'react-bootstrap'; import ResourceRenderer from '../../helpers/ResourceRenderer'; import Icon from 'react-fontawesome'; +import GroupsName from '../GroupsName'; import withLinks from '../../../hoc/withLinks'; const GroupsList = ({ @@ -13,29 +13,31 @@ const GroupsList = ({ renderButtons = () => null, links: { GROUP_URI_FACTORY }, ...props -}) => ( +}) => - {(...groups) => ( + {(...groups) => - {groups.map(({ id, name }) => ( + {groups.map(({ id, name, localizedTexts }) => - ))} + )} -
- {name} + {renderButtons(id)}
- )} -
-); + } + ; GroupsList.propTypes = { groups: ImmutablePropTypes.map.isRequired, diff --git a/src/components/Groups/GroupsName/GroupsName.js b/src/components/Groups/GroupsName/GroupsName.js index 29d623e4a..dbc059c62 100644 --- a/src/components/Groups/GroupsName/GroupsName.js +++ b/src/components/Groups/GroupsName/GroupsName.js @@ -2,20 +2,29 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router'; import withLinks from '../../../hoc/withLinks'; +import { LocalizedGroupName } from '../../helpers/LocalizedNames'; -const GroupsName = ({ id, name, noLink, links: { GROUP_URI_FACTORY } }) => ( +const GroupsName = ({ + id, + name, + localizedTexts, + noLink, + links: { GROUP_URI_FACTORY } +}) => - {noLink ? ( - {name} - ) : ( - {name} - )} - -); + {noLink + ? + + + : + + } + ; GroupsName.propTypes = { id: PropTypes.string.isRequired, name: PropTypes.string.isRequired, + localizedTexts: PropTypes.array.isRequired, noLink: PropTypes.bool, links: PropTypes.object }; diff --git a/src/components/Groups/ResultsTable/ResultsTable.js b/src/components/Groups/ResultsTable/ResultsTable.js index 33eabdc15..f078c3e5b 100644 --- a/src/components/Groups/ResultsTable/ResultsTable.js +++ b/src/components/Groups/ResultsTable/ResultsTable.js @@ -10,7 +10,7 @@ import LoadingResultsTableRow from './LoadingResultsTableRow'; import NoResultsAvailableRow from './NoResultsAvailableRow'; import withLinks from '../../../hoc/withLinks'; import ResourceRenderer from '../../helpers/ResourceRenderer'; -import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../helpers/LocalizedNames'; import styles from './ResultsTable.less'; const ResultsTable = ({ diff --git a/src/components/Groups/StudentsView/StudentsView.js b/src/components/Groups/StudentsView/StudentsView.js index 850101086..563c89f1e 100644 --- a/src/components/Groups/StudentsView/StudentsView.js +++ b/src/components/Groups/StudentsView/StudentsView.js @@ -1,21 +1,22 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl } from 'react-intl'; import { Row, Col } from 'react-bootstrap'; import Box from '../../widgets/Box'; import AssignmentsTable from '../../Assignments/Assignment/AssignmentsTable'; import StudentsListContainer from '../../../containers/StudentsListContainer'; -import LeaveJoinGroupButtonContainer - from '../../../containers/LeaveJoinGroupButtonContainer'; +import LeaveJoinGroupButtonContainer from '../../../containers/LeaveJoinGroupButtonContainer'; +import { getLocalizedName } from '../../../helpers/getLocalizedData'; const StudentsView = ({ group, statuses = [], assignments, - isAdmin = false -}) => ( + isAdmin = false, + intl: { locale } +}) =>
@@ -23,7 +24,7 @@ const StudentsView = ({ @@ -75,14 +76,14 @@ const StudentsView = ({ -
-); +
; StudentsView.propTypes = { group: PropTypes.object.isRequired, assignments: ImmutablePropTypes.list.isRequired, statuses: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), - isAdmin: PropTypes.bool + isAdmin: PropTypes.bool, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; -export default StudentsView; +export default injectIntl(StudentsView); diff --git a/src/components/Groups/SupervisorsView/SupervisorsView.js b/src/components/Groups/SupervisorsView/SupervisorsView.js index 45db80f13..15e333e75 100644 --- a/src/components/Groups/SupervisorsView/SupervisorsView.js +++ b/src/components/Groups/SupervisorsView/SupervisorsView.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl } from 'react-intl'; import { connect } from 'react-redux'; import { Row, Col } from 'react-bootstrap'; import { LinkContainer } from 'react-router-bootstrap'; @@ -19,6 +19,7 @@ import AssignExerciseButton from '../../../components/buttons/AssignExerciseButt import { deleteExercise } from '../../../redux/modules/exercises'; import Confirm from '../../../components/forms/Confirm'; import withLinks from '../../../hoc/withLinks'; +import { getLocalizedName } from '../../../helpers/getLocalizedData'; const SupervisorsView = ({ group, @@ -29,8 +30,9 @@ const SupervisorsView = ({ deleteExercise, users, publicAssignments, - links: { EXERCISE_EDIT_URI_FACTORY, EXERCISE_EDIT_CONFIG_URI_FACTORY } -}) => ( + links: { EXERCISE_EDIT_URI_FACTORY, EXERCISE_EDIT_CONFIG_URI_FACTORY }, + intl: { locale } +}) =>
@@ -38,7 +40,7 @@ const SupervisorsView = ({ @@ -57,9 +59,8 @@ const SupervisorsView = ({ noPadding > - {(...assignments) => ( - - )} + {(...assignments) => + } @@ -133,10 +134,10 @@ const SupervisorsView = ({ isOpen > - {(...exercises) => ( + {(...exercises) => ( + createActions={(exerciseId, isLocked) =>
-
- )} - /> - )} +
} + />}
-
-); + ; SupervisorsView.propTypes = { group: PropTypes.object, @@ -208,7 +206,8 @@ SupervisorsView.propTypes = { deleteExercise: PropTypes.func.isRequired, users: PropTypes.array.isRequired, publicAssignments: ImmutablePropTypes.list.isRequired, - links: PropTypes.object + links: PropTypes.object, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; export default withLinks( @@ -217,5 +216,5 @@ export default withLinks( dispatch => ({ deleteExercise: id => dispatch(deleteExercise(id)) }) - )(SupervisorsView) + )(injectIntl(SupervisorsView)) ); diff --git a/src/components/forms/EditAssignmentForm/EditAssignmentForm.js b/src/components/forms/EditAssignmentForm/EditAssignmentForm.js index 7fecff7fc..c22f061ea 100644 --- a/src/components/forms/EditAssignmentForm/EditAssignmentForm.js +++ b/src/components/forms/EditAssignmentForm/EditAssignmentForm.js @@ -11,7 +11,7 @@ import LocalizedTextsFormField from '../LocalizedTextsFormField'; import SubmitButton from '../SubmitButton'; import { validateAssignment } from '../../../redux/modules/assignments'; -import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../helpers/LocalizedNames'; const EditAssignmentForm = ({ initialValues: assignment, diff --git a/src/components/forms/EditExerciseForm/EditExerciseForm.js b/src/components/forms/EditExerciseForm/EditExerciseForm.js index 611c402e7..81be6c89f 100644 --- a/src/components/forms/EditExerciseForm/EditExerciseForm.js +++ b/src/components/forms/EditExerciseForm/EditExerciseForm.js @@ -18,7 +18,7 @@ import FormBox from '../../widgets/FormBox'; import SubmitButton from '../SubmitButton'; import Button from '../../widgets/FlatButton'; import LocalizedTextsFormField from '../LocalizedTextsFormField'; -import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../helpers/LocalizedNames'; import { validateExercise } from '../../../redux/modules/exercises'; import withLinks from '../../../hoc/withLinks'; diff --git a/src/components/forms/ForkExerciseForm/ForkExerciseForm.js b/src/components/forms/ForkExerciseForm/ForkExerciseForm.js index 203da743e..20119434d 100644 --- a/src/components/forms/ForkExerciseForm/ForkExerciseForm.js +++ b/src/components/forms/ForkExerciseForm/ForkExerciseForm.js @@ -13,6 +13,7 @@ import { SuccessIcon } from '../../../components/icons'; import { forkStatuses } from '../../../redux/modules/exercises'; import { getFork } from '../../../redux/selectors/exercises'; import ResourceRenderer from '../../helpers/ResourceRenderer'; +import { getLocalizedName } from '../../../helpers/getLocalizedData'; import withLinks from '../../../hoc/withLinks'; @@ -40,7 +41,7 @@ class ForkExerciseForm extends Component { hasSucceeded = false, invalid, groups, - intl + intl: { locale } } = this.props; switch (forkStatus) { @@ -78,13 +79,11 @@ class ForkExerciseForm extends Component { label={''} options={[{ key: '', name: '_Public_' }].concat( groups - .sort((a, b) => - a.name.localeCompare(b.name, intl.locale) - ) + .sort((a, b) => a.name.localeCompare(b.name, locale)) .filter((item, pos, arr) => arr.indexOf(item) === pos) .map(group => ({ key: group.id, - name: group.name + name: getLocalizedName(group, locale) })) )} />} diff --git a/src/components/forms/SisBindGroupForm/SisBindGroupForm.js b/src/components/forms/SisBindGroupForm/SisBindGroupForm.js index afe7a0cc3..454210a72 100644 --- a/src/components/forms/SisBindGroupForm/SisBindGroupForm.js +++ b/src/components/forms/SisBindGroupForm/SisBindGroupForm.js @@ -1,14 +1,15 @@ import React from 'react'; import PropTypes from 'prop-types'; import { reduxForm, Field } from 'redux-form'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl } from 'react-intl'; +import { Alert } from 'react-bootstrap'; import FormBox from '../../widgets/FormBox'; import { SelectField } from '../Fields'; - -import { Alert } from 'react-bootstrap'; import SubmitButton from '../SubmitButton'; +import { getLocalizedName } from '../../../helpers/getLocalizedData'; + const SisBindGroupForm = ({ invalid, anyTouched, @@ -16,7 +17,8 @@ const SisBindGroupForm = ({ submitFailed: hasFailed, submitting, hasSucceeded, - groups + groups, + intl: { locale } }) => } options={[{ key: '', name: '...' }].concat( - groups.map(group => ({ key: group.id, name: group.name })) + groups.map(group => ({ + key: group.id, + name: getLocalizedName(group, locale) + })) )} /> ; @@ -92,7 +97,8 @@ SisBindGroupForm.propTypes = { submitting: PropTypes.bool, hasSucceeded: PropTypes.bool, submitFailed: PropTypes.bool, - groups: PropTypes.array + groups: PropTypes.array, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; const validate = ({ groupId }) => { @@ -113,4 +119,4 @@ const validate = ({ groupId }) => { export default reduxForm({ form: 'sisBindGroup', validate -})(SisBindGroupForm); +})(injectIntl(SisBindGroupForm)); diff --git a/src/components/forms/SisCreateGroupForm/SisCreateGroupForm.js b/src/components/forms/SisCreateGroupForm/SisCreateGroupForm.js index 715ea626c..928c70e55 100644 --- a/src/components/forms/SisCreateGroupForm/SisCreateGroupForm.js +++ b/src/components/forms/SisCreateGroupForm/SisCreateGroupForm.js @@ -1,13 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types'; import { reduxForm, Field } from 'redux-form'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl } from 'react-intl'; +import { Alert } from 'react-bootstrap'; import FormBox from '../../widgets/FormBox'; import { SelectField } from '../Fields'; - -import { Alert } from 'react-bootstrap'; import SubmitButton from '../SubmitButton'; +import { getLocalizedName } from '../../../helpers/getLocalizedData'; const SisCreateGroupForm = ({ invalid, @@ -16,7 +16,8 @@ const SisCreateGroupForm = ({ submitFailed: hasFailed, submitting, hasSucceeded, - groups + groups, + intl: { locale } }) => } options={[{ key: '', name: '...' }].concat( - groups.map(group => ({ key: group.id, name: group.name })) + groups.map(group => ({ + key: group.id, + name: getLocalizedName(group, locale) + })) )} /> ; @@ -92,7 +96,8 @@ SisCreateGroupForm.propTypes = { submitting: PropTypes.bool, hasSucceeded: PropTypes.bool, submitFailed: PropTypes.bool, - groups: PropTypes.array + groups: PropTypes.array, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; const validate = ({ parentGroupId }) => { @@ -113,4 +118,4 @@ const validate = ({ parentGroupId }) => { export default reduxForm({ form: 'sisCreateGroup', validate -})(SisCreateGroupForm); +})(injectIntl(SisCreateGroupForm)); diff --git a/src/components/helpers/LocalizedExerciseName/index.js b/src/components/helpers/LocalizedExerciseName/index.js deleted file mode 100644 index d9cd439e2..000000000 --- a/src/components/helpers/LocalizedExerciseName/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './LocalizedExerciseName'; diff --git a/src/components/helpers/LocalizedExerciseName/LocalizedExerciseName.js b/src/components/helpers/LocalizedNames/LocalizedExerciseName.js similarity index 91% rename from src/components/helpers/LocalizedExerciseName/LocalizedExerciseName.js rename to src/components/helpers/LocalizedNames/LocalizedExerciseName.js index c46ece4f5..89ba8e17b 100644 --- a/src/components/helpers/LocalizedExerciseName/LocalizedExerciseName.js +++ b/src/components/helpers/LocalizedNames/LocalizedExerciseName.js @@ -20,8 +20,8 @@ const LocalizedExerciseName = ({ entity, intl: { locale } }) => { placement="right" overlay={ n.name).join(', ')}> - {otherNames.map(name => -
+ {otherNames.map((name, i) => +
{name.name} [{name.locale}]
)} diff --git a/src/components/helpers/LocalizedNames/LocalizedGroupName.js b/src/components/helpers/LocalizedNames/LocalizedGroupName.js new file mode 100644 index 000000000..df2bcb925 --- /dev/null +++ b/src/components/helpers/LocalizedNames/LocalizedGroupName.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { injectIntl } from 'react-intl'; +import { OverlayTrigger, Tooltip } from 'react-bootstrap'; +import Icon from 'react-fontawesome'; + +import { + getLocalizedName, + getOtherLocalizedNames +} from '../../../helpers/getLocalizedData'; + +const LocalizedGroupName = ({ entity, intl: { locale } }) => { + const otherNames = getOtherLocalizedNames(entity, locale); + return ( + + {getLocalizedName(entity, locale)} + {otherNames.length > 0 && + +   n.name).join(', ')}> + {otherNames.map((name, i) => +
+ {name.name} [{name.locale}] +
+ )} + + } + > + +
  +
} +
+ ); +}; + +LocalizedGroupName.propTypes = { + entity: PropTypes.object, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired +}; + +export default injectIntl(LocalizedGroupName); diff --git a/src/components/helpers/LocalizedNames/index.js b/src/components/helpers/LocalizedNames/index.js new file mode 100644 index 000000000..9907bce02 --- /dev/null +++ b/src/components/helpers/LocalizedNames/index.js @@ -0,0 +1,2 @@ +export { default as LocalizedExerciseName } from './LocalizedExerciseName'; +export { default as LocalizedGroupName } from './LocalizedGroupName'; diff --git a/src/containers/SisIntegrationContainer/SisIntegrationContainer.js b/src/containers/SisIntegrationContainer/SisIntegrationContainer.js index 917ce0ca5..848feded8 100644 --- a/src/containers/SisIntegrationContainer/SisIntegrationContainer.js +++ b/src/containers/SisIntegrationContainer/SisIntegrationContainer.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl } from 'react-intl'; import Box from '../../components/widgets/Box'; import { Table } from 'react-bootstrap'; import Button from '../../components/widgets/FlatButton'; @@ -15,6 +15,7 @@ import { sisSubscribedGroupsSelector } from '../../redux/selectors/sisSubscribed import { loggedInUserIdSelector } from '../../redux/selectors/auth'; import ResourceRenderer from '../../components/helpers/ResourceRenderer'; import LeaveJoinGroupButtonContainer from '../LeaveJoinGroupButtonContainer'; +import { getLocalizedName } from '../../helpers/getLocalizedData'; import withLinks from '../../hoc/withLinks'; @@ -40,7 +41,8 @@ class SisIntegrationContainer extends Component { sisStatus, currentUserId, sisGroups, - links: { GROUP_URI_FACTORY } + links: { GROUP_URI_FACTORY }, + intl: { locale } } = this.props; return ( - {group.name} + {getLocalizedName(group, locale)} @@ -161,7 +163,8 @@ SisIntegrationContainer.propTypes = { currentUserId: PropTypes.string, loadData: PropTypes.func.isRequired, sisGroups: PropTypes.func.isRequired, - links: PropTypes.object + links: PropTypes.object, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; export default withLinks( @@ -179,5 +182,5 @@ export default withLinks( loadData: loggedInUserId => SisIntegrationContainer.loadData(dispatch, loggedInUserId) }) - )(SisIntegrationContainer) + )(injectIntl(SisIntegrationContainer)) ); diff --git a/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js b/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js index 620930f2d..219bf26b7 100644 --- a/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js +++ b/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js @@ -22,6 +22,7 @@ import { loggedInUserIdSelector } from '../../redux/selectors/auth'; import ResourceRenderer from '../../components/helpers/ResourceRenderer'; import SisCreateGroupForm from '../../components/forms/SisCreateGroupForm'; import SisBindGroupForm from '../../components/forms/SisBindGroupForm'; +import { getLocalizedName } from '../../helpers/getLocalizedData'; import withLinks from '../../hoc/withLinks'; import './SisSupervisorGroupsContainer.css'; @@ -189,7 +190,10 @@ class SisSupervisorGroupsContainer extends Component { {course.groups.map((group, i) => - {group.name} + {getLocalizedName( + group, + locale + )} diff --git a/src/pages/Assignment/Assignment.js b/src/pages/Assignment/Assignment.js index d588ca906..d25c00f06 100644 --- a/src/pages/Assignment/Assignment.js +++ b/src/pages/Assignment/Assignment.js @@ -35,7 +35,7 @@ import { runtimeEnvironmentSelector } from '../../redux/selectors/runtimeEnviron import Page from '../../components/layout/Page'; import ResourceRenderer from '../../components/helpers/ResourceRenderer'; -import LocalizedExerciseName from '../../components/helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../components/helpers/LocalizedNames'; import UsersNameContainer from '../../containers/UsersNameContainer'; import { ResubmitAllSolutionsContainer } from '../../containers/ResubmitSolutionContainer'; import HierarchyLineContainer from '../../containers/HierarchyLineContainer'; diff --git a/src/pages/Dashboard/Dashboard.js b/src/pages/Dashboard/Dashboard.js index 56e719d2e..eeaae5c9c 100644 --- a/src/pages/Dashboard/Dashboard.js +++ b/src/pages/Dashboard/Dashboard.js @@ -16,6 +16,7 @@ import UsersNameContainer from '../../containers/UsersNameContainer'; import StudentsListContainer from '../../containers/StudentsListContainer'; import AssignmentsTable from '../../components/Assignments/Assignment/AssignmentsTable'; import UsersStats from '../../components/Users/UsersStats'; +import GroupsName from '../../components/Groups/GroupsName'; import { fetchAssignmentsForGroup } from '../../redux/modules/assignments'; import { fetchUserIfNeeded } from '../../redux/modules/users'; import { @@ -206,7 +207,9 @@ class Dashboard extends Component { loading={ - + } + /> } @@ -224,7 +227,7 @@ class Dashboard extends Component { } collapsable noPadding isOpen @@ -287,7 +290,7 @@ class Dashboard extends Component { > {statistics => } collapsable noPadding isOpen diff --git a/src/pages/EditExercise/EditExercise.js b/src/pages/EditExercise/EditExercise.js index 16381e4fc..8e54ddd5d 100644 --- a/src/pages/EditExercise/EditExercise.js +++ b/src/pages/EditExercise/EditExercise.js @@ -12,7 +12,7 @@ import Box from '../../components/widgets/Box'; import EditExerciseForm from '../../components/forms/EditExerciseForm'; import AdditionalExerciseFilesTableContainer from '../../containers/AdditionalExerciseFilesTableContainer'; import DeleteExerciseButtonContainer from '../../containers/DeleteExerciseButtonContainer'; -import LocalizedExerciseName from '../../components/helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../components/helpers/LocalizedNames'; import { fetchExerciseIfNeeded, diff --git a/src/pages/EditExerciseConfig/EditExerciseConfig.js b/src/pages/EditExerciseConfig/EditExerciseConfig.js index 4eaaecc5f..ae2514f1d 100644 --- a/src/pages/EditExerciseConfig/EditExerciseConfig.js +++ b/src/pages/EditExerciseConfig/EditExerciseConfig.js @@ -9,7 +9,7 @@ import { getFormValues } from 'redux-form'; import Page from '../../components/layout/Page'; import Box from '../../components/widgets/Box'; import ResourceRenderer from '../../components/helpers/ResourceRenderer'; -import LocalizedExerciseName from '../../components/helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../components/helpers/LocalizedNames'; import EditExerciseConfigForm from '../../components/forms/EditExerciseConfigForm/EditExerciseConfigForm'; import EditEnvironmentConfigForm from '../../components/forms/EditEnvironmentConfigForm'; diff --git a/src/pages/EditGroup/EditGroup.js b/src/pages/EditGroup/EditGroup.js index 19a5f7751..0713a4a9a 100644 --- a/src/pages/EditGroup/EditGroup.js +++ b/src/pages/EditGroup/EditGroup.js @@ -9,9 +9,9 @@ import { reset } from 'redux-form'; import Page from '../../components/layout/Page'; import EditGroupForm from '../../components/forms/EditGroupForm'; -import DeleteGroupButtonContainer - from '../../containers/DeleteGroupButtonContainer'; +import DeleteGroupButtonContainer from '../../containers/DeleteGroupButtonContainer'; import Box from '../../components/widgets/Box'; +import { LocalizedGroupName } from '../../components/helpers/LocalizedNames'; import { fetchGroupIfNeeded, editGroup } from '../../redux/modules/groups'; import { groupSelector } from '../../redux/selectors/groups'; @@ -46,7 +46,7 @@ class EditGroup extends Component { return ( group.name} + title={group => } description={ - {group => ( + {group =>
0) + (group.childGroups && group.childGroups.length > 0) } onDeleted={() => push(GROUP_URI_FACTORY(group.parentGroupId))} @@ -114,8 +114,7 @@ class EditGroup extends Component {

-
- )} + } ); } diff --git a/src/pages/Exercise/Exercise.js b/src/pages/Exercise/Exercise.js index e93a7a573..ebc1b32dd 100644 --- a/src/pages/Exercise/Exercise.js +++ b/src/pages/Exercise/Exercise.js @@ -17,7 +17,7 @@ import Page from '../../components/layout/Page'; import ExerciseDetail from '../../components/Exercises/ExerciseDetail'; import LocalizedTexts from '../../components/helpers/LocalizedTexts'; import ResourceRenderer from '../../components/helpers/ResourceRenderer'; -import LocalizedExerciseName from '../../components/helpers/LocalizedExerciseName'; +import { LocalizedExerciseName } from '../../components/helpers/LocalizedNames'; import GroupsList from '../../components/Groups/GroupsList'; import ReferenceSolutionsList from '../../components/Exercises/ReferenceSolutionsList'; import SubmitSolutionContainer from '../../containers/SubmitSolutionContainer'; diff --git a/src/pages/Group/Group.js b/src/pages/Group/Group.js index 25da87fda..0ccadbd79 100644 --- a/src/pages/Group/Group.js +++ b/src/pages/Group/Group.js @@ -19,6 +19,7 @@ import SupervisorsView from '../../components/Groups/SupervisorsView'; import StudentsView from '../../components/Groups/StudentsView'; import HierarchyLine from '../../components/Groups/HierarchyLine'; import { EditIcon } from '../../components/icons'; +import { LocalizedGroupName } from '../../components/helpers/LocalizedNames'; import { isReady, getJsData } from '../../redux/helpers/resourceManager'; import { @@ -188,7 +189,7 @@ class Group extends Component { return ( group.name} + title={group => } description={ - + } + />
} @@ -162,7 +165,7 @@ class User extends Component { } collapsable noPadding isOpen From 7e4f03d22373c3652b993b53a67f63b9959077a1 Mon Sep 17 00:00:00 2001 From: Petr Stefan Date: Sat, 11 Nov 2017 15:52:39 +0100 Subject: [PATCH 3/4] Translate group description, sidebar & edit form --- .../Groups/GroupDetail/GroupDetail.js | 14 ++++- .../forms/EditGroupForm/EditGroupForm.js | 1 + ...Field.js => LocalizedExerciseFormField.js} | 6 +-- .../LocalizedGroupFormField.js | 51 +++++++++++++++++++ .../LocalizedTextsFormField.js | 33 +++++++----- .../widgets/Sidebar/MenuGroup/MenuGroup.js | 18 +++++-- 6 files changed, 101 insertions(+), 22 deletions(-) rename src/components/forms/LocalizedTextsFormField/{LocalizedTextFormField.js => LocalizedExerciseFormField.js} (90%) create mode 100644 src/components/forms/LocalizedTextsFormField/LocalizedGroupFormField.js diff --git a/src/components/Groups/GroupDetail/GroupDetail.js b/src/components/Groups/GroupDetail/GroupDetail.js index 710a3275d..df879e5a8 100644 --- a/src/components/Groups/GroupDetail/GroupDetail.js +++ b/src/components/Groups/GroupDetail/GroupDetail.js @@ -9,7 +9,10 @@ import Box from '../../widgets/Box'; import SupervisorsList from '../../Users/SupervisorsList'; import { MaybeSucceededIcon } from '../../icons'; import GroupTree from '../GroupTree'; -import { getLocalizedName } from '../../../helpers/getLocalizedData'; +import { + getLocalizedName, + getLocalizedDescription +} from '../../../helpers/getLocalizedData'; const GroupDetail = ({ group: { @@ -43,7 +46,14 @@ const GroupDetail = ({ defaultMessage="Group description" /> } - description={} + description={ + + } type="primary" collapsable noPadding diff --git a/src/components/forms/EditGroupForm/EditGroupForm.js b/src/components/forms/EditGroupForm/EditGroupForm.js index 5187e174e..d82e3686c 100644 --- a/src/components/forms/EditGroupForm/EditGroupForm.js +++ b/src/components/forms/EditGroupForm/EditGroupForm.js @@ -86,6 +86,7 @@ const EditGroupForm = ({ name="localizedTexts" localizedTexts={localizedTexts} component={LocalizedTextsFormField} + isGroup={true} /> +const LocalizedExerciseFormField = ({ prefix }) =>
/>
; -LocalizedTextFormField.propTypes = { +LocalizedExerciseFormField.propTypes = { prefix: PropTypes.string.isRequired }; -export default LocalizedTextFormField; +export default LocalizedExerciseFormField; diff --git a/src/components/forms/LocalizedTextsFormField/LocalizedGroupFormField.js b/src/components/forms/LocalizedTextsFormField/LocalizedGroupFormField.js new file mode 100644 index 000000000..a7b72322e --- /dev/null +++ b/src/components/forms/LocalizedTextsFormField/LocalizedGroupFormField.js @@ -0,0 +1,51 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import { Field } from 'redux-form'; +import { + MarkdownTextAreaField, + LanguageSelectField, + TextField +} from '../Fields'; + +const LocalizedGroupFormField = ({ prefix }) => +
+ + } + /> + + + } + /> + + + } + /> +
; + +LocalizedGroupFormField.propTypes = { + prefix: PropTypes.string.isRequired +}; + +export default LocalizedGroupFormField; diff --git a/src/components/forms/LocalizedTextsFormField/LocalizedTextsFormField.js b/src/components/forms/LocalizedTextsFormField/LocalizedTextsFormField.js index cce4d5e8b..68f054bcd 100644 --- a/src/components/forms/LocalizedTextsFormField/LocalizedTextsFormField.js +++ b/src/components/forms/LocalizedTextsFormField/LocalizedTextsFormField.js @@ -2,45 +2,52 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; import { TabbedArrayField } from '../Fields'; -import LocalizedTextFormField from './LocalizedTextFormField'; +import LocalizedExerciseFormField from './LocalizedExerciseFormField'; +import LocalizedGroupFormField from './LocalizedGroupFormField'; -const LocalizedTextsFormField = ({ localizedTexts = [], ...props }) => ( +const LocalizedTextsFormField = ({ + localizedTexts = [], + isGroup = false, + ...props +}) => localizedTexts && localizedTexts[i] && localizedTexts[i].locale ? localizedTexts[i].locale : } - ContentComponent={LocalizedTextFormField} + ContentComponent={ + isGroup ? LocalizedGroupFormField : LocalizedExerciseFormField + } emptyMessage={ } addMessage={ } removeQuestion={ } - id="localized-assignments" + id="localized-texts" add remove - /> -); + />; LocalizedTextsFormField.propTypes = { - localizedTexts: PropTypes.array + localizedTexts: PropTypes.array, + isGroup: PropTypes.bool }; export default LocalizedTextsFormField; diff --git a/src/components/widgets/Sidebar/MenuGroup/MenuGroup.js b/src/components/widgets/Sidebar/MenuGroup/MenuGroup.js index 095100e49..ce717a189 100644 --- a/src/components/widgets/Sidebar/MenuGroup/MenuGroup.js +++ b/src/components/widgets/Sidebar/MenuGroup/MenuGroup.js @@ -1,11 +1,13 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import { injectIntl } from 'react-intl'; import classNames from 'classnames'; import Icon from 'react-fontawesome'; import MenuItem from '../MenuItem'; import LoadingMenuItem from '../LoadingMenuItem'; import { isLoading } from '../../../../redux/helpers/resourceManager'; +import { getLocalizedName } from '../../../../helpers/getLocalizedData'; class MenuGroup extends Component { componentWillMount = () => @@ -43,7 +45,8 @@ class MenuGroup extends Component { createLink, currentPath, notifications, - forceOpen = false + forceOpen = false, + intl: { locale } } = this.props; const dropdownStyles = { @@ -91,7 +94,13 @@ class MenuGroup extends Component { ? : Date: Sat, 11 Nov 2017 22:07:49 +0100 Subject: [PATCH 4/4] Fix group localization against API --- src/components/Groups/GroupTree/GroupTree.js | 11 +++- src/components/Users/UsersStats/UsersStats.js | 60 +++++++++++-------- .../forms/EditGroupForm/EditGroupForm.js | 2 +- .../widgets/Sidebar/MenuGroup/MenuGroup.js | 4 +- .../widgets/TreeView/TreeViewLeaf.js | 3 +- src/pages/Dashboard/Dashboard.js | 13 ++-- src/pages/EditGroup/EditGroup.js | 10 +++- src/pages/Group/Group.js | 14 +++-- 8 files changed, 75 insertions(+), 42 deletions(-) diff --git a/src/components/Groups/GroupTree/GroupTree.js b/src/components/Groups/GroupTree/GroupTree.js index 0ae3d0297..2d2f0ade9 100644 --- a/src/components/Groups/GroupTree/GroupTree.js +++ b/src/components/Groups/GroupTree/GroupTree.js @@ -6,6 +6,7 @@ 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 withLinks from '../../../hoc/withLinks'; @@ -59,6 +60,7 @@ class GroupTree extends Component { const { name, + localizedTexts, admins, childGroups: { all: allChildGroups, public: publicChildGroups }, canView @@ -68,7 +70,14 @@ class GroupTree extends Component { {level !== 0 && + } level={level} admins={admins} isOpen={currentGroupId === id || isOpen} diff --git a/src/components/Users/UsersStats/UsersStats.js b/src/components/Users/UsersStats/UsersStats.js index 9706f8969..51089ac14 100644 --- a/src/components/Users/UsersStats/UsersStats.js +++ b/src/components/Users/UsersStats/UsersStats.js @@ -1,37 +1,46 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { FormattedMessage, FormattedNumber } from 'react-intl'; +import { FormattedMessage, FormattedNumber, injectIntl } from 'react-intl'; import InfoBox from '../../widgets/InfoBox'; +import { getLocalizedName } from '../../../helpers/getLocalizedData'; const UsersStats = ({ id, name, - stats: { assignments, points, hasLimit, passesLimit } -}) => ( - 0 ? points.gained / points.total : 0} - style="percent" - /> - } - progress={points.total > 0 ? Math.min(1, points.gained / points.total) : 0} - description={ - - } - /> -); + localizedTexts, + stats: { assignments, points, hasLimit, passesLimit }, + intl: { locale } +}) => { + const localizedName = getLocalizedName({ name, localizedTexts }, locale); + return ( + 0 ? points.gained / points.total : 0} + style="percent" + /> + } + progress={ + points.total > 0 ? Math.min(1, points.gained / points.total) : 0 + } + description={ + + } + /> + ); +}; UsersStats.propTypes = { id: PropTypes.string.isRequired, name: PropTypes.string.isRequired, + localizedTexts: PropTypes.array.isRequired, stats: PropTypes.shape({ assignments: PropTypes.shape({ total: PropTypes.number.isRequired, @@ -41,7 +50,8 @@ UsersStats.propTypes = { total: PropTypes.number.isRequired, gained: PropTypes.number.isRequired }) - }).isRequired + }).isRequired, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; -export default UsersStats; +export default injectIntl(UsersStats); diff --git a/src/components/forms/EditGroupForm/EditGroupForm.js b/src/components/forms/EditGroupForm/EditGroupForm.js index d82e3686c..867d9f50a 100644 --- a/src/components/forms/EditGroupForm/EditGroupForm.js +++ b/src/components/forms/EditGroupForm/EditGroupForm.js @@ -48,7 +48,7 @@ const EditGroupForm = ({ submit: createNew ? : } + title={getLocalizedName(group, locale)} /> @@ -342,7 +344,8 @@ Dashboard.propTypes = { usersStatistics: PropTypes.func.isRequired, allGroups: PropTypes.array, isAdmin: PropTypes.bool, - links: PropTypes.object + links: PropTypes.object, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; export default withLinks( @@ -373,5 +376,5 @@ export default withLinks( loadAsync: loggedInUserId => Dashboard.loadAsync(params, dispatch, loggedInUserId) }) - )(Dashboard) + )(injectIntl(Dashboard)) ); diff --git a/src/pages/EditGroup/EditGroup.js b/src/pages/EditGroup/EditGroup.js index 0713a4a9a..d8e71c7a8 100644 --- a/src/pages/EditGroup/EditGroup.js +++ b/src/pages/EditGroup/EditGroup.js @@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl'; import { HelpBlock } from 'react-bootstrap'; import { connect } from 'react-redux'; import { push } from 'react-router-redux'; -import { reset } from 'redux-form'; +import { reset, getFormValues } from 'redux-form'; import Page from '../../components/layout/Page'; import EditGroupForm from '../../components/forms/EditGroupForm'; @@ -40,6 +40,7 @@ class EditGroup extends Component { group, links: { GROUP_URI_FACTORY }, editGroup, + formValues, push } = this.props; @@ -75,6 +76,7 @@ class EditGroup extends Component { isSupervisorOf(userId, groupId)(state) + isStudentOf: groupId => isSupervisorOf(userId, groupId)(state), + formValues: getFormValues('editGroup')(state) }; }, (dispatch, { params: { groupId } }) => ({ diff --git a/src/pages/Group/Group.js b/src/pages/Group/Group.js index 0ccadbd79..94f671b29 100644 --- a/src/pages/Group/Group.js +++ b/src/pages/Group/Group.js @@ -5,7 +5,7 @@ import { connect } from 'react-redux'; import { push } from 'react-router-redux'; import { LinkContainer } from 'react-router-bootstrap'; import Button from '../../components/widgets/FlatButton'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl } from 'react-intl'; import { List, Map } from 'immutable'; import Page from '../../components/layout/Page'; @@ -62,6 +62,7 @@ import { getStatuses } from '../../redux/selectors/stats'; import { fetchInstanceIfNeeded } from '../../redux/modules/instances'; import { instanceSelector } from '../../redux/selectors/instances'; +import { getLocalizedName } from '../../helpers/getLocalizedData'; import withLinks from '../../hoc/withLinks'; class Group extends Component { @@ -124,7 +125,7 @@ class Group extends Component { } getBreadcrumbs = () => { - const { group, instance } = this.props; + const { group, instance, intl: { locale } } = this.props; const breadcrumbs = [ { resource: instance, @@ -139,7 +140,7 @@ class Group extends Component { resource: group, iconName: 'group', breadcrumb: data => ({ - text: data.name + text: getLocalizedName(data, locale) }) } ]; @@ -294,7 +295,8 @@ Group.propTypes = { assignExercise: PropTypes.func.isRequired, createGroupExercise: PropTypes.func.isRequired, push: PropTypes.func.isRequired, - links: PropTypes.object + links: PropTypes.object, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; Group.contextTypes = { @@ -351,4 +353,6 @@ const mapDispatchToProps = (dispatch, { params }) => ({ push: url => dispatch(push(url)) }); -export default withLinks(connect(mapStateToProps, mapDispatchToProps)(Group)); +export default withLinks( + connect(mapStateToProps, mapDispatchToProps)(injectIntl(Group)) +);