Compare commits

...
Author SHA1 Message Date
Pranav 0003ded9fe fix spec 2024-12-09 15:42:37 -08:00
Pranav 087f74bb11 Merge branch 'develop' into feat/captain-inbox-selector 2024-12-09 15:20:21 -08:00
Shivam Mishra e303a33cd2 fix: button styles 2024-08-14 16:27:17 +05:30
Shivam Mishra de3084572a fix: v-if toggle for captain 2024-08-14 16:14:29 +05:30
Shivam Mishra cbb4c29bf3 feat: add empty state for captain 2024-08-14 16:13:57 +05:30
Shivam Mishra 6fda73c609 feat: revert NewHook 2024-08-14 13:44:11 +05:30
Shivam Mishra 76ee84fa09 feat: kickstart new page for captain 2024-08-14 13:43:22 +05:30
Shivam Mishra 1e9d3d0a0e refactor: separate layout component 2024-08-14 13:23:47 +05:30
Shivam Mishra 95cb7cf2c8 Merge branch 'develop' of github.com:chatwoot/chatwoot into feat/captain-inbox-selector 2024-08-14 13:15:31 +05:30
Shivam Mishra 9823a00cd7 style: no need to import defineEmits
it is a compiler macro, and does not need importing
2024-08-14 11:00:36 +05:30
Shivam Mishra 0d7cec57f1 chore: add todo 2024-08-12 14:30:10 +05:30
Shivam Mishra c4b48edbe8 feat: add inbox_ids to captain integration payload 2024-08-12 14:29:57 +05:30
Shivam Mishra d8e89cc713 feat: add isCaptainCheck 2024-08-12 13:04:35 +05:30
Shivam Mishra b97be238cb chore: remove comment 2024-08-07 20:07:32 +05:30
Shivam Mishra 5892bf2caf test: multiple prop in inbox filter 2024-08-07 20:04:49 +05:30
Shivam Mishra b103e89224 feat: remove inbox_ids from captain schema 2024-08-07 20:01:59 +05:30
Shivam Mishra 3530404534 feat: allow multiple prop in inbox filters 2024-08-07 20:01:07 +05:30
Shivam Mishra c6fff300db feat: rename mapGetter to useMapGetter 2024-08-07 19:59:52 +05:30
Shivam Mishra 7898af32b0 feat: check installation config instead of just ENV 2024-08-07 19:02:41 +05:30
10 changed files with 191 additions and 24 deletions
@@ -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",
@@ -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>
@@ -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'],
},
},
],
},
{
@@ -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"
@@ -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');
});
});