'use strict'; import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {Trans} from 'react-i18next'; import {withTranslation} from '../../lib/i18n'; import {LinkButton, requiresAuthenticatedUser, Title, withPageHelpers} from '../../lib/page' import { ACEEditor, Button, ButtonRow, Dropdown, filterData, Form, FormSendMethod, InputField, TextArea, withForm, withFormErrorHandlers } from '../../lib/form'; import {withErrorHandling} from '../../lib/error-handling'; import {NamespaceSelect, validateNamespace} from '../../lib/namespace'; import {DeleteModalDialog} from "../../lib/modals"; import mailtrainConfig from 'mailtrainConfig'; import 'brace/mode/javascript'; import 'brace/mode/json'; import 'brace/mode/handlebars'; import {withComponentMixins} from "../../lib/decorator-helpers"; @withComponentMixins([ withTranslation, withForm, withErrorHandling, withPageHelpers, requiresAuthenticatedUser ]) export default class CUD extends Component { constructor(props) { super(props); this.state = {}; this.initForm(); } static propTypes = { action: PropTypes.string.isRequired, wizard: PropTypes.string, entity: PropTypes.object } submitFormValuesMutator(data) { return filterData(data, ['name', 'description', 'mime_type', 'user_fields', 'js', 'hbs', 'namespace']); } componentDidMount() { if (this.props.entity) { this.getFormValuesFromEntity(this.props.entity); } else { const wizard = this.props.wizard; if (wizard === 'open-counts') { this.populateFormValues({ name: '', description: 'Generates a campaign report listing all subscribers along with open counts.', namespace: mailtrainConfig.user.namespace, mime_type: 'text/html', user_fields: '[\n' + ' {\n' + ' "id": "campaign",\n' + ' "name": "Campaign",\n' + ' "type": "campaign",\n' + ' "minOccurences": 1,\n' + ' "maxOccurences": 1\n' + ' }\n' + ']', js: 'const results = await campaigns.getCampaignOpenStatistics(inputs.campaign, ["*"])\n' + 'render({ results })', hbs: '

{{title}}

\n' + '\n' + '
\n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' {{#if results}}\n' + ' \n' + ' {{#each results}}\n' + ' \n' + ' \n' + ' \n' + ' \n' + ' {{/each}}\n' + ' \n' + ' {{/if}}\n' + '
\n' + ' Email\n' + ' \n' + ' Open Count\n' + '
\n' + ' {{subscription:email}}\n' + ' \n' + ' {{tracker:count}}\n' + '
\n' + '
' }); } else if (wizard === 'open-counts-csv') { this.populateFormValues({ name: '', description: 'Generates a campaign report as CSV that lists all subscribers along with open counts.', namespace: mailtrainConfig.user.namespace, mime_type: 'text/csv', user_fields: '[\n' + ' {\n' + ' "id": "campaign",\n' + ' "name": "Campaign",\n' + ' "type": "campaign",\n' + ' "minOccurences": 1,\n' + ' "maxOccurences": 1\n' + ' }\n' + ']', js: 'const results = await campaigns.getCampaignOpenStatisticsStream(inputs.campaign, [\'subscription:email\', \'tracker:count\'], null, (query, col) => query.where(col(\'subscription:status\'), SubscriptionStatus.SUBSCRIBED));\n' + '\n' + 'await renderCsvFromStream(\n' + ' results, \n' + ' {\n' + ' header: true,\n' + ' columns: [ { key: \'subscription:email\', header: \'Email\' }, { key: \'tracker:count\', header: \'Open count\' } ],\n' + ' delimiter: \',\'\n' + ' },\n' + ' async (row, encoding) => row\n' + ');', hbs: '' }); } else if (wizard === 'aggregated-open-counts') { this.populateFormValues({ name: '', description: 'Generates a campaign report with results are aggregated by "Country" custom field. (Note that this custom field has to be presents in the subscription custom fields.)', namespace: mailtrainConfig.user.namespace, mime_type: 'text/html', user_fields: '[\n' + ' {\n' + ' "id": "campaign",\n' + ' "name": "Campaign",\n' + ' "type": "campaign",\n' + ' "minOccurences": 1,\n' + ' "maxOccurences": 1\n' + ' }\n' + ']', js: 'const results = await campaigns.getCampaignOpenStatistics(inputs.campaign, ["field:country", "count_opened", "count_all"], (query, col) =>\n' + ' query.count("* AS count_all")\n' + ' .select(knex.raw("SUM(IF(`" + col("tracker:count") +"` IS NULL, 0, 1)) AS count_opened"))\n' + ' .groupBy(col("field:country"))\n' + ')\n' + '\n' + 'for (const row of results) {\n' + ' row.percentage = Math.round((row["tracker:count"] / row.count_all) * 100)\n' + '}\n' + '\n' + 'render({ results })', hbs: '

{{title}}

\n' + '\n' + '
\n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' {{#if results}}\n' + ' \n' + ' {{#each results}}\n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' {{/each}}\n' + ' \n' + ' {{/if}}\n' + '
\n' + ' Country\n' + ' \n' + ' Opened\n' + ' \n' + ' All\n' + ' \n' + ' Percentage\n' + '
\n' + ' {{field:merge_country}}\n' + ' \n' + ' {{count_opened}}\n' + ' \n' + ' {{count_all}}\n' + ' \n' + ' {{percentage}}%\n' + '
\n' + '
' }); } else { this.populateFormValues({ name: '', description: '', namespace: mailtrainConfig.user.namespace, mime_type: 'text/html', user_fields: '', js: '', hbs: '' }); } } } localValidateFormValues(state) { const t = this.props.t; if (!state.getIn(['name', 'value'])) { state.setIn(['name', 'error'], t('nameMustNotBeEmpty')); } else { state.setIn(['name', 'error'], null); } if (!state.getIn(['mime_type', 'value'])) { state.setIn(['mime_type', 'error'], t('mimeTypeMustBeSelected')); } else { state.setIn(['mime_type', 'error'], null); } try { const userFields = JSON.parse(state.getIn(['user_fields', 'value'])); state.setIn(['user_fields', 'error'], null); } catch (err) { if (err instanceof SyntaxError) { state.setIn(['user_fields', 'error'], t('syntaxErrorInTheUserFieldsSpecification')); } } validateNamespace(t, state); } @withFormErrorHandlers async submitHandler(submitAndLeave) { const t = this.props.t; let sendMethod, url; if (this.props.entity) { sendMethod = FormSendMethod.PUT; url = `rest/report-templates/${this.props.entity.id}` } else { sendMethod = FormSendMethod.POST; url = 'rest/report-templates' } this.disableForm(); this.setFormStatusMessage('info', t('saving')); const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url); if (submitResult) { if (this.props.entity) { if (submitAndLeave) { this.navigateToWithFlashMessage('/reports/templates', 'success', t('Report template updated')); } else { await this.getFormValuesFromURL(`rest/report-templates/${this.props.entity.id}`); this.enableForm(); this.setFormStatusMessage('success', t('Report template updated')); } } else { if (submitAndLeave) { this.navigateToWithFlashMessage('/reports/templates', 'success', t('Report template created')); } else { this.navigateToWithFlashMessage(`/reports/templates/${submitResult}/edit`, 'success', t('Report template created')); } } } else { this.enableForm(); this.setFormStatusMessage('warning', t('thereAreErrorsInTheFormPleaseFixThemAnd')); } } render() { const t = this.props.t; const isEdit = !!this.props.entity; const canDelete = isEdit && this.props.entity.permissions.includes('delete'); return (
{canDelete && } {isEdit ? t('editReportTemplate') : t('createReportTemplate')}