Skip to content

Commit

Permalink
System messages global edit page
Browse files Browse the repository at this point in the history
  • Loading branch information
SemaiCZE authored and Martin Kruliš committed May 17, 2019
1 parent d055c69 commit 4619ed4
Show file tree
Hide file tree
Showing 18 changed files with 628 additions and 2 deletions.
122 changes: 122 additions & 0 deletions src/components/SystemMessages/MessagesList/MessagesList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
import { defaultMemoize } from 'reselect';

import SortableTable, { SortableTableColumnDescriptor } from '../../widgets/SortableTable';
import { getLocalizedText } from '../../../helpers/localizedData';
import DateTime from '../../widgets/DateTime';
import { roleLabels } from '../../helpers/usersRoles';
import UsersNameContainer from '../../../containers/UsersNameContainer';

class MessagesList extends Component {
prepareColumnDescriptors = defaultMemoize((systemMessages, locale) => {
const columns = [
new SortableTableColumnDescriptor(
'text',
<FormattedMessage id="app.systemMessagesList.text" defaultMessage="Text" />,
{
className: 'text-left',
comparator: ({ text: t1 }, { text: t2 }) =>
getLocalizedText(t1, locale).localeCompare(getLocalizedText(t2, locale), locale),
cellRenderer: text => text && <i>{getLocalizedText(text, locale)}</i>,
}
),

new SortableTableColumnDescriptor(
'visibleFrom',
<FormattedMessage id="app.systemMessagesList.visibleFrom" defaultMessage="Visible From" />,
{
comparator: ({ visibleFrom: f1 }, { visibleFrom: f2 }) => f2 - f1,
cellRenderer: visibleFrom => visibleFrom && <DateTime unixts={visibleFrom} />,
}
),

new SortableTableColumnDescriptor(
'visibleTo',
<FormattedMessage id="app.systemMessagesList.visibleTo" defaultMessage="Visible To" />,
{
comparator: ({ visibleTo: t1 }, { visibleTo: t2 }) => t2 - t1,
cellRenderer: visibleTo => visibleTo && <DateTime unixts={visibleTo} />,
}
),

new SortableTableColumnDescriptor('authorId', <FormattedMessage id="generic.name" defaultMessage="Name" />, {
cellRenderer: authorId => authorId && <UsersNameContainer userId={authorId} />,
}),

new SortableTableColumnDescriptor(
'role',
<FormattedMessage id="app.systemMessagesList.role" defaultMessage="Role" />,
{
comparator: ({ role: r1 }, { role: r2 }) => r1.localeCompare(r2, locale),
cellRenderer: role => role && roleLabels[role],
}
),

new SortableTableColumnDescriptor(
'type',
<FormattedMessage id="app.systemMessagesList.type" defaultMessage="Type" />,
{
comparator: ({ type: t1 }, { type: t2 }) => t1.localeCompare(t2, locale),
cellRenderer: type => type && <span>{type}</span>,
}
),

new SortableTableColumnDescriptor('buttons', '', {
className: 'text-right',
}),
];

return columns;
});

prepareData = defaultMemoize(systemMessages => {
const { renderActions } = this.props;

return systemMessages.map(message => {
const data = {
text: { localizedTexts: message.localizedTexts },
visibleFrom: message.visibleFrom,
visibleTo: message.visibleTo,
authorId: message.authorId,
role: message.role,
type: message.type,
buttons: renderActions && renderActions(message),
};
return data;
});
});

render() {
const {
systemMessages,
intl: { locale },
} = this.props;

return (
<SortableTable
hover
columns={this.prepareColumnDescriptors(systemMessages, locale)}
defaultOrder="visibleTo"
data={this.prepareData(systemMessages)}
empty={
<div className="text-center text-muted">
<FormattedMessage
id="app.systemMessagesList.noMessages"
defaultMessage="There are currently no system messages."
/>
</div>
}
/>
);
}
}

MessagesList.propTypes = {
systemMessages: PropTypes.array.isRequired,
intl: intlShape.isRequired,
renderActions: PropTypes.func,
};

