Custom forms list and CUD.
This commit is contained in:
parent
f6e1938ff9
commit
361af18384
12 changed files with 1068 additions and 209 deletions
450
client/src/lists/forms/CUD.js
Normal file
450
client/src/lists/forms/CUD.js
Normal file
|
@ -0,0 +1,450 @@
|
|||
'use strict';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { translate, Trans } from 'react-i18next';
|
||||
import {requiresAuthenticatedUser, withPageHelpers, Title, NavButton} from '../../lib/page';
|
||||
import {
|
||||
withForm, Form, FormSendMethod, InputField, TextArea, TableSelect, ButtonRow, Button,
|
||||
Fieldset, Dropdown, AlignedRow, ACEEditor
|
||||
} from '../../lib/form';
|
||||
import { withErrorHandling, withAsyncErrorHandler } from '../../lib/error-handling';
|
||||
import { validateNamespace, NamespaceSelect } from '../../lib/namespace';
|
||||
import {DeleteModalDialog} from "../../lib/delete";
|
||||
import mailtrainConfig from 'mailtrainConfig';
|
||||
|
||||
@translate()
|
||||
@withForm
|
||||
@withPageHelpers
|
||||
@withErrorHandling
|
||||
@requiresAuthenticatedUser
|
||||
export default class CUD extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
|
||||
if (props.edit) {
|
||||
this.state.entityId = parseInt(props.match.params.id);
|
||||
}
|
||||
|
||||
this.serverValidatedFields = [
|
||||
'layout',
|
||||
'web_subscribe',
|
||||
'web_confirm_subscription_notice',
|
||||
'mail_confirm_subscription_html',
|
||||
'mail_confirm_subscription_text',
|
||||
'mail_already_subscribed_html',
|
||||
'mail_already_subscribed_text',
|
||||
'web_subscribed_notice',
|
||||
'mail_subscription_confirmed_html',
|
||||
'mail_subscription_confirmed_text',
|
||||
'web_manage',
|
||||
'web_manage_address',
|
||||
'web_updated_notice',
|
||||
'web_unsubscribe',
|
||||
'web_confirm_unsubscription_notice',
|
||||
'mail_confirm_unsubscription_html',
|
||||
'mail_confirm_unsubscription_text',
|
||||
'mail_confirm_address_change_html',
|
||||
'mail_confirm_address_change_text',
|
||||
'web_unsubscribed_notice',
|
||||
'mail_unsubscription_confirmed_html',
|
||||
'mail_unsubscription_confirmed_text',
|
||||
'web_manual_unsubscribe_notice'
|
||||
];
|
||||
|
||||
this.initForm({
|
||||
serverValidation: {
|
||||
url: '/rest/forms-validate',
|
||||
changed: this.serverValidatedFields
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const t = props.t;
|
||||
|
||||
const helpEmailText = t('The plaintext version for this email');
|
||||
const helpMjmlGeneral = <Trans>Custom forms use MJML for formatting. See the MJML documentation <a className="mjml-documentation">here</a></Trans>;
|
||||
|
||||
this.templateSettings = {
|
||||
layout: {
|
||||
label: t('Layout'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral,
|
||||
isLayout: true
|
||||
},
|
||||
form_input_style: {
|
||||
label: t('Form Input Style'),
|
||||
mode: 'css',
|
||||
help: t('This CSS stylesheet defines the appearance of form input elements and alerts')
|
||||
},
|
||||
web_subscribe: {
|
||||
label: t('Web - Subscribe'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
web_confirm_subscription_notice: {
|
||||
label: t('Web - Confirm Subscription Notice'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_confirm_subscription_html: {
|
||||
label: t('Mail - Confirm Subscription (MJML)'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_confirm_subscription_text: {
|
||||
label: t('Mail - Confirm Subscription (Text)'),
|
||||
mode: 'text',
|
||||
help: helpEmailText
|
||||
},
|
||||
mail_already_subscribed_html: {
|
||||
label: t('Mail - Already Subscribed (MJML)'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_already_subscribed_text: {
|
||||
label: t('Mail - Already Subscribed (Text)'),
|
||||
mode: 'text',
|
||||
help: helpEmailText
|
||||
},
|
||||
web_subscribed_notice: {
|
||||
label: t('Web - Subscribed Notice'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_subscription_confirmed_html: {
|
||||
label: t('Mail - Subscription Confirmed (MJML)'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_subscription_confirmed_text: {
|
||||
label: t('Mail - Subscription Confirmed (Text)'),
|
||||
mode: 'text',
|
||||
help: helpEmailText
|
||||
},
|
||||
web_manage: {
|
||||
label: t('Web - Manage Preferences'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
web_manage_address: {
|
||||
label: t('Web - Manage Address'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_confirm_address_change_html: {
|
||||
label: t('Mail - Confirm Address Change (MJML)'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_confirm_address_change_text: {
|
||||
label: t('Mail - Confirm Address Change (Text)'),
|
||||
mode: 'text',
|
||||
help: helpEmailText
|
||||
},
|
||||
web_updated_notice: {
|
||||
label: t('Web - Updated Notice'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
web_unsubscribe: {
|
||||
label: t('Web - Unsubscribe'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
web_confirm_unsubscription_notice: {
|
||||
label: t('Web - Confirm Unsubscription Notice'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_confirm_unsubscription_html: {
|
||||
label: t('Mail - Confirm Unsubscription (MJML)'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_confirm_unsubscription_text: {
|
||||
label: t('Mail - Confirm Unsubscription (Text)'),
|
||||
mode: 'text',
|
||||
help: helpEmailText
|
||||
},
|
||||
web_unsubscribed_notice: {
|
||||
label: t('Web - Unsubscribed Notice'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_unsubscription_confirmed_html: {
|
||||
label: t('Mail - Unsubscription Confirmed (MJML)'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
mail_unsubscription_confirmed_text: {
|
||||
label: t('Mail - Unsubscription Confirmed (Text)'),
|
||||
mode: 'text',
|
||||
help: helpEmailText
|
||||
},
|
||||
web_manual_unsubscribe_notice: {
|
||||
label: t('Web - Manual Unsubscribe Notice'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
}
|
||||
};
|
||||
|
||||
this.templateGroups = {
|
||||
general: {
|
||||
label: t('General'),
|
||||
options: [
|
||||
'layout',
|
||||
'form_input_style'
|
||||
]
|
||||
},
|
||||
subscribe: {
|
||||
label: t('Subscribe'),
|
||||
options: [
|
||||
'web_subscribe',
|
||||
'web_confirm_subscription_notice',
|
||||
'mail_confirm_subscription_html',
|
||||
'mail_confirm_subscription_text',
|
||||
'mail_already_subscribed_html',
|
||||
'mail_already_subscribed_text',
|
||||
'web_subscribed_notice',
|
||||
'mail_subscription_confirmed_html',
|
||||
'mail_subscription_confirmed_text'
|
||||
]
|
||||
},
|
||||
manage: {
|
||||
label: t('Manage'),
|
||||
options: [
|
||||
'web_manage',
|
||||
'web_manage_address',
|
||||
'mail_confirm_address_change_html',
|
||||
'mail_confirm_address_change_text',
|
||||
'web_updated_notice'
|
||||
]
|
||||
},
|
||||
unsubscribe: {
|
||||
label: t('Unsubscribe'),
|
||||
options: [
|
||||
'web_unsubscribe',
|
||||
'web_confirm_unsubscription_notice',
|
||||
'mail_confirm_unsubscription_html',
|
||||
'mail_confirm_unsubscription_text',
|
||||
'web_unsubscribed_notice',
|
||||
'mail_unsubscription_confirmed_html',
|
||||
'mail_unsubscription_confirmed_text',
|
||||
'web_manual_unsubscribe_notice'
|
||||
]
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
edit: PropTypes.bool
|
||||
}
|
||||
|
||||
@withAsyncErrorHandler
|
||||
async loadOrPopulateFormValues() {
|
||||
function supplyDefaults(data) {
|
||||
for (const key in mailtrainConfig.defaultCustomFormValues) {
|
||||
if (!data[key]) {
|
||||
data[key] = mailtrainConfig.defaultCustomFormValues[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.edit) {
|
||||
await this.getFormValuesFromURL(`/rest/forms/${this.state.entityId}`, data => {
|
||||
data.selectedTemplate = 'layout';
|
||||
supplyDefaults(data);
|
||||
});
|
||||
} else {
|
||||
const data = {
|
||||
name: '',
|
||||
description: '',
|
||||
selectedTemplate: 'layout',
|
||||
namespace: null
|
||||
};
|
||||
supplyDefaults(data);
|
||||
|
||||
this.populateFormValues(data);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadOrPopulateFormValues();
|
||||
}
|
||||
|
||||
localValidateFormValues(state) {
|
||||
const t = this.props.t;
|
||||
const edit = this.props.edit;
|
||||
|
||||
if (!state.getIn(['name', 'value'])) {
|
||||
state.setIn(['name', 'error'], t('Name must not be empty'));
|
||||
} else {
|
||||
state.setIn(['name', 'error'], null);
|
||||
}
|
||||
|
||||
validateNamespace(t, state);
|
||||
|
||||
let formsServerValidationRunning = false;
|
||||
const formsErrors = [];
|
||||
|
||||
for (const fld of this.serverValidatedFields) {
|
||||
const serverValidation = state.getIn([fld, 'serverValidation']);
|
||||
|
||||
if (serverValidation && serverValidation.errors) {
|
||||
formsErrors.push(...serverValidation.errors.map(x => <div><em>{this.templateSettings[fld].label}</em>{' '}–{' '}{x}</div>));
|
||||
} else if (!serverValidation) {
|
||||
formsServerValidationRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!formsErrors.length && formsServerValidationRunning) {
|
||||
formsErrors.push(t('Validation is in progress...'));
|
||||
}
|
||||
|
||||
if (formsErrors.length) {
|
||||
state.setIn(['selectedTemplate', 'error'],
|
||||
<div><strong>{t('List of errors in templates') + ':'}</strong>
|
||||
<ul>
|
||||
{formsErrors.map((msg, idx) => <li key={idx}>{msg}</li>)}
|
||||
</ul>
|
||||
</div>);
|
||||
} else {
|
||||
state.setIn(['selectedTemplate', 'error'], null);
|
||||
}
|
||||
}
|
||||
|
||||
async submitHandler() {
|
||||
const t = this.props.t;
|
||||
const edit = this.props.edit;
|
||||
|
||||
let sendMethod, url;
|
||||
if (edit) {
|
||||
sendMethod = FormSendMethod.PUT;
|
||||
url = `/rest/forms/${this.state.entityId}`
|
||||
} else {
|
||||
sendMethod = FormSendMethod.POST;
|
||||
url = '/rest/forms'
|
||||
}
|
||||
|
||||
this.disableForm();
|
||||
this.setFormStatusMessage('info', t('Saving forms ...'));
|
||||
|
||||
const submitSuccessful = await this.validateAndSendFormValuesToURL(sendMethod, url, data => {
|
||||
delete data.selectedTemplate;
|
||||
delete data.previewList;
|
||||
});
|
||||
|
||||
if (submitSuccessful) {
|
||||
this.navigateToWithFlashMessage('/lists/forms', 'success', t('Forms saved'));
|
||||
} else {
|
||||
this.enableForm();
|
||||
this.setFormStatusMessage('warning', t('There are errors in the form. Please fix them and submit again.'));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
const edit = this.props.edit;
|
||||
|
||||
const templateOptGroups = [];
|
||||
|
||||
for (const grpKey in this.templateGroups) {
|
||||
const grp = this.templateGroups[grpKey];
|
||||
templateOptGroups.push({
|
||||
key: grpKey,
|
||||
label: grp.label,
|
||||
options: grp.options.map(opt => ({
|
||||
key: opt,
|
||||
label: this.templateSettings[opt].label
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const listsColumns = [
|
||||
{ data: 0, title: "#" },
|
||||
{ data: 1, title: t('Name') },
|
||||
{ data: 2, title: t('ID'), render: data => `<code>${data}</code>` },
|
||||
{ data: 5, title: t('Namespace') }
|
||||
];
|
||||
|
||||
const previewListId = this.getFormValue('previewList');
|
||||
const selectedTemplate = this.getFormValue('selectedTemplate');
|
||||
|
||||
return (
|
||||
<div>
|
||||
{edit &&
|
||||
<DeleteModalDialog
|
||||
stateOwner={this}
|
||||
visible={this.props.match.params.action === 'delete'}
|
||||
deleteUrl={`/rest/forms/${this.state.entityId}`}
|
||||
cudUrl={`/lists/forms/edit/${this.state.entityId}`}
|
||||
listUrl="/lists/forms"
|
||||
deletingMsg={t('Deleting form ...')}
|
||||
deletedMsg={t('Form deleted')}/>
|
||||
}
|
||||
|
||||
<Title>{edit ? t('Edit Custom Forms') : t('Create Custom Forms')}</Title>
|
||||
|
||||
<Form stateOwner={this} onSubmitAsync={::this.submitHandler}>
|
||||
<InputField id="name" label={t('Name')}/>
|
||||
|
||||
<TextArea id="description" label={t('Description')} help={t('HTML is allowed')}/>
|
||||
|
||||
<NamespaceSelect/>
|
||||
|
||||
<Fieldset label={t('Forms Preview')}>
|
||||
<TableSelect id="previewList" label={t('List To Preview On')} withHeader dropdown dataUrl='/rest/lists-table' columns={listsColumns} selectionLabelIndex={1} help={t('Select list whose fields will be used to preview the forms.')}/>
|
||||
|
||||
{ previewListId &&
|
||||
<AlignedRow>
|
||||
<div className="help-block">
|
||||
<small>
|
||||
Note: These links are solely for a quick preview. If you submit a preview form you'll get redirected to the list's default form.
|
||||
</small>
|
||||
</div>
|
||||
<p>
|
||||
<a href={`/lists/forms/preview/${previewListId}`} target="_blank">Subscribe</a>
|
||||
|
|
||||
<a href={`/lists/forms/preview/${previewListId}/confirm-subscription-notice`} target="_blank">Confirm Subscription Notice</a>
|
||||
|
|
||||
<a href={`/lists/forms/preview/${previewListId}/confirm-unsubscription-notice`} target="_blank">Confirm Unsubscription Notice</a>
|
||||
|
|
||||
<a href={`/lists/forms/preview/${previewListId}/subscribed-notice`} target="_blank">Subscribed Notice</a>
|
||||
|
|
||||
<a href={`/lists/forms/preview/${previewListId}/updated-notice`} target="_blank">Updated Notice</a>
|
||||
|
|
||||
<a href={`/lists/forms/preview/${previewListId}/unsubscribed-notice`} target="_blank">Unsubscribed Notice</a>
|
||||
|
|
||||
<a href={`/lists/forms/preview/${previewListId}/manual-unsubscribe-notice`} target="_blank">Manual Unsubscribe Notice</a>
|
||||
|
|
||||
<a href={`/lists/forms/preview/${previewListId}/unsubscribe`} target="_blank">Unsubscribe</a>
|
||||
|
|
||||
<a href={`/lists/forms/preview/${previewListId}/manage`} target="_blank">Manage</a>
|
||||
|
|
||||
<a href={`/lists/forms/preview/${previewListId}/manage-address`} target="_blank">Manage Address</a>
|
||||
</p>
|
||||
</AlignedRow>
|
||||
}
|
||||
</Fieldset>
|
||||
|
||||
{ selectedTemplate &&
|
||||
<Fieldset label={t('Templates')}>
|
||||
<Dropdown id="selectedTemplate" label={t('Edit')} optGroups={templateOptGroups} help={this.templateSettings[selectedTemplate].help}/>
|
||||
<ACEEditor id={selectedTemplate} height="500px" mode={this.templateSettings[selectedTemplate].mode}/>
|
||||
</Fieldset>
|
||||
}
|
||||
|
||||
<ButtonRow>
|
||||
<Button type="submit" className="btn-primary" icon="ok" label={t('Save')}/>
|
||||
{edit && <NavButton className="btn-danger" icon="remove" label={t('Delete')} linkTo={`/lists/forms/edit/${this.state.entityId}/delete`}/>}
|
||||
</ButtonRow>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue