mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 19:17:48 +00:00 
			
		
		
		
	feat: Dedicated tab for campaigns (#2741)
This commit is contained in:
		| @@ -363,6 +363,8 @@ GEM | ||||
|       connection_pool (~> 2.2) | ||||
|     netrc (0.11.0) | ||||
|     nio4r (2.5.7) | ||||
|     nokogiri (1.11.7-arm64-darwin) | ||||
|       racc (~> 1.4) | ||||
|     nokogiri (1.11.7-x86_64-darwin) | ||||
|       racc (~> 1.4) | ||||
|     nokogiri (1.11.7-x86_64-linux) | ||||
| @@ -626,6 +628,7 @@ GEM | ||||
|     zeitwerk (2.4.2) | ||||
|  | ||||
| PLATFORMS | ||||
|   arm64-darwin-20 | ||||
|   x86_64-darwin-21 | ||||
|   x86_64-linux | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,38 @@ | ||||
| <template> | ||||
|   <div class="inbox"> | ||||
|     <i :class="icon" /> | ||||
|     <span>{{ inbox.name }}</span> | ||||
|   </div> | ||||
| </template> | ||||
| <script> | ||||
| import { INBOX_TYPES } from 'shared/mixins/inboxMixin'; | ||||
| export default { | ||||
|   props: { | ||||
|     inbox: { | ||||
|       type: Object, | ||||
|       default: () => {}, | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     icon() { | ||||
|       if (this.inbox.channel_type === INBOX_TYPES.WEB) { | ||||
|         return 'icon ion-earth'; | ||||
|       } | ||||
|       return 'icon ion-android-textsms'; | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| .inbox { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   .icon { | ||||
|     margin-right: var(--space-micro); | ||||
|     min-width: var(--space-normal); | ||||
|     position: relative; | ||||
|     top: -1px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -47,6 +47,13 @@ export const getSidebarItems = accountId => ({ | ||||
|         toState: frontendURL(`accounts/${accountId}/reports`), | ||||
|         toStateName: 'settings_account_reports', | ||||
|       }, | ||||
|       campaigns: { | ||||
|         icon: 'ion-speakerphone', | ||||
|         label: 'CAMPAIGNS', | ||||
|         hasSubMenu: false, | ||||
|         toState: frontendURL(`accounts/${accountId}/campaigns`), | ||||
|         toStateName: 'settings_account_campaigns', | ||||
|       }, | ||||
|       settings: { | ||||
|         icon: 'ion-settings', | ||||
|         label: 'SETTINGS', | ||||
| @@ -105,6 +112,32 @@ export const getSidebarItems = accountId => ({ | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|   campaigns: { | ||||
|     routes: ['settings_account_campaigns', 'one_off'], | ||||
|     menuItems: { | ||||
|       back: { | ||||
|         icon: 'ion-ios-arrow-back', | ||||
|         label: 'HOME', | ||||
|         hasSubMenu: false, | ||||
|         toStateName: 'home', | ||||
|         toState: frontendURL(`accounts/${accountId}/dashboard`), | ||||
|       }, | ||||
|       ongoingCampaigns: { | ||||
|         icon: 'ion-arrow-swap', | ||||
|         label: 'ONGOING', | ||||
|         hasSubMenu: false, | ||||
|         toState: frontendURL(`accounts/${accountId}/campaigns/ongoing`), | ||||
|         toStateName: 'settings_account_campaigns', | ||||
|       }, | ||||
|       onOffCampaigns: { | ||||
|         icon: 'ion-radio-waves', | ||||
|         label: 'ONE_OFF', | ||||
|         hasSubMenu: false, | ||||
|         toState: frontendURL(`accounts/${accountId}/campaigns/one_off`), | ||||
|         toStateName: 'one_off', | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|   settings: { | ||||
|     routes: [ | ||||
|       'agent_list', | ||||
|   | ||||
| @@ -2,7 +2,10 @@ | ||||
|   "CAMPAIGN": { | ||||
|     "HEADER": "Campaigns", | ||||
|     "SIDEBAR_TXT": "Proactive messages allow the customer to send outbound messages to their contacts which would trigger more conversations. Click on <b>Add Campaign</b> to create a new campaign. You can also edit or delete an existing campaign by clicking on the Edit or Delete button.", | ||||
|     "HEADER_BTN_TXT": "Create a campaign", | ||||
|     "HEADER_BTN_TXT": { | ||||
|       "ONE_OFF": "Create a one off campaign", | ||||
|       "ONGOING": "Create a ongoing campaign" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Create a campaign", | ||||
|       "DESC": "Proactive messages allow the customer to send outbound messages to their contacts which would trigger more conversations.", | ||||
| @@ -25,6 +28,11 @@ | ||||
|           "PLACEHOLDER": "Select the customer labels", | ||||
|           "ERROR": "Audience is required" | ||||
|         }, | ||||
|         "INBOX": { | ||||
|           "LABEL": "Select Inbox", | ||||
|           "PLACEHOLDER": "Select Inbox", | ||||
|           "ERROR": "Inbox is required" | ||||
|         }, | ||||
|         "MESSAGE": { | ||||
|           "LABEL": "Message", | ||||
|           "PLACEHOLDER": "Please enter the message of campaign", | ||||
| @@ -80,6 +88,7 @@ | ||||
|       "TABLE_HEADER": { | ||||
|         "TITLE": "Title", | ||||
|         "MESSAGE": "Message", | ||||
|         "INBOX": "Inbox", | ||||
|         "STATUS": "Status", | ||||
|         "SENDER": "Sender", | ||||
|         "URL": "URL", | ||||
| @@ -101,6 +110,16 @@ | ||||
|       "SENDER": { | ||||
|         "BOT": "Bot" | ||||
|       } | ||||
|     }, | ||||
|     "ONE_OFF": { | ||||
|       "HEADER": "One off campaigns", | ||||
|       "404": "There are no one off campaigns created", | ||||
|       "INBOXES_NOT_FOUND": "Please create an sms inbox and start adding campaigns" | ||||
|     }, | ||||
|     "ONGOING": { | ||||
|       "HEADER": "Ongoing campaigns", | ||||
|       "404": "There are no ongoing campaigns created", | ||||
|       "INBOXES_NOT_FOUND": "Please create an website inbox and start adding campaigns" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -127,8 +127,8 @@ | ||||
|   "SIDEBAR": { | ||||
|     "CONVERSATIONS": "Conversations", | ||||
|     "REPORTS": "Reports", | ||||
|     "CONTACTS": "Contacts", | ||||
|     "SETTINGS": "Settings", | ||||
|     "CONTACTS": "Contacts", | ||||
|     "HOME": "Home", | ||||
|     "AGENTS": "Agents", | ||||
|     "INBOXES": "Inboxes", | ||||
| @@ -142,7 +142,10 @@ | ||||
|     "ALL_CONTACTS": "All Contacts", | ||||
|     "TAGGED_WITH": "Tagged with", | ||||
|     "REPORTS_OVERVIEW": "Overview", | ||||
|     "CSAT": "CSAT" | ||||
|     "CSAT": "CSAT", | ||||
|     "CAMPAIGNS": "Campaigns", | ||||
|     "ONGOING": "Ongoing", | ||||
|     "ONE_OFF": "One off" | ||||
|   }, | ||||
|   "CREATE_ACCOUNT": { | ||||
|     "NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.", | ||||
|   | ||||
| @@ -44,14 +44,16 @@ | ||||
|           </span> | ||||
|         </label> | ||||
| 
 | ||||
|         <label v-if="isOnOffType"> | ||||
|           {{ $t('CAMPAIGN.ADD.FORM.SCHEDULED_AT.LABEL') }} | ||||
|           <woot-date-time-picker | ||||
|             :value="scheduledAt" | ||||
|             :confirm-text="$t('CAMPAIGN.ADD.FORM.SCHEDULED_AT.CONFIRM')" | ||||
|             :placeholder="$t('CAMPAIGN.ADD.FORM.SCHEDULED_AT.PLACEHOLDER')" | ||||
|             @change="onChange" | ||||
|           /> | ||||
|         <label :class="{ error: $v.selectedInbox.$error }"> | ||||
|           {{ $t('CAMPAIGN.ADD.FORM.INBOX.LABEL') }} | ||||
|           <select v-model="selectedInbox" @change="onChangeInbox($event)"> | ||||
|             <option v-for="item in inboxes" :key="item.name" :value="item.id"> | ||||
|               {{ item.name }} | ||||
|             </option> | ||||
|           </select> | ||||
|           <span v-if="$v.selectedInbox.$error" class="message"> | ||||
|             {{ $t('CAMPAIGN.ADD.FORM.INBOX.ERROR') }} | ||||
|           </span> | ||||
|         </label> | ||||
| 
 | ||||
|         <label | ||||
| @@ -99,6 +101,16 @@ | ||||
|           </span> | ||||
|         </label> | ||||
| 
 | ||||
|         <label v-if="isOnOffType"> | ||||
|           {{ $t('CAMPAIGN.ADD.FORM.SCHEDULED_AT.LABEL') }} | ||||
|           <woot-date-time-picker | ||||
|             :value="scheduledAt" | ||||
|             :confirm-text="$t('CAMPAIGN.ADD.FORM.SCHEDULED_AT.CONFIRM')" | ||||
|             :placeholder="$t('CAMPAIGN.ADD.FORM.SCHEDULED_AT.PLACEHOLDER')" | ||||
|             @change="onChange" | ||||
|           /> | ||||
|         </label> | ||||
| 
 | ||||
|         <woot-input | ||||
|           v-if="isOngoingType" | ||||
|           v-model="endPoint" | ||||
| @@ -137,10 +149,7 @@ | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="modal-footer"> | ||||
|         <woot-button | ||||
|           :is-disabled="buttonDisabled" | ||||
|           :is-loading="uiFlags.isCreating" | ||||
|         > | ||||
|         <woot-button :is-loading="uiFlags.isCreating"> | ||||
|           {{ $t('CAMPAIGN.ADD.CREATE_BUTTON_TEXT') }} | ||||
|         </woot-button> | ||||
|         <woot-button variant="clear" @click.prevent="onClose"> | ||||
| @@ -166,83 +175,69 @@ export default { | ||||
|   }, | ||||
| 
 | ||||
|   mixins: [alertMixin, campaignMixin], | ||||
| 
 | ||||
|   props: { | ||||
|     senderList: { | ||||
|       type: Array, | ||||
|       default: () => [], | ||||
|     }, | ||||
|     audienceList: { | ||||
|       type: Array, | ||||
|       default: () => [], | ||||
|     }, | ||||
|   }, | ||||
| 
 | ||||
|   data() { | ||||
|     return { | ||||
|       title: '', | ||||
|       message: '', | ||||
|       selectedSender: 0, | ||||
|       selectedInbox: null, | ||||
|       endPoint: '', | ||||
|       timeOnPage: 10, | ||||
|       show: true, | ||||
|       enabled: true, | ||||
|       scheduledAt: null, | ||||
|       selectedAudience: [], | ||||
|       senderList: [], | ||||
|     }; | ||||
|   }, | ||||
| 
 | ||||
|   validations: { | ||||
|     title: { | ||||
|       required, | ||||
|     }, | ||||
|     message: { | ||||
|       required, | ||||
|     }, | ||||
|     selectedSender: { | ||||
|       required, | ||||
|     }, | ||||
|     endPoint: { | ||||
|       required, | ||||
|       minLength: minLength(7), | ||||
|       url, | ||||
|     }, | ||||
|     timeOnPage: { | ||||
|       required, | ||||
|     }, | ||||
|     selectedAudience: { | ||||
|       isEmpty() { | ||||
|         return !!this.selectedAudience.length; | ||||
|   validations() { | ||||
|     const commonValidations = { | ||||
|       title: { | ||||
|         required, | ||||
|       }, | ||||
|     }, | ||||
|       message: { | ||||
|         required, | ||||
|       }, | ||||
|       selectedInbox: { | ||||
|         required, | ||||
|       }, | ||||
|     }; | ||||
|     if (this.isOngoingType) { | ||||
|       return { | ||||
|         ...commonValidations, | ||||
|         selectedSender: { | ||||
|           required, | ||||
|         }, | ||||
|         endPoint: { | ||||
|           required, | ||||
|           minLength: minLength(7), | ||||
|           url, | ||||
|         }, | ||||
|         timeOnPage: { | ||||
|           required, | ||||
|         }, | ||||
|       }; | ||||
|     } | ||||
|     return { | ||||
|       ...commonValidations, | ||||
|       selectedAudience: { | ||||
|         isEmpty() { | ||||
|           return !!this.selectedAudience.length; | ||||
|         }, | ||||
|       }, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapGetters({ | ||||
|       uiFlags: 'campaigns/getUIFlags', | ||||
|       audienceList: 'labels/getLabels', | ||||
|     }), | ||||
|     currentInboxId() { | ||||
|       return this.$route.params.inboxId; | ||||
|     }, | ||||
|     inbox() { | ||||
|       return this.$store.getters['inboxes/getInbox'](this.currentInboxId); | ||||
|     }, | ||||
|     buttonDisabled() { | ||||
|     inboxes() { | ||||
|       if (this.isOngoingType) { | ||||
|         return ( | ||||
|           this.$v.message.$invalid || | ||||
|           this.$v.title.$invalid || | ||||
|           this.$v.selectedSender.$invalid || | ||||
|           this.$v.endPoint.$invalid || | ||||
|           this.$v.timeOnPage.$invalid || | ||||
|           this.uiFlags.isCreating | ||||
|         ); | ||||
|         return this.$store.getters['inboxes/getWebsiteInboxes']; | ||||
|       } | ||||
|       return ( | ||||
|         this.$v.message.$invalid || | ||||
|         this.$v.title.$invalid || | ||||
|         this.$v.selectedAudience.$invalid || | ||||
|         this.uiFlags.isCreating | ||||
|       ); | ||||
|       return this.$store.getters['inboxes/getTwilioInboxes']; | ||||
|     }, | ||||
|     sendersAndBotList() { | ||||
|       return [ | ||||
| @@ -261,13 +256,28 @@ export default { | ||||
|     onChange(value) { | ||||
|       this.scheduledAt = value; | ||||
|     }, | ||||
|     async onChangeInbox() { | ||||
|       try { | ||||
|         const response = await this.$store.dispatch('inboxMembers/get', { | ||||
|           inboxId: this.selectedInbox, | ||||
|         }); | ||||
|         const { | ||||
|           data: { payload: inboxMembers }, | ||||
|         } = response; | ||||
|         this.senderList = inboxMembers; | ||||
|       } catch (error) { | ||||
|         const errorMessage = | ||||
|           error?.response?.message || this.$t('CAMPAIGN.ADD.API.ERROR_MESSAGE'); | ||||
|         this.showAlert(errorMessage); | ||||
|       } | ||||
|     }, | ||||
|     getCampaignDetails() { | ||||
|       let campaignDetails = null; | ||||
|       if (this.isOngoingType) { | ||||
|         campaignDetails = { | ||||
|           title: this.title, | ||||
|           message: this.message, | ||||
|           inbox_id: this.$route.params.inboxId, | ||||
|           inbox_id: this.selectedInbox, | ||||
|           sender_id: this.selectedSender || null, | ||||
|           enabled: this.enabled, | ||||
|           trigger_rules: { | ||||
| @@ -285,7 +295,7 @@ export default { | ||||
|         campaignDetails = { | ||||
|           title: this.title, | ||||
|           message: this.message, | ||||
|           inbox_id: this.$route.params.inboxId, | ||||
|           inbox_id: this.selectedInbox, | ||||
|           scheduled_at: this.scheduledAt, | ||||
|           audience, | ||||
|         }; | ||||
| @@ -293,6 +303,10 @@ export default { | ||||
|       return campaignDetails; | ||||
|     }, | ||||
|     async addCampaign() { | ||||
|       this.$v.$touch(); | ||||
|       if (this.$v.$invalid) { | ||||
|         return; | ||||
|       } | ||||
|       try { | ||||
|         const campaignDetails = this.getCampaignDetails(); | ||||
|         await this.$store.dispatch('campaigns/create', campaignDetails); | ||||
| @@ -1,31 +1,16 @@ | ||||
| <template> | ||||
|   <div class="column content-box"> | ||||
|     <div class="row button-wrapper"> | ||||
|       <woot-button icon="ion-android-add-circle" @click="openAddPopup"> | ||||
|         {{ $t('CAMPAIGN.HEADER_BTN_TXT') }} | ||||
|       </woot-button> | ||||
|     </div> | ||||
|     <campaigns-table | ||||
|       :campaigns="records" | ||||
|       :campaigns="campaigns" | ||||
|       :show-empty-result="showEmptyResult" | ||||
|       :is-loading="uiFlags.isFetching" | ||||
|       :campaign-type="type" | ||||
|       @on-edit-click="openEditPopup" | ||||
|       @on-delete-click="openDeletePopup" | ||||
|     /> | ||||
| 
 | ||||
|     <woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup"> | ||||
|       <add-campaign | ||||
|         :sender-list="selectedAgents" | ||||
|         :audience-list="labelList" | ||||
|         :campaign-type="type" | ||||
|         @on-close="hideAddPopup" | ||||
|       /> | ||||
|     </woot-modal> | ||||
|     <woot-modal :show.sync="showEditPopup" :on-close="hideEditPopup"> | ||||
|       <edit-campaign | ||||
|         :selected-campaign="selectedCampaign" | ||||
|         :sender-list="selectedAgents" | ||||
|         @on-close="hideEditPopup" | ||||
|       /> | ||||
|     </woot-modal> | ||||
| @@ -43,21 +28,16 @@ | ||||
| <script> | ||||
| import { mapGetters } from 'vuex'; | ||||
| import alertMixin from 'shared/mixins/alertMixin'; | ||||
| import AddCampaign from './AddCampaign'; | ||||
| import campaignMixin from 'shared/mixins/campaignMixin'; | ||||
| import CampaignsTable from './CampaignsTable'; | ||||
| import EditCampaign from './EditCampaign'; | ||||
| export default { | ||||
|   components: { | ||||
|     AddCampaign, | ||||
|     CampaignsTable, | ||||
|     EditCampaign, | ||||
|   }, | ||||
|   mixins: [alertMixin], | ||||
|   mixins: [alertMixin, campaignMixin], | ||||
|   props: { | ||||
|     selectedAgents: { | ||||
|       type: Array, | ||||
|       default: () => [], | ||||
|     }, | ||||
|     type: { | ||||
|       type: String, | ||||
|       default: '', | ||||
| @@ -65,8 +45,6 @@ export default { | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       campaigns: [], | ||||
|       showAddPopup: false, | ||||
|       showEditPopup: false, | ||||
|       selectedCampaign: {}, | ||||
|       showDeleteConfirmationPopup: false, | ||||
| @@ -74,28 +52,19 @@ export default { | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapGetters({ | ||||
|       records: 'campaigns/getCampaigns', | ||||
|       uiFlags: 'campaigns/getUIFlags', | ||||
|       labelList: 'labels/getLabels', | ||||
|     }), | ||||
|     campaigns() { | ||||
|       return this.$store.getters['campaigns/getCampaigns'](this.campaignType); | ||||
|     }, | ||||
|     showEmptyResult() { | ||||
|       const hasEmptyResults = | ||||
|         !this.uiFlags.isFetching && this.records.length === 0; | ||||
|         !this.uiFlags.isFetching && this.campaigns.length === 0; | ||||
|       return hasEmptyResults; | ||||
|     }, | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.$store.dispatch('campaigns/get', { | ||||
|       inboxId: this.$route.params.inboxId, | ||||
|     }); | ||||
|   }, | ||||
|   methods: { | ||||
|     openAddPopup() { | ||||
|       this.showAddPopup = true; | ||||
|     }, | ||||
|     hideAddPopup() { | ||||
|       this.showAddPopup = false; | ||||
|     }, | ||||
|     openEditPopup(response) { | ||||
|       const { row: campaign } = response; | ||||
|       this.selectedCampaign = campaign; | ||||
| @@ -1,13 +1,13 @@ | ||||
| <template> | ||||
|   <section class="campaigns-table-wrap"> | ||||
|     <empty-state v-if="showEmptyResult" :title="emptyMessage" /> | ||||
|     <ve-table | ||||
|       v-else | ||||
|       :columns="columns" | ||||
|       scroll-width="155rem" | ||||
|       :table-data="tableData" | ||||
|       :border-around="true" | ||||
|     /> | ||||
| 
 | ||||
|     <empty-state v-if="showEmptyResult" :title="$t('CAMPAIGN.LIST.404')" /> | ||||
|     <div v-if="isLoading" class="campaign--loader"> | ||||
|       <spinner /> | ||||
|       <span>{{ $t('CAMPAIGN.LIST.LOADING_MESSAGE') }}</span> | ||||
| @@ -24,6 +24,7 @@ import EmptyState from 'dashboard/components/widgets/EmptyState.vue'; | ||||
| import WootButton from 'dashboard/components/ui/WootButton.vue'; | ||||
| import messageFormatterMixin from 'shared/mixins/messageFormatterMixin'; | ||||
| import UserAvatarWithName from 'dashboard/components/widgets/UserAvatarWithName'; | ||||
| import InboxIconWithName from 'dashboard/components/widgets/InboxIconWithName'; | ||||
| import campaignMixin from 'shared/mixins/campaignMixin'; | ||||
| import timeMixin from 'dashboard/mixins/time'; | ||||
| 
 | ||||
| @@ -58,6 +59,23 @@ export default { | ||||
|     inbox() { | ||||
|       return this.$store.getters['inboxes/getInbox'](this.currentInboxId); | ||||
|     }, | ||||
|     inboxes() { | ||||
|       if (this.isOngoingType) { | ||||
|         return this.$store.getters['inboxes/getWebsiteInboxes']; | ||||
|       } | ||||
|       return this.$store.getters['inboxes/getTwilioInboxes']; | ||||
|     }, | ||||
|     emptyMessage() { | ||||
|       if (this.isOngoingType) { | ||||
|         return this.inboxes.length | ||||
|           ? this.$t('CAMPAIGN.ONGOING.404') | ||||
|           : this.$t('CAMPAIGN.ONGOING.INBOXES_NOT_FOUND'); | ||||
|       } | ||||
| 
 | ||||
|       return this.inboxes.length | ||||
|         ? this.$t('CAMPAIGN.ONE_OFF.404') | ||||
|         : this.$t('CAMPAIGN.ONE_OFF.INBOXES_NOT_FOUND'); | ||||
|     }, | ||||
|     tableData() { | ||||
|       if (this.isLoading) { | ||||
|         return []; | ||||
| @@ -107,6 +125,15 @@ export default { | ||||
|             return ''; | ||||
|           }, | ||||
|         }, | ||||
|         { | ||||
|           field: 'inbox', | ||||
|           key: 'inbox', | ||||
|           title: this.$t('CAMPAIGN.LIST.TABLE_HEADER.INBOX'), | ||||
|           align: 'left', | ||||
|           renderBodyCell: ({ row }) => { | ||||
|             return <InboxIconWithName inbox={row.inbox} />; | ||||
|           }, | ||||
|         }, | ||||
|       ]; | ||||
|       if (this.isOngoingType) { | ||||
|         return [ | ||||
| @@ -26,6 +26,19 @@ | ||||
|             {{ $t('CAMPAIGN.ADD.FORM.MESSAGE.ERROR') }} | ||||
|           </span> | ||||
|         </label> | ||||
| 
 | ||||
|         <label :class="{ error: $v.selectedInbox.$error }"> | ||||
|           {{ $t('CAMPAIGN.ADD.FORM.INBOX.LABEL') }} | ||||
|           <select v-model="selectedInbox" @change="onChangeInbox($event)"> | ||||
|             <option v-for="item in inboxes" :key="item.name" :value="item.id"> | ||||
|               {{ item.name }} | ||||
|             </option> | ||||
|           </select> | ||||
|           <span v-if="$v.selectedInbox.$error" class="message"> | ||||
|             {{ $t('CAMPAIGN.ADD.FORM.INBOX.ERROR') }} | ||||
|           </span> | ||||
|         </label> | ||||
| 
 | ||||
|         <label :class="{ error: $v.selectedSender.$error }"> | ||||
|           {{ $t('CAMPAIGN.ADD.FORM.SENT_BY.LABEL') }} | ||||
|           <select v-model="selectedSender"> | ||||
| @@ -76,10 +89,7 @@ | ||||
|         </label> | ||||
|       </div> | ||||
|       <div class="modal-footer"> | ||||
|         <woot-button | ||||
|           :is-disabled="buttonDisabled" | ||||
|           :is-loading="uiFlags.isCreating" | ||||
|         > | ||||
|         <woot-button :is-loading="uiFlags.isCreating"> | ||||
|           {{ $t('CAMPAIGN.EDIT.UPDATE_BUTTON_TEXT') }} | ||||
|         </woot-button> | ||||
|         <woot-button variant="clear" @click.prevent="onClose"> | ||||
| @@ -95,30 +105,29 @@ import { mapGetters } from 'vuex'; | ||||
| import { required, url, minLength } from 'vuelidate/lib/validators'; | ||||
| import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor'; | ||||
| import alertMixin from 'shared/mixins/alertMixin'; | ||||
| import campaignMixin from 'shared/mixins/campaignMixin'; | ||||
| export default { | ||||
|   components: { | ||||
|     WootMessageEditor, | ||||
|   }, | ||||
|   mixins: [alertMixin], | ||||
|   mixins: [alertMixin, campaignMixin], | ||||
|   props: { | ||||
|     selectedCampaign: { | ||||
|       type: Object, | ||||
|       default: () => {}, | ||||
|     }, | ||||
|     senderList: { | ||||
|       type: Array, | ||||
|       default: () => [], | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       title: '', | ||||
|       message: '', | ||||
|       selectedSender: '', | ||||
|       selectedInbox: null, | ||||
|       endPoint: '', | ||||
|       timeOnPage: 10, | ||||
|       show: true, | ||||
|       enabled: true, | ||||
|       senderList: [], | ||||
|     }; | ||||
|   }, | ||||
|   validations: { | ||||
| @@ -139,20 +148,20 @@ export default { | ||||
|     timeOnPage: { | ||||
|       required, | ||||
|     }, | ||||
|     selectedInbox: { | ||||
|       required, | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapGetters({ | ||||
|       uiFlags: 'campaigns/getUIFlags', | ||||
|       inboxes: 'inboxes/getTwilioInboxes', | ||||
|     }), | ||||
|     buttonDisabled() { | ||||
|       return ( | ||||
|         this.$v.message.$invalid || | ||||
|         this.$v.title.$invalid || | ||||
|         this.$v.selectedSender.$invalid || | ||||
|         this.$v.endPoint.$invalid || | ||||
|         this.$v.timeOnPage.$invalid || | ||||
|         this.uiFlags.isCreating | ||||
|       ); | ||||
|     inboxes() { | ||||
|       if (this.isOngoingType) { | ||||
|         return this.$store.getters['inboxes/getWebsiteInboxes']; | ||||
|       } | ||||
|       return this.$store.getters['inboxes/getTwilioInboxes']; | ||||
|     }, | ||||
|     pageTitle() { | ||||
|       return `${this.$t('CAMPAIGN.EDIT.TITLE')} - ${ | ||||
| @@ -176,11 +185,31 @@ export default { | ||||
|     onClose() { | ||||
|       this.$emit('on-close'); | ||||
|     }, | ||||
| 
 | ||||
|     async loadInboxMembers() { | ||||
|       try { | ||||
|         const response = await this.$store.dispatch('inboxMembers/get', { | ||||
|           inboxId: this.selectedInbox, | ||||
|         }); | ||||
|         const { | ||||
|           data: { payload: inboxMembers }, | ||||
|         } = response; | ||||
|         this.senderList = inboxMembers; | ||||
|       } catch (error) { | ||||
|         const errorMessage = | ||||
|           error?.response?.message || this.$t('CAMPAIGN.ADD.API.ERROR_MESSAGE'); | ||||
|         this.showAlert(errorMessage); | ||||
|       } | ||||
|     }, | ||||
|     onChangeInbox() { | ||||
|       this.loadInboxMembers(); | ||||
|     }, | ||||
|     setFormValues() { | ||||
|       const { | ||||
|         title, | ||||
|         message, | ||||
|         enabled, | ||||
|         inbox: { id: inboxId }, | ||||
|         trigger_rules: { url: endPoint, time_on_page: timeOnPage }, | ||||
|         sender, | ||||
|       } = this.selectedCampaign; | ||||
| @@ -188,10 +217,16 @@ export default { | ||||
|       this.message = message; | ||||
|       this.endPoint = endPoint; | ||||
|       this.timeOnPage = timeOnPage; | ||||
|       this.selectedInbox = inboxId; | ||||
|       this.selectedSender = (sender && sender.id) || 0; | ||||
|       this.enabled = enabled; | ||||
|       this.loadInboxMembers(); | ||||
|     }, | ||||
|     async editCampaign() { | ||||
|       this.$v.$touch(); | ||||
|       if (this.$v.$invalid) { | ||||
|         return; | ||||
|       } | ||||
|       try { | ||||
|         await this.$store.dispatch('campaigns/update', { | ||||
|           id: this.selectedCampaign.id, | ||||
| @@ -0,0 +1,52 @@ | ||||
| <template> | ||||
|   <div class="column content-box"> | ||||
|     <woot-button | ||||
|       color-scheme="success" | ||||
|       class-names="button--fixed-right-top" | ||||
|       icon="ion-android-add-circle" | ||||
|       @click="openAddPopup" | ||||
|     > | ||||
|       {{ buttonText }} | ||||
|     </woot-button> | ||||
|     <campaign /> | ||||
|     <woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup"> | ||||
|       <add-campaign @on-close="hideAddPopup" /> | ||||
|     </woot-modal> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import campaignMixin from 'shared/mixins/campaignMixin'; | ||||
| import Campaign from './Campaign.vue'; | ||||
| import AddCampaign from './AddCampaign'; | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
|     Campaign, | ||||
|     AddCampaign, | ||||
|   }, | ||||
|   mixins: [campaignMixin], | ||||
|   data() { | ||||
|     return { showAddPopup: false }; | ||||
|   }, | ||||
|   computed: { | ||||
|     buttonText() { | ||||
|       if (this.isOngoingType) { | ||||
|         return this.$t('CAMPAIGN.HEADER_BTN_TXT.ONGOING'); | ||||
|       } | ||||
|       return this.$t('CAMPAIGN.HEADER_BTN_TXT.ONE_OFF'); | ||||
|     }, | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.$store.dispatch('campaigns/get'); | ||||
|   }, | ||||
|   methods: { | ||||
|     openAddPopup() { | ||||
|       this.showAddPopup = true; | ||||
|     }, | ||||
|     hideAddPopup() { | ||||
|       this.showAddPopup = false; | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| @@ -0,0 +1,44 @@ | ||||
| import Index from './Index'; | ||||
| import SettingsContent from '../Wrapper'; | ||||
| import { frontendURL } from '../../../../helper/URLHelper'; | ||||
|  | ||||
| export default { | ||||
|   routes: [ | ||||
|     { | ||||
|       path: frontendURL('accounts/:accountId/campaigns'), | ||||
|       component: SettingsContent, | ||||
|       props: { | ||||
|         headerTitle: 'CAMPAIGN.ONGOING.HEADER', | ||||
|         icon: 'ion-arrow-swap', | ||||
|       }, | ||||
|       children: [ | ||||
|         { | ||||
|           path: '', | ||||
|           redirect: 'ongoing', | ||||
|         }, | ||||
|         { | ||||
|           path: 'ongoing', | ||||
|           name: 'settings_account_campaigns', | ||||
|           roles: ['administrator'], | ||||
|           component: { ...Index }, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
|       path: frontendURL('accounts/:accountId/campaigns'), | ||||
|       component: SettingsContent, | ||||
|       props: { | ||||
|         headerTitle: 'CAMPAIGN.ONE_OFF.HEADER', | ||||
|         icon: 'ion-radio-waves', | ||||
|       }, | ||||
|       children: [ | ||||
|         { | ||||
|           path: 'one_off', | ||||
|           name: 'one_off', | ||||
|           roles: ['administrator'], | ||||
|           component: { ...Index }, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|   ], | ||||
| }; | ||||
| @@ -286,9 +286,6 @@ | ||||
|     <div v-if="selectedTabKey === 'businesshours'"> | ||||
|       <weekly-availability :inbox="inbox" /> | ||||
|     </div> | ||||
|     <div v-if="selectedTabKey === 'campaign'"> | ||||
|       <campaign :selected-agents="selectedAgents" /> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| @@ -303,7 +300,6 @@ import inboxMixin from 'shared/mixins/inboxMixin'; | ||||
| import FacebookReauthorize from './facebook/Reauthorize'; | ||||
| import PreChatFormSettings from './PreChatForm/Settings'; | ||||
| import WeeklyAvailability from './components/WeeklyAvailability'; | ||||
| import Campaign from './components/Campaign'; | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
| @@ -312,7 +308,6 @@ export default { | ||||
|     FacebookReauthorize, | ||||
|     PreChatFormSettings, | ||||
|     WeeklyAvailability, | ||||
|     Campaign, | ||||
|   }, | ||||
|   mixins: [alertMixin, configMixin, inboxMixin], | ||||
|   data() { | ||||
| @@ -368,10 +363,6 @@ export default { | ||||
|       if (this.isAWebWidgetInbox) { | ||||
|         return [ | ||||
|           ...visibleToAllChannelTabs, | ||||
|           { | ||||
|             key: 'campaign', | ||||
|             name: this.$t('INBOX_MGMT.TABS.CAMPAIGN'), | ||||
|           }, | ||||
|           { | ||||
|             key: 'preChatForm', | ||||
|             name: this.$t('INBOX_MGMT.TABS.PRE_CHAT_FORM'), | ||||
| @@ -387,21 +378,7 @@ export default { | ||||
|         ]; | ||||
|       } | ||||
|  | ||||
|       if (this.isATwilioSMSChannel) { | ||||
|         return [ | ||||
|           ...visibleToAllChannelTabs, | ||||
|           { | ||||
|             key: 'campaign', | ||||
|             name: this.$t('INBOX_MGMT.TABS.CAMPAIGN'), | ||||
|           }, | ||||
|           { | ||||
|             key: 'configuration', | ||||
|             name: this.$t('INBOX_MGMT.TABS.CONFIGURATION'), | ||||
|           }, | ||||
|         ]; | ||||
|       } | ||||
|  | ||||
|       if (this.isATwilioWhatsappChannel) { | ||||
|       if (this.isATwilioChannel) { | ||||
|         return [ | ||||
|           ...visibleToAllChannelTabs, | ||||
|           { | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import integrationapps from './integrationapps/integrations.routes'; | ||||
| import labels from './labels/labels.routes'; | ||||
| import profile from './profile/profile.routes'; | ||||
| import reports from './reports/reports.routes'; | ||||
| import campaigns from './campaigns/campaigns.routes'; | ||||
| import teams from './teams/teams.routes'; | ||||
| import store from '../../../store'; | ||||
|  | ||||
| @@ -33,6 +34,7 @@ export default { | ||||
|     ...profile.routes, | ||||
|     ...reports.routes, | ||||
|     ...teams.routes, | ||||
|     ...campaigns.routes, | ||||
|     ...integrationapps.routes, | ||||
|   ], | ||||
| }; | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers'; | ||||
| import types from '../mutation-types'; | ||||
| import CampaignsAPI from '../../api/campaigns'; | ||||
| import InboxesAPI from '../../api/inboxes'; | ||||
|  | ||||
| export const state = { | ||||
|   records: [], | ||||
| @@ -15,16 +14,18 @@ export const getters = { | ||||
|   getUIFlags(_state) { | ||||
|     return _state.uiFlags; | ||||
|   }, | ||||
|   getCampaigns(_state) { | ||||
|     return _state.records; | ||||
|   getCampaigns: _state => campaignType => { | ||||
|     return _state.records.filter( | ||||
|       record => record.campaign_type === campaignType | ||||
|     ); | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| export const actions = { | ||||
|   get: async function getCampaigns({ commit }, { inboxId }) { | ||||
|   get: async function getCampaigns({ commit }) { | ||||
|     commit(types.SET_CAMPAIGN_UI_FLAG, { isFetching: true }); | ||||
|     try { | ||||
|       const response = await InboxesAPI.getCampaigns(inboxId); | ||||
|       const response = await CampaignsAPI.get(); | ||||
|       commit(types.SET_CAMPAIGNS, response.data); | ||||
|     } catch (error) { | ||||
|       // Ignore error | ||||
|   | ||||
| @@ -66,8 +66,11 @@ export const getters = { | ||||
|     return $state.uiFlags; | ||||
|   }, | ||||
|   getWebsiteInboxes($state) { | ||||
|     return $state.records.filter(item => item.channel_type === INBOX_TYPES.WEB); | ||||
|   }, | ||||
|   getTwilioInboxes($state) { | ||||
|     return $state.records.filter( | ||||
|       item => item.channel_type === 'Channel::WebWidget' | ||||
|       item => item.channel_type === INBOX_TYPES.TWILIO | ||||
|     ); | ||||
|   }, | ||||
| }; | ||||
|   | ||||
| @@ -4,107 +4,7 @@ export default [ | ||||
|     title: 'Welcome', | ||||
|     description: null, | ||||
|     account_id: 1, | ||||
|     inbox: { | ||||
|       id: 37, | ||||
|       channel_id: 1, | ||||
|       name: 'Chatwoot', | ||||
|       channel_type: 'Channel::WebWidget', | ||||
|       greeting_enabled: true, | ||||
|       greeting_message: '', | ||||
|       working_hours_enabled: true, | ||||
|       out_of_office_message: | ||||
|         'We are unavailable at the moment. Leave a message we will respond once we are back.', | ||||
|       working_hours: [ | ||||
|         { | ||||
|           day_of_week: 0, | ||||
|           closed_all_day: true, | ||||
|           open_hour: null, | ||||
|           open_minutes: null, | ||||
|           close_hour: null, | ||||
|           close_minutes: null, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 1, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 2, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 3, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 4, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 5, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 6, | ||||
|           closed_all_day: true, | ||||
|           open_hour: null, | ||||
|           open_minutes: null, | ||||
|           close_hour: null, | ||||
|           close_minutes: null, | ||||
|         }, | ||||
|       ], | ||||
|       timezone: 'Asia/Kolkata', | ||||
|       avatar_url: '', | ||||
|       page_id: null, | ||||
|       widget_color: '#1F93FF', | ||||
|       website_url: 'chatwoot.com', | ||||
|       welcome_title: 'Hi there ! 🙌🏼', | ||||
|       welcome_tagline: | ||||
|         'We make it simple to connect with us. Ask us anything, or share your feedback.', | ||||
|       enable_auto_assignment: true, | ||||
|       website_token: '', | ||||
|       forward_to_email: null, | ||||
|       phone_number: null, | ||||
|       selected_feature_flags: ['attachments', 'emoji_picker'], | ||||
|       reply_time: 'in_a_few_hours', | ||||
|       hmac_token: '', | ||||
|       pre_chat_form_enabled: true, | ||||
|       pre_chat_form_options: { | ||||
|         require_email: true, | ||||
|         pre_chat_message: 'Share your queries or comments here.', | ||||
|       }, | ||||
|     }, | ||||
|     sender: { | ||||
|       account_id: 1, | ||||
|       availability_status: 'offline', | ||||
|       confirmed: true, | ||||
|       email: 'sojan@chatwoot.com', | ||||
|       available_name: 'Sojan', | ||||
|       id: 10, | ||||
|       name: 'Sojan', | ||||
|       role: 'administrator', | ||||
|       thumbnail: | ||||
|         'http://0.0.0.0:3000/rails/active_storage/representations/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBDZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--bfa5e8a4563aef73980771fc9b8007d380e586e5/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCam9MY21WemFYcGxTU0lNTWpVd2VESTFNQVk2QmtWVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--c13bd5229b2a2a692444606e22f76ad61c634661/73185.jpeg', | ||||
|     }, | ||||
|     campaign_type: 'ongoing', | ||||
|     message: 'Hey, What brings you today', | ||||
|     enabled: true, | ||||
|     trigger_rules: { | ||||
| @@ -119,110 +19,7 @@ export default [ | ||||
|     title: 'Onboarding Campaign', | ||||
|     description: null, | ||||
|     account_id: 1, | ||||
|     inbox: { | ||||
|       id: 37, | ||||
|       channel_id: 1, | ||||
|       name: 'Chatwoot', | ||||
|       channel_type: 'Channel::WebWidget', | ||||
|       greeting_enabled: true, | ||||
|       greeting_message: '', | ||||
|       working_hours_enabled: true, | ||||
|       out_of_office_message: | ||||
|         'We are unavailable at the moment. Leave a message we will respond once we are back.', | ||||
|       working_hours: [ | ||||
|         { | ||||
|           day_of_week: 0, | ||||
|           closed_all_day: true, | ||||
|           open_hour: null, | ||||
|           open_minutes: null, | ||||
|           close_hour: null, | ||||
|           close_minutes: null, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 1, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 2, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 3, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 4, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 5, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 6, | ||||
|           closed_all_day: true, | ||||
|           open_hour: null, | ||||
|           open_minutes: null, | ||||
|           close_hour: null, | ||||
|           close_minutes: null, | ||||
|         }, | ||||
|       ], | ||||
|       timezone: 'Asia/Kolkata', | ||||
|       avatar_url: '', | ||||
|       page_id: null, | ||||
|       widget_color: '#1F93FF', | ||||
|       website_url: 'chatwoot.com', | ||||
|       welcome_title: 'Hi there ! 🙌🏼', | ||||
|       welcome_tagline: | ||||
|         'We make it simple to connect with us. Ask us anything, or share your feedback.', | ||||
|       enable_auto_assignment: true, | ||||
|       web_widget_script: '', | ||||
|       website_token: '', | ||||
|       forward_to_email: null, | ||||
|       phone_number: null, | ||||
|       selected_feature_flags: ['attachments', 'emoji_picker'], | ||||
|       reply_time: 'in_a_few_hours', | ||||
|       hmac_token: '', | ||||
|       pre_chat_form_enabled: true, | ||||
|       pre_chat_form_options: { | ||||
|         require_email: true, | ||||
|         pre_chat_message: 'Share your queries or comments here.', | ||||
|       }, | ||||
|     }, | ||||
|     sender: { | ||||
|       account_id: 1, | ||||
|       availability_status: 'offline', | ||||
|       confirmed: true, | ||||
|       email: 'sojan@chatwoot.com', | ||||
|       available_name: 'Sojan', | ||||
|       id: 10, | ||||
|       name: 'Sojan', | ||||
|       role: 'administrator', | ||||
|       thumbnail: | ||||
|         'http://0.0.0.0:3000/rails/active_storage/representations/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBDZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--bfa5e8a4563aef73980771fc9b8007d380e586e5/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCam9MY21WemFYcGxTU0lNTWpVd2VESTFNQVk2QmtWVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--c13bd5229b2a2a692444606e22f76ad61c634661/73185.jpeg', | ||||
|     }, | ||||
|     message: 'Begin your onboarding campaign with a welcome message', | ||||
|     enabled: true, | ||||
|     campaign_type: 'one_off', | ||||
|     trigger_rules: { | ||||
|       url: 'https://chatwoot.com', | ||||
|       time_on_page: '20', | ||||
| @@ -235,107 +32,7 @@ export default [ | ||||
|     title: 'Thanks', | ||||
|     description: null, | ||||
|     account_id: 1, | ||||
|     inbox: { | ||||
|       id: 37, | ||||
|       channel_id: 1, | ||||
|       name: 'Chatwoot', | ||||
|       channel_type: 'Channel::WebWidget', | ||||
|       greeting_enabled: true, | ||||
|       greeting_message: '', | ||||
|       working_hours_enabled: true, | ||||
|       out_of_office_message: | ||||
|         'We are unavailable at the moment. Leave a message we will respond once we are back.', | ||||
|       working_hours: [ | ||||
|         { | ||||
|           day_of_week: 0, | ||||
|           closed_all_day: true, | ||||
|           open_hour: null, | ||||
|           open_minutes: null, | ||||
|           close_hour: null, | ||||
|           close_minutes: null, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 1, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 2, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 3, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 4, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 5, | ||||
|           closed_all_day: false, | ||||
|           open_hour: 11, | ||||
|           open_minutes: 0, | ||||
|           close_hour: 23, | ||||
|           close_minutes: 30, | ||||
|         }, | ||||
|         { | ||||
|           day_of_week: 6, | ||||
|           closed_all_day: true, | ||||
|           open_hour: null, | ||||
|           open_minutes: null, | ||||
|           close_hour: null, | ||||
|           close_minutes: null, | ||||
|         }, | ||||
|       ], | ||||
|       timezone: 'Asia/Kolkata', | ||||
|       avatar_url: '', | ||||
|       page_id: null, | ||||
|       widget_color: '#1F93FF', | ||||
|       website_url: 'chatwoot.com', | ||||
|       welcome_title: 'Hi there ! 🙌🏼', | ||||
|       welcome_tagline: | ||||
|         'We make it simple to connect with us. Ask us anything, or share your feedback.', | ||||
|       enable_auto_assignment: true, | ||||
|       web_widget_script: '', | ||||
|       website_token: '', | ||||
|       forward_to_email: null, | ||||
|       phone_number: null, | ||||
|       selected_feature_flags: ['attachments', 'emoji_picker'], | ||||
|       reply_time: 'in_a_few_hours', | ||||
|       hmac_token: '', | ||||
|       pre_chat_form_enabled: true, | ||||
|       pre_chat_form_options: { | ||||
|         require_email: true, | ||||
|         pre_chat_message: 'Share your queries or comments here.', | ||||
|       }, | ||||
|     }, | ||||
|     sender: { | ||||
|       account_id: 1, | ||||
|       availability_status: 'offline', | ||||
|       confirmed: true, | ||||
|       email: 'nithin@chatwoot.com', | ||||
|       available_name: 'Nithin', | ||||
|       id: 13, | ||||
|       name: 'Nithin', | ||||
|       role: 'administrator', | ||||
|       thumbnail: '', | ||||
|     }, | ||||
|     campaign_type: 'ongoing', | ||||
|     message: 'Thanks for coming to the show. How may I help you?', | ||||
|     enabled: false, | ||||
|     trigger_rules: { | ||||
|   | ||||
| @@ -2,9 +2,60 @@ import { getters } from '../../campaigns'; | ||||
| import campaigns from './fixtures'; | ||||
|  | ||||
| describe('#getters', () => { | ||||
|   it('getCampaigns', () => { | ||||
|   it('get ongoing campaigns', () => { | ||||
|     const state = { records: campaigns }; | ||||
|     expect(getters.getCampaigns(state)).toEqual(campaigns); | ||||
|     expect(getters.getCampaigns(state)('ongoing')).toEqual([ | ||||
|       { | ||||
|         id: 1, | ||||
|         title: 'Welcome', | ||||
|         description: null, | ||||
|         account_id: 1, | ||||
|         campaign_type: 'ongoing', | ||||
|         message: 'Hey, What brings you today', | ||||
|         enabled: true, | ||||
|         trigger_rules: { | ||||
|           url: 'https://github.com', | ||||
|           time_on_page: 10, | ||||
|         }, | ||||
|         created_at: '2021-05-03T04:53:36.354Z', | ||||
|         updated_at: '2021-05-03T04:53:36.354Z', | ||||
|       }, | ||||
|       { | ||||
|         id: 3, | ||||
|         title: 'Thanks', | ||||
|         description: null, | ||||
|         account_id: 1, | ||||
|         campaign_type: 'ongoing', | ||||
|         message: 'Thanks for coming to the show. How may I help you?', | ||||
|         enabled: false, | ||||
|         trigger_rules: { | ||||
|           url: 'https://noshow.com', | ||||
|           time_on_page: 10, | ||||
|         }, | ||||
|         created_at: '2021-05-03T10:22:51.025Z', | ||||
|         updated_at: '2021-05-03T10:22:51.025Z', | ||||
|       }, | ||||
|     ]); | ||||
|   }); | ||||
|  | ||||
|   it('get one_off campaigns', () => { | ||||
|     const state = { records: campaigns }; | ||||
|     expect(getters.getCampaigns(state)('one_off')).toEqual([ | ||||
|       { | ||||
|         id: 2, | ||||
|         title: 'Onboarding Campaign', | ||||
|         description: null, | ||||
|         account_id: 1, | ||||
|         campaign_type: 'one_off', | ||||
|  | ||||
|         trigger_rules: { | ||||
|           url: 'https://chatwoot.com', | ||||
|           time_on_page: '20', | ||||
|         }, | ||||
|         created_at: '2021-05-03T08:15:35.828Z', | ||||
|         updated_at: '2021-05-03T08:15:35.828Z', | ||||
|       }, | ||||
|     ]); | ||||
|   }); | ||||
|  | ||||
|   it('getUIFlags', () => { | ||||
|   | ||||
| @@ -43,4 +43,16 @@ export default [ | ||||
|     website_token: 'randomid125', | ||||
|     enable_auto_assignment: true, | ||||
|   }, | ||||
|   { | ||||
|     id: 5, | ||||
|     channel_id: 5, | ||||
|     name: 'Test Widget 5', | ||||
|     channel_type: 'Channel::TwilioSms', | ||||
|     avatar_url: null, | ||||
|     page_id: null, | ||||
|     widget_color: '#68BC00', | ||||
|     website_token: 'randomid125', | ||||
|     enable_auto_assignment: true, | ||||
|   }, | ||||
|  | ||||
| ]; | ||||
|   | ||||
| @@ -14,6 +14,11 @@ describe('#getters', () => { | ||||
|     expect(getters.getWebsiteInboxes(state).length).toEqual(3); | ||||
|   }); | ||||
|  | ||||
|   it('getTwilioInboxes', () => { | ||||
|     const state = { records: inboxList }; | ||||
|     expect(getters.getTwilioInboxes(state).length).toEqual(1); | ||||
|   }); | ||||
|  | ||||
|   it('getInbox', () => { | ||||
|     const state = { | ||||
|       records: inboxList, | ||||
|   | ||||
| @@ -1,13 +1,10 @@ | ||||
| import { CAMPAIGN_TYPES } from '../constants/campaign'; | ||||
| import inboxMixin from './inboxMixin'; | ||||
| export default { | ||||
|   mixins: [inboxMixin], | ||||
|   computed: { | ||||
|     campaignType() { | ||||
|       if (this.isAWebWidgetInbox) { | ||||
|         return CAMPAIGN_TYPES.ONGOING; | ||||
|       } | ||||
|       return CAMPAIGN_TYPES.ONE_OFF; | ||||
|       const pageURL = window.location.href; | ||||
|       const type = pageURL.substr(pageURL.lastIndexOf('/') + 1); | ||||
|       return type; | ||||
|     }, | ||||
|     isOngoingType() { | ||||
|       return this.campaignType === CAMPAIGN_TYPES.ONGOING; | ||||
|   | ||||
| @@ -1,49 +1,38 @@ | ||||
| import { shallowMount } from '@vue/test-utils'; | ||||
| import campaignMixin from '../campaignMixin'; | ||||
| import inboxMixin from '../inboxMixin'; | ||||
|  | ||||
| describe('campaignMixin', () => { | ||||
|   beforeEach(() => { | ||||
|     global.window = Object.create(window); | ||||
|   }); | ||||
|   it('returns the correct campaign type', () => { | ||||
|     const url = 'http://localhost:3000/app/accounts/1/campaigns/one_off'; | ||||
|     Object.defineProperty(window, 'location', { | ||||
|       value: { | ||||
|         href: url, | ||||
|       }, | ||||
|     }); | ||||
|     window.location.href = url; | ||||
|     const Component = { | ||||
|       render() {}, | ||||
|       mixins: [campaignMixin, inboxMixin], | ||||
|       data() { | ||||
|         return { | ||||
|           inbox: { | ||||
|             channel_type: 'Channel::TwilioSms', | ||||
|             phone_number: '+91944444444', | ||||
|           }, | ||||
|         }; | ||||
|       }, | ||||
|       mixins: [campaignMixin], | ||||
|     }; | ||||
|     const wrapper = shallowMount(Component); | ||||
|     expect(wrapper.vm.campaignType).toBe('one_off'); | ||||
|   }); | ||||
|   it('isOnOffType returns true if campaign type is ongoing', () => { | ||||
|   it('isOnOffType returns true if campaign type is one_off', () => { | ||||
|     const url = 'http://localhost:3000/app/accounts/1/campaigns/one_off'; | ||||
|     Object.defineProperty(window, 'location', { | ||||
|       value: { | ||||
|         href: url, | ||||
|       }, | ||||
|     }); | ||||
|     const Component = { | ||||
|       render() {}, | ||||
|       mixins: [campaignMixin, inboxMixin], | ||||
|       data() { | ||||
|         return { inbox: { channel_type: 'Channel::WebWidget' } }; | ||||
|       }, | ||||
|     }; | ||||
|     const wrapper = shallowMount(Component); | ||||
|     expect(wrapper.vm.isOngoingType).toBe(true); | ||||
|   }); | ||||
|   it('isOngoingType returns true if campaign type is one_off', () => { | ||||
|     const Component = { | ||||
|       render() {}, | ||||
|       mixins: [campaignMixin, inboxMixin], | ||||
|       data() { | ||||
|         return { | ||||
|           inbox: { | ||||
|             channel_type: 'Channel::TwilioSms', | ||||
|             phone_number: '+91944444444', | ||||
|           }, | ||||
|         }; | ||||
|       }, | ||||
|       mixins: [campaignMixin], | ||||
|     }; | ||||
|     const wrapper = shallowMount(Component); | ||||
|     expect(wrapper.vm.isOnOffType).toBe(true); | ||||
|   }); | ||||
|  | ||||
| }); | ||||
|   | ||||
| @@ -31,4 +31,5 @@ module.exports = { | ||||
|   ], | ||||
|   testURL: 'http://localhost/', | ||||
|   globalSetup: './jest.setup.js', | ||||
|   testEnvironment: 'jsdom', | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Muhsin Keloth
					Muhsin Keloth