Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0003ded9fe | ||
|
|
087f74bb11 | ||
|
|
e303a33cd2 | ||
|
|
de3084572a | ||
|
|
cbb4c29bf3 | ||
|
|
6fda73c609 | ||
|
|
76ee84fa09 | ||
|
|
1e9d3d0a0e | ||
|
|
95cb7cf2c8 | ||
|
|
9823a00cd7 | ||
|
|
0d7cec57f1 | ||
|
|
c4b48edbe8 | ||
|
|
d8e89cc713 | ||
|
|
b97be238cb | ||
|
|
5892bf2caf | ||
|
|
b103e89224 | ||
|
|
3530404534 | ||
|
|
c6fff300db | ||
|
|
7898af32b0 |
@@ -27,6 +27,7 @@ const settings = accountId => ({
|
|||||||
'settings_inboxes_add_agents',
|
'settings_inboxes_add_agents',
|
||||||
'settings_inboxes_page_channel',
|
'settings_inboxes_page_channel',
|
||||||
'settings_integrations_dashboard_apps',
|
'settings_integrations_dashboard_apps',
|
||||||
|
'settings_integrations_captain',
|
||||||
'settings_integrations_integration',
|
'settings_integrations_integration',
|
||||||
'settings_integrations_slack',
|
'settings_integrations_slack',
|
||||||
'settings_integrations_webhook',
|
'settings_integrations_webhook',
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const FEATURE_HELP_URLS = {
|
|||||||
message_reply_to: 'https://chwt.app/hc/reply-to',
|
message_reply_to: 'https://chwt.app/hc/reply-to',
|
||||||
reports: 'https://chwt.app/hc/reports',
|
reports: 'https://chwt.app/hc/reports',
|
||||||
sla: 'https://chwt.app/hc/sla',
|
sla: 'https://chwt.app/hc/sla',
|
||||||
|
captain: 'https://chwt.app/hc/captain',
|
||||||
team_management: 'https://chwt.app/hc/teams',
|
team_management: 'https://chwt.app/hc/teams',
|
||||||
webhook: 'https://chwt.app/hc/webhooks',
|
webhook: 'https://chwt.app/hc/webhooks',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,10 +5,22 @@
|
|||||||
"LEARN_MORE": "Learn more about integrations",
|
"LEARN_MORE": "Learn more about integrations",
|
||||||
"LOADING": "Fetching integrations",
|
"LOADING": "Fetching integrations",
|
||||||
"CAPTAIN": {
|
"CAPTAIN": {
|
||||||
|
"TITLE": "Captain",
|
||||||
|
"DESCRIPTION": "Captain is a native AI assistant built for your product and trained on your company's knowledge base. It responds like a human and resolves customer queries effectively. Configure it to your inboxes easily.",
|
||||||
|
"LEARN_MORE": "Learn more about Captain",
|
||||||
"DISABLED": "Captain is not enabled on your account.",
|
"DISABLED": "Captain is not enabled on your account.",
|
||||||
|
"HEADER_BTN_TXT": "Connect new inbox",
|
||||||
"CLICK_HERE_TO_CONFIGURE": "Click here to configure",
|
"CLICK_HERE_TO_CONFIGURE": "Click here to configure",
|
||||||
"LOADING_CONSOLE": "Loading Captain Console...",
|
"LOADING_CONSOLE": "Loading Captain Console...",
|
||||||
"FAILED_TO_LOAD_CONSOLE": "Failed to load Captain Console. Please refresh and try again."
|
"FAILED_TO_LOAD_CONSOLE": "Failed to load Captain Console. Please refresh and try again.",
|
||||||
|
"ERRORS": {
|
||||||
|
"CREATE_INBOX_IDS": "Please select at least one inbox to enable Captain"
|
||||||
|
},
|
||||||
|
"LIST": {
|
||||||
|
"EMPTY_STATE": "There are no inboxes configured with captain. To enable captain, click on the setup.",
|
||||||
|
"TABLE_HEADER": ["Inbox Name", "Type"],
|
||||||
|
"CONFIGURE": "Setup Captain"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"WEBHOOK": {
|
"WEBHOOK": {
|
||||||
"SUBSCRIBED_EVENTS": "Subscribed Events",
|
"SUBSCRIBED_EVENTS": "Subscribed Events",
|
||||||
|
|||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
inbox: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
defineEmits(['delete']);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<tr class="max-w-full py-1">
|
||||||
|
<td
|
||||||
|
class="py-4 pr-4 text-sm w-40 max-w-[10rem] truncate"
|
||||||
|
:title="inbox.name"
|
||||||
|
>
|
||||||
|
{{ inbox.name }}
|
||||||
|
</td>
|
||||||
|
<td class="max-w-lg py-4 pr-4 text-sm truncate" :title="inbox.type">
|
||||||
|
{{ inbox.type }}
|
||||||
|
</td>
|
||||||
|
<td class="flex gap-2 py-4 pr-4 text-sm sm:pr-0 justify-end">
|
||||||
|
<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>
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
<script setup>
|
||||||
|
import CaptainInboxRow from './CaptainInboxRow.vue';
|
||||||
|
|
||||||
|
const dummyInboxes = [
|
||||||
|
{ name: 'Website Live Chat', type: 'Channel::WebWidget' },
|
||||||
|
{ name: 'Email', type: 'Channel::Email' },
|
||||||
|
{ name: 'Facebook Messenger', type: 'Channel::Facebook' },
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full min-h-[12rem] relative">
|
||||||
|
<table class="min-w-full divide-y divide-slate-75 dark:divide-slate-700">
|
||||||
|
<thead>
|
||||||
|
<th
|
||||||
|
v-for="thHeader in $t(
|
||||||
|
'INTEGRATION_SETTINGS.CAPTAIN.LIST.TABLE_HEADER'
|
||||||
|
)"
|
||||||
|
:key="thHeader"
|
||||||
|
class="py-4 pr-4 font-semibold text-left text-slate-700 dark:text-slate-300"
|
||||||
|
>
|
||||||
|
{{ thHeader }}
|
||||||
|
</th>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-slate-50 dark:divide-slate-800">
|
||||||
|
<CaptainInboxRow
|
||||||
|
v-for="inbox in dummyInboxes"
|
||||||
|
:key="inbox.name"
|
||||||
|
:inbox="inbox"
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 flex flex-col items-center justify-center w-full h-full bg-gradient-to-t from-white dark:from-slate-900 to-transparent"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import IntegrationSettingsLayout from '../Layout.vue';
|
||||||
|
import EmptyState from './EmptyState.vue';
|
||||||
|
import BaseSettingsHeader from 'dashboard/routes/dashboard/settings/components/BaseSettingsHeader.vue';
|
||||||
|
|
||||||
|
const isSetup = computed(() => {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<IntegrationSettingsLayout>
|
||||||
|
<template #header>
|
||||||
|
<BaseSettingsHeader
|
||||||
|
:title="$t('INTEGRATION_SETTINGS.CAPTAIN.TITLE')"
|
||||||
|
:description="$t('INTEGRATION_SETTINGS.CAPTAIN.DESCRIPTION')"
|
||||||
|
:link-text="$t('INTEGRATION_SETTINGS.CAPTAIN.LEARN_MORE')"
|
||||||
|
feature-name="captain"
|
||||||
|
:back-button-label="$t('INTEGRATION_SETTINGS.HEADER')"
|
||||||
|
>
|
||||||
|
<template #actions>
|
||||||
|
<woot-button
|
||||||
|
v-if="isSetup"
|
||||||
|
class="rounded-md button nice"
|
||||||
|
icon="plus-sign"
|
||||||
|
>
|
||||||
|
{{ $t('INTEGRATION_SETTINGS.CAPTAIN.HEADER_BTN_TXT') }}
|
||||||
|
</woot-button>
|
||||||
|
</template>
|
||||||
|
</BaseSettingsHeader>
|
||||||
|
</template>
|
||||||
|
<EmptyState v-if="!isSetup">
|
||||||
|
<p class="max-w-xs text-sm font-medium text-center">
|
||||||
|
{{ $t('INTEGRATION_SETTINGS.CAPTAIN.LIST.EMPTY_STATE') }}
|
||||||
|
</p>
|
||||||
|
<woot-button class="px-5 mt-4 rounded-xl" icon="plus-sign">
|
||||||
|
{{ $t('INTEGRATION_SETTINGS.CAPTAIN.LIST.CONFIGURE') }}
|
||||||
|
</woot-button>
|
||||||
|
</EmptyState>
|
||||||
|
</IntegrationSettingsLayout>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col flex-1 gap-8 overflow-auto">
|
||||||
|
<slot name="header" />
|
||||||
|
<div class="w-full overflow-x-auto text-slate-700 dark:text-slate-200">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
<slot name="footer" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -5,6 +5,7 @@ import Index from './Index.vue';
|
|||||||
import Webhook from './Webhooks/Index.vue';
|
import Webhook from './Webhooks/Index.vue';
|
||||||
import DashboardApps from './DashboardApps/Index.vue';
|
import DashboardApps from './DashboardApps/Index.vue';
|
||||||
import Slack from './Slack.vue';
|
import Slack from './Slack.vue';
|
||||||
|
import Captain from './Captain/Index.vue';
|
||||||
import SettingsContent from '../Wrapper.vue';
|
import SettingsContent from '../Wrapper.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -38,6 +39,14 @@ export default {
|
|||||||
permissions: ['administrator'],
|
permissions: ['administrator'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'captain',
|
||||||
|
component: Captain,
|
||||||
|
name: 'settings_integrations_captain',
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+23
-22
@@ -1,27 +1,27 @@
|
|||||||
<script>
|
<script setup>
|
||||||
import { mapGetters } from 'vuex';
|
// TODO: Make this component a standard across the app and use it in other places
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useMapGetter, useStore } from 'dashboard/composables/store';
|
||||||
|
|
||||||
export default {
|
defineProps({
|
||||||
name: 'ReportsFiltersInboxes',
|
multiple: {
|
||||||
emits: ['inboxFilterSelection'],
|
type: Boolean,
|
||||||
data() {
|
default: false,
|
||||||
return {
|
|
||||||
selectedOption: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters({
|
|
||||||
options: 'inboxes/getInboxes',
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$store.dispatch('inboxes/get');
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleInput() {
|
|
||||||
this.$emit('inboxFilterSelection', this.selectedOption);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['inboxFilterSelection']);
|
||||||
|
const store = useStore();
|
||||||
|
const options = useMapGetter('inboxes/getInboxes');
|
||||||
|
|
||||||
|
const selectedOption = ref(null);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
store.dispatch('inboxes/get');
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleInput = () => {
|
||||||
|
emit('inboxFilterSelection', selectedOption.value);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -33,6 +33,7 @@ export default {
|
|||||||
:placeholder="$t('INBOX_REPORTS.FILTER_DROPDOWN_LABEL')"
|
:placeholder="$t('INBOX_REPORTS.FILTER_DROPDOWN_LABEL')"
|
||||||
label="name"
|
label="name"
|
||||||
track-by="id"
|
track-by="id"
|
||||||
|
:multiple="multiple"
|
||||||
:options="options"
|
:options="options"
|
||||||
:option-height="24"
|
:option-height="24"
|
||||||
:show-labels="false"
|
:show-labels="false"
|
||||||
|
|||||||
+17
-1
@@ -55,11 +55,27 @@ describe('ReportsFiltersInboxes.vue', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const selectedInbox = { id: 1, name: 'Inbox 1' };
|
const selectedInbox = { id: 1, name: 'Inbox 1' };
|
||||||
await wrapper.setData({ selectedOption: selectedInbox });
|
wrapper.vm.selectedOption = selectedInbox;
|
||||||
|
|
||||||
await wrapper.vm.handleInput();
|
await wrapper.vm.handleInput();
|
||||||
|
|
||||||
expect(wrapper.emitted('inboxFilterSelection')).toBeTruthy();
|
expect(wrapper.emitted('inboxFilterSelection')).toBeTruthy();
|
||||||
expect(wrapper.emitted('inboxFilterSelection')[0]).toEqual([selectedInbox]);
|
expect(wrapper.emitted('inboxFilterSelection')[0]).toEqual([selectedInbox]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('passes the correct "multiple" prop to multiselect component', () => {
|
||||||
|
const wrapper = shallowMount(ReportsFiltersInboxes, {
|
||||||
|
global: {
|
||||||
|
plugins: [store],
|
||||||
|
...mountParams.global,
|
||||||
|
},
|
||||||
|
propsData: {
|
||||||
|
multiple: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const multiselect = wrapper.findComponent({ name: 'multiselect' });
|
||||||
|
const attributes = multiselect.attributes();
|
||||||
|
expect(attributes.multiple).toBe('true');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user