parent
7c21cf2255
commit
6bfa551c85
16 changed files with 142 additions and 38 deletions
|
@ -28,7 +28,7 @@ class Api::V1::Accounts::CampaignsController < Api::V1::Accounts::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def campaign_params
|
def campaign_params
|
||||||
params.require(:campaign).permit(:title, :description, :message, :enabled, :inbox_id, :sender_id,
|
params.require(:campaign).permit(:title, :description, :message, :enabled, :trigger_only_during_business_hours, :inbox_id, :sender_id,
|
||||||
:scheduled_at, audience: [:type, :id], trigger_rules: {})
|
:scheduled_at, audience: [:type, :id], trigger_rules: {})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
"ERROR": "Time on page is required"
|
"ERROR": "Time on page is required"
|
||||||
},
|
},
|
||||||
"ENABLED": "Enable campaign",
|
"ENABLED": "Enable campaign",
|
||||||
|
"TRIGGER_ONLY_BUSINESS_HOURS": "Trigger only during business hours",
|
||||||
"SUBMIT": "Add Campaign"
|
"SUBMIT": "Add Campaign"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
|
|
|
@ -146,6 +146,15 @@
|
||||||
/>
|
/>
|
||||||
{{ $t('CAMPAIGN.ADD.FORM.ENABLED') }}
|
{{ $t('CAMPAIGN.ADD.FORM.ENABLED') }}
|
||||||
</label>
|
</label>
|
||||||
|
<label v-if="isOngoingType">
|
||||||
|
<input
|
||||||
|
v-model="triggerOnlyDuringBusinessHours"
|
||||||
|
type="checkbox"
|
||||||
|
value="triggerOnlyDuringBusinessHours"
|
||||||
|
name="triggerOnlyDuringBusinessHours"
|
||||||
|
/>
|
||||||
|
{{ $t('CAMPAIGN.ADD.FORM.TRIGGER_ONLY_BUSINESS_HOURS') }}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
@ -185,6 +194,7 @@ export default {
|
||||||
timeOnPage: 10,
|
timeOnPage: 10,
|
||||||
show: true,
|
show: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
triggerOnlyDuringBusinessHours: false,
|
||||||
scheduledAt: null,
|
scheduledAt: null,
|
||||||
selectedAudience: [],
|
selectedAudience: [],
|
||||||
senderList: [],
|
senderList: [],
|
||||||
|
@ -280,6 +290,9 @@ export default {
|
||||||
inbox_id: this.selectedInbox,
|
inbox_id: this.selectedInbox,
|
||||||
sender_id: this.selectedSender || null,
|
sender_id: this.selectedSender || null,
|
||||||
enabled: this.enabled,
|
enabled: this.enabled,
|
||||||
|
trigger_only_during_business_hours:
|
||||||
|
// eslint-disable-next-line prettier/prettier
|
||||||
|
this.triggerOnlyDuringBusinessHours,
|
||||||
trigger_rules: {
|
trigger_rules: {
|
||||||
url: this.endPoint,
|
url: this.endPoint,
|
||||||
time_on_page: this.timeOnPage,
|
time_on_page: this.timeOnPage,
|
||||||
|
|
|
@ -87,6 +87,15 @@
|
||||||
/>
|
/>
|
||||||
{{ $t('CAMPAIGN.ADD.FORM.ENABLED') }}
|
{{ $t('CAMPAIGN.ADD.FORM.ENABLED') }}
|
||||||
</label>
|
</label>
|
||||||
|
<label v-if="isOngoingType">
|
||||||
|
<input
|
||||||
|
v-model="triggerOnlyDuringBusinessHours"
|
||||||
|
type="checkbox"
|
||||||
|
value="triggerOnlyDuringBusinessHours"
|
||||||
|
name="triggerOnlyDuringBusinessHours"
|
||||||
|
/>
|
||||||
|
{{ $t('CAMPAIGN.ADD.FORM.TRIGGER_ONLY_BUSINESS_HOURS') }}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<woot-button :is-loading="uiFlags.isCreating">
|
<woot-button :is-loading="uiFlags.isCreating">
|
||||||
|
@ -125,6 +134,7 @@ export default {
|
||||||
selectedInbox: null,
|
selectedInbox: null,
|
||||||
endPoint: '',
|
endPoint: '',
|
||||||
timeOnPage: 10,
|
timeOnPage: 10,
|
||||||
|
triggerOnlyDuringBusinessHours: false,
|
||||||
show: true,
|
show: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
senderList: [],
|
senderList: [],
|
||||||
|
@ -209,6 +219,7 @@ export default {
|
||||||
title,
|
title,
|
||||||
message,
|
message,
|
||||||
enabled,
|
enabled,
|
||||||
|
trigger_only_during_business_hours: triggerOnlyDuringBusinessHours,
|
||||||
inbox: { id: inboxId },
|
inbox: { id: inboxId },
|
||||||
trigger_rules: { url: endPoint, time_on_page: timeOnPage },
|
trigger_rules: { url: endPoint, time_on_page: timeOnPage },
|
||||||
sender,
|
sender,
|
||||||
|
@ -218,6 +229,7 @@ export default {
|
||||||
this.endPoint = endPoint;
|
this.endPoint = endPoint;
|
||||||
this.timeOnPage = timeOnPage;
|
this.timeOnPage = timeOnPage;
|
||||||
this.selectedInbox = inboxId;
|
this.selectedInbox = inboxId;
|
||||||
|
this.triggerOnlyDuringBusinessHours = triggerOnlyDuringBusinessHours;
|
||||||
this.selectedSender = (sender && sender.id) || 0;
|
this.selectedSender = (sender && sender.id) || 0;
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
this.loadInboxMembers();
|
this.loadInboxMembers();
|
||||||
|
@ -233,6 +245,9 @@ export default {
|
||||||
title: this.title,
|
title: this.title,
|
||||||
message: this.message,
|
message: this.message,
|
||||||
inbox_id: this.$route.params.inboxId,
|
inbox_id: this.$route.params.inboxId,
|
||||||
|
trigger_only_during_business_hours:
|
||||||
|
// eslint-disable-next-line prettier/prettier
|
||||||
|
this.triggerOnlyDuringBusinessHours,
|
||||||
sender_id: this.selectedSender || null,
|
sender_id: this.selectedSender || null,
|
||||||
enabled: this.enabled,
|
enabled: this.enabled,
|
||||||
trigger_rules: {
|
trigger_rules: {
|
||||||
|
|
|
@ -19,12 +19,14 @@ import Router from './views/Router';
|
||||||
import { getLocale } from './helpers/urlParamsHelper';
|
import { getLocale } from './helpers/urlParamsHelper';
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
import { isEmptyObject } from 'widget/helpers/utils';
|
import { isEmptyObject } from 'widget/helpers/utils';
|
||||||
|
import availabilityMixin from 'widget/mixins/availability';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
Router,
|
Router,
|
||||||
},
|
},
|
||||||
|
mixins: [availabilityMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showUnreadView: false,
|
showUnreadView: false,
|
||||||
|
@ -219,7 +221,11 @@ export default {
|
||||||
this.scrollConversationToBottom();
|
this.scrollConversationToBottom();
|
||||||
} else if (message.event === 'change-url') {
|
} else if (message.event === 'change-url') {
|
||||||
const { referrerURL, referrerHost } = message;
|
const { referrerURL, referrerHost } = message;
|
||||||
this.initCampaigns({ currentURL: referrerURL, websiteToken });
|
this.initCampaigns({
|
||||||
|
currentURL: referrerURL,
|
||||||
|
websiteToken,
|
||||||
|
isInBusinessHours: this.isInBusinessHours,
|
||||||
|
});
|
||||||
window.referrerURL = referrerURL;
|
window.referrerURL = referrerURL;
|
||||||
bus.$emit(BUS_EVENTS.SET_REFERRER_HOST, referrerHost);
|
bus.$emit(BUS_EVENTS.SET_REFERRER_HOST, referrerHost);
|
||||||
} else if (message.event === 'toggle-close-button') {
|
} else if (message.event === 'toggle-close-button') {
|
||||||
|
|
|
@ -7,17 +7,24 @@ export const formatCampaigns = ({ campaigns }) => {
|
||||||
return campaigns.map(item => {
|
return campaigns.map(item => {
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
|
triggerOnlyDuringBusinessHours:
|
||||||
|
item.trigger_only_during_business_hours || false,
|
||||||
timeOnPage: item?.trigger_rules?.time_on_page,
|
timeOnPage: item?.trigger_rules?.time_on_page,
|
||||||
url: item?.trigger_rules?.url,
|
url: item?.trigger_rules?.url,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find all campaigns that matches the current URL
|
// Filter all campaigns based on current URL and business availability time
|
||||||
export const filterCampaigns = ({ campaigns, currentURL }) => {
|
export const filterCampaigns = ({
|
||||||
return campaigns.filter(
|
campaigns,
|
||||||
item =>
|
currentURL,
|
||||||
stripTrailingSlash({ URL: item.url }) ===
|
isInBusinessHours,
|
||||||
stripTrailingSlash({ URL: currentURL })
|
}) => {
|
||||||
|
return campaigns.filter(item =>
|
||||||
|
item.triggerOnlyDuringBusinessHours
|
||||||
|
? isInBusinessHours
|
||||||
|
: stripTrailingSlash({ URL: item.url }) ===
|
||||||
|
stripTrailingSlash({ URL: currentURL })
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
|
trigger_only_during_business_hours: false,
|
||||||
trigger_rules: {
|
trigger_rules: {
|
||||||
time_on_page: 3,
|
time_on_page: 3,
|
||||||
url: 'https://www.chatwoot.com/pricing',
|
url: 'https://www.chatwoot.com/pricing',
|
||||||
|
@ -8,6 +9,7 @@ export default [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
|
trigger_only_during_business_hours: false,
|
||||||
trigger_rules: {
|
trigger_rules: {
|
||||||
time_on_page: 6,
|
time_on_page: 6,
|
||||||
url: 'https://www.chatwoot.com/about',
|
url: 'https://www.chatwoot.com/about',
|
|
@ -3,8 +3,12 @@ import {
|
||||||
formatCampaigns,
|
formatCampaigns,
|
||||||
filterCampaigns,
|
filterCampaigns,
|
||||||
} from '../campaignHelper';
|
} from '../campaignHelper';
|
||||||
import campaigns from './camapginFixtures';
|
import campaigns from './campaignFixtures';
|
||||||
describe('#Campagin Helper', () => {
|
|
||||||
|
global.chatwootWebChannel = {
|
||||||
|
workingHoursEnabled: false,
|
||||||
|
};
|
||||||
|
describe('#Campaigns Helper', () => {
|
||||||
describe('stripTrailingSlash', () => {
|
describe('stripTrailingSlash', () => {
|
||||||
it('should return striped trailing slash if url with trailing slash is passed', () => {
|
it('should return striped trailing slash if url with trailing slash is passed', () => {
|
||||||
expect(
|
expect(
|
||||||
|
@ -14,15 +18,17 @@ describe('#Campagin Helper', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('formatCampaigns', () => {
|
describe('formatCampaigns', () => {
|
||||||
it('should return formated campaigns if camapgins are passed', () => {
|
it('should return formatted campaigns if campaigns are passed', () => {
|
||||||
expect(formatCampaigns({ campaigns })).toStrictEqual([
|
expect(formatCampaigns({ campaigns })).toStrictEqual([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
timeOnPage: 3,
|
timeOnPage: 3,
|
||||||
|
triggerOnlyDuringBusinessHours: false,
|
||||||
url: 'https://www.chatwoot.com/pricing',
|
url: 'https://www.chatwoot.com/pricing',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
|
triggerOnlyDuringBusinessHours: false,
|
||||||
timeOnPage: 6,
|
timeOnPage: 6,
|
||||||
url: 'https://www.chatwoot.com/about',
|
url: 'https://www.chatwoot.com/about',
|
||||||
},
|
},
|
||||||
|
@ -30,7 +36,7 @@ describe('#Campagin Helper', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('filterCampaigns', () => {
|
describe('filterCampaigns', () => {
|
||||||
it('should return filtered campaigns if formatted camapgins are passed', () => {
|
it('should return filtered campaigns if formatted campaigns are passed', () => {
|
||||||
expect(
|
expect(
|
||||||
filterCampaigns({
|
filterCampaigns({
|
||||||
campaigns: [
|
campaigns: [
|
||||||
|
|
|
@ -58,5 +58,9 @@ export default {
|
||||||
closeMinute: workingHourConfig.close_minutes,
|
closeMinute: workingHourConfig.close_minutes,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
isInBusinessHours() {
|
||||||
|
const { workingHoursEnabled } = window.chatwootWebChannel;
|
||||||
|
return workingHoursEnabled ? this.isInBetweenTheWorkingHours : true;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,12 +14,18 @@ const state = {
|
||||||
activeCampaign: {},
|
activeCampaign: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetCampaignTimers = (campaigns, currentURL, websiteToken) => {
|
const resetCampaignTimers = (
|
||||||
|
campaigns,
|
||||||
|
currentURL,
|
||||||
|
websiteToken,
|
||||||
|
isInBusinessHours
|
||||||
|
) => {
|
||||||
const formattedCampaigns = formatCampaigns({ campaigns });
|
const formattedCampaigns = formatCampaigns({ campaigns });
|
||||||
// Find all campaigns that matches the current URL
|
// Find all campaigns that matches the current URL
|
||||||
const filteredCampaigns = filterCampaigns({
|
const filteredCampaigns = filterCampaigns({
|
||||||
campaigns: formattedCampaigns,
|
campaigns: formattedCampaigns,
|
||||||
currentURL,
|
currentURL,
|
||||||
|
isInBusinessHours,
|
||||||
});
|
});
|
||||||
campaignTimer.initTimers({ campaigns: filteredCampaigns }, websiteToken);
|
campaignTimer.initTimers({ campaigns: filteredCampaigns }, websiteToken);
|
||||||
};
|
};
|
||||||
|
@ -31,13 +37,21 @@ export const getters = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
fetchCampaigns: async ({ commit }, { websiteToken, currentURL }) => {
|
fetchCampaigns: async (
|
||||||
|
{ commit },
|
||||||
|
{ websiteToken, currentURL, isInBusinessHours }
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const { data: campaigns } = await getCampaigns(websiteToken);
|
const { data: campaigns } = await getCampaigns(websiteToken);
|
||||||
commit('setCampaigns', campaigns);
|
commit('setCampaigns', campaigns);
|
||||||
commit('setError', false);
|
commit('setError', false);
|
||||||
commit('setHasFetched', true);
|
commit('setHasFetched', true);
|
||||||
resetCampaignTimers(campaigns, currentURL, websiteToken);
|
resetCampaignTimers(
|
||||||
|
campaigns,
|
||||||
|
currentURL,
|
||||||
|
websiteToken,
|
||||||
|
isInBusinessHours
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
commit('setError', true);
|
commit('setError', true);
|
||||||
commit('setHasFetched', true);
|
commit('setHasFetched', true);
|
||||||
|
@ -45,12 +59,21 @@ export const actions = {
|
||||||
},
|
},
|
||||||
initCampaigns: async (
|
initCampaigns: async (
|
||||||
{ getters: { getCampaigns: campaigns }, dispatch },
|
{ getters: { getCampaigns: campaigns }, dispatch },
|
||||||
{ currentURL, websiteToken }
|
{ currentURL, websiteToken, isInBusinessHours }
|
||||||
) => {
|
) => {
|
||||||
if (!campaigns.length) {
|
if (!campaigns.length) {
|
||||||
dispatch('fetchCampaigns', { websiteToken, currentURL });
|
dispatch('fetchCampaigns', {
|
||||||
|
websiteToken,
|
||||||
|
currentURL,
|
||||||
|
isInBusinessHours,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
resetCampaignTimers(campaigns, currentURL, websiteToken);
|
resetCampaignTimers(
|
||||||
|
campaigns,
|
||||||
|
currentURL,
|
||||||
|
websiteToken,
|
||||||
|
isInBusinessHours
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
startCampaign: async ({ commit }, { websiteToken, campaignId }) => {
|
startCampaign: async ({ commit }, { websiteToken, campaignId }) => {
|
||||||
|
|
|
@ -15,7 +15,11 @@ describe('#actions', () => {
|
||||||
API.get.mockResolvedValue({ data: campaigns });
|
API.get.mockResolvedValue({ data: campaigns });
|
||||||
await actions.fetchCampaigns(
|
await actions.fetchCampaigns(
|
||||||
{ commit },
|
{ commit },
|
||||||
{ websiteToken: 'XDsafmADasd', currentURL: 'https://chatwoot.com' }
|
{
|
||||||
|
websiteToken: 'XDsafmADasd',
|
||||||
|
currentURL: 'https://chatwoot.com',
|
||||||
|
isInBusinessHours: true,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
['setCampaigns', campaigns],
|
['setCampaigns', campaigns],
|
||||||
|
@ -25,7 +29,12 @@ describe('#actions', () => {
|
||||||
expect(campaignTimer.initTimers).toHaveBeenCalledWith(
|
expect(campaignTimer.initTimers).toHaveBeenCalledWith(
|
||||||
{
|
{
|
||||||
campaigns: [
|
campaigns: [
|
||||||
{ id: 11, timeOnPage: '20', url: 'https://chatwoot.com' },
|
{
|
||||||
|
id: 11,
|
||||||
|
timeOnPage: '20',
|
||||||
|
url: 'https://chatwoot.com',
|
||||||
|
triggerOnlyDuringBusinessHours: false,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'XDsafmADasd'
|
'XDsafmADasd'
|
||||||
|
@ -35,7 +44,11 @@ describe('#actions', () => {
|
||||||
API.get.mockRejectedValue({ message: 'Authentication required' });
|
API.get.mockRejectedValue({ message: 'Authentication required' });
|
||||||
await actions.fetchCampaigns(
|
await actions.fetchCampaigns(
|
||||||
{ commit },
|
{ commit },
|
||||||
{ websiteToken: 'XDsafmADasd', currentURL: 'https://www.chatwoot.com' }
|
{
|
||||||
|
websiteToken: 'XDsafmADasd',
|
||||||
|
currentURL: 'https://www.chatwoot.com',
|
||||||
|
isInBusinessHours: true,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
['setError', true],
|
['setError', true],
|
||||||
|
@ -65,7 +78,12 @@ describe('#actions', () => {
|
||||||
expect(campaignTimer.initTimers).toHaveBeenCalledWith(
|
expect(campaignTimer.initTimers).toHaveBeenCalledWith(
|
||||||
{
|
{
|
||||||
campaigns: [
|
campaigns: [
|
||||||
{ id: 11, timeOnPage: '20', url: 'https://chatwoot.com' },
|
{
|
||||||
|
id: 11,
|
||||||
|
timeOnPage: '20',
|
||||||
|
url: 'https://chatwoot.com',
|
||||||
|
triggerOnlyDuringBusinessHours: false,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'XDsafmADasd'
|
'XDsafmADasd'
|
||||||
|
|
|
@ -2,22 +2,23 @@
|
||||||
#
|
#
|
||||||
# Table name: campaigns
|
# Table name: campaigns
|
||||||
#
|
#
|
||||||
# id :bigint not null, primary key
|
# id :bigint not null, primary key
|
||||||
# audience :jsonb
|
# audience :jsonb
|
||||||
# campaign_status :integer default("active"), not null
|
# campaign_status :integer default("active"), not null
|
||||||
# campaign_type :integer default("ongoing"), not null
|
# campaign_type :integer default("ongoing"), not null
|
||||||
# description :text
|
# description :text
|
||||||
# enabled :boolean default(TRUE)
|
# enabled :boolean default(TRUE)
|
||||||
# message :text not null
|
# message :text not null
|
||||||
# scheduled_at :datetime
|
# scheduled_at :datetime
|
||||||
# title :string not null
|
# title :string not null
|
||||||
# trigger_rules :jsonb
|
# trigger_only_during_business_hours :boolean default(FALSE)
|
||||||
# created_at :datetime not null
|
# trigger_rules :jsonb
|
||||||
# updated_at :datetime not null
|
# created_at :datetime not null
|
||||||
# account_id :bigint not null
|
# updated_at :datetime not null
|
||||||
# display_id :integer not null
|
# account_id :bigint not null
|
||||||
# inbox_id :bigint not null
|
# display_id :integer not null
|
||||||
# sender_id :integer
|
# inbox_id :bigint not null
|
||||||
|
# sender_id :integer
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
|
|
@ -17,5 +17,6 @@ if resource.campaign_type == 'one_off'
|
||||||
json.audience resource.audience
|
json.audience resource.audience
|
||||||
end
|
end
|
||||||
json.trigger_rules resource.trigger_rules
|
json.trigger_rules resource.trigger_rules
|
||||||
|
json.trigger_only_during_business_hours resource.trigger_only_during_business_hours
|
||||||
json.created_at resource.created_at
|
json.created_at resource.created_at
|
||||||
json.updated_at resource.updated_at
|
json.updated_at resource.updated_at
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
json.array! @campaigns do |campaign|
|
json.array! @campaigns do |campaign|
|
||||||
json.id campaign.display_id
|
json.id campaign.display_id
|
||||||
json.trigger_rules campaign.trigger_rules
|
json.trigger_rules campaign.trigger_rules
|
||||||
|
json.trigger_only_during_business_hours campaign.trigger_only_during_business_hours
|
||||||
json.message campaign.message
|
json.message campaign.message
|
||||||
json.sender campaign.sender&.slice(:name, :avatar_url)
|
json.sender campaign.sender&.slice(:name, :avatar_url)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddTriggerOnlyDuringBusinessHoursCollectToCampaigns < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
add_column :campaigns, :trigger_only_during_business_hours, :boolean, default: false
|
||||||
|
end
|
||||||
|
end
|
|
@ -147,6 +147,7 @@ ActiveRecord::Schema.define(version: 2021_09_29_150415) do
|
||||||
t.integer "campaign_status", default: 0, null: false
|
t.integer "campaign_status", default: 0, null: false
|
||||||
t.jsonb "audience", default: []
|
t.jsonb "audience", default: []
|
||||||
t.datetime "scheduled_at"
|
t.datetime "scheduled_at"
|
||||||
|
t.boolean "trigger_only_during_business_hours", default: false
|
||||||
t.index ["account_id"], name: "index_campaigns_on_account_id"
|
t.index ["account_id"], name: "index_campaigns_on_account_id"
|
||||||
t.index ["campaign_status"], name: "index_campaigns_on_campaign_status"
|
t.index ["campaign_status"], name: "index_campaigns_on_campaign_status"
|
||||||
t.index ["campaign_type"], name: "index_campaigns_on_campaign_type"
|
t.index ["campaign_type"], name: "index_campaigns_on_campaign_type"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue