Fixed some bugs in subscription process

Added timezone selector to campaign scheduling
Fixed problems with pausing campaign.
This commit is contained in:
Tomas Bures 2019-07-10 02:06:56 +04:00
parent 4113cb8476
commit e3a5a3c4eb
23 changed files with 218 additions and 99 deletions

View file

@ -13,11 +13,11 @@ 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 moment from 'moment-timezone';
import campaignsStyles from "./styles.scss";
import {withComponentMixins} from "../lib/decorator-helpers";
import {TestSendModalDialog, TestSendModalDialogMode} from "./TestSendModalDialog";
import styles from "../lib/styles.scss";
@withComponentMixins([
withTranslation,
@ -114,6 +114,8 @@ class SendControls extends Component {
this.initForm({
leaveConfirmation: false
});
this.timezoneOptions = moment.tz.names().map(x => [x]);
}
static propTypes = {
@ -126,6 +128,7 @@ class SendControls extends Component {
state.setIn(['date', 'error'], null);
state.setIn(['time', 'error'], null);
state.setIn(['timezone', 'error'], null);
if (state.getIn(['sendLater', 'value'])) {
const dateValue = state.getIn(['date', 'value']).trim();
@ -141,36 +144,54 @@ class SendControls extends Component {
} else if (!moment(timeValue, 'HH:mm', true).isValid()) {
state.setIn(['time', 'error'], t('timeIsInvalid'));
}
const timezone = state.getIn(['timezone', 'value']);
if (!timezone) {
state.setIn(['timezone', 'error'], t('Timezone must be selected'));
}
}
}
componentDidMount() {
populateSendLater() {
const entity = this.props.entity;
if (entity.scheduled) {
const date = moment.utc(entity.scheduled);
const timezone = entity.data.timezone || moment.tz.guess();
const date = moment.tz(entity.scheduled, timezone);
this.populateFormValues({
sendLater: true,
date: date.format('YYYY-MM-DD'),
time: date.format('HH:mm')
time: date.format('HH:mm'),
timezone
});
} else {
this.populateFormValues({
sendLater: false,
date: '',
time: ''
time: '',
timezone: moment.tz.guess()
});
}
}
componentDidMount() {
this.populateSendLater();
}
componentDidUpdate(prevProps) {
if (prevProps.entity.scheduled !== this.props.entity.scheduled) {
this.populateSendLater();
}
}
async refreshEntity() {
await this.props.refreshEntity();
}
async postAndMaskStateError(url) {
async postAndMaskStateError(url, data) {
try {
await axios.post(getUrl(url));
await axios.post(getUrl(url), data);
} catch (err) {
if (err instanceof interoperableErrors.InvalidStateError) {
// Just mask the fact that it's not possible to start anything and refresh instead.
@ -183,17 +204,12 @@ class SendControls extends Component {
async scheduleAsync() {
if (this.isFormWithoutErrors()) {
const data = this.getFormValues();
const date = moment(data.date, 'YYYY-MM-DD');
const time = moment(data.time, 'HH:mm');
const dateTime = moment.tz(data.date + ' ' + data.time, 'YYYY-MM-DD HH:mm', data.timezone);
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()}`);
await this.postAndMaskStateError(`rest/campaign-start-at/${this.props.entity.id}`, {
startAt: dateTime.valueOf(),
timezone: data.timezone
});
} else {
this.showFormValidation();
@ -219,7 +235,17 @@ class SendControls extends Component {
t('doYouWantToLaunchTheCampaign?'),
async () => {
await this.startAsync();
await this.refreshEntity();
}
);
}
async confirmSchedule() {
const t = this.props.t;
this.actionDialog(
t('confirmLaunch'),
t('Do you want to schedule the campaign for launch?'),
async () => {
await this.scheduleAsync();
}
);
}
@ -305,10 +331,30 @@ class SendControls extends Component {
const subscrInfo = entity.subscriptionsToSend === undefined ? '' : ` (${entity.subscriptionsToSend} ${t('subscribers-1')})`;
const timezoneColumns = [
{ data: 0, title: t('Timezone') }
];
const dateValue = (this.getFormValue('date') || '').trim();
const timeValue = (this.getFormValue('time') || '').trim();
const timezone = this.getFormValue('timezone');
let dateTimeHelp = t('Select date, time and a timezone to display the date and time with offset');
let dateTimeAlert = null;
if (moment(dateValue, 'YYYY-MM-DD', true).isValid() && moment(timeValue, 'HH:mm', true).isValid() && timezone) {
const dateTime = moment.tz(dateValue + ' ' + timeValue, 'YYYY-MM-DD HH:mm', timezone);
dateTimeHelp = dateTime.toString();
if (!moment().isBefore(dateTime)) {
dateTimeAlert = <div className="alert alert-danger" role="alert">{t('Scheduled date/time seems to be in the past. If you schedule the send, campaign will be sent immediately.')}</div>;
}
}
return (
<div>{dialogs}
<AlignedRow label={t('sendStatus')}>
{entity.scheduled ? t('campaignIsScheduledForDelivery') : t('campaignIsReadyToBeSentOut')}
{entity.status === CampaignStatus.SCHEDULED ? t('campaignIsScheduledForDelivery') : t('campaignIsReadyToBeSentOut')}
</AlignedRow>
<Form stateOwner={this}>
@ -317,16 +363,20 @@ class SendControls extends Component {
<div>
<DatePicker id="date" label={t('date')} />
<InputField id="time" label={t('time')} help={t('enter24HourTimeInFormatHhmmEg1348')}/>
{/* TODO: Timezone selector */}
<TableSelect id="timezone" label={t('Timezone')} dropdown columns={timezoneColumns} selectionKeyIndex={0} selectionLabelIndex={0} data={this.timezoneOptions}
help={dateTimeHelp}
/>
{dateTimeAlert && <AlignedRow>{dateTimeAlert}</AlignedRow>}
</div>
}
</Form>
<ButtonRow className={campaignsStyles.sendButtonRow}>
{this.getFormValue('sendLater') ?
<Button className="btn-primary" icon="play" label={(entity.scheduled ? t('rescheduleSend') : t('scheduleSend')) + subscrInfo} onClickAsync={::this.scheduleAsync}/>
<Button className="btn-primary" icon="play" label={(entity.status === CampaignStatus.SCHEDULED ? t('rescheduleSend') : t('scheduleSend')) + subscrInfo} onClickAsync={::this.confirmSchedule}/>
:
<Button className="btn-primary" icon="play" label={t('send') + subscrInfo} onClickAsync={::this.confirmStart}/>
}
{entity.status === CampaignStatus.SCHEDULED && <Button className="btn-primary" icon="pause" label={t('Pause')} onClickAsync={::this.stopAsync}/>}
{entity.status === CampaignStatus.PAUSED && <Button className="btn-primary" icon="redo" label={t('reset')} onClickAsync={::this.resetAsync}/>}
{entity.status === CampaignStatus.PAUSED && <LinkButton className="btn-secondary" icon="signal" label={t('viewStatistics')} to={`/campaigns/${entity.id}/statistics`}/>}
{testButtons}