Work in progress on confirmation dialogs displayed when one navigates from a page with unsaved changes.

Optimized imports.
This commit is contained in:
Tomas Bures 2019-05-12 00:00:09 +02:00
parent 3921a6b2cb
commit 008fd21b51
30 changed files with 324 additions and 231 deletions

View file

@ -42,10 +42,7 @@ export default class CUD extends Component {
this.state = {};
this.initForm({
loadMutator: ::this.getFormValuesMutator,
submitMutator: ::this.submitFormValuesMutator
});
this.initForm();
this.mailerTypes = getMailerTypes(props.t);
}

View file

@ -12,12 +12,14 @@ import {
CheckBox,
Dropdown,
Fieldset,
filterData,
Form,
FormSendMethod,
InputField,
StaticField,
TableSelect,
withForm
withForm,
withFormErrorHandlers
} from '../../lib/form';
import {withErrorHandling} from '../../lib/error-handling';
import {DeleteModalDialog} from "../../lib/modals";
@ -117,9 +119,58 @@ export default class CUD extends Component {
data.orderManageBefore = data.orderManageBefore.toString();
}
submitFormValuesMutator(data) {
if (data.default_value.trim() === '') {
data.default_value = null;
}
if (!data.isInGroup) {
data.group = null;
}
data.settings = {};
switch (data.type) {
case 'checkbox-grouped':
case 'radio-grouped':
case 'dropdown-grouped':
case 'json':
data.settings.renderTemplate = data.renderTemplate;
break;
case 'radio-enum':
case 'dropdown-enum':
data.settings.options = this.parseEnumOptions(data.enumOptions).options;
data.settings.renderTemplate = data.renderTemplate;
break;
case 'date':
case 'birthday':
data.settings.dateFormat = data.dateFormat;
break;
case 'option':
if (!data.isInGroup) {
data.settings.checkedLabel = data.checkedLabel;
data.settings.uncheckedLabel = data.uncheckedLabel;
}
break;
}
if (data.group !== null) {
data.orderListBefore = data.orderSubscribeBefore = data.orderManageBefore = 'none';
} else {
data.orderListBefore = Number.parseInt(data.orderListBefore) || data.orderListBefore;
data.orderSubscribeBefore = Number.parseInt(data.orderSubscribeBefore) || data.orderSubscribeBefore;
data.orderManageBefore = Number.parseInt(data.orderManageBefore) || data.orderManageBefore;
}
return filterData(data, ['name', 'key', 'default_value', 'type', 'group', 'settings',
'orderListBefore', 'orderSubscribeBefore', 'orderManageBefore']);
}
componentDidMount() {
if (this.props.entity) {
this.getFormValuesFromEntity(this.props.entity, ::this.getFormValuesMutator);
this.getFormValuesFromEntity(this.props.entity);
} else {
this.populateFormValues({
@ -237,6 +288,7 @@ export default class CUD extends Component {
}
@withFormErrorHandlers
async submitHandler(submitAndLeave) {
const t = this.props.t;
@ -253,65 +305,14 @@ export default class CUD extends Component {
this.disableForm();
this.setFormStatusMessage('info', t('saving'));
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url, data => {
if (data.default_value.trim() === '') {
data.default_value = null;
}
if (!data.isInGroup) {
data.group = null;
}
data.settings = {};
switch (data.type) {
case 'checkbox-grouped':
case 'radio-grouped':
case 'dropdown-grouped':
case 'json':
data.settings.renderTemplate = data.renderTemplate;
break;
case 'radio-enum':
case 'dropdown-enum':
data.settings.options = this.parseEnumOptions(data.enumOptions).options;
data.settings.renderTemplate = data.renderTemplate;
break;
case 'date':
case 'birthday':
data.settings.dateFormat = data.dateFormat;
break;
case 'option':
if (!data.isInGroup) {
data.settings.checkedLabel = data.checkedLabel;
data.settings.uncheckedLabel = data.uncheckedLabel;
}
break;
}
delete data.renderTemplate;
delete data.enumOptions;
delete data.dateFormat;
delete data.checkedLabel;
delete data.uncheckedLabel;
delete data.isInGroup;
if (data.group !== null) {
data.orderListBefore = data.orderSubscribeBefore = data.orderManageBefore = 'none';
} else {
data.orderListBefore = Number.parseInt(data.orderListBefore) || data.orderListBefore;
data.orderSubscribeBefore = Number.parseInt(data.orderSubscribeBefore) || data.orderSubscribeBefore;
data.orderManageBefore = Number.parseInt(data.orderManageBefore) || data.orderManageBefore;
}
});
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url);
if (submitResult) {
if (this.props.entity) {
if (submitAndLeave) {
this.navigateToWithFlashMessage(`/lists/${this.props.list.id}/fields`, 'success', t('Field updated'));
} else {
await this.getFormValuesFromURL(`rest/fields/${this.props.list.id}/${this.props.entity.id}`, ::this.getFormValuesMutator);
await this.getFormValuesFromURL(`rest/fields/${this.props.list.id}/${this.props.entity.id}`);
this.enableForm();
this.setFormStatusMessage('success', t('Field updated'));
}

View file

@ -12,12 +12,14 @@ import {
ButtonRow,
Dropdown,
Fieldset,
filterData,
Form,
FormSendMethod,
InputField,
TableSelect,
TextArea,
withForm
withForm,
withFormErrorHandlers
} from '../../lib/form';
import {withErrorHandling} from '../../lib/error-handling';
import {NamespaceSelect, validateNamespace} from '../../lib/namespace';
@ -293,14 +295,38 @@ export default class CUD extends Component {
getFormValuesMutator(data) {
this.supplyDefaults(data);
data.selectedTemplate = data.selectedTemplate || 'layout';
}
submitFormValuesMutator(data) {
return filterData(data, ['name', 'description', 'layout', 'form_input_style', 'namespace',
'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', 'web_privacy_policy_notice'
]);
}
componentDidMount() {
if (this.props.entity) {
this.getFormValuesFromEntity(this.props.entity, data => {
this.getFormValuesMutator(data);
data.selectedTemplate = 'layout';
});
this.getFormValuesFromEntity(this.props.entity);
} else {
const data = {
@ -355,6 +381,7 @@ export default class CUD extends Component {
}
}
@withFormErrorHandlers
async submitHandler(submitAndLeave) {
const t = this.props.t;
@ -370,17 +397,14 @@ export default class CUD extends Component {
this.disableForm();
this.setFormStatusMessage('info', t('saving'));
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url, data => {
delete data.selectedTemplate;
delete data.previewList;
});
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url);
if (submitResult) {
if (this.props.entity) {
if (submitAndLeave) {
this.navigateToWithFlashMessage('/lists/forms', 'success', t('Custom forms updated'));
} else {
await this.getFormValuesFromURL(`rest/forms/${this.props.entity.id}`, ::this.getFormValuesMutator);
await this.getFormValuesFromURL(`rest/forms/${this.props.entity.id}`);
this.enableForm();
this.setFormStatusMessage('success', t('Custom forms updated'));
}

View file

@ -11,12 +11,14 @@ import {
CheckBox,
Dropdown,
Fieldset,
filterData,
Form,
FormSendMethod,
InputField,
StaticField,
TextArea,
withForm
withForm,
withFormErrorHandlers
} from '../../lib/form';
import {withAsyncErrorHandler, withErrorHandling} from '../../lib/error-handling';
import {DeleteModalDialog} from "../../lib/modals";
@ -81,36 +83,101 @@ export default class CUD extends Component {
entity: PropTypes.object
}
initFromEntity(entity) {
this.getFormValuesFromEntity(entity, data => {
data.settings = data.settings || {};
const mapping = data.mapping || {};
getFormValuesMutator(data) {
data.settings = data.settings || {};
const mapping = data.mapping || {};
if (data.source === ImportSource.CSV_FILE) {
data.csvFileName = data.settings.csv.originalname;
data.csvDelimiter = data.settings.csv.delimiter;
}
const mappingSettings = mapping.settings || {};
data.mapping_settings_checkEmails = 'checkEmails' in mappingSettings ? !!mappingSettings.checkEmails : true;
const mappingFlds = mapping.fields || {};
for (const field of this.props.fieldsGrouped) {
if (field.column) {
const colMapping = mappingFlds[field.column] || {};
data['mapping_fields_' + field.column + '_column'] = colMapping.column || '';
} else {
for (const option of field.settings.options) {
const col = field.groupedOptions[option.key].column;
const colMapping = mappingFlds[col] || {};
data['mapping_fields_' + col + '_column'] = colMapping.column || '';
}
}
}
const emailMapping = mappingFlds.email || {};
data.mapping_fields_email_column = emailMapping.column || '';
}
submitFormValuesMutator(data) {
const isEdit = !!this.props.entity;
data.source = Number.parseInt(data.source);
data.settings = {};
const formData = new FormData();
if (!isEdit) {
if (data.source === ImportSource.CSV_FILE) {
data.csvFileName = data.settings.csv.originalname;
data.csvDelimiter = data.settings.csv.delimiter;
data.settings.csv = {};
formData.append('csvFile', this.csvFile.files[0]);
data.settings.csv.delimiter = data.csvDelimiter.trim();
}
const mappingSettings = mapping.settings || {};
data.mapping_settings_checkEmails = 'checkEmails' in mappingSettings ? !!mappingSettings.checkEmails : true;
} else {
data.mapping_type = Number.parseInt(data.mapping_type);
const mapping = {
fields: {},
settings: {}
};
const mappingFlds = mapping.fields || {};
for (const field of this.props.fieldsGrouped) {
if (field.column) {
const colMapping = mappingFlds[field.column] || {};
data['mapping_fields_' + field.column + '_column'] = colMapping.column || '';
} else {
for (const option of field.settings.options) {
const col = field.groupedOptions[option.key].column;
const colMapping = mappingFlds[col] || {};
data['mapping_fields_' + col + '_column'] = colMapping.column || '';
if (data.mapping_type === MappingType.BASIC_SUBSCRIBE) {
mapping.settings.checkEmails = data.mapping_settings_checkEmails;
for (const field of this.props.fieldsGrouped) {
if (field.column) {
const colMapping = data['mapping_fields_' + field.column + '_column'];
if (colMapping) {
mapping.fields[field.column] = {
column: colMapping
};
}
} else {
for (const option of field.settings.options) {
const col = field.groupedOptions[option.key].column;
const colMapping = data['mapping_fields_' + col + '_column'];
if (colMapping) {
mapping.fields[col] = {
column: colMapping
};
}
}
}
}
}
const emailMapping = mappingFlds.email || {};
data.mapping_fields_email_column = emailMapping.column || '';
});
if (data.mapping_type === MappingType.BASIC_SUBSCRIBE || data.mapping_type === MappingType.BASIC_UNSUBSCRIBE) {
mapping.fields.email = {
column: data.mapping_fields_email_column
};
}
data.mapping = mapping;
}
formData.append('entity', JSON.stringify(
filterData(data, ['name', 'description', 'source', 'settings', 'mapping_type', 'mapping'])
));
return formData;
}
initFromEntity(entity) {
this.getFormValuesFromEntity(entity);
if (inProgress(entity.status)) {
this.refreshTimeoutId = setTimeout(this.refreshTimeoutHandler, 1000);
@ -179,6 +246,7 @@ export default class CUD extends Component {
await this.save();
}
@withFormErrorHandlers
async save(runAfterSave) {
const t = this.props.t;
const isEdit = !!this.props.entity;
@ -197,74 +265,7 @@ export default class CUD extends Component {
this.disableForm();
this.setFormStatusMessage('info', t('saving'));
const submitResponse = await this.validateAndSendFormValuesToURL(sendMethod, url, data => {
data.source = Number.parseInt(data.source);
data.settings = {};
const formData = new FormData();
if (!isEdit) {
if (data.source === ImportSource.CSV_FILE) {
data.settings.csv = {};
formData.append('csvFile', this.csvFile.files[0]);
data.settings.csv.delimiter = data.csvDelimiter.trim();
}
} else {
data.mapping_type = Number.parseInt(data.mapping_type);
const mapping = {
fields: {},
settings: {}
};
if (data.mapping_type === MappingType.BASIC_SUBSCRIBE) {
mapping.settings.checkEmails = data.mapping_settings_checkEmails;
for (const field of this.props.fieldsGrouped) {
if (field.column) {
const colMapping = data['mapping_fields_' + field.column + '_column'];
if (colMapping) {
mapping.fields[field.column] = {
column: colMapping
};
}
} else {
for (const option of field.settings.options) {
const col = field.groupedOptions[option.key].column;
const colMapping = data['mapping_fields_' + col + '_column'];
if (colMapping) {
mapping.fields[col] = {
column: colMapping
};
}
}
}
}
}
if (data.mapping_type === MappingType.BASIC_SUBSCRIBE || data.mapping_type === MappingType.BASIC_UNSUBSCRIBE) {
mapping.fields.email = {
column: data.mapping_fields_email_column
};
}
data.mapping = mapping;
}
for (const key in data) {
if (key.startsWith('mapping_fields') || key.startsWith('mapping_settings')) {
delete data[key];
}
}
delete data.csvFile;
delete data.csvDelimiter;
delete data.sampleRow;
formData.append('entity', JSON.stringify(data));
return formData;
});
const submitResponse = await this.validateAndSendFormValuesToURL(sendMethod, url);
if (submitResponse) {
if (!isEdit) {

View file

@ -4,7 +4,16 @@ import React, {Component} from "react";
import PropTypes from "prop-types";
import {withTranslation} from '../../lib/i18n';
import {LinkButton, requiresAuthenticatedUser, Title, Toolbar, withPageHelpers} from "../../lib/page";
import {ButtonRow, Dropdown, Form, FormSendMethod, InputField, withForm} from "../../lib/form";
import {
ButtonRow,
Dropdown,
filterData,
Form,
FormSendMethod,
InputField,
withForm,
withFormErrorHandlers
} from "../../lib/form";
import {withErrorHandling} from "../../lib/error-handling";
import {DeleteModalDialog} from "../../lib/modals";
@ -104,9 +113,14 @@ export default class CUD extends Component {
});
}
submitFormValuesMutator(data) {
data.settings.rootRule.type = data.rootRuleType;
return filterData(data, ['name', 'settings']);
}
componentDidMount() {
if (this.props.entity) {
this.getFormValuesFromEntity(this.props.entity, ::this.getFormValuesMutator);
this.getFormValuesFromEntity(this.props.entity);
} else {
this.populateFormValues({
@ -137,6 +151,7 @@ export default class CUD extends Component {
}
}
@withFormErrorHandlers
async submitHandler(submitAndLeave) {
const t = this.props.t;
@ -153,21 +168,14 @@ export default class CUD extends Component {
this.disableForm();
this.setFormStatusMessage('info', t('saving'));
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url, data => {
const keep = ['name', 'settings', 'originalHash'];
data.settings.rootRule.type = data.rootRuleType;
delete data.rootRuleType;
delete data.selectedRule;
});
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url);
if (submitResult) {
if (this.props.entity) {
if (submitAndLeave) {
this.navigateToWithFlashMessage(`/lists/${this.props.list.id}/segments`, 'success', t('Segment updated'));
} else {
await this.getFormValuesFromURL(`rest/segments/${this.props.list.id}/${this.props.entity.id}`, ::this.getFormValuesMutator);
await this.getFormValuesFromURL(`rest/segments/${this.props.list.id}/${this.props.entity.id}`);
this.enableForm();
this.setFormStatusMessage('success', t('Segment updated'));

View file

@ -30,6 +30,7 @@ export default class RuleSettingsPane extends PureComponent {
this.state = {};
this.initForm({
leaveConfirmation: false,
onChangeBeforeValidation: ::this.populateRuleDefaults,
onChange: ::this.onFormChange
});

View file

@ -11,15 +11,17 @@ import {
ButtonRow,
CheckBox,
Dropdown,
filterData,
Form,
FormSendMethod,
InputField,
withForm
withForm,
withFormErrorHandlers
} from '../../lib/form';
import {withErrorHandling} from '../../lib/error-handling';
import {RestActionModalDialog} from "../../lib/modals";
import interoperableErrors from '../../../../shared/interoperable-errors';
import {SubscriptionStatus} from '../../../../shared/lists';
import {getFieldColumn, SubscriptionStatus} from '../../../../shared/lists';
import {getFieldTypes, getSubscriptionStatusLabels} from './helpers';
import moment from 'moment-timezone';
import {withComponentMixins} from "../../lib/decorator-helpers";
@ -67,9 +69,23 @@ export default class CUD extends Component {
}
}
submitFormValuesMutator(data) {
data.status = parseInt(data.status);
data.tz = data.tz || null;
const allowedCols = ['email', 'tz', 'is_test', 'status'];
for (const fld of this.props.fieldsGrouped) {
this.fieldTypes[fld.type].assignEntity(fld, data);
allowedCols.push(getFieldColumn(fld));
}
return filterData(data, allowedCols);
}
componentDidMount() {
if (this.props.entity) {
this.getFormValuesFromEntity(this.props.entity, ::this.getFormValuesMutator);
this.getFormValuesFromEntity(this.props.entity);
} else {
const data = {
@ -106,6 +122,7 @@ export default class CUD extends Component {
}
}
@withFormErrorHandlers
async submitHandler(submitAndLeave) {
const t = this.props.t;
@ -122,21 +139,14 @@ export default class CUD extends Component {
this.disableForm();
this.setFormStatusMessage('info', t('saving'));
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url, data => {
data.status = parseInt(data.status);
data.tz = data.tz || null;
for (const fld of this.props.fieldsGrouped) {
this.fieldTypes[fld.type].assignEntity(fld, data);
}
});
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url);
if (submitResult) {
if (this.props.entity) {
if (submitAndLeave) {
this.navigateToWithFlashMessage(`/lists/${this.props.list.id}/subscriptions`, 'success', t('Subscription updated'));
} else {
await this.getFormValuesFromURL(`rest/subscriptions/${this.props.list.id}/${this.props.entity.id}`, ::this.getFormValuesMutator);
await this.getFormValuesFromURL(`rest/subscriptions/${this.props.list.id}/${this.props.entity.id}`);
this.enableForm();
this.setFormStatusMessage('success', t('Subscription updated'));
}

View file

@ -42,6 +42,7 @@ export default class List extends Component {
this.fieldTypes = getFieldTypes(t);
this.initForm({
leaveConfirmation: false,
onChange: {
segment: (newState, key, oldValue, value) => {
this.navigateTo(`/lists/${this.props.list.id}/subscriptions` + (value ? '?segment=' + value : ''));