export default injectIntl(MessagesList);
2 changes: 2 additions & 0 deletions src/components/SystemMessages/MessagesList/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import MessagesList from './MessagesList';
export default MessagesList;
168 changes: 168 additions & 0 deletions src/components/forms/EditSystemMessageForm/EditSystemMessageForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import React from 'react';
import PropTypes from 'prop-types';
import { reduxForm, Field, FieldArray } from 'redux-form';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import { Alert, Modal, Button } from 'react-bootstrap';

import { SelectField, DatetimeField } from '../Fields';

import SubmitButton from '../SubmitButton';
import LocalizedTextsFormField from '../LocalizedTextsFormField';
import { validateLocalizedTextsFormData } from '../../../helpers/localizedData';
import withLinks from '../../../helpers/withLinks';
import { CloseIcon } from '../../icons';
import { roleLabelsSimpleMessages } from '../../helpers/usersRoles';

const EditSystemMessageForm = ({
initialValues,
error,
dirty,
submitting,
handleSubmit,
submitFailed,
submitSucceeded,
invalid,
asyncValidating,
isOpen,
onClose,
intl: { formatMessage },
}) => (
<Modal show={isOpen} backdrop="static" size="lg" onHide={onClose}>
<Modal.Header closeButton>
<Modal.Title>
<FormattedMessage id="app.editSystemMessageForm.title" defaultMessage="Edit System Message" />
</Modal.Title>
</Modal.Header>
<Modal.Body>
{submitFailed && (
<Alert bsStyle="danger">
<FormattedMessage id="generic.savingFailed" defaultMessage="Saving failed. Please try again later." />
</Alert>
)}

<FieldArray name="localizedTexts" component={LocalizedTextsFormField} fieldType="systemMessage" />

<Field
name="type"
component={SelectField}
options={[
{ key: 'info', name: 'Info' },
{ key: 'warning', name: 'Warning' },
{ key: 'danger', name: 'Danger' },
]}
addEmptyOption
label={<FormattedMessage id="app.editSystemMessageForm.type" defaultMessage="Type of the notification." />}
/>

<Field
name="role"
component={SelectField}
options={Object.keys(roleLabelsSimpleMessages).map(role => ({
key: role,
name: formatMessage(roleLabelsSimpleMessages[role]),
}))}
addEmptyOption
label={
<FormattedMessage
id="app.editSystemMessageForm.role"
defaultMessage="Users with this role and its children can see notification."
/>
}
/>

<Field
name="visibleFrom"
component={DatetimeField}
label={
<FormattedMessage
id="app.editSystemMessageForm.visibleFrom"
defaultMessage="Date from which is notification visible."
/>
}
/>

<Field
name="visibleTo"
component={DatetimeField}
label={
<FormattedMessage
id="app.editSystemMessageForm.isLocked"
defaultMessage="Date to which is notification visible."
/>
}
/>

{error && dirty && <Alert bsStyle="danger">{error}</Alert>}
</Modal.Body>
<Modal.Footer>
<div className="text-center">
<SubmitButton
id="editSystemMessage"
invalid={invalid}
submitting={submitting}
dirty={dirty}
hasSucceeded={submitSucceeded}
hasFailed={submitFailed}
handleSubmit={handleSubmit}
asyncValidating={asyncValidating}
messages={{
submit: <FormattedMessage id="generic.save" defaultMessage="Save" />,
submitting: <FormattedMessage id="generic.saving" defaultMessage="Saving..." />,
success: <FormattedMessage id="generic.saved" defaultMessage="Saved" />,
validating: <FormattedMessage id="generic.validating" defaultMessage="Validating..." />,
}}
/>

<Button bsStyle="default" className="btn-flat" onClick={onClose}>
<CloseIcon gapRight />
<FormattedMessage id="generic.close" defaultMessage="Close" />
</Button>
</div>
</Modal.Footer>
</Modal>
);

EditSystemMessageForm.propTypes = {
error: PropTypes.any,
initialValues: PropTypes.object.isRequired,
values: PropTypes.object,
handleSubmit: PropTypes.func.isRequired,
dirty: PropTypes.bool,
submitting: PropTypes.bool,
submitFailed: PropTypes.bool,
submitSucceeded: PropTypes.bool,
invalid: PropTypes.bool,
asyncValidating: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
links: PropTypes.object,
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
intl: intlShape.isRequired,
};

