feat: Edit campaigns (#2230)

* add edit campaign component
This commit is contained in:
Muhsin Keloth 2021-05-10 12:40:48 +05:30 committed by GitHub
parent 7f6abdc987
commit db31bfcee4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 321 additions and 11 deletions

View file

@ -42,6 +42,14 @@
"ERROR_MESSAGE": "There was an error. Please try again." "ERROR_MESSAGE": "There was an error. Please try again."
} }
}, },
"EDIT": {
"TITLE": "Edit campaign",
"UPDATE_BUTTON_TEXT": "Update",
"API": {
"SUCCESS_MESSAGE": "Campaign updated successfully",
"ERROR_MESSAGE": "There was an error, please try again"
}
},
"LIST": { "LIST": {
"LOADING_MESSAGE": "Loading campaigns...", "LOADING_MESSAGE": "Loading campaigns...",
"TABLE_HEADER": { "TABLE_HEADER": {

View file

@ -335,10 +335,6 @@ export default {
if (this.isAWebWidgetInbox) { if (this.isAWebWidgetInbox) {
return [ return [
...visibleToAllChannelTabs, ...visibleToAllChannelTabs,
// {
// key: 'campaign',
// name: this.$t('INBOX_MGMT.TABS.CAMPAIGN'),
// },
{ {
key: 'preChatForm', key: 'preChatForm',
name: this.$t('INBOX_MGMT.TABS.PRE_CHAT_FORM'), name: this.$t('INBOX_MGMT.TABS.PRE_CHAT_FORM'),

View file

@ -1,8 +1,7 @@
<template> <template>
<div class="column content-box"> <div class="column content-box">
<div class="row button-wrapper"> <div class="row button-wrapper">
<woot-button @click="openAddPopup"> <woot-button icon="ion-android-add-circle" @click="openAddPopup">
<i class="icon ion-android-add-circle"></i>
{{ $t('CAMPAIGN.HEADER_BTN_TXT') }} {{ $t('CAMPAIGN.HEADER_BTN_TXT') }}
</woot-button> </woot-button>
</div> </div>
@ -10,22 +9,32 @@
:campaigns="records" :campaigns="records"
:show-empty-state="showEmptyResult" :show-empty-state="showEmptyResult"
:is-loading="uiFlags.isFetching" :is-loading="uiFlags.isFetching"
:on-edit-click="openEditPopup"
/> />
<woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup"> <woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup">
<add-campaign :on-close="hideAddPopup" :sender-list="selectedAgents" /> <add-campaign :on-close="hideAddPopup" :sender-list="selectedAgents" />
</woot-modal> </woot-modal>
<woot-modal :show.sync="showEditPopup" :on-close="hideEditPopup">
<edit-campaign
:on-close="hideEditPopup"
:selected-campaign="selectedCampaign"
:sender-list="selectedAgents"
/>
</woot-modal>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import AddCampaign from './AddCampaign'; import AddCampaign from './AddCampaign';
import CampaignsTable from './CampaignsTable'; import CampaignsTable from './CampaignsTable';
import EditCampaign from './EditCampaign';
export default { export default {
components: { components: {
AddCampaign, AddCampaign,
CampaignsTable, CampaignsTable,
EditCampaign,
}, },
props: { props: {
selectedAgents: { selectedAgents: {
@ -37,6 +46,8 @@ export default {
return { return {
campaigns: [], campaigns: [],
showAddPopup: false, showAddPopup: false,
showEditPopup: false,
selectedCampaign: {},
}; };
}, },
computed: { computed: {
@ -59,6 +70,14 @@ export default {
hideAddPopup() { hideAddPopup() {
this.showAddPopup = false; this.showAddPopup = false;
}, },
openEditPopup(response) {
const { row: campaign } = response;
this.selectedCampaign = campaign;
this.showEditPopup = true;
},
hideEditPopup() {
this.showEditPopup = false;
},
}, },
}; };
</script> </script>

View file

@ -44,6 +44,10 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
onEditClick: {
type: Function,
default: () => {},
},
}, },
data() { data() {
@ -130,14 +134,14 @@ export default {
key: 'buttons', key: 'buttons',
title: '', title: '',
align: 'left', align: 'left',
renderBodyCell: () => ( renderBodyCell: (row) => (
<div class="button-wrapper"> <div class="button-wrapper">
<WootButton <WootButton
variant="clear" variant="clear"
icon="ion-edit" icon="ion-edit"
color-scheme="secondary" color-scheme="secondary"
classNames="hollow grey-btn" classNames="hollow grey-btn"
click="openEditPopup(label)" onClick={() => this.onEditClick(row)}
> >
{this.$t('CAMPAIGN.LIST.BUTTONS.EDIT')} {this.$t('CAMPAIGN.LIST.BUTTONS.EDIT')}
</WootButton> </WootButton>

View file

@ -0,0 +1,236 @@
<template>
<modal :show.sync="show" :on-close="onClose">
<div class="column content-box">
<woot-modal-header :header-title="pageTitle" />
<form class="row" @submit.prevent="editCampaign">
<div class="medium-12 columns">
<label :class="{ error: $v.title.$error }">
{{ $t('CAMPAIGN.ADD.FORM.TITLE.LABEL') }}
<input
v-model.trim="title"
type="text"
:placeholder="$t('CAMPAIGN.ADD.FORM.TITLE.PLACEHOLDER')"
@input="$v.title.$touch"
/>
<span v-if="$v.title.$error" class="message">
{{ $t('CAMPAIGN.ADD.FORM.TITLE.ERROR') }}
</span>
</label>
</div>
<div class="medium-12 columns">
<label :class="{ error: $v.message.$error }">
{{ $t('CAMPAIGN.ADD.FORM.MESSAGE.LABEL') }}
<textarea
v-model.trim="message"
rows="5"
type="text"
:placeholder="$t('CAMPAIGN.ADD.FORM.MESSAGE.PLACEHOLDER')"
@input="$v.message.$touch"
/>
<span v-if="$v.message.$error" class="message">
{{ $t('CAMPAIGN.ADD.FORM.MESSAGE.ERROR') }}
</span>
</label>
</div>
<div class="medium-12 columns">
<label :class="{ error: $v.selectedSender.$error }">
{{ $t('CAMPAIGN.ADD.FORM.SENT_BY.LABEL') }}
<select v-model="selectedSender">
<option
v-for="sender in senderList"
:key="sender.name"
:value="sender.id"
>
{{ sender.name }}
</option>
</select>
<span v-if="$v.selectedSender.$error" class="message">
{{ $t('CAMPAIGN.ADD.FORM.SENT_BY.ERROR') }}
</span>
</label>
</div>
<div class="medium-12 columns">
<label :class="{ error: $v.endPoint.$error }">
{{ $t('CAMPAIGN.ADD.FORM.END_POINT.LABEL') }}
<input
v-model.trim="endPoint"
type="text"
:placeholder="$t('CAMPAIGN.ADD.FORM.END_POINT.PLACEHOLDER')"
@input="$v.endPoint.$touch"
/>
<span v-if="$v.endPoint.$error" class="message">
{{ $t('CAMPAIGN.ADD.FORM.END_POINT.ERROR') }}
</span>
</label>
</div>
<div class="medium-12 columns">
<label :class="{ error: $v.timeOnPage.$error }">
{{ $t('CAMPAIGN.ADD.FORM.TIME_ON_PAGE.LABEL') }}
<input
v-model.trim="timeOnPage"
type="number"
:placeholder="$t('CAMPAIGN.ADD.FORM.TIME_ON_PAGE.PLACEHOLDER')"
@input="$v.timeOnPage.$touch"
/>
<span v-if="$v.timeOnPage.$error" class="message">
{{ $t('CAMPAIGN.ADD.FORM.TIME_ON_PAGE.ERROR') }}
</span>
</label>
</div>
<div class="medium-12 columns">
<label>
<input
v-model="enabled"
type="checkbox"
value="enabled"
name="enabled"
/>
{{ $t('CAMPAIGN.ADD.FORM.ENABLED') }}
</label>
</div>
<div class="modal-footer">
<woot-button :disabled="buttonDisabled" :loading="uiFlags.isCreating">
{{ $t('CAMPAIGN.EDIT.UPDATE_BUTTON_TEXT') }}
</woot-button>
<woot-button
class="button clear"
:disabled="buttonDisabled"
:loading="uiFlags.isCreating"
@click.prevent="onClose"
>
{{ $t('CAMPAIGN.ADD.CANCEL_BUTTON_TEXT') }}
</woot-button>
</div>
</form>
</div>
</modal>
</template>
<script>
import { mapGetters } from 'vuex';
import { required, url, minLength } from 'vuelidate/lib/validators';
import Modal from 'dashboard/components/Modal';
import alertMixin from 'shared/mixins/alertMixin';
export default {
components: {
Modal,
},
mixins: [alertMixin],
props: {
onClose: {
type: Function,
default: () => {},
},
selectedCampaign: {
type: Object,
default: () => {},
},
senderList: {
type: Array,
default: () => [],
},
},
data() {
return {
title: '',
message: '',
selectedSender: '',
endPoint: '',
timeOnPage: 10,
show: true,
enabled: true,
};
},
validations: {
title: {
required,
},
message: {
required,
},
selectedSender: {
required,
},
endPoint: {
required,
minLength: minLength(7),
url,
},
timeOnPage: {
required,
},
},
computed: {
...mapGetters({
uiFlags: 'campaigns/getUIFlags',
}),
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
);
},
pageTitle() {
return `${this.$t('CAMPAIGN.EDIT.TITLE')} - ${
this.selectedCampaign.title
}`;
},
},
mounted() {
this.setFormValues();
},
methods: {
setFormValues() {
const {
title,
message,
enabled,
trigger_rules: { url: endPoint, time_on_page: timeOnPage },
sender: { id: selectedSender },
} = this.selectedCampaign;
this.title = title;
this.message = message;
this.endPoint = endPoint;
this.timeOnPage = timeOnPage;
this.selectedSender = selectedSender;
this.enabled = enabled;
},
async editCampaign() {
try {
await this.$store.dispatch('campaigns/update', {
id: this.selectedCampaign.id,
title: this.title,
message: this.message,
inbox_id: this.$route.params.inboxId,
sender_id: this.selectedSender,
enabled: this.enabled,
trigger_rules: {
url: this.endPoint,
time_on_page: this.timeOnPage,
},
});
this.showAlert(this.$t('CAMPAIGN.EDIT.API.SUCCESS_MESSAGE'));
this.onClose();
} catch (error) {
this.showAlert(this.$t('CAMPAIGN.EDIT.API.ERROR_MESSAGE'));
}
},
},
};
</script>
<style lang="scss" scoped>
.content-box .page-top-bar::v-deep {
padding: var(--space-large) var(--space-large) var(--space-zero);
}
</style>

View file

@ -42,6 +42,17 @@ export const actions = {
commit(types.SET_CAMPAIGN_UI_FLAG, { isCreating: false }); commit(types.SET_CAMPAIGN_UI_FLAG, { isCreating: false });
} }
}, },
update: async ({ commit }, { id, ...updateObj }) => {
commit(types.SET_CAMPAIGN_UI_FLAG, { isUpdating: true });
try {
const response = await CampaignsAPI.update(id, updateObj);
commit(types.EDIT_CAMPAIGN, response.data);
} catch (error) {
throw new Error(error);
} finally {
commit(types.SET_CAMPAIGN_UI_FLAG, { isUpdating: false });
}
},
}; };
export const mutations = { export const mutations = {
@ -54,6 +65,7 @@ export const mutations = {
[types.ADD_CAMPAIGN]: MutationHelpers.create, [types.ADD_CAMPAIGN]: MutationHelpers.create,
[types.SET_CAMPAIGNS]: MutationHelpers.set, [types.SET_CAMPAIGNS]: MutationHelpers.set,
[types.EDIT_CAMPAIGN]: MutationHelpers.update,
}; };
export default { export default {

View file

@ -46,4 +46,26 @@ describe('#actions', () => {
]); ]);
}); });
}); });
describe('#update', () => {
it('sends correct actions if API is success', async () => {
axios.patch.mockResolvedValue({ data: campaignList[0] });
await actions.update({ commit }, campaignList[0]);
expect(commit.mock.calls).toEqual([
[types.default.SET_CAMPAIGN_UI_FLAG, { isUpdating: true }],
[types.default.EDIT_CAMPAIGN, campaignList[0]],
[types.default.SET_CAMPAIGN_UI_FLAG, { isUpdating: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.patch.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.update({ commit }, campaignList[0])).rejects.toThrow(
Error
);
expect(commit.mock.calls).toEqual([
[types.default.SET_CAMPAIGN_UI_FLAG, { isUpdating: true }],
[types.default.SET_CAMPAIGN_UI_FLAG, { isUpdating: false }],
]);
});
});
}); });

View file

@ -1,6 +1,6 @@
export default [ export default [
{ {
id: 8, id: 1,
title: 'Welcome', title: 'Welcome',
description: null, description: null,
account_id: 1, account_id: 1,
@ -115,7 +115,7 @@ export default [
updated_at: '2021-05-03T04:53:36.354Z', updated_at: '2021-05-03T04:53:36.354Z',
}, },
{ {
id: 11, id: 2,
title: 'Onboarding Campaign', title: 'Onboarding Campaign',
description: null, description: null,
account_id: 1, account_id: 1,
@ -231,7 +231,7 @@ export default [
updated_at: '2021-05-03T08:15:35.828Z', updated_at: '2021-05-03T08:15:35.828Z',
}, },
{ {
id: 12, id: 3,
title: 'Thanks', title: 'Thanks',
description: null, description: null,
account_id: 1, account_id: 1,

View file

@ -18,4 +18,16 @@ describe('#mutations', () => {
expect(state.records).toEqual([campaigns[0], campaigns[1]]); expect(state.records).toEqual([campaigns[0], campaigns[1]]);
}); });
}); });
describe('#EDIT_CAMPAIGN', () => {
it('update campaign record', () => {
const state = { records: [campaigns[0]] };
mutations[types.EDIT_CAMPAIGN](state, {
id: 1,
title: 'Welcome',
account_id: 1,
message: 'Hey, What brings you today',
});
expect(state.records[0].message).toEqual('Hey, What brings you today');
});
});
}); });

View file

@ -150,4 +150,5 @@ export default {
SET_CAMPAIGN_UI_FLAG: 'SET_CAMPAIGN_UI_FLAG', SET_CAMPAIGN_UI_FLAG: 'SET_CAMPAIGN_UI_FLAG',
SET_CAMPAIGNS: 'SET_CAMPAIGNS', SET_CAMPAIGNS: 'SET_CAMPAIGNS',
ADD_CAMPAIGN: 'ADD_CAMPAIGN', ADD_CAMPAIGN: 'ADD_CAMPAIGN',
EDIT_CAMPAIGN: 'EDIT_CAMPAIGN',
}; };