diff --git a/client/src/campaigns/Status.js b/client/src/campaigns/Status.js
index a348daa0..4eab8df2 100644
--- a/client/src/campaigns/Status.js
+++ b/client/src/campaigns/Status.js
@@ -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 =
{t('Scheduled date/time seems to be in the past. If you schedule the send, campaign will be sent immediately.')}
;
+ }
+ }
+
+
return (
{dialogs}
- {entity.scheduled ? t('campaignIsScheduledForDelivery') : t('campaignIsReadyToBeSentOut')}
+ {entity.status === CampaignStatus.SCHEDULED ? t('campaignIsScheduledForDelivery') : t('campaignIsReadyToBeSentOut')}
{this.getFormValue('sendLater') ?
-