form to edit email templates

This commit is contained in:
cyril lopez 2018-01-19 13:34:55 +01:00
parent f6497639e7
commit 11c5905d25
9 changed files with 165 additions and 92 deletions

View File

@ -141,7 +141,7 @@ class ListRow extends React.Component { // eslint-disable-line react/prefer-stat
} }
case 'providers': case 'providers':
case 'email-templates': case 'email-templates':
return router.push(`${router.location.pathname}#edit::${this.props.settingType}::${this.props.item.id}`); return this.context.setDataToEdit(this.props.item.name);
default: default:
return; return;
} }
@ -168,6 +168,10 @@ class ListRow extends React.Component { // eslint-disable-line react/prefer-stat
} }
} }
ListRow.contextTypes = {
setDataToEdit: PropTypes.func.isRequired,
};
ListRow.defaultProps = { ListRow.defaultProps = {
item: { item: {
name: 'Owner', name: 'Owner',

View File

@ -8,16 +8,16 @@ import React from 'react';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { get } from 'lodash'; import { get, isObject, includes, map, take, takeRight } from 'lodash';
import { router } from 'app';
// Translations
import en from 'translations/en.json';
import Input from 'components/Input'; import Input from 'components/Input';
import styles from './styles.scss'; import styles from './styles.scss';
class PopUpForm extends React.Component { // eslint-disable-line react/prefer-stateless-function class PopUpForm extends React.Component { // eslint-disable-line react/prefer-stateless-function
toggleModal = () => router.push(router.location.pathname);
renderButton = () => { renderButton = () => {
if (this.props.showLoader) { if (this.props.showLoader) {
return ( return (
@ -37,6 +37,8 @@ class PopUpForm extends React.Component { // eslint-disable-line react/prefer-st
} }
renderForm = () => { renderForm = () => {
const { dataToEdit, values } = this.props;
if (this.props.settingType === 'providers') { if (this.props.settingType === 'providers') {
return ( return (
<div className="row"> <div className="row">
@ -64,71 +66,74 @@ class PopUpForm extends React.Component { // eslint-disable-line react/prefer-st
); );
} }
const form = Object.keys(values.options || {}).reduce((acc, current) => {
if (isObject(get(values, ['options', current]))) {
return Object.keys(get(values, ['options', current], {}))
.reduce((acc, curr) => {
acc.push(`options.${current}.${curr}`);
return acc;
}, []).concat(acc);
} else {
acc.push(`options.${current}`);
}
return acc;
}, []);
return ( return (
<div className="row"> <div className="row">
<Input {map(take(form, 3), (value, key) => (
autoFocus <Input
label="users-permissions.PopUpForm.inputText.shipperName.label" autoFocus={key === 0}
name="shipperName" key={value}
onChange={this.props.onChange} label={`users-permissions.PopUpForm.Email.${value}.label`}
value={get(this.props.values, 'shipperName')} name={`${dataToEdit}.${value}`}
placeholder="users-permissions.PopUpForm.inputText.shipperName.placeholder" onChange={this.props.onChange}
type="text" placeholder={`users-permissions.PopUpForm.Email.${value}.placeholder`}
validations={{}} type={includes(value, 'email') ? 'email' : 'text'}
/> value={get(values, value)}
<Input validations={{}}
label="users-permissions.PopUpForm.inputEmail.shipperEmail.label" />
name="shipperEmail" ))}
onChange={this.props.onChange}
placeholder="users-permissions.PopUpForm.inputEmail.placeholder"
type="email"
validations={{ required: true }}
value={get(this.props.values, 'shipperEmail')}
/>
<Input
label="users-permissions.PopUpForm.inputEmail.responseEmail.label"
name="responseEmail"
onChange={this.props.onChange}
placeholder="users-permissions.PopUpForm.inputEmail.placeholder"
type="email"
validations={{}}
value={get(this.props.values, 'responseEmail')}
/>
<div className="col-md-6" /> <div className="col-md-6" />
<Input {map(takeRight(form, 2), (value) => (
customBootstrapClass="col-md-12" <Input
label="users-permissions.PopUpForm.inputText.emailObject.label" key={value}
name="emailObject" customBootstrapClass="col-md-12"
onChange={this.props.onChange} label={`users-permissions.PopUpForm.Email.${value}.label`}
placeholder="users-permissions.PopUpForm.inputText.emailObject.placeholder" name={`${dataToEdit}.${value}`}
type="text" onChange={this.props.onChange}
validations={{}} placeholder={`users-permissions.PopUpForm.Email.${this.props.dataToEdit}.${value}.placeholder`}
value={get(this.props.values, 'emailObject')} type={includes(value, 'object') ? 'text' : 'textarea'}
/> validations={{}}
<Input value={get(values, value)}
customBootstrapClass="col-md-12" />
label="users-permissions.PopUpForm.inputTextArea.message.label" ))}
name="message"
onChange={this.props.onChange}
placeholder="users-permissions.PopUpForm.inputTextArea.message.placeholder"
type="textarea"
validations={{}}
value={get(this.props.values, 'message')}
/>
</div> </div>
); );
} }
render() { render() {
const { actionType, dataToEdit, display, settingType } = this.props.values;
let header = <span>{dataToEdit}</span>;
if (actionType) {
header = <FormattedMessage id={`users-permissions.PopUpForm.header.${actionType}.${settingType}`} />;
}
if (display && en[display]) {
header = <FormattedMessage id={`users-permissions.${display}`} />;
}
return ( return (
<div className={styles.popUpForm}> <div className={styles.popUpForm}>
<Modal isOpen={this.props.isOpen} toggle={this.toggleModal} className={`${styles.modalPosition}`}> <Modal isOpen={this.props.isOpen} toggle={this.context.unsetDataToEdit} className={`${styles.modalPosition}`}>
<ModalHeader toggle={this.toggleModal} className={styles.modalHeader} /> <ModalHeader toggle={this.context.unsetDataToEdit} className={styles.modalHeader} />
<div className={styles.headerContainer}> <div className={styles.headerContainer}>
<div> <div>
{this.props.actionType ? ( {header}
<FormattedMessage id={`users-permissions.PopUpForm.header.${this.props.actionType}.${this.props.settingType}`} />
) : <div />}
</div> </div>
</div> </div>
<form onSubmit={this.props.onSubmit}> <form onSubmit={this.props.onSubmit}>
@ -138,7 +143,7 @@ class PopUpForm extends React.Component { // eslint-disable-line react/prefer-st
</div> </div>
</ModalBody> </ModalBody>
<ModalFooter className={styles.modalFooter}> <ModalFooter className={styles.modalFooter}>
<Button onClick={() => router.push(router.location.pathname)} className={styles.secondary}> <Button onClick={() => this.context.unsetDataToEdit()} className={styles.secondary}>
<FormattedMessage id="users-permissions.PopUpForm.button.cancel" /> <FormattedMessage id="users-permissions.PopUpForm.button.cancel" />
</Button> </Button>
{this.renderButton()} {this.renderButton()}
@ -150,6 +155,10 @@ class PopUpForm extends React.Component { // eslint-disable-line react/prefer-st
} }
} }
PopUpForm.contextTypes = {
unsetDataToEdit: PropTypes.func.isRequired,
};
PopUpForm.defaultProps = { PopUpForm.defaultProps = {
settingType: 'providers', settingType: 'providers',
showLoader: false, showLoader: false,
@ -157,6 +166,7 @@ PopUpForm.defaultProps = {
PopUpForm.propTypes = { PopUpForm.propTypes = {
actionType: PropTypes.string.isRequired, actionType: PropTypes.string.isRequired,
dataToEdit: PropTypes.string.isRequired,
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired,

View File

@ -3,7 +3,7 @@
* HomePage actions * HomePage actions
* *
*/ */
import { Map } from 'immutable'; import { fromJS } from 'immutable';
import { isArray } from 'lodash'; import { isArray } from 'lodash';
import { import {
CANCEL_CHANGES, CANCEL_CHANGES,
@ -12,9 +12,11 @@ import {
FETCH_DATA, FETCH_DATA,
FETCH_DATA_SUCCEEDED, FETCH_DATA_SUCCEEDED,
ON_CHANGE, ON_CHANGE,
SET_DATA_TO_EDIT,
SET_FORM, SET_FORM,
SUBMIT, SUBMIT,
SUBMIT_SUCCEEDED, SUBMIT_SUCCEEDED,
UNSET_DATA_TO_EDIT,
} from './constants'; } from './constants';
export function cancelChanges() { export function cancelChanges() {
@ -57,12 +59,14 @@ export function fetchDataSucceeded(data) {
return { return {
type: FETCH_DATA_SUCCEEDED, type: FETCH_DATA_SUCCEEDED,
data: list, data: list,
modifiedData: fromJS(data),
}; };
} }
return { return {
type: FETCH_DATA_SUCCEEDED, type: FETCH_DATA_SUCCEEDED,
data, data,
modifiedData: Map({}),
}; };
} }
@ -74,6 +78,13 @@ export function onChange({ target }) {
}; };
} }
export function setDataToEdit(dataToEdit) {
return {
type: SET_DATA_TO_EDIT,
dataToEdit,
};
}
export function setForm(data) { export function setForm(data) {
// const form = generateForm(formType); // const form = generateForm(formType);
return { return {
@ -95,6 +106,12 @@ export function submitSucceeded() {
}; };
} }
export function unsetDataToEdit() {
return {
type: UNSET_DATA_TO_EDIT,
};
}
// Utils // Utils
// function generateForm(formType) { // function generateForm(formType) {

View File

@ -10,6 +10,8 @@ export const DELETE_DATA_SUCCEEDED = 'UsersPermissions/HomePage/DELETE_DATA_SUCC
export const FETCH_DATA = 'UsersPermissions/HomePage/FETCH_DATA'; export const FETCH_DATA = 'UsersPermissions/HomePage/FETCH_DATA';
export const FETCH_DATA_SUCCEEDED = 'UsersPermissions/HomePage/FETCH_DATA_SUCCEEDED'; export const FETCH_DATA_SUCCEEDED = 'UsersPermissions/HomePage/FETCH_DATA_SUCCEEDED';
export const ON_CHANGE = 'UsersPermissions/HomePage/ON_CHANGE'; export const ON_CHANGE = 'UsersPermissions/HomePage/ON_CHANGE';
export const SET_DATA_TO_EDIT = 'UsersPermissions/HomePage/SET_DATA_TO_EDIT';
export const SET_FORM = 'UsersPermissions/HomePage/SET_FORM'; export const SET_FORM = 'UsersPermissions/HomePage/SET_FORM';
export const SUBMIT = 'UsersPermissions/HomePage/SUBMIT'; export const SUBMIT = 'UsersPermissions/HomePage/SUBMIT';
export const SUBMIT_SUCCEEDED = 'UsersPermissions/HomePage/SUBMIT_SUCCEEDED'; export const SUBMIT_SUCCEEDED = 'UsersPermissions/HomePage/SUBMIT_SUCCEEDED';
export const UNSET_DATA_TO_EDIT = 'UsersPermissions/HomePage/UNSET_DATA_TO_EDIT';

View File

@ -10,7 +10,7 @@ import { connect } from 'react-redux';
import { injectIntl } from 'react-intl'; import { injectIntl } from 'react-intl';
import { bindActionCreators, compose } from 'redux'; import { bindActionCreators, compose } from 'redux';
import cn from 'classnames'; import cn from 'classnames';
import { clone, includes, isEqual, isEmpty, replace } from 'lodash'; import { clone, includes, isEqual, isEmpty } from 'lodash';
// Design // Design
import EditForm from 'components/EditForm'; import EditForm from 'components/EditForm';
@ -35,7 +35,9 @@ import {
deleteData, deleteData,
fetchData, fetchData,
onChange, onChange,
setDataToEdit,
submit, submit,
unsetDataToEdit,
} from './actions'; } from './actions';
import reducer from './reducer'; import reducer from './reducer';
@ -44,7 +46,14 @@ import saga from './saga';
const keyBoardShortCuts = [18, 78]; const keyBoardShortCuts = [18, 78];
export class HomePage extends React.Component { export class HomePage extends React.Component {
state = { mapKey: {} }; state = { mapKey: {}, showModalEdit: false };
getChildContext = () => (
{
setDataToEdit: this.props.setDataToEdit,
unsetDataToEdit: this.props.unsetDataToEdit,
}
);
componentDidMount() { componentDidMount() {
this.props.fetchData(this.props.match.params.settingType); this.props.fetchData(this.props.match.params.settingType);
@ -52,6 +61,12 @@ export class HomePage extends React.Component {
document.addEventListener('keyup', this.handleKeyBoardShortCut); document.addEventListener('keyup', this.handleKeyBoardShortCut);
} }
componentWillReceiveProps(nextProps) {
if (nextProps.dataToEdit !== this.props.dataToEdit) {
this.setState({ showModalEdit: !isEmpty(nextProps.dataToEdit) });
}
}
componentWillUpdate(nextProps) { componentWillUpdate(nextProps) {
const allowedPaths = ['roles', 'providers', 'email-templates', 'advanced']; const allowedPaths = ['roles', 'providers', 'email-templates', 'advanced'];
const shouldRedirect = allowedPaths.filter(el => el === nextProps.match.params.settingType).length === 0; const shouldRedirect = allowedPaths.filter(el => el === nextProps.match.params.settingType).length === 0;
@ -111,7 +126,7 @@ export class HomePage extends React.Component {
]; ];
render() { render() {
const { modifiedData, initialData, match } = this.props; const { modifiedData, initialData, match, dataToEdit } = this.props;
const headerActions = match.params.settingType === 'advanced' && !isEqual(modifiedData, initialData) ? const headerActions = match.params.settingType === 'advanced' && !isEqual(modifiedData, initialData) ?
this.pluginHeaderActions : []; this.pluginHeaderActions : [];
const noButtonList = match.params.settingType === 'email-templates' || match.params.settingType === 'providers'; const noButtonList = match.params.settingType === 'email-templates' || match.params.settingType === 'providers';
@ -122,10 +137,9 @@ export class HomePage extends React.Component {
deleteData={this.props.deleteData} deleteData={this.props.deleteData}
noButton={noButtonList} noButton={noButtonList}
onButtonClick={this.handleButtonClick} onButtonClick={this.handleButtonClick}
settingType={this.props.match.params.settingType} settingType={match.params.settingType}
/> />
); );
const hashArray = replace(this.props.location.hash, '#', '').split('::');
return ( return (
<div> <div>
@ -140,14 +154,15 @@ export class HomePage extends React.Component {
{component} {component}
</div> </div>
<PopUpForm <PopUpForm
actionType={hashArray[0]} actionType={'edit'}
isOpen={!isEmpty(this.props.location.hash)} isOpen={this.state.showModalEdit}
dataToEdit={dataToEdit}
onChange={this.props.onChange} onChange={this.props.onChange}
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
}} }}
settingType={hashArray[1]} settingType={match.params.settingType}
values={modifiedData} values={modifiedData[dataToEdit] || {}}
/> />
</form> </form>
</div> </div>
@ -155,11 +170,17 @@ export class HomePage extends React.Component {
} }
} }
HomePage.childContextTypes = {
setDataToEdit: PropTypes.func,
unsetDataToEdit: PropTypes.func,
};
HomePage.defaultProps = {}; HomePage.defaultProps = {};
HomePage.propTypes = { HomePage.propTypes = {
cancelChanges: PropTypes.func.isRequired, cancelChanges: PropTypes.func.isRequired,
data: PropTypes.array.isRequired, data: PropTypes.array.isRequired,
dataToEdit: PropTypes.string.isRequired,
deleteData: PropTypes.func.isRequired, deleteData: PropTypes.func.isRequired,
fetchData: PropTypes.func.isRequired, fetchData: PropTypes.func.isRequired,
history: PropTypes.object.isRequired, history: PropTypes.object.isRequired,
@ -168,7 +189,9 @@ HomePage.propTypes = {
match: PropTypes.object.isRequired, match: PropTypes.object.isRequired,
modifiedData: PropTypes.object.isRequired, modifiedData: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
setDataToEdit: PropTypes.func.isRequired,
submit: PropTypes.func.isRequired, submit: PropTypes.func.isRequired,
unsetDataToEdit: PropTypes.func.isRequired,
}; };
@ -179,7 +202,9 @@ function mapDispatchToProps(dispatch) {
deleteData, deleteData,
fetchData, fetchData,
onChange, onChange,
setDataToEdit,
submit, submit,
unsetDataToEdit,
}, },
dispatch, dispatch,
); );

View File

@ -12,13 +12,16 @@ import {
DELETE_DATA_SUCCEEDED, DELETE_DATA_SUCCEEDED,
FETCH_DATA_SUCCEEDED, FETCH_DATA_SUCCEEDED,
ON_CHANGE, ON_CHANGE,
SET_DATA_TO_EDIT,
SET_FORM, SET_FORM,
SUBMIT_SUCCEEDED, SUBMIT_SUCCEEDED,
UNSET_DATA_TO_EDIT,
} from './constants'; } from './constants';
const initialState = fromJS({ const initialState = fromJS({
data: List([]), data: List([]),
dataToDelete: Map({}), dataToDelete: Map({}),
dataToEdit: '',
deleteEndPoint: '', deleteEndPoint: '',
initialData: Map({}), initialData: Map({}),
modifiedData: Map({}), modifiedData: Map({}),
@ -27,8 +30,7 @@ const initialState = fromJS({
function homePageReducer(state = initialState, action) { function homePageReducer(state = initialState, action) {
switch (action.type) { switch (action.type) {
case CANCEL_CHANGES: case CANCEL_CHANGES:
return state return state.update('modifiedData', () => state.get('initialData'));
.update('modifiedData', () => state.get('initialData'));
case DELETE_DATA: case DELETE_DATA:
return state return state
.set('dataToDelete', Map(action.dataToDelete)) .set('dataToDelete', Map(action.dataToDelete))
@ -39,17 +41,25 @@ function homePageReducer(state = initialState, action) {
.set('deleteEndPoint', '') .set('deleteEndPoint', '')
.set('dataToDelete', Map({})); .set('dataToDelete', Map({}));
case FETCH_DATA_SUCCEEDED: case FETCH_DATA_SUCCEEDED:
return state.set('data', List(action.data)); return state
.set('data', List(action.data))
.set('initialData', action.modifiedData)
.set('modifiedData', action.modifiedData);
case ON_CHANGE: case ON_CHANGE:
return state return state
.updateIn(action.keys, () => action.value); .updateIn(action.keys, () => action.value);
case SET_DATA_TO_EDIT:
return state.update('dataToEdit', () => action.dataToEdit);
case SET_FORM: case SET_FORM:
return state return state
.set('initialData', action.form) .set('initialData', action.form)
.set('modifiedData', action.form); .set('modifiedData', action.form);
case SUBMIT_SUCCEEDED: case SUBMIT_SUCCEEDED:
return state.update('initialData', () => state.get('modifiedData'));
case UNSET_DATA_TO_EDIT:
return state return state
.update('initialData', () => state.get('modifiedData')); .update('dataToEdit', () => '')
.update('modifiedData', () => state.get('initialData'));
default: default:
return state; return state;
} }

View File

@ -138,13 +138,18 @@
"PopUpForm.inputSelect.providers.label": "Choose the provider", "PopUpForm.inputSelect.providers.label": "Choose the provider",
"PopUpForm.inputToggle.providers.label": "Enable", "PopUpForm.inputToggle.providers.label": "Enable",
"PopUpForm.inputToggle.providers.description": "If disabled, the users won't be able to use this provider.", "PopUpForm.inputToggle.providers.description": "If disabled, the users won't be able to use this provider.",
"PopUpForm.inputText.shipperName.label": "Shipper name", "PopUpForm.Email.options.from.name.label": "Shipper name",
"PopUpForm.inputEmail.shipperEmail.label": "Shipper email", "PopUpForm.Email.options.from.email.label": "Shipper email",
"PopUpForm.inputEmail.responseEmail.label": "Response email", "PopUpForm.Email.options.response_email.label": "Response email",
"PopUpForm.inputText.emailObject.label": "Object", "PopUpForm.Email.options.object.label": "Object",
"PopUpForm.inputText.emailObject.placeholder": "Please confirm your email address for %APP_NAME%", "PopUpForm.Email.options.message.label": "Message",
"PopUpForm.inputTextArea.message.label": "Message", "PopUpForm.Email.validation_email.options.object.placeholder": "Please confirm your email address for %APP_NAME%",
"PopUpForm.inputTextArea.message.placeholder": "<p>Please click on this link to validate your account</p>", "PopUpForm.Email.reset_password.options.object.placeholder": "Please confirm your email address for %APP_NAME%",
"PopUpForm.inputEmail.placeholder": "johndoe@gmail.com", "PopUpForm.Email.success_register.options.object.placeholder": "Please confirm your email address for %APP_NAME%",
"PopUpForm.inputText.shipperName.placeholder": "John Doe" "PopUpForm.Email.validation_email.options.message.placeholder": "<p>Please click on this link to validate your account</p>",
"PopUpForm.Email.reset_password.options.message.placeholder": "<p>Please click on this link to validate your account</p>",
"PopUpForm.Email.success_register.options.message.placeholder": "<p>Please click on this link to validate your account</p>",
"PopUpForm.Email.options.from.email.placeholder": "johndoe@gmail.com",
"PopUpForm.Email.options.response_email.placeholder": "johndoe@gmail.com",
"PopUpForm.Email.options.from.name.placeholder": "John Doe"
} }

View File

@ -82,10 +82,10 @@ module.exports = cb => {
icon: 'envelope', icon: 'envelope',
options: { options: {
from: { from: {
email: '', name: 'Administration Panel',
name: '' email: 'no-reply@strapi.io'
}, },
respond: '', response_email: '',
object: '', object: '',
message: '' message: ''
} }
@ -95,10 +95,10 @@ module.exports = cb => {
icon: 'refresh', icon: 'refresh',
options: { options: {
from: { from: {
email: '', name: 'Administration Panel',
name: '' email: 'no-reply@strapi.io'
}, },
respond: '', response_email: '',
object: '­Reset password 🔑 ', object: '­Reset password 🔑 ',
message: `<p>We heard that you lost your password. Sorry about that!</p> message: `<p>We heard that you lost your password. Sorry about that!</p>
@ -114,10 +114,10 @@ module.exports = cb => {
icon: 'check', icon: 'check',
options: { options: {
from: { from: {
email: '', name: 'Administration Panel',
name: '' email: 'no-reply@strapi.io'
}, },
respond: '', response_email: '',
object: '', object: '',
message: '' message: ''
} }

View File

@ -144,7 +144,7 @@ module.exports = {
await strapi.plugins['email'].services.email.send({ await strapi.plugins['email'].services.email.send({
to: user.email, to: user.email,
from: (settings.from.email || settings.from.email) ? `"${settings.from.name}" <${settings.from.email}>` : undefined, from: (settings.from.email || settings.from.email) ? `"${settings.from.name}" <${settings.from.email}>` : undefined,
replyTo: settings.respond, replyTo: settings.response_email,
subject: object, subject: object,
text: message, text: message,
html: message html: message