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_page_channel',
|
||||
'settings_integrations_dashboard_apps',
|
||||
'settings_integrations_captain',
|
||||
'settings_integrations_integration',
|
||||
'settings_integrations_slack',
|
||||
'settings_integrations_webhook',
|
||||
|
||||
@@ -16,6 +16,7 @@ const FEATURE_HELP_URLS = {
|
||||
message_reply_to: 'https://chwt.app/hc/reply-to',
|
||||
reports: 'https://chwt.app/hc/reports',
|
||||
sla: 'https://chwt.app/hc/sla',
|
||||
captain: 'https://chwt.app/hc/captain',
|
||||
team_management: 'https://chwt.app/hc/teams',
|
||||
webhook: 'https://chwt.app/hc/webhooks',
|
||||
};
|
||||
|
||||
@@ -5,10 +5,22 @@
|
||||
"LEARN_MORE": "Learn more about integrations",
|
||||
"LOADING": "Fetching integrations",
|
||||
"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.",
|
||||
"HEADER_BTN_TXT": "Connect new inbox",
|
||||
"CLICK_HERE_TO_CONFIGURE": "Click here to configure",
|
||||
"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": {
|
||||
"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 DashboardApps from './DashboardApps/Index.vue';
|
||||
import Slack from './Slack.vue';
|
||||
import Captain from './Captain/Index.vue';
|
||||
import SettingsContent from '../Wrapper.vue';
|
||||
|
||||
export default {
|
||||
@@ -38,6 +39,14 @@ export default {
|
||||
permissions: ['administrator'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'captain',
|
||||
component: Captain,
|
||||
name: 'settings_integrations_captain',
|
||||
meta: {
|
||||
permissions: ['administrator'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
+23
-22
@@ -1,27 +1,27 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
<script setup>
|
||||
// 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 {
|
||||
name: 'ReportsFiltersInboxes',
|
||||
emits: ['inboxFilterSelection'],
|
||||
data() {
|
||||
return {
|
||||
selectedOption: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
options: 'inboxes/getInboxes',
|
||||
}),
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('inboxes/get');
|
||||
},
|
||||
methods: {
|
||||
handleInput() {
|
||||
this.$emit('inboxFilterSelection', this.selectedOption);
|
||||
},
|
||||
defineProps({
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
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>
|
||||
|
||||
@@ -33,6 +33,7 @@ export default {
|
||||
:placeholder="$t('INBOX_REPORTS.FILTER_DROPDOWN_LABEL')"
|
||||
label="name"
|
||||
track-by="id"
|
||||
:multiple="multiple"
|
||||
:options="options"
|
||||
:option-height="24"
|
||||
:show-labels="false"
|
||||
|
||||
+17
-1
@@ -55,11 +55,27 @@ describe('ReportsFiltersInboxes.vue', () => {
|
||||
});
|
||||
|
||||
const selectedInbox = { id: 1, name: 'Inbox 1' };
|
||||
await wrapper.setData({ selectedOption: selectedInbox });
|
||||
wrapper.vm.selectedOption = selectedInbox;
|
||||
|
||||
await wrapper.vm.handleInput();
|
||||
|
||||
expect(wrapper.emitted('inboxFilterSelection')).toBeTruthy();
|
||||
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