const validate = ({ localizedTexts }) => {
const errors = {};
validateLocalizedTextsFormData(errors, localizedTexts, ({ text }) => {
const textErrors = {};
if (!text.trim()) {
textErrors.text = (
<FormattedMessage
id="app.editSystemMessageForm.validation.localizedText.text"
defaultMessage="Please fill the description."
/>
);
}

return textErrors;
});

return errors;
};

export default withLinks(
reduxForm({
form: 'editSystemMessage',
validate,
enableReinitialize: true,
keepDirtyOnReinitialize: false,
})(injectIntl(EditSystemMessageForm))
);
2 changes: 2 additions & 0 deletions src/components/forms/EditSystemMessageForm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import EditSystemMessageForm from './EditSystemMessageForm';
export default EditSystemMessageForm;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Well } from 'react-bootstrap';
import { FormattedMessage } from 'react-intl';
import { Field } from 'redux-form';

import { MarkdownTextAreaField, CheckboxField } from '../Fields';

const LocalizedSystemMessageFormField = ({ prefix, data: enabled }) => (
<Well>
<Field
name={`${prefix}._enabled`}
component={CheckboxField}
onOff
label={
<FormattedMessage
id="app.editLocalizedTextForm.localeEnabledCheckbox"
defaultMessage="Enable this localization"
/>
}
/>

<Field
name={`${prefix}.text`}
component={MarkdownTextAreaField}
disabled={!enabled}
label={
<FormattedMessage
id="app.editLocalizedTextForm.localized.description"
defaultMessage="Text of system message:"
/>
}
/>
</Well>
);

LocalizedSystemMessageFormField.propTypes = {
prefix: PropTypes.string.isRequired,
data: PropTypes.bool.isRequired,
};

export default LocalizedSystemMessageFormField;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import LocalizedAssignmentFormField from './LocalizedAssignmentFormField';
import LocalizedShadowAssignmentFormField from './LocalizedShadowAssignmentFormField';
import LocalizedExerciseFormField from './LocalizedExerciseFormField';
import LocalizedGroupFormField from './LocalizedGroupFormField';
import LocalizedSystemMessageFormField from './LocalizedSystemMessageFormField';
import { WarningIcon } from '../../icons';
import { knownLocalesNames } from '../../../helpers/localizedData';

Expand All @@ -16,6 +17,7 @@ const fieldTypes = {
shadowAssignment: LocalizedShadowAssignmentFormField,
exercise: LocalizedExerciseFormField,
group: LocalizedGroupFormField,
systemMessage: LocalizedSystemMessageFormField,
};

const renderTitle = ({ locale, _enabled }) => (
Expand Down
10 changes: 9 additions & 1 deletion src/components/helpers/usersRoles.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage, defineMessages } from 'react-intl';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
import { SuperadminIcon, EmpoweredSupervisorIcon, SupervisorIcon, SupervisorStudentIcon, UserIcon } from '../icons';

Expand All @@ -23,6 +23,14 @@ export const roleLabels = {
[SUPERADMIN_ROLE]: <FormattedMessage id="app.roles.superadmin" defaultMessage="Main Administrator" />,
};

export const roleLabelsSimpleMessages = defineMessages({
[STUDENT_ROLE]: { id: 'app.roles.student', defaultMessage: 'Student' },
[SUPERVISOR_STUDENT_ROLE]: { id: 'app.roles.supervisorStudent', defaultMessage: 'Supervisor-student' },
[SUPERVISOR_ROLE]: { id: 'app.roles.supervisor', defaultMessage: 'Supervisor' },
[EMPOWERED_SUPERVISOR_ROLE]: { id: 'app.roles.empoweredSupervisor', defaultMessage: 'Empowered Supervisor' },
[SUPERADMIN_ROLE]: { id: 'app.roles.superadmin', defaultMessage: 'Main Administrator' },
});

export const roleLabelsPlural = {
[STUDENT_ROLE]: <FormattedMessage id="app.roles.students" defaultMessage="Students" />,
[SUPERVISOR_STUDENT_ROLE]: (
Expand Down
Loading

0 comments on commit 4619ed4

Please sign in to comment.