'use strict'; import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {withTranslation} from '../lib/i18n'; import {LinkButton, requiresAuthenticatedUser, Title, withPageHelpers} from '../lib/page'; import {AlignedRow, ButtonRow, CheckBox, DatePicker, Form, InputField, TableSelect, withForm} from '../lib/form'; import {withAsyncErrorHandler, withErrorHandling} from '../lib/error-handling'; import {getCampaignLabels} from './helpers'; import {Table} from "../lib/table"; import {Button, Icon, ModalDialog} from "../lib/bootstrap-components"; import axios from "../lib/axios"; import {getPublicUrl, getUrl} from "../lib/urls"; import interoperableErrors from '../../../shared/interoperable-errors'; import {CampaignStatus, CampaignType} from "../../../shared/campaigns"; import moment from 'moment'; import campaignsStyles from "./styles.scss"; import {withComponentMixins} from "../lib/decorator-helpers"; import {TestSendModalDialog, TestSendModalDialogMode} from "./TestSendModalDialog"; @withComponentMixins([ withTranslation, withForm, withErrorHandling, withPageHelpers, requiresAuthenticatedUser ]) class PreviewForTestUserModalDialog extends Component { constructor(props) { super(props); this.initForm({ leaveConfirmation: false }); } static propTypes = { visible: PropTypes.bool.isRequired, onHide: PropTypes.func.isRequired, entity: PropTypes.object.isRequired, } localValidateFormValues(state) { const t = this.props.t; if (!state.getIn(['testUser', 'value'])) { state.setIn(['testUser', 'error'], t('subscriptionHasToBeSelectedToShowThe')) } else { state.setIn(['testUser', 'error'], null); } } componentDidMount() { this.populateFormValues({ testUser: null, }); } async previewAsync() { if (this.isFormWithoutErrors()) { const campaignCid = this.props.entity.cid; const [listCid, subscriptionCid] = this.getFormValue('testUser').split(':'); window.open(getPublicUrl(`archive/${campaignCid}/${listCid}/${subscriptionCid}`, {withLocale: true}), '_blank'); } else { this.showFormValidation(); } } async hideModal() { this.props.onHide(); } render() { const t = this.props.t; const testUsersColumns = [ { data: 1, title: t('email') }, { data: 2, title: t('subscriptionId'), render: data => {data} }, { data: 3, title: t('listId'), render: data => {data} }, { data: 4, title: t('list') }, { data: 5, title: t('listNamespace') } ]; return ( this.hideModal()} buttons={[ { label: t('preview'), className: 'btn-primary', onClickAsync: ::this.previewAsync }, { label: t('close'), className: 'btn-danger', onClickAsync: ::this.hideModal } ]}> ); } } @withComponentMixins([ withTranslation, withForm, withErrorHandling, withPageHelpers, requiresAuthenticatedUser ]) class SendControls extends Component { constructor(props) { super(props); this.state = { showTestSendModal: false, previewForTestUserVisible: false }; this.initForm({ leaveConfirmation: false }); } static propTypes = { entity: PropTypes.object.isRequired, refreshEntity: PropTypes.func.isRequired } localValidateFormValues(state) { const t = this.props.t; state.setIn(['date', 'error'], null); state.setIn(['time', 'error'], null); if (state.getIn(['sendLater', 'value'])) { const dateValue = state.getIn(['date', 'value']).trim(); if (!dateValue) { state.setIn(['date', 'error'], t('dateMustNotBeEmpty')); } else if (!moment(dateValue, 'YYYY-MM-DD', true).isValid()) { state.setIn(['date', 'error'], t('dateIsInvalid')); } const timeValue = state.getIn(['time', 'value']).trim(); if (!timeValue) { state.setIn(['time', 'error'], t('timeMustNotBeEmpty')); } else if (!moment(timeValue, 'HH:mm', true).isValid()) { state.setIn(['time', 'error'], t('timeIsInvalid')); } } } componentDidMount() { const entity = this.props.entity; if (entity.scheduled) { const date = moment(entity.scheduled); this.populateFormValues({ sendLater: true, date: date.format('YYYY-MM-DD'), time: date.format('HH:mm') }); } else { this.populateFormValues({ sendLater: false, date: '', time: '' }); } } async refreshEntity() { await this.props.refreshEntity(); } async postAndMaskStateError(url) { try { await axios.post(getUrl(url)); } catch (err) { if (err instanceof interoperableErrors.InvalidStateError) { // Just mask the fact that it's not possible to start anything and refresh instead. } else { throw err; } } } async scheduleAsync() { if (this.isFormWithoutErrors()) { const data = this.getFormValues(); const date = moment(data.date, 'YYYY-MM-DD'); const time = moment(data.time, 'HH:mm'); date.hour(time.hour()); date.minute(time.minute()); date.second(0); date.millisecond(0); date.utcOffset(0, true); // TODO, process offset from user settings await this.postAndMaskStateError(`rest/campaign-start-at/${this.props.entity.id}/${date.valueOf()}`); } else { this.showFormValidation(); } await this.refreshEntity(); } async startAsync() { await this.postAndMaskStateError(`rest/campaign-start/${this.props.entity.id}`); await this.refreshEntity(); } async stopAsync() { await this.postAndMaskStateError(`rest/campaign-stop/${this.props.entity.id}`); await this.refreshEntity(); } async confirmStart() { const t = this.props.t; this.actionDialog( t('confirmLaunch'), t('doYouWantToLaunchTheCampaign?'), async () => { await this.startAsync(); await this.refreshEntity(); } ); } async resetAsync() { const t = this.props.t; this.actionDialog( t('confirmReset'), t('doYouWantToResetTheCampaign?All'), async () => { await this.postAndMaskStateError(`rest/campaign-reset/${this.props.entity.id}`); await this.refreshEntity(); } ); } async enableAsync() { await this.postAndMaskStateError(`rest/campaign-enable/${this.props.entity.id}`); await this.refreshEntity(); } async disableAsync() { await this.postAndMaskStateError(`rest/campaign-disable/${this.props.entity.id}`); await this.refreshEntity(); } actionDialog(title, message, callback) { this.setState({ modalTitle: title, modalMessage: message, modalCallback: callback, modalVisible: true }); } modalAction(isYes) { if (isYes && this.state.modalCallback) { this.state.modalCallback(); } this.setState({ modalTitle: '', modalMessage: '', modalCallback: null, modalVisible: false }); } render() { const t = this.props.t; const entity = this.props.entity; const dialogs = ( <> this.setState({showTestSendModal: false})} campaign={this.props.entity} /> this.setState({previewForTestUserVisible: false})} entity={this.props.entity} /> this.modalAction(false)} buttons={[ { label: t('no'), className: 'btn-primary', onClickAsync: () => this.modalAction(false) }, { label: t('yes'), className: 'btn-danger', onClickAsync: () => this.modalAction(true) } ]}> {this.state.modalMessage} > ); const testButtons = ( <> this.setState({previewForTestUserVisible: true})}/> this.setState({showTestSendModal: true})}/> > ); if (entity.status === CampaignStatus.IDLE || entity.status === CampaignStatus.PAUSED || (entity.status === CampaignStatus.SCHEDULED && entity.scheduled)) { const subscrInfo = entity.subscriptionsToSend === undefined ? '' : ` (${entity.subscriptionsToSend} ${t('subscribers-1')})`; return ( {dialogs} {entity.scheduled ? t('campaignIsScheduledForDelivery') : t('campaignIsReadyToBeSentOut')} {this.getFormValue('sendLater') && {/* TODO: Timezone selector */} } {this.getFormValue('sendLater') ? : } {entity.status === CampaignStatus.PAUSED && } {entity.status === CampaignStatus.PAUSED && } {testButtons} ); } else if (entity.status === CampaignStatus.PAUSING) { return ( {dialogs} {t('Campaign is being paused. Please wait.')} {testButtons} ); } else if (entity.status === CampaignStatus.SENDING || (entity.status === CampaignStatus.SCHEDULED && !entity.scheduled)) { return ( {dialogs} {t('campaignIsBeingSentOut')} {testButtons} ); } else if (entity.status === CampaignStatus.FINISHED) { const subscrInfo = entity.subscriptionsToSend === undefined ? '' : ` (${entity.subscriptionsToSend} ${t('subscribers-1')})`; return ( {dialogs} {t('allMessagesSent!HitContinueIfYouYouWant')} {testButtons} ); } else if (entity.status === CampaignStatus.INACTIVE) { return ( {dialogs} {t('yourCampaignIsCurrentlyDisabledClick')} {testButtons} ); } else if (entity.status === CampaignStatus.ACTIVE) { return ( {dialogs} {t('yourCampaignIsEnabledAndSendingMessages')} {testButtons} ); } else { return null; } } } @withComponentMixins([ withTranslation, withErrorHandling, withPageHelpers, requiresAuthenticatedUser ]) export default class Status extends Component { constructor(props) { super(props); const t = props.t; this.state = { entity: props.entity, sendConfiguration: null }; const { campaignTypeLabels, campaignStatusLabels } = getCampaignLabels(t); this.campaignTypeLabels = campaignTypeLabels; this.campaignStatusLabels = campaignStatusLabels; this.refreshTimeoutHandler = ::this.periodicRefreshTask; this.refreshTimeoutId = 0; } static propTypes = { entity: PropTypes.object } @withAsyncErrorHandler async refreshEntity() { let resp; resp = await axios.get(getUrl(`rest/campaigns-stats/${this.props.entity.id}`)); const entity = resp.data; resp = await axios.get(getUrl(`rest/send-configurations-public/${entity.send_configuration}`)); const sendConfiguration = resp.data; this.setState({ entity, sendConfiguration }); } async periodicRefreshTask() { // The periodic task runs all the time, so that we don't have to worry about starting/stopping it as a reaction to the buttons. await this.refreshEntity(); if (this.refreshTimeoutHandler) { // For some reason the task gets rescheduled if server is restarted while the page is shown. That why we have this check here. this.refreshTimeoutId = setTimeout(this.refreshTimeoutHandler, 10000); } } componentDidMount() { // noinspection JSIgnoredPromiseFromCall this.periodicRefreshTask(); } componentWillUnmount() { clearTimeout(this.refreshTimeoutId); this.refreshTimeoutHandler = null; } render() { const t = this.props.t; const entity = this.state.entity; let sendSettings; if (this.state.sendConfiguration) { sendSettings = []; const addOverridable = (id, label) => { if(this.state.sendConfiguration[id + '_overridable'] == 1 && entity[id + '_override'] != null){ sendSettings.push({entity[id + '_override']}); } else{ sendSettings.push({this.state.sendConfiguration[id]}); } }; addOverridable('from_name', t('fromName')); addOverridable('from_email', t('fromEmailAddress')); addOverridable('reply_to', t('replytoEmailAddress')); sendSettings.push({entity.subject}); } else { sendSettings = {t('loadingSendConfiguration')} } const listsColumns = [ { data: 1, title: t('name') }, { data: 2, title: t('id'), render: data => {data} }, { data: 4, title: t('segment') }, { data: 3, title: t('listNamespace') } ]; const campaignsChildrenColumns = [ { data: 1, title: t('name') }, { data: 2, title: t('id'), render: data => {data} }, { data: 5, title: t('status'), render: (data, display, rowData) => this.campaignStatusLabels[data] }, { data: 8, title: t('created'), render: data => moment(data).fromNow() }, { actions: data => { const actions = []; const perms = data[10]; const campaignType = data[4]; const campaignSource = data[7]; if (perms.includes('viewStats')) { actions.push({ label: , link: `/campaigns/${data[0]}/status` }); } return actions; } } ]; return ( {t('campaignStatus')} {entity.name} {entity.delivered} {this.campaignStatusLabels[entity.status]} {sendSettings} {entity.type === CampaignType.RSS && RSS Entries {t('ifANewEntryIsFoundFromCampaignFeedANew')} } ); } }
{data}
{t('ifANewEntryIsFoundFromCampaignFeedANew')}