Field setup wizard for new list - addresses 1st line of #510
Bugfixes to address #511
This commit is contained in:
parent
de55870561
commit
b26f5008da
10 changed files with 144 additions and 59 deletions
|
@ -107,8 +107,6 @@ export default class StatisticsOpened extends Component {
|
|||
{ data: 5, title: t('opensCount') }
|
||||
];
|
||||
|
||||
console.log(this.state.statisticsOpened);
|
||||
|
||||
const renderNavPill = (key, label) => (
|
||||
<li role="presentation" className={agg === key ? 'active' : ''}>
|
||||
<Link to={`/campaigns/${entity.id}/statistics/opened/${key}`}>{label}</Link>
|
||||
|
@ -223,8 +221,6 @@ export default class StatisticsOpened extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
console.log(mailtrainConfig);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Title>{t('detailedStatistics')}</Title>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@import "../../static/scss/variables";
|
||||
|
||||
.form { // This is here to give the styles below higher priority than Bootstrap has
|
||||
:global .DayPicker {
|
||||
border-left: 1px solid lightgray;
|
||||
|
@ -148,7 +150,8 @@
|
|||
}
|
||||
|
||||
.iconDisabled {
|
||||
color: #bf3e11;
|
||||
color: $link-color;
|
||||
text-decoration: $link-decoration;
|
||||
}
|
||||
|
||||
.dependenciesList {
|
||||
|
|
|
@ -30,7 +30,7 @@ import {
|
|||
NamespaceSelect,
|
||||
validateNamespace
|
||||
} from '../lib/namespace';
|
||||
import {UnsubscriptionMode} from '../../../shared/lists';
|
||||
import {UnsubscriptionMode, FieldWizard} from '../../../shared/lists';
|
||||
import styles
|
||||
from "../lib/styles.scss";
|
||||
import mailtrainConfig
|
||||
|
@ -78,7 +78,8 @@ export default class CUD extends Component {
|
|||
homepage: '',
|
||||
unsubscription_mode: UnsubscriptionMode.ONE_STEP,
|
||||
namespace: mailtrainConfig.user.namespace,
|
||||
to_name: '[MERGE_FIRST_NAME] [MERGE_LAST_NAME]',
|
||||
to_name: '',
|
||||
fieldWizard: FieldWizard.FIRST_LAST_NAME,
|
||||
send_configuration: null,
|
||||
listunsubscribe_disabled: false
|
||||
});
|
||||
|
@ -129,6 +130,10 @@ export default class CUD extends Component {
|
|||
data.default_form = null;
|
||||
}
|
||||
delete data.form;
|
||||
|
||||
if (data.fieldWizard === FieldWizard.FIRST_LAST_NAME || data.fieldWizard === FieldWizard.NAME) {
|
||||
data.to_name = null;
|
||||
}
|
||||
});
|
||||
|
||||
if (submitSuccessful) {
|
||||
|
@ -193,6 +198,32 @@ export default class CUD extends Component {
|
|||
{ data: 6, title: t('namespace') }
|
||||
];
|
||||
|
||||
let toNameFields;
|
||||
if (isEdit) {
|
||||
toNameFields = <InputField id="to_name" label={t('recipientsNameTemplate')} help={t('specifyUsingMergeTagsOfThisListHowTo')}/>;
|
||||
} else {
|
||||
const fieldWizardOptions = [
|
||||
{key: FieldWizard.NONE, label: t('Empty / Custom (no fields)')},
|
||||
{key: FieldWizard.NAME, label: t('Name (one field)')},
|
||||
{key: FieldWizard.FIRST_LAST_NAME, label: t('First name and Last name (two fields)')},
|
||||
];
|
||||
|
||||
const fieldWizardValue = this.getFormValue('fieldWizard');
|
||||
|
||||
const fieldWizardSelector = <Dropdown id="fieldWizard" label={t('Representation of subscriber\'s name')} options={fieldWizardOptions} help={t('Select how the name of a subscriber will be represented. The fields in list will be created accordingly. You can always adjust the choice later by editing the list fields. If you select "Empty / Custom", provide a template below in "Recipients name template" that will be used as subscriber\'s name as it will appear in the emails\' "To" field.')}/>
|
||||
|
||||
if (fieldWizardValue === FieldWizard.NONE) {
|
||||
toNameFields = (
|
||||
<>
|
||||
{fieldWizardSelector}
|
||||
<InputField id="to_name" label={t('recipientsNameTemplate')} help={t('specifyUsingMergeTagsOfThisListHowTo')}/>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
toNameFields = fieldWizardSelector;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{canDelete &&
|
||||
|
@ -221,7 +252,7 @@ export default class CUD extends Component {
|
|||
|
||||
<InputField id="contact_email" label={t('contactEmail')} help={t('contactEmailUsedInSubscriptionFormsAnd')}/>
|
||||
<InputField id="homepage" label={t('homepage')} help={t('homepageUrlUsedInSubscriptionFormsAnd')}/>
|
||||
<InputField id="to_name" label={t('recipientsNameTemplate')} help={t('specifyUsingMergeTagsOfThisListHowTo')}/>
|
||||
{toNameFields}
|
||||
<TableSelect id="send_configuration" label={t('sendConfiguration')} withHeader dropdown dataUrl='rest/send-configurations-table' columns={sendConfigurationsColumns} selectionLabelIndex={1} help={t('sendConfigurationThatWillBeUsedFor')}/>
|
||||
|
||||
<NamespaceSelect/>
|
||||
|
|
|
@ -71,6 +71,10 @@ export default class CUD extends Component {
|
|||
};
|
||||
|
||||
this.initForm();
|
||||
|
||||
this.onRuleSettingsPaneUpdatedHandler = ::this.onRuleSettingsPaneUpdated;
|
||||
this.onRuleSettingsPaneCloseHandler = ::this.onRuleSettingsPaneClose;
|
||||
this.onRuleSettingsPaneDeleteHandler = ::this.onRuleSettingsPaneDelete;
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
|
@ -334,7 +338,6 @@ export default class CUD extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<div>
|
||||
|
@ -411,7 +414,7 @@ export default class CUD extends Component {
|
|||
<div className={styles.rightPane}>
|
||||
<div className={styles.rightPaneInner}>
|
||||
{selectedRule &&
|
||||
<RuleSettingsPane rule={selectedRule} fields={this.props.fields} onChange={::this.onRuleSettingsPaneUpdated} onClose={::this.onRuleSettingsPaneClose} onDelete={::this.onRuleSettingsPaneDelete} forceShowValidation={this.isFormValidationShown()}/>}
|
||||
<RuleSettingsPane rule={selectedRule} fields={this.props.fields} onChange={this.onRuleSettingsPaneUpdatedHandler} onClose={this.onRuleSettingsPaneCloseHandler} onDelete={this.onRuleSettingsPaneDeleteHandler} forceShowValidation={this.isFormValidationShown()}/>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
import React, {Component} from "react";
|
||||
import React, {PureComponent} from "react";
|
||||
import PropTypes
|
||||
from "prop-types";
|
||||
import {withTranslation} from '../../lib/i18n';
|
||||
|
@ -31,7 +31,7 @@ import {withComponentMixins} from "../../lib/decorator-helpers";
|
|||
withPageHelpers,
|
||||
requiresAuthenticatedUser
|
||||
])
|
||||
export default class CUD extends Component {
|
||||
export default class RuleSettingsPane extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
@ -136,7 +136,7 @@ export default class CUD extends Component {
|
|||
if (type) {
|
||||
const settings = ruleHelpers.primitiveRuleTypes[colType][type];
|
||||
if (!settings) {
|
||||
// The existing rule type does not fit the newly changed column. This resets the rule type chooser to "-- Select ---"
|
||||
// The existing rule type does not fit the newly changed column. This resets the rule type chooser to "--- Select ---"
|
||||
mutState.setIn(['type', 'value'], '');
|
||||
}
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ export default class CUD extends Component {
|
|||
|
||||
const ruleType = this.getFormValue('type');
|
||||
if (ruleType) {
|
||||
ruleSettings = ruleHelpers.primitiveRuleTypes[colType][ruleType].form;
|
||||
ruleSettings = ruleHelpers.primitiveRuleTypes[colType][ruleType].getForm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -226,8 +226,6 @@ export default class CUD extends Component {
|
|||
</div>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className={styles.ruleOptions}>
|
||||
<h3>{t('ruleOptions')}</h3>
|
||||
|
|
|
@ -216,7 +216,7 @@ export function getRuleHelpers(t, fields) {
|
|||
|
||||
|
||||
const stringValueSettings = allowEmpty => ({
|
||||
form: <InputField id="value" label={t('value')} />,
|
||||
getForm: () => <InputField id="value" label={t('value')} />,
|
||||
getFormData: rule => ({
|
||||
value: rule.value
|
||||
}),
|
||||
|
@ -233,7 +233,7 @@ export function getRuleHelpers(t, fields) {
|
|||
});
|
||||
|
||||
const numberValueSettings = {
|
||||
form: <InputField id="value" label={t('value')} />,
|
||||
getForm: () => <InputField id="value" label={t('value')} />,
|
||||
getFormData: rule => ({
|
||||
value: rule.value.toString()
|
||||
}),
|
||||
|
@ -253,7 +253,7 @@ export function getRuleHelpers(t, fields) {
|
|||
};
|
||||
|
||||
const birthdayValueSettings = {
|
||||
form: <DatePicker id="birthday" label={t('date')} birthday />,
|
||||
getForm: () => <DatePicker id="birthday" label={t('date')} birthday />,
|
||||
getFormData: rule => ({
|
||||
birthday: formatBirthday(DateFormat.INTL, rule.value)
|
||||
}),
|
||||
|
@ -274,7 +274,7 @@ export function getRuleHelpers(t, fields) {
|
|||
};
|
||||
|
||||
const dateValueSettings = {
|
||||
form: <DatePicker id="date" label={t('date')} />,
|
||||
getForm: () => <DatePicker id="date" label={t('date')} />,
|
||||
getFormData: rule => ({
|
||||
date: formatDate(DateFormat.INTL, rule.value)
|
||||
}),
|
||||
|
@ -295,7 +295,7 @@ export function getRuleHelpers(t, fields) {
|
|||
};
|
||||
|
||||
const dateRelativeValueSettings = {
|
||||
form:
|
||||
getForm: () =>
|
||||
<div>
|
||||
<InputField id="daysValue" label={t('numberOfDays')}/>
|
||||
<Dropdown id="direction" label={t('beforeAfter')} options={[
|
||||
|
@ -324,7 +324,7 @@ export function getRuleHelpers(t, fields) {
|
|||
};
|
||||
|
||||
const optionValueSettings = {
|
||||
form: null,
|
||||
getForm: () => null,
|
||||
getFormData: rule => ({}),
|
||||
assignRuleSettings: (rule, getter) => {},
|
||||
validate: state => {}
|
||||
|
|
|
@ -532,8 +532,7 @@ async function _sortIn(tx, listId, entityId, orderListBefore, orderSubscribeBefo
|
|||
}
|
||||
}
|
||||
|
||||
async function create(context, listId, entity) {
|
||||
return await knex.transaction(async tx => {
|
||||
async function createTx(tx, context, listId, entity) {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageFields');
|
||||
|
||||
await _validateAndPreprocess(tx, listId, entity, true);
|
||||
|
@ -567,6 +566,11 @@ async function create(context, listId, entity) {
|
|||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
async function create(context, listId, entity) {
|
||||
return await knex.transaction(async tx => {
|
||||
return await createTx(tx, context, listId, entity);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -833,6 +837,7 @@ module.exports.listByOrderListTx = listByOrderListTx;
|
|||
module.exports.listDTAjax = listDTAjax;
|
||||
module.exports.listGroupedDTAjax = listGroupedDTAjax;
|
||||
module.exports.create = create;
|
||||
module.exports.createTx = createTx;
|
||||
module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck;
|
||||
module.exports.remove = remove;
|
||||
module.exports.removeAllByListIdTx = removeAllByListIdTx;
|
||||
|
|
|
@ -14,7 +14,7 @@ const imports = require('./imports');
|
|||
const entitySettings = require('../lib/entity-settings');
|
||||
const dependencyHelpers = require('../lib/dependency-helpers');
|
||||
|
||||
const UnsubscriptionMode = require('../../shared/lists').UnsubscriptionMode;
|
||||
const {UnsubscriptionMode, FieldWizard} = require('../../shared/lists');
|
||||
|
||||
const allowedKeys = new Set(['name', 'description', 'default_form', 'public_subscribe', 'unsubscription_mode', 'contact_email', 'homepage', 'namespace', 'to_name', 'listunsubscribe_disabled', 'send_configuration']);
|
||||
|
||||
|
@ -112,6 +112,47 @@ async function create(context, entity) {
|
|||
|
||||
await _validateAndPreprocess(tx, entity);
|
||||
|
||||
const fieldsToAdd = [];
|
||||
|
||||
const fieldWizard = entity.fieldWizard;
|
||||
if (fieldWizard === FieldWizard.FIRST_LAST_NAME) {
|
||||
if (entity.to_name === null) {
|
||||
entity.to_name = '[MERGE_FIRST_NAME] [MERGE_LAST_NAME]';
|
||||
}
|
||||
|
||||
fieldsToAdd.push({
|
||||
name: 'First name',
|
||||
key: 'MERGE_FIRST_NAME',
|
||||
default_value: '',
|
||||
type: 'text',
|
||||
group: null,
|
||||
settings: {}
|
||||
});
|
||||
|
||||
fieldsToAdd.push({
|
||||
name: 'Last name',
|
||||
key: 'MERGE_LAST_NAME',
|
||||
default_value: '',
|
||||
type: 'text',
|
||||
group: null,
|
||||
settings: {}
|
||||
});
|
||||
|
||||
} else if (fieldWizard === FieldWizard.NAME) {
|
||||
if (entity.to_name === null) {
|
||||
entity.to_name = '[MERGE_NAME]';
|
||||
}
|
||||
|
||||
fieldsToAdd.push({
|
||||
name: 'Name',
|
||||
key: 'MERGE_NAME',
|
||||
default_value: '',
|
||||
type: 'text',
|
||||
group: null,
|
||||
settings: {}
|
||||
});
|
||||
}
|
||||
|
||||
const filteredEntity = filterObject(entity, allowedKeys);
|
||||
filteredEntity.cid = shortid.generate();
|
||||
|
||||
|
@ -148,6 +189,10 @@ async function create(context, entity) {
|
|||
|
||||
await shares.rebuildPermissionsTx(tx, { entityTypeId: 'list', entityId: id });
|
||||
|
||||
for (const fld of fieldsToAdd) {
|
||||
await fields.createTx(tx, context, id, fld);
|
||||
}
|
||||
|
||||
return id;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -44,9 +44,6 @@ router.post('/login', passport.csrfProtection, passport.restLogin);
|
|||
router.post('/logout', passport.csrfProtection, passport.restLogout);
|
||||
|
||||
router.postAsync('/password-reset-send', passport.csrfProtection, async (req, res) => {
|
||||
// FIXME
|
||||
console.log(req.locale);
|
||||
console.log(req.cookies);
|
||||
await users.sendPasswordReset(req.locale, req.body.usernameOrEmail);
|
||||
return res.json();
|
||||
});
|
||||
|
|
|
@ -32,6 +32,12 @@ const SubscriptionSource = {
|
|||
ERASED: -6
|
||||
};
|
||||
|
||||
const FieldWizard = {
|
||||
NONE: 'none',
|
||||
NAME: 'full_name',
|
||||
FIRST_LAST_NAME: 'first_last_name'
|
||||
}
|
||||
|
||||
function getFieldColumn(field) {
|
||||
return field.column || 'grouped_' + field.id;
|
||||
}
|
||||
|
@ -40,5 +46,6 @@ module.exports = {
|
|||
UnsubscriptionMode,
|
||||
SubscriptionStatus,
|
||||
SubscriptionSource,
|
||||
FieldWizard,
|
||||
getFieldColumn
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue