Editing of triggers seems to work.
Some further fixes.
This commit is contained in:
parent
ffc26a4836
commit
965f30cea7
23 changed files with 855 additions and 377 deletions
|
@ -217,34 +217,28 @@ export default class CUD extends Component {
|
|||
const t = this.props.t;
|
||||
const isEdit = !!this.props.entity;
|
||||
|
||||
for (const key of state.keys()) {
|
||||
state.setIn([key, 'error'], null);
|
||||
}
|
||||
|
||||
if (!state.getIn(['name', 'value'])) {
|
||||
state.setIn(['name', 'error'], t('Name must not be empty'));
|
||||
} else {
|
||||
state.setIn(['name', 'error'], null);
|
||||
}
|
||||
|
||||
if (!state.getIn(['list', 'value'])) {
|
||||
state.setIn(['list', 'error'], t('List must be selected'));
|
||||
} else {
|
||||
state.setIn(['list', 'error'], null);
|
||||
}
|
||||
|
||||
if (state.getIn(['useSegmentation', 'value']) && !state.getIn(['segment', 'value'])) {
|
||||
state.setIn(['segment', 'error'], t('Segment must be selected'));
|
||||
} else {
|
||||
state.setIn(['segment', 'error'], null);
|
||||
}
|
||||
|
||||
if (!state.getIn(['send_configuration', 'value'])) {
|
||||
state.setIn(['send_configuration', 'error'], t('Send configuration must be selected'));
|
||||
} else {
|
||||
state.setIn(['send_configuration', 'error'], null);
|
||||
}
|
||||
|
||||
if (state.getIn(['from_email_overriden', 'value']) && !state.getIn(['from_email_override', 'value'])) {
|
||||
state.setIn(['from_email_override', 'error'], t('"From" email must not be empty'));
|
||||
} else {
|
||||
state.setIn(['from_email_override', 'error'], null);
|
||||
}
|
||||
|
||||
|
||||
|
@ -252,12 +246,6 @@ export default class CUD extends Component {
|
|||
|
||||
const sourceTypeKey = Number.parseInt(state.getIn(['source', 'value']));
|
||||
|
||||
for (const key of state.keys()) {
|
||||
if (key.startsWith('data_')) {
|
||||
state.setIn([key, 'error'], null);
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceTypeKey === CampaignSource.TEMPLATE || (!isEdit && sourceTypeKey === CampaignSource.CUSTOM_FROM_TEMPLATE)) {
|
||||
if (!state.getIn(['data_sourceTemplate', 'value'])) {
|
||||
state.setIn(['data_sourceTemplate', 'error'], t('Template must be selected'));
|
||||
|
@ -292,7 +280,6 @@ export default class CUD extends Component {
|
|||
}
|
||||
|
||||
validateNamespace(t, state);
|
||||
|
||||
}
|
||||
|
||||
async submitHandler() {
|
||||
|
@ -361,8 +348,11 @@ export default class CUD extends Component {
|
|||
});
|
||||
|
||||
if (submitResponse) {
|
||||
const sourceTypeKey = Number.parseInt(this.getFormValue('source'));
|
||||
if (this.props.entity) {
|
||||
this.navigateToWithFlashMessage('/campaigns', 'success', t('Campaign saved'));
|
||||
} else if (sourceTypeKey === CampaignSource.CUSTOM || sourceTypeKey === CampaignSource.CUSTOM_FROM_TEMPLATE || sourceTypeKey === CampaignSource.CUSTOM_FROM_CAMPAIGN) {
|
||||
this.navigateToWithFlashMessage(`/campaigns/${submitResponse}/content`, 'success', t('Campaign saved'));
|
||||
} else {
|
||||
this.navigateToWithFlashMessage(`/campaigns/${submitResponse}/edit`, 'success', t('Campaign saved'));
|
||||
}
|
||||
|
@ -494,6 +484,14 @@ export default class CUD extends Component {
|
|||
templateEdit = <InputField id="data_sourceUrl" label={t('Render URL')} help={t('If a message is sent then this URL will be POSTed to using Merge Tags as POST body. Use this if you want to generate the HTML message yourself.')}/>
|
||||
}
|
||||
|
||||
let saveButtonLabel;
|
||||
if (isEdit) {
|
||||
saveButtonLabel = t('Save');
|
||||
} else if (sourceTypeKey === CampaignSource.CUSTOM || sourceTypeKey === CampaignSource.CUSTOM_FROM_TEMPLATE || sourceTypeKey === CampaignSource.CUSTOM_FROM_CAMPAIGN) {
|
||||
saveButtonLabel = t('Save and edit content');
|
||||
} else {
|
||||
saveButtonLabel = t('Save and edit campaign');
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -545,7 +543,7 @@ export default class CUD extends Component {
|
|||
{templateEdit}
|
||||
|
||||
<ButtonRow>
|
||||
<Button type="submit" className="btn-primary" icon="ok" label={isEdit ? t('Save') : t('Save and edit campaign')}/>
|
||||
<Button type="submit" className="btn-primary" icon="ok" label={saveButtonLabel}/>
|
||||
{canDelete && <NavButton className="btn-danger" icon="remove" label={t('Delete')} linkTo={`/campaigns/${this.props.entity.id}/delete`}/> }
|
||||
</ButtonRow>
|
||||
</Form>
|
||||
|
|
|
@ -97,6 +97,7 @@ export default class List extends Component {
|
|||
actions: data => {
|
||||
const actions = [];
|
||||
const perms = data[9];
|
||||
const campaignType = data[3];
|
||||
const campaignSource = data[6];
|
||||
|
||||
if (perms.includes('edit')) {
|
||||
|
@ -127,6 +128,13 @@ export default class List extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
if (campaignType === CampaignType.TRIGGERED && perms.includes('viewTriggers')) {
|
||||
actions.push({
|
||||
label: <Icon icon="flash" title={t('Triggers')}/>,
|
||||
link: `/campaigns/${data[0]}/triggers`
|
||||
});
|
||||
}
|
||||
|
||||
if (perms.includes('share')) {
|
||||
actions.push({
|
||||
label: <Icon icon="share-alt" title={t('Share')}/>,
|
||||
|
|
|
@ -7,7 +7,12 @@ import Content from './Content';
|
|||
import CampaignsList from './List';
|
||||
import Share from '../shares/Share';
|
||||
import Files from "../lib/files";
|
||||
import {CampaignSource, CampaignType} from "../../../shared/campaigns";
|
||||
import {
|
||||
CampaignSource,
|
||||
CampaignType
|
||||
} from "../../../shared/campaigns";
|
||||
import TriggersCUD from './triggers/CUD';
|
||||
import TriggersList from './triggers/List';
|
||||
|
||||
|
||||
function getMenus(t) {
|
||||
|
@ -51,6 +56,32 @@ function getMenus(t) {
|
|||
visible: resolved => resolved.campaign.permissions.includes('viewAttachments'),
|
||||
panelRender: props => <Files title={t('Attachments')} help={t('These files will be attached to the campaign emails as proper attachments. This means they count towards to resulting eventual size of the email.')} entity={props.resolved.campaign} entityTypeId="campaign" entitySubTypeId="attachment" managePermission="manageAttachments"/>
|
||||
},
|
||||
triggers: {
|
||||
title: t('Triggers'),
|
||||
link: params => `/campaigns/${params.campaignId}/triggers/`,
|
||||
visible: resolved => resolved.campaign.type === CampaignType.TRIGGERED && resolved.campaign.permissions.includes('viewTriggers'),
|
||||
panelRender: props => <TriggersList campaign={props.resolved.campaign} />,
|
||||
children: {
|
||||
':triggerId([0-9]+)': {
|
||||
title: resolved => t('Trigger "{{name}}"', {name: resolved.trigger.name}),
|
||||
resolve: {
|
||||
trigger: params => `rest/triggers/${params.campaignId}/${params.triggerId}`,
|
||||
},
|
||||
link: params => `/campaigns/${params.campaignId}/triggers/${params.triggerId}/edit`,
|
||||
navs: {
|
||||
':action(edit|delete)': {
|
||||
title: t('Edit'),
|
||||
link: params => `/campaigns/${params.campaignId}/triggers/${params.triggerId}/edit`,
|
||||
panelRender: props => <TriggersCUD action={props.match.params.action} entity={props.resolved.trigger} campaign={props.resolved.campaign} />
|
||||
}
|
||||
}
|
||||
},
|
||||
create: {
|
||||
title: t('Create'),
|
||||
panelRender: props => <TriggersCUD action="create" campaign={props.resolved.campaign} />
|
||||
}
|
||||
}
|
||||
},
|
||||
share: {
|
||||
title: t('Share'),
|
||||
link: params => `/campaigns/${params.campaignId}/share`,
|
||||
|
|
237
client/src/campaigns/triggers/CUD.js
Normal file
237
client/src/campaigns/triggers/CUD.js
Normal file
|
@ -0,0 +1,237 @@
|
|||
'use strict';
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {translate} from 'react-i18next';
|
||||
import {
|
||||
NavButton,
|
||||
requiresAuthenticatedUser,
|
||||
Title,
|
||||
withPageHelpers
|
||||
} from '../../lib/page';
|
||||
import {
|
||||
AlignedRow,
|
||||
Button,
|
||||
ButtonRow,
|
||||
CheckBox,
|
||||
Dropdown,
|
||||
Form,
|
||||
FormSendMethod,
|
||||
InputField,
|
||||
TableSelect,
|
||||
TextArea,
|
||||
withForm
|
||||
} from '../../lib/form';
|
||||
import {withErrorHandling} from '../../lib/error-handling';
|
||||
import {DeleteModalDialog} from "../../lib/modals";
|
||||
import {getTriggerTypes} from './helpers';
|
||||
import {
|
||||
Entity,
|
||||
Event
|
||||
} from '../../../../shared/triggers';
|
||||
import moment from 'moment';
|
||||
import {getCampaignTypeLabels} from "../helpers";
|
||||
|
||||
|
||||
@translate()
|
||||
@withForm
|
||||
@withPageHelpers
|
||||
@withErrorHandling
|
||||
@requiresAuthenticatedUser
|
||||
export default class CUD extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
|
||||
this.campaignTypes = getCampaignTypeLabels(props.t);
|
||||
|
||||
const {entityLabels, eventLabels} = getTriggerTypes(props.t);
|
||||
this.entityLabels = entityLabels;
|
||||
|
||||
this.entityOptions = [
|
||||
{key: Entity.SUBSCRIPTION, label: entityLabels[Entity.SUBSCRIPTION]},
|
||||
{key: Entity.CAMPAIGN, label: entityLabels[Entity.CAMPAIGN]}
|
||||
];
|
||||
|
||||
const SubscriptionEvent = Event[Entity.SUBSCRIPTION];
|
||||
const CampaignEvent = Event[Entity.CAMPAIGN];
|
||||
this.eventOptions = {
|
||||
[Entity.SUBSCRIPTION]: [
|
||||
{key: SubscriptionEvent.CREATED, label: eventLabels[Entity.SUBSCRIPTION][SubscriptionEvent.CREATED]},
|
||||
{key: SubscriptionEvent.LATEST_OPEN, label: eventLabels[Entity.SUBSCRIPTION][SubscriptionEvent.LATEST_OPEN]},
|
||||
{key: SubscriptionEvent.LATEST_CLICK, label: eventLabels[Entity.SUBSCRIPTION][SubscriptionEvent.LATEST_CLICK]}
|
||||
],
|
||||
[Entity.CAMPAIGN]: [
|
||||
{key: CampaignEvent.DELIVERED, label: eventLabels[Entity.CAMPAIGN][CampaignEvent.DELIVERED]},
|
||||
{key: CampaignEvent.OPENED, label: eventLabels[Entity.CAMPAIGN][CampaignEvent.OPENED]},
|
||||
{key: CampaignEvent.CLICKED, label: eventLabels[Entity.CAMPAIGN][CampaignEvent.CLICKED]},
|
||||
{key: CampaignEvent.NOT_OPENED, label: eventLabels[Entity.CAMPAIGN][CampaignEvent.NOT_OPENED]},
|
||||
{key: CampaignEvent.NOT_CLICKED, label: eventLabels[Entity.CAMPAIGN][CampaignEvent.NOT_CLICKED]}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
this.initForm();
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
action: PropTypes.string.isRequired,
|
||||
campaign: PropTypes.object,
|
||||
entity: PropTypes.object
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.entity) {
|
||||
this.getFormValuesFromEntity(this.props.entity, data => {
|
||||
data.daysAfter = (Math.round(data.seconds_after / (3600 * 24))).toString();
|
||||
|
||||
if (data.entity === Entity.SUBSCRIPTION) {
|
||||
data.subscriptionEvent = data.event;
|
||||
} else {
|
||||
data.subscriptionEvent = Event[Entity.SUBSCRIPTION].CREATED;
|
||||
}
|
||||
|
||||
if (data.entity === Entity.CAMPAIGN) {
|
||||
data.campaignEvent = data.event;
|
||||
} else {
|
||||
data.campaignEvent = Event[Entity.CAMPAIGN].DELIVERED;
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
this.populateFormValues({
|
||||
name: '',
|
||||
description: '',
|
||||
entity: Entity.SUBSCRIPTION,
|
||||
subscriptionEvent: Event[Entity.SUBSCRIPTION].CREATED,
|
||||
campaignEvent: Event[Entity.CAMPAIGN].DELIVERED,
|
||||
daysAfter: '',
|
||||
enabled: true,
|
||||
source_campaign: null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
localValidateFormValues(state) {
|
||||
const t = this.props.t;
|
||||
|
||||
const entityKey = state.getIn(['entity', 'value']);
|
||||
|
||||
if (!state.getIn(['name', 'value'])) {
|
||||
state.setIn(['name', 'error'], t('Name must not be empty'));
|
||||
} else {
|
||||
state.setIn(['name', 'error'], null);
|
||||
}
|
||||
|
||||
const daysAfter = state.getIn(['daysAfter', 'value']).trim();
|
||||
if (daysAfter === '') {
|
||||
state.setIn(['daysAfter', 'error'], t('Values must not be empty'));
|
||||
} else if (isNaN(daysAfter) || Number.parseInt(daysAfter) < 0) {
|
||||
state.setIn(['daysAfter', 'error'], t('Value must be a non-negative number'));
|
||||
} else {
|
||||
state.setIn(['daysAfter', 'error'], null);
|
||||
}
|
||||
|
||||
if (entityKey === Entity.CAMPAIGN && !state.getIn(['source_campaign', 'value'])) {
|
||||
state.setIn(['source_campaign', 'error'], t('Source campaign must not be empty'));
|
||||
} else {
|
||||
state.setIn(['source_campaign', 'error'], null);
|
||||
}
|
||||
}
|
||||
|
||||
async submitHandler() {
|
||||
const t = this.props.t;
|
||||
|
||||
let sendMethod, url;
|
||||
if (this.props.entity) {
|
||||
sendMethod = FormSendMethod.PUT;
|
||||
url = `rest/triggers/${this.props.campaign.id}/${this.props.entity.id}`
|
||||
} else {
|
||||
sendMethod = FormSendMethod.POST;
|
||||
url = `rest/triggers/${this.props.campaign.id}`
|
||||
}
|
||||
|
||||
try {
|
||||
this.disableForm();
|
||||
this.setFormStatusMessage('info', t('Saving ...'));
|
||||
|
||||
const submitSuccessful = await this.validateAndSendFormValuesToURL(sendMethod, url, data => {
|
||||
data.seconds_after = Number.parseInt(data.daysAfter) * 3600 * 24;
|
||||
|
||||
if (data.entity === Entity.SUBSCRIPTION) {
|
||||
data.event = data.subscriptionEvent;
|
||||
} else if (data.entity === Entity.CAMPAIGN) {
|
||||
data.event = data.campaignEvent;
|
||||
}
|
||||
});
|
||||
|
||||
if (submitSuccessful) {
|
||||
this.navigateToWithFlashMessage(`/campaigns/${this.props.campaign.id}/triggers`, 'success', t('Trigger saved'));
|
||||
} else {
|
||||
this.enableForm();
|
||||
this.setFormStatusMessage('warning', t('There are errors in the form. Please fix them and submit again.'));
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
const isEdit = !!this.props.entity;
|
||||
|
||||
const entityKey = this.getFormValue('entity');
|
||||
|
||||
const campaignsColumns = [
|
||||
{ data: 1, title: t('Name') },
|
||||
{ data: 2, title: t('Description') },
|
||||
{ data: 3, title: t('Type'), render: data => this.campaignTypes[data] },
|
||||
{ data: 4, title: t('Created'), render: data => moment(data).fromNow() },
|
||||
{ data: 5, title: t('Namespace') }
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isEdit &&
|
||||
<DeleteModalDialog
|
||||
stateOwner={this}
|
||||
visible={this.props.action === 'delete'}
|
||||
deleteUrl={`rest/triggers/${this.props.campaign.id}/${this.props.entity.id}`}
|
||||
cudUrl={`/campaigns/${this.props.campaign.id}/triggers/${this.props.entity.id}/edit`}
|
||||
listUrl={`/campaigns/${this.props.campaign.id}/triggers`}
|
||||
deletingMsg={t('Deleting trigger ...')}
|
||||
deletedMsg={t('Trigger deleted')}/>
|
||||
}
|
||||
|
||||
<Title>{isEdit ? t('Edit Trigger') : t('Create Trigger')}</Title>
|
||||
|
||||
<Form stateOwner={this} onSubmitAsync={::this.submitHandler}>
|
||||
<InputField id="name" label={t('Name')}/>
|
||||
<TextArea id="description" label={t('Description')}/>
|
||||
|
||||
<Dropdown id="entity" label={t('Entity')} options={this.entityOptions} help={t('Select the type of the trigger rule.')}/>
|
||||
|
||||
<InputField id="daysAfter" label={t('Trigger fires')}/>
|
||||
|
||||
<AlignedRow>days after:</AlignedRow>
|
||||
|
||||
{entityKey === Entity.SUBSCRIPTION && <Dropdown id="subscriptionEvent" label={t('Event')} options={this.eventOptions[Entity.SUBSCRIPTION]} help={t('Select the event that triggers sending the campaign.')}/>}
|
||||
|
||||
{entityKey === Entity.CAMPAIGN && <Dropdown id="campaignEvent" label={t('Event')} options={this.eventOptions[Entity.CAMPAIGN]} help={t('Select the event that triggers sending the campaign.')}/>}
|
||||
|
||||
{entityKey === Entity.CAMPAIGN &&
|
||||
<TableSelect id="source_campaign" label={t('Campaign')} withHeader dropdown dataUrl={`rest/campaigns-others-by-list-table/${this.props.campaign.id}/${this.props.campaign.list}`} columns={campaignsColumns} selectionLabelIndex={1} />
|
||||
}
|
||||
|
||||
<CheckBox id="enabled" text={t('Enabled')}/>
|
||||
|
||||
<ButtonRow>
|
||||
<Button type="submit" className="btn-primary" icon="ok" label={t('Save')}/>
|
||||
{isEdit && <NavButton className="btn-danger" icon="remove" label={t('Delete')} linkTo={`/campaigns/${this.props.campaign.id}/triggers/${this.props.entity.id}/delete`}/>}
|
||||
</ButtonRow>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
81
client/src/campaigns/triggers/List.js
Normal file
81
client/src/campaigns/triggers/List.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
'use strict';
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {translate} from 'react-i18next';
|
||||
import {
|
||||
NavButton,
|
||||
requiresAuthenticatedUser,
|
||||
Title,
|
||||
Toolbar,
|
||||
withPageHelpers
|
||||
} from '../../lib/page';
|
||||
import {withErrorHandling} from '../../lib/error-handling';
|
||||
import {Table} from '../../lib/table';
|
||||
import {getTriggerTypes} from './helpers';
|
||||
import {Icon} from "../../lib/bootstrap-components";
|
||||
|
||||
@translate()
|
||||
@withPageHelpers
|
||||
@withErrorHandling
|
||||
@requiresAuthenticatedUser
|
||||
export default class List extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const {entityLabels, eventLabels} = getTriggerTypes(props.t);
|
||||
this.entityLabels = entityLabels;
|
||||
this.eventLabels = eventLabels;
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
campaign: PropTypes.object
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
}
|
||||
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
|
||||
const columns = [
|
||||
{ data: 1, title: t('Name') },
|
||||
{ data: 2, title: t('Description') },
|
||||
{ data: 3, title: t('List') },
|
||||
{ data: 4, title: t('Entity'), render: data => this.entityLabels[data], searchable: false },
|
||||
{ data: 5, title: t('Event'), render: (data, cmd, rowData) => this.eventLabels[rowData[4]][data], searchable: false },
|
||||
{ data: 6, title: t('Days after'), render: data => Math.round(data / (3600 * 24)) },
|
||||
{ data: 7, title: t('Enabled'), render: data => data ? t('Yes') : t('No'), searchable: false},
|
||||
{
|
||||
actions: data => {
|
||||
const actions = [];
|
||||
|
||||
if (this.props.campaign.permissions.includes('manageTriggers')) {
|
||||
actions.push({
|
||||
label: <Icon icon="edit" title={t('Edit')}/>,
|
||||
link: `/campaigns/${this.props.campaign.id}/triggers/${data[0]}/edit`
|
||||
});
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{this.props.campaign.permissions.includes('manageTriggers') &&
|
||||
<Toolbar>
|
||||
<NavButton linkTo={`/campaigns/${this.props.campaign.id}/triggers/create`} className="btn-primary" icon="plus" label={t('Create Trigger')}/>
|
||||
</Toolbar>
|
||||
}
|
||||
|
||||
<Title>{t('Triggers')}</Title>
|
||||
|
||||
<Table withHeader dataUrl={`rest/triggers-by-campaign-table/${this.props.campaign.id}`} columns={columns} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
35
client/src/campaigns/triggers/helpers.js
Normal file
35
client/src/campaigns/triggers/helpers.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
|
||||
import {Entity, Event} from '../../../../shared/triggers';
|
||||
|
||||
export function getTriggerTypes(t) {
|
||||
|
||||
const entityLabels = {
|
||||
[Entity.SUBSCRIPTION]: t('Subscription'),
|
||||
[Entity.CAMPAIGN]: t('Campaign')
|
||||
};
|
||||
|
||||
const SubscriptionEvent = Event[Entity.SUBSCRIPTION];
|
||||
const CampaignEvent = Event[Entity.CAMPAIGN];
|
||||
|
||||
const eventLabels = {
|
||||
[Entity.SUBSCRIPTION]: {
|
||||
[SubscriptionEvent.CREATED]: t('Created'),
|
||||
[SubscriptionEvent.LATEST_OPEN]: t('Latest open'),
|
||||
[SubscriptionEvent.LATEST_CLICK]: t('Latest click')
|
||||
},
|
||||
[Entity.CAMPAIGN]: {
|
||||
[CampaignEvent.DELIVERED]: t('Delivered'),
|
||||
[CampaignEvent.OPENED]: t('Opened'),
|
||||
[CampaignEvent.CLICKED]: t('Clicked'),
|
||||
[CampaignEvent.NOT_OPENED]: t('Not opened'),
|
||||
[CampaignEvent.NOT_CLICKED]: t('Not clicked')
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
entityLabels,
|
||||
eventLabels
|
||||
};
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
import {anonymousRestrictedAccessToken} from '../../../shared/urls';
|
||||
import mailtrainConfig from "mailtrainConfig";
|
||||
|
||||
let restrictedAccessToken = 'ANONYMOUS';
|
||||
let restrictedAccessToken = anonymousRestrictedAccessToken;
|
||||
|
||||
function setRestrictedAccessToken(token) {
|
||||
restrictedAccessToken = token;
|
||||
|
@ -28,7 +29,7 @@ function getBaseDir() {
|
|||
if (mailtrainConfig.trusted) {
|
||||
return mailtrainConfig.trustedUrlBaseDir;
|
||||
} else {
|
||||
return mailtrainConfig.sandboxUrlBaseDir + 'ANONYMOUS';
|
||||
return mailtrainConfig.sandboxUrlBaseDir + anonymousRestrictedAccessToken;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ export default class List extends Component {
|
|||
data: 1,
|
||||
title: t('Name'),
|
||||
actions: data => {
|
||||
const perms = data[6];
|
||||
const perms = data[7];
|
||||
if (perms.includes('viewSubscriptions')) {
|
||||
return [{label: data[1], link: `/lists/${data[0]}/subscriptions`}];
|
||||
} else {
|
||||
|
@ -62,7 +62,9 @@ export default class List extends Component {
|
|||
{
|
||||
actions: data => {
|
||||
const actions = [];
|
||||
const perms = data[6];
|
||||
const triggersCount = data[6];
|
||||
const perms = data[7];
|
||||
console.log(data);
|
||||
|
||||
if (perms.includes('viewSubscriptions')) {
|
||||
actions.push({
|
||||
|
@ -92,6 +94,13 @@ export default class List extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
if (triggersCount > 0) {
|
||||
actions.push({
|
||||
label: <Icon icon="flash" title={t('Triggers')}/>,
|
||||
link: `/lists/${data[0]}/triggers`
|
||||
});
|
||||
}
|
||||
|
||||
if (perms.includes('share')) {
|
||||
actions.push({
|
||||
label: <Icon icon="share-alt" title={t('Share')}/>,
|
||||
|
|
75
client/src/lists/TriggersList.js
Normal file
75
client/src/lists/TriggersList.js
Normal file
|
@ -0,0 +1,75 @@
|
|||
'use strict';
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {translate} from 'react-i18next';
|
||||
import {
|
||||
requiresAuthenticatedUser,
|
||||
Title,
|
||||
withPageHelpers
|
||||
} from '../lib/page';
|
||||
import {withErrorHandling} from '../lib/error-handling';
|
||||
import {Table} from '../lib/table';
|
||||
import {getTriggerTypes} from '../campaigns/triggers/helpers';
|
||||
import {Icon} from "../lib/bootstrap-components";
|
||||
|
||||
@translate()
|
||||
@withPageHelpers
|
||||
@withErrorHandling
|
||||
@requiresAuthenticatedUser
|
||||
export default class List extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const {entityLabels, eventLabels} = getTriggerTypes(props.t);
|
||||
this.entityLabels = entityLabels;
|
||||
this.eventLabels = eventLabels;
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
list: PropTypes.object
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
}
|
||||
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
|
||||
const columns = [
|
||||
{ data: 1, title: t('Name') },
|
||||
{ data: 2, title: t('Description') },
|
||||
{ data: 3, title: t('Campaign') },
|
||||
{ data: 4, title: t('Entity'), render: data => this.entityLabels[data], searchable: false },
|
||||
{ data: 5, title: t('Event'), render: (data, cmd, rowData) => this.eventLabels[rowData[4]][data], searchable: false },
|
||||
{ data: 6, title: t('Days after'), render: data => Math.round(data / (3600 * 24)) },
|
||||
{ data: 7, title: t('Enabled'), render: data => data ? t('Yes') : t('No'), searchable: false},
|
||||
{
|
||||
actions: data => {
|
||||
const actions = [];
|
||||
const perms = data[9];
|
||||
const campaignId = data[8];
|
||||
|
||||
if (perms.includes('manageTriggers')) {
|
||||
actions.push({
|
||||
label: <Icon icon="edit" title={t('Edit')}/>,
|
||||
link: `/campaigns/${campaignId}/triggers/${data[0]}/edit`
|
||||
});
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Title>{t('Triggers')}</Title>
|
||||
|
||||
<Table withHeader dataUrl={`rest/triggers-by-list-table/${this.props.list.id}`} columns={columns} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ import SubscriptionsCUD from './subscriptions/CUD';
|
|||
import SegmentsList from './segments/List';
|
||||
import SegmentsCUD from './segments/CUD';
|
||||
import Share from '../shares/Share';
|
||||
|
||||
import TriggersList from './TriggersList';
|
||||
|
||||
function getMenus(t) {
|
||||
return {
|
||||
|
@ -127,6 +127,11 @@ function getMenus(t) {
|
|||
}
|
||||
}
|
||||
},
|
||||
triggers: {
|
||||
title: t('Triggers'),
|
||||
link: params => `/lists/${params.listId}/triggers`,
|
||||
panelRender: props => <TriggersList list={props.resolved.list} />
|
||||
},
|
||||
share: {
|
||||
title: t('Share'),
|
||||
link: params => `/lists/${params.listId}/share`,
|
||||
|
|
|
@ -94,17 +94,17 @@ export default class CUD extends Component {
|
|||
localValidateFormValues(state) {
|
||||
const t = this.props.t;
|
||||
|
||||
for (const key of state.keys()) {
|
||||
state.setIn([key, 'error'], null);
|
||||
}
|
||||
|
||||
if (!state.getIn(['name', 'value'])) {
|
||||
state.setIn(['name', 'error'], t('Name must not be empty'));
|
||||
} else {
|
||||
state.setIn(['name', 'error'], null);
|
||||
}
|
||||
|
||||
const typeKey = state.getIn(['type', 'value']);
|
||||
if (!typeKey) {
|
||||
state.setIn(['type', 'error'], t('Type must be selected'));
|
||||
} else {
|
||||
state.setIn(['type', 'error'], null);
|
||||
}
|
||||
|
||||
validateNamespace(t, state);
|
||||
|
|
|
@ -112,14 +112,12 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
const mosaicoTemplate = state.getIn([prefix + 'mosaicoTemplate', 'value']);
|
||||
if (!mosaicoTemplate) {
|
||||
state.setIn([prefix + 'mosaicoTemplate', 'error'], t('Mosaico template must be selected'));
|
||||
} else {
|
||||
state.setIn([prefix + 'mosaicoTemplate', 'error'], null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const mosaicoFsTemplatesOptions = mailtrainConfig.mosaico.fsTemplates.map(([key, label]) => ({key, label}));
|
||||
const mosaicoFsTemplatesLabels = new Map(mailtrainConfig.mosaico.fsTemplates);
|
||||
const mosaicoFsTemplatesOptions = mailtrainConfig.mosaico.fsTemplates;
|
||||
const mosaicoFsTemplatesLabels = new Map(mailtrainConfig.mosaico.fsTemplates.map(({key, label}) => ([key, label])));
|
||||
|
||||
templateTypes.mosaicoWithFsTemplate = {
|
||||
typeName: t('Mosaico with predefined templates'),
|
||||
|
@ -151,7 +149,7 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
});
|
||||
},
|
||||
initData: () => ({
|
||||
[prefix + 'mosaicoFsTemplate']: mailtrainConfig.mosaico.fsTemplates[0][0],
|
||||
[prefix + 'mosaicoFsTemplate']: mailtrainConfig.mosaico.fsTemplates[0].key,
|
||||
[prefix + 'mosaicoData']: {}
|
||||
}),
|
||||
afterLoad: data => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue