feat: Add the ability to create dashboard apps from the UI (#4924)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
@@ -18,6 +18,7 @@ const settings = accountId => ({
|
||||
'settings_integrations_webhook',
|
||||
'settings_integrations_integration',
|
||||
'settings_applications',
|
||||
'settings_integrations_dashboard_apps',
|
||||
'settings_applications_webhook',
|
||||
'settings_applications_integration',
|
||||
'general_settings',
|
||||
|
||||
@@ -35,7 +35,10 @@
|
||||
"LIST": {
|
||||
"404": "There are no webhooks configured for this account.",
|
||||
"TITLE": "Manage webhooks",
|
||||
"TABLE_HEADER": ["Webhook endpoint", "Actions"]
|
||||
"TABLE_HEADER": [
|
||||
"Webhook endpoint",
|
||||
"Actions"
|
||||
]
|
||||
},
|
||||
"EDIT": {
|
||||
"BUTTON_TEXT": "Edit",
|
||||
@@ -68,7 +71,7 @@
|
||||
}
|
||||
},
|
||||
"SLACK": {
|
||||
"HELP_TEXT" : {
|
||||
"HELP_TEXT": {
|
||||
"TITLE": "Using Slack Integration",
|
||||
"BODY": "<br/><p>Chatwoot will now sync all the incoming conversations into the <b><i>customer-conversations</i></b> channel inside your slack workplace.</p><p>Replying to a conversation thread in <b><i>customer-conversations</i></b> slack channel will create a response back to the customer through chatwoot.</p><p>Start the replies with <b><i>note:</i></b> to create private notes instead of replies.</p><p>If the replier on slack has an agent profile in chatwoot under the same email, the replies will be associated accordingly.</p><p>When the replier doesn't have an associated agent profile, the replies will be made from the bot profile.</p>"
|
||||
}
|
||||
@@ -81,6 +84,52 @@
|
||||
},
|
||||
"CONNECT": {
|
||||
"BUTTON_TEXT": "Connect"
|
||||
},
|
||||
"DASHBOARD_APPS": {
|
||||
"TITLE": "Dashboard Apps",
|
||||
"HEADER_BTN_TXT": "Add a new dashboard app",
|
||||
"SIDEBAR_TXT": "<p><b>Dashboard Apps</b></p><p>Dashboard Apps allow organizations to embed an application inside the Chatwoot dashboard to provide the context for customer support agents. This feature allows you to create an application independently and embed that inside the dashboard to provide user information, their orders, or their previous payment history.</p><p>When you embed your application using the dashboard in Chatwoot, your application will get the context of the conversation and contact as a window event. Implement a listener for the message event on your page to receive the context.</p><p>To add a new dashboard app, click on the button 'Add a new dashboard app'.</p>",
|
||||
"DESCRIPTION": "Dashboard Apps allow organizations to embed an application inside the dashboard to provide the context for customer support agents. This feature allows you to create an application independently and embed that to provide user information, their orders, or their previous payment history.",
|
||||
"LIST": {
|
||||
"404": "There are no dashboard apps configured on this account yet",
|
||||
"LOADING": "Fetching dashboard apps...",
|
||||
"TABLE_HEADER": [
|
||||
"Name",
|
||||
"Endpoint"
|
||||
],
|
||||
"EDIT_TOOLTIP": "Edit app",
|
||||
"DELETE_TOOLTIP": "Delete app"
|
||||
},
|
||||
"FORM": {
|
||||
"TITLE_LABEL": "Name",
|
||||
"TITLE_PLACEHOLDER": "Enter a name for your dashboard app",
|
||||
"TITLE_ERROR": "A name for the dashboard app is required",
|
||||
"URL_LABEL": "Endpoint",
|
||||
"URL_PLACEHOLDER": "Enter the endpoint URL where your app is hosted",
|
||||
"URL_ERROR": "A valid URL is required"
|
||||
},
|
||||
"CREATE": {
|
||||
"HEADER": "Add a new dashboard app",
|
||||
"FORM_SUBMIT": "Submit",
|
||||
"FORM_CANCEL": "Cancel",
|
||||
"API_SUCCESS": "Dashboard app configured successfully",
|
||||
"API_ERROR": "We couldn't create an app. Please try again later"
|
||||
},
|
||||
"UPDATE": {
|
||||
"HEADER": "Edit dashboard app",
|
||||
"FORM_SUBMIT": "Update",
|
||||
"FORM_CANCEL": "Cancel",
|
||||
"API_SUCCESS": "Dashboard app updated successfully",
|
||||
"API_ERROR": "We couldn't update the app. Please try again later"
|
||||
},
|
||||
"DELETE": {
|
||||
"CONFIRM_YES": "Yes, delete it",
|
||||
"CONFIRM_NO": "No, keep it",
|
||||
"TITLE": "Confirm deletion",
|
||||
"MESSAGE": "Are you sure to delete the app - %{appName}?",
|
||||
"API_SUCCESS": "Dashboard app deleted successfully",
|
||||
"API_ERROR": "We couldn't delete the app. Please try again later"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<woot-modal :show="show" :on-close="closeModal">
|
||||
<div class="column content-box">
|
||||
<woot-modal-header :header-title="header" />
|
||||
|
||||
<form class="row" @submit.prevent="submit">
|
||||
<woot-input
|
||||
v-model.trim="app.title"
|
||||
:class="{ error: $v.app.title.$error }"
|
||||
class="medium-12 columns"
|
||||
:label="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.FORM.TITLE_LABEL')"
|
||||
:placeholder="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.FORM.TITLE_PLACEHOLDER')
|
||||
"
|
||||
:error="
|
||||
$v.app.title.$error
|
||||
? $t('INTEGRATION_SETTINGS.DASHBOARD_APPS.FORM.TITLE_ERROR')
|
||||
: null
|
||||
"
|
||||
data-testid="app-title"
|
||||
@input="$v.app.title.$touch"
|
||||
/>
|
||||
<woot-input
|
||||
v-model.trim="app.content.url"
|
||||
:class="{ error: $v.app.content.url.$error }"
|
||||
class="medium-12 columns app--url_input"
|
||||
:label="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.FORM.URL_LABEL')"
|
||||
:placeholder="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.FORM.URL_PLACEHOLDER')
|
||||
"
|
||||
:error="
|
||||
$v.app.content.url.$error
|
||||
? $t('INTEGRATION_SETTINGS.DASHBOARD_APPS.FORM.URL_ERROR')
|
||||
: null
|
||||
"
|
||||
data-testid="app-url"
|
||||
@input="$v.app.content.url.$touch"
|
||||
/>
|
||||
<div class="modal-footer">
|
||||
<div class="medium-12 columns">
|
||||
<woot-button
|
||||
:is-loading="isLoading"
|
||||
:is-disabled="$v.$invalid"
|
||||
data-testid="label-submit"
|
||||
>
|
||||
{{ submitButtonLabel }}
|
||||
</woot-button>
|
||||
<woot-button class="button clear" @click.prevent="closeModal">
|
||||
{{ $t('INTEGRATION_SETTINGS.DASHBOARD_APPS.CREATE.FORM_CANCEL') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</woot-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { required, url } from 'vuelidate/lib/validators';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
|
||||
export default {
|
||||
mixins: [alertMixin],
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'create',
|
||||
},
|
||||
selectedAppData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
validations: {
|
||||
app: {
|
||||
title: { required },
|
||||
content: {
|
||||
type: { required },
|
||||
url: { required, url },
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
app: {
|
||||
title: '',
|
||||
content: {
|
||||
type: 'frame',
|
||||
url: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
header() {
|
||||
return this.$t(`INTEGRATION_SETTINGS.DASHBOARD_APPS.${this.mode}.HEADER`);
|
||||
},
|
||||
submitButtonLabel() {
|
||||
return this.$t(
|
||||
`INTEGRATION_SETTINGS.DASHBOARD_APPS.${this.mode}.FORM_SUBMIT`
|
||||
);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.mode === 'UPDATE' && this.selectedAppData) {
|
||||
this.app.title = this.selectedAppData.title;
|
||||
this.app.content = this.selectedAppData.content[0];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeModal() {
|
||||
// Reset the data once closed
|
||||
this.app = {
|
||||
title: '',
|
||||
content: { type: 'frame', url: '' },
|
||||
};
|
||||
this.$emit('close');
|
||||
},
|
||||
async submit() {
|
||||
try {
|
||||
this.$v.$touch();
|
||||
if (this.$v.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const action = this.mode.toLowerCase();
|
||||
const payload = {
|
||||
title: this.app.title,
|
||||
content: [this.app.content],
|
||||
};
|
||||
|
||||
if (action === 'update') {
|
||||
payload.id = this.selectedAppData.id;
|
||||
}
|
||||
|
||||
this.isLoading = true;
|
||||
await this.$store.dispatch(`dashboardApps/${action}`, payload);
|
||||
this.showAlert(
|
||||
this.$t(
|
||||
`INTEGRATION_SETTINGS.DASHBOARD_APPS.${this.mode}.API_SUCCESS`
|
||||
)
|
||||
);
|
||||
this.closeModal();
|
||||
} catch (err) {
|
||||
this.showAlert(
|
||||
this.$t(`INTEGRATION_SETTINGS.DASHBOARD_APPS.${this.mode}.API_ERROR`)
|
||||
);
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.content-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
.app--url_input {
|
||||
flex: 1;
|
||||
}
|
||||
.app--url_add_btn {
|
||||
margin-left: var(--space-one);
|
||||
margin-top: var(--space-one);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<tr>
|
||||
<td>{{ app.title }}</td>
|
||||
<td>{{ app.content[0].url }}</td>
|
||||
<td class="button-wrapper">
|
||||
<woot-button
|
||||
v-tooltip.top="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.EDIT_TOOLTIP')
|
||||
"
|
||||
variant="smooth"
|
||||
size="tiny"
|
||||
color-scheme="secondary"
|
||||
class-names="grey-btn"
|
||||
icon="edit"
|
||||
@click="$emit('edit', app)"
|
||||
/>
|
||||
<woot-button
|
||||
v-tooltip.top="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.DELETE_TOOLTIP')
|
||||
"
|
||||
variant="smooth"
|
||||
color-scheme="alert"
|
||||
size="tiny"
|
||||
icon="dismiss-circle"
|
||||
class-names="grey-btn"
|
||||
@click="$emit('delete', app)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
app: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div class="row content-box full-height">
|
||||
<woot-button
|
||||
color-scheme="success"
|
||||
class-names="button--fixed-right-top"
|
||||
icon="add-circle"
|
||||
@click="openCreatePopup"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.DASHBOARD_APPS.HEADER_BTN_TXT') }}
|
||||
</woot-button>
|
||||
<div class="row">
|
||||
<div class="small-8 columns with-right-space ">
|
||||
<p
|
||||
v-if="!uiFlags.isFetching && !records.length"
|
||||
class="no-items-error-message"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.404') }}
|
||||
</p>
|
||||
<woot-loading-state
|
||||
v-if="uiFlags.isFetching"
|
||||
:message="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.LOADING')"
|
||||
/>
|
||||
<table v-if="!uiFlags.isFetching && records.length" class="woot-table">
|
||||
<thead>
|
||||
<th
|
||||
v-for="thHeader in $t(
|
||||
'INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.TABLE_HEADER'
|
||||
)"
|
||||
:key="thHeader"
|
||||
>
|
||||
{{ thHeader }}
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<dashboard-apps-row
|
||||
v-for="(dashboardAppItem, index) in records"
|
||||
:key="dashboardAppItem.id"
|
||||
:index="index"
|
||||
:app="dashboardAppItem"
|
||||
@edit="editApp"
|
||||
@delete="openDeletePopup"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="small-4 columns">
|
||||
<span
|
||||
v-dompurify-html="
|
||||
useInstallationName(
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.SIDEBAR_TXT'),
|
||||
globalConfig.installationName
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dashboard-app-modal
|
||||
v-if="showDashboardAppPopup"
|
||||
:show="showDashboardAppPopup"
|
||||
:mode="mode"
|
||||
:selected-app-data="selectedApp"
|
||||
@close="toggleDashboardAppPopup"
|
||||
/>
|
||||
|
||||
<woot-delete-modal
|
||||
:show.sync="showDeleteConfirmationPopup"
|
||||
:on-close="closeDeletePopup"
|
||||
:on-confirm="confirmDeletion"
|
||||
:title="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.TITLE')"
|
||||
:message="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.MESSAGE', {
|
||||
appName: selectedApp.title,
|
||||
})
|
||||
"
|
||||
:confirm-text="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.CONFIRM_YES')
|
||||
"
|
||||
:reject-text="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.CONFIRM_NO')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import DashboardAppModal from './DashboardAppModal.vue';
|
||||
import DashboardAppsRow from './DashboardAppsRow.vue';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DashboardAppModal,
|
||||
DashboardAppsRow,
|
||||
},
|
||||
mixins: [alertMixin, globalConfigMixin],
|
||||
data() {
|
||||
return {
|
||||
loading: {},
|
||||
showDashboardAppPopup: false,
|
||||
showDeleteConfirmationPopup: false,
|
||||
selectedApp: {},
|
||||
mode: 'CREATE',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
globalConfig: 'globalConfig/get',
|
||||
records: 'dashboardApps/getRecords',
|
||||
uiFlags: 'dashboardApps/getUIFlags',
|
||||
}),
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('dashboardApps/get');
|
||||
},
|
||||
methods: {
|
||||
toggleDashboardAppPopup() {
|
||||
this.showDashboardAppPopup = !this.showDashboardAppPopup;
|
||||
this.selectedApp = {};
|
||||
},
|
||||
openDeletePopup(response) {
|
||||
this.showDeleteConfirmationPopup = true;
|
||||
this.selectedApp = response;
|
||||
},
|
||||
openCreatePopup() {
|
||||
this.mode = 'CREATE';
|
||||
this.selectedApp = {};
|
||||
this.showDashboardAppPopup = true;
|
||||
},
|
||||
closeDeletePopup() {
|
||||
this.showDeleteConfirmationPopup = false;
|
||||
},
|
||||
editApp(app) {
|
||||
this.loading[app.id] = true;
|
||||
this.mode = 'UPDATE';
|
||||
this.selectedApp = app;
|
||||
this.showDashboardAppPopup = true;
|
||||
},
|
||||
confirmDeletion() {
|
||||
this.loading[this.selectedApp.id] = true;
|
||||
this.closeDeletePopup();
|
||||
this.deleteApp(this.selectedApp.id);
|
||||
},
|
||||
async deleteApp(id) {
|
||||
try {
|
||||
await this.$store.dispatch('dashboardApps/delete', id);
|
||||
this.showAlert(
|
||||
this.$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.API_SUCCESS')
|
||||
);
|
||||
} catch (error) {
|
||||
this.showAlert(
|
||||
this.$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.API_ERROR')
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -17,6 +17,20 @@
|
||||
:integration-action="item.action"
|
||||
/>
|
||||
</div>
|
||||
<div class="small-12 columns integration">
|
||||
<integration
|
||||
integration-id="dashboard-apps"
|
||||
integration-logo="dashboard-apps.svg"
|
||||
:integration-name="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.TITLE')
|
||||
"
|
||||
:integration-description="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DESCRIPTION')
|
||||
"
|
||||
integration-enabled
|
||||
integration-action="/dashboard-apps"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Index from './Index';
|
||||
import SettingsContent from '../Wrapper';
|
||||
import Webhook from './Webhooks/Index';
|
||||
import DashboardApps from './DashboardApps/Index';
|
||||
import ShowIntegration from './ShowIntegration';
|
||||
import { frontendURL } from '../../../../helper/URLHelper';
|
||||
|
||||
@@ -35,6 +36,12 @@ export default {
|
||||
name: 'settings_integrations_webhook',
|
||||
roles: ['administrator'],
|
||||
},
|
||||
{
|
||||
path: 'dashboard-apps',
|
||||
component: DashboardApps,
|
||||
name: 'settings_integrations_dashboard_apps',
|
||||
roles: ['administrator'],
|
||||
},
|
||||
{
|
||||
path: ':integration_id',
|
||||
name: 'settings_integrations_integration',
|
||||
|
||||
@@ -32,6 +32,40 @@ export const actions = {
|
||||
commit(types.SET_DASHBOARD_APPS_UI_FLAG, { isFetching: false });
|
||||
}
|
||||
},
|
||||
create: async function createApp({ commit }, appObj) {
|
||||
commit(types.SET_DASHBOARD_APPS_UI_FLAG, { isCreating: true });
|
||||
try {
|
||||
const response = await DashboardAppsAPI.create(appObj);
|
||||
commit(types.CREATE_DASHBOARD_APP, response.data);
|
||||
} catch (error) {
|
||||
const errorMessage = error?.response?.data?.message;
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
commit(types.SET_DASHBOARD_APPS_UI_FLAG, { isCreating: false });
|
||||
}
|
||||
},
|
||||
update: async function updateApp({ commit }, { id, ...updateObj }) {
|
||||
commit(types.SET_DASHBOARD_APPS_UI_FLAG, { isUpdating: true });
|
||||
try {
|
||||
const response = await DashboardAppsAPI.update(id, updateObj);
|
||||
commit(types.EDIT_DASHBOARD_APP, response.data);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
} finally {
|
||||
commit(types.SET_DASHBOARD_APPS_UI_FLAG, { isUpdating: false });
|
||||
}
|
||||
},
|
||||
delete: async function deleteApp({ commit }, id) {
|
||||
commit(types.SET_DASHBOARD_APPS_UI_FLAG, { isDeleting: true });
|
||||
try {
|
||||
await DashboardAppsAPI.delete(id);
|
||||
commit(types.DELETE_DASHBOARD_APP, id);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
} finally {
|
||||
commit(types.SET_DASHBOARD_APPS_UI_FLAG, { isDeleting: false });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const mutations = {
|
||||
@@ -43,6 +77,9 @@ export const mutations = {
|
||||
},
|
||||
|
||||
[types.SET_DASHBOARD_APPS]: MutationHelpers.set,
|
||||
[types.CREATE_DASHBOARD_APP]: MutationHelpers.create,
|
||||
[types.EDIT_DASHBOARD_APP]: MutationHelpers.update,
|
||||
[types.DELETE_DASHBOARD_APP]: MutationHelpers.destroy,
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import axios from 'axios';
|
||||
import { actions } from '../../dashboardApps';
|
||||
import types from '../../../mutation-types';
|
||||
|
||||
import { payload, automationsList } from './fixtures';
|
||||
const commit = jest.fn();
|
||||
global.axios = axios;
|
||||
jest.mock('axios');
|
||||
@@ -18,4 +18,68 @@ describe('#actions', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#create', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
axios.post.mockResolvedValue({ data: payload });
|
||||
await actions.create({ commit }, payload);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isCreating: true }],
|
||||
[types.CREATE_DASHBOARD_APP, payload],
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isCreating: false }],
|
||||
]);
|
||||
});
|
||||
it('sends correct actions if API is error', async () => {
|
||||
axios.post.mockRejectedValue({ message: 'Incorrect header' });
|
||||
await expect(actions.create({ commit })).rejects.toThrow(Error);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isCreating: true }],
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isCreating: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#update', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
axios.patch.mockResolvedValue({ data: automationsList[0] });
|
||||
await actions.update({ commit }, automationsList[0]);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isUpdating: true }],
|
||||
[types.EDIT_DASHBOARD_APP, automationsList[0]],
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isUpdating: false }],
|
||||
]);
|
||||
});
|
||||
it('sends correct actions if API is error', async () => {
|
||||
axios.patch.mockRejectedValue({ message: 'Incorrect header' });
|
||||
await expect(
|
||||
actions.update({ commit }, automationsList[0])
|
||||
).rejects.toThrow(Error);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isUpdating: true }],
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isUpdating: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#delete', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
axios.delete.mockResolvedValue({ data: automationsList[0] });
|
||||
await actions.delete({ commit }, automationsList[0].id);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isDeleting: true }],
|
||||
[types.DELETE_DASHBOARD_APP, automationsList[0].id],
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isDeleting: false }],
|
||||
]);
|
||||
});
|
||||
it('sends correct actions if API is error', async () => {
|
||||
axios.delete.mockRejectedValue({ message: 'Incorrect header' });
|
||||
await expect(
|
||||
actions.delete({ commit }, automationsList[0].id)
|
||||
).rejects.toThrow(Error);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isDeleting: true }],
|
||||
[types.SET_DASHBOARD_APPS_UI_FLAG, { isDeleting: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
export const payload = {
|
||||
title: 'Test',
|
||||
content: [
|
||||
{ url: 'https://example.com', type: 'frame' },
|
||||
{ url: 'https://chatwoot.com', type: 'frame' },
|
||||
],
|
||||
};
|
||||
|
||||
export const automationsList = [
|
||||
{
|
||||
id: 15,
|
||||
title: 'Test',
|
||||
content: [
|
||||
{ url: 'https://example.com', type: 'frame' },
|
||||
{ url: 'https://chatwoot.com', type: 'frame' },
|
||||
],
|
||||
created_at: '2022-06-27T08:28:29.841Z',
|
||||
},
|
||||
];
|
||||
@@ -1,5 +1,6 @@
|
||||
import types from '../../../mutation-types';
|
||||
import { mutations } from '../../dashboardApps';
|
||||
import { automationsList } from './fixtures';
|
||||
|
||||
describe('#mutations', () => {
|
||||
describe('#SET_DASHBOARD_APPS_UI_FLAG', () => {
|
||||
@@ -17,4 +18,31 @@ describe('#mutations', () => {
|
||||
expect(state.records).toEqual([{ title: 'Title 1' }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#ADD_DASHBOARD_APP', () => {
|
||||
it('push newly created app to the store', () => {
|
||||
const state = { records: [automationsList[0]] };
|
||||
mutations[types.CREATE_DASHBOARD_APP](state, automationsList[1]);
|
||||
expect(state.records).toEqual([automationsList[0], automationsList[1]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#EDIT_DASHBOARD_APP', () => {
|
||||
it('update label record', () => {
|
||||
const state = { records: [automationsList[0]] };
|
||||
mutations[types.EDIT_DASHBOARD_APP](state, {
|
||||
id: 15,
|
||||
title: 'updated-title',
|
||||
});
|
||||
expect(state.records[0].title).toEqual('updated-title');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#DELETE_DASHBOARD_APP', () => {
|
||||
it('delete label record', () => {
|
||||
const state = { records: [automationsList[0]] };
|
||||
mutations[types.DELETE_DASHBOARD_APP](state, 15);
|
||||
expect(state.records).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -217,4 +217,7 @@ export default {
|
||||
// Dashboard Apps
|
||||
SET_DASHBOARD_APPS_UI_FLAG: 'SET_DASHBOARD_APPS_UI_FLAG',
|
||||
SET_DASHBOARD_APPS: 'SET_DASHBOARD_APPS',
|
||||
CREATE_DASHBOARD_APP: 'CREATE_DASHBOARD_APP',
|
||||
EDIT_DASHBOARD_APP: 'EDIT_DASHBOARD_APP',
|
||||
DELETE_DASHBOARD_APP: 'DELETE_DASHBOARD_APP',